<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Doma's Blog RSS Feed]]></title><description><![CDATA[Where Doma posts blogs and weeklies.]]></description><link>https://doma.land</link><generator>GatsbyJS</generator><lastBuildDate>Tue, 26 Dec 2023 16:48:54 GMT</lastBuildDate><item><title><![CDATA[23w49-51]]></title><description><![CDATA[这三周新收藏的歌，有三首。]]></description><link>https://doma.land/weekly/23w49-51/</link><guid isPermaLink="false">https://doma.land/weekly/23w49-51/</guid><pubDate>Sun, 24 Dec 2023 16:47:57 GMT</pubDate><content:encoded>&lt;p&gt;这三周新收藏的歌，有三首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;听歌&lt;/h2&gt;
&lt;p&gt;最近在看《Legal High》，也去找了原声带来听。
《justice》很动人心弦所以点了收藏，看了评论才发现原来我原来收藏过作者林ゆうき的另一首曲子《cocoon》，想来风格确实有些相似。&lt;/p&gt;
&lt;p&gt;之后日推又刷到了《女神の KISS》，才发现原来它并不在原创音乐专辑里。
前奏一响起，秘书说着“看来，真正能打败古美门的，注定只有三木先生啊”的画面感就跃然眼前。&lt;/p&gt;
&lt;p&gt;不过这几首收藏都无法播放，于是点一首以前收藏的，同样来自林ゆうき的《cocoon》。&lt;/p&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;p&gt;我的项目又进行了一次阶段性的 demo，这次面向了一些 target user，并且也在非生产环境上线了。
接下来要做一些 pilot adoption，争取一月底之前能出现一些 successful case。&lt;/p&gt;
&lt;p&gt;公司举行了年会，不过我什么都没抽中。&lt;/p&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;p&gt;大多数时候天气很冷，有时阴云密布甚至下雨，让人提不起劲。
少数时候晴天有阳光。&lt;/p&gt;
&lt;p&gt;看了《Legal High》第二季。看了《模仿犯》。看了《河边的错误》。看了《Undoing》。
现在在看《三体》。&lt;/p&gt;
&lt;p&gt;车子被追尾一次，轮胎扎钉子一次。&lt;/p&gt;
&lt;p&gt;冬至吃了饺子。
平安夜和女朋友去吃了铁板烧，帝王蟹好吃。
最近体重上升了 2kg，吃饱了才好过冬。&lt;/p&gt;
&lt;p&gt;想放假出去玩。&lt;/p&gt;
&lt;h2&gt;This weekly&lt;/h2&gt;
&lt;p&gt;写 weekly 已有一年出头了，这期间我的生活也发生了大大小小的变化，包括换了工作。
现在的工作相较原来更加忙碌，在休息日我也需要更加充分地休息和放松。
在周末写 weekly 这件事渐渐地变得不太契合现在的工作生活节奏，于是我决定暂停更新 weekly。
感谢至今以来阅读过我的 weekly，向我留言，以及因为 weekly 而关注我的读者。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;开启新节奏。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w48]]></title><description><![CDATA[本周没有新收藏的歌。]]></description><link>https://doma.land/weekly/23w48/</link><guid isPermaLink="false">https://doma.land/weekly/23w48/</guid><pubDate>Sun, 03 Dec 2023 21:20:36 GMT</pubDate><content:encoded>&lt;p&gt;本周没有新收藏的歌。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;p&gt;这周正在做产品上的一个新功能。
其中的一个界面将会通过 Module Federation 以组件的形式提供给其他团队使用。&lt;/p&gt;
&lt;h2&gt;开源和 Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;周二的晚上更新了上周的 weekly，推送后 Vercel 却没有自动构建，amend 提交后重新推送也没有反应，不知道是哪一方出了问题。
只好临时用 Vercel CLI 进行了部署。
第二天看到了 Vercel 的 &lt;a href=&quot;https://www.vercel-status.com/incidents/3cgg0wbzpdfw&quot;&gt;incident&lt;/a&gt; 记录。&lt;/li&gt;
&lt;li&gt;Runaway 动态图片的演示效果有点酷 &lt;a href=&quot;https://twitter.com/0xgaut/status/1729177927445860374&quot;&gt;https://twitter.com/0xgaut/status/1729177927445860374&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;建了一个仓库 &lt;a href=&quot;https://github.com/SevenOutman/apollo-practice&quot;&gt;apollo-practice&lt;/a&gt; 来练习使用 Apollo Server 作为 BFF 的一些实践。
这周学到了：
&lt;ul&gt;
&lt;li&gt;使用 &lt;code&gt;json-rpc-2.0&lt;/code&gt; 库建立 JSON RPC 服务端和客户端&lt;/li&gt;
&lt;li&gt;使用 &lt;code&gt;@grpc/grpc-js&lt;/code&gt; 库建立 gRPC 服务端和客户端&lt;/li&gt;
&lt;li&gt;使用 mermaid 画一个简单的流程图&lt;/li&gt;
&lt;li&gt;使用 OpenTelemetry SDK 进行服务间 tracing&lt;/li&gt;
&lt;li&gt;通过 winston custom format 来限制输出日志的内容和格式&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;这周尝试用 OpenTelemetry SDK 来 trace 服务之间的调用，按照文档为上下游服务都添加了 instrumentation 但是却总是两个不同的 trace id。
查来查去原来是 OpenTelemetry SDK 现在还不支持 instrument Fetch API。改为使用 axios (基于 node:http) 去调上游服务就能正常地 trace 了。
相关 &lt;a href=&quot;https://github.com/open-telemetry/opentelemetry-js/discussions/3346&quot;&gt;discussion&lt;/a&gt; 和 &lt;a href=&quot;https://github.com/open-telemetry/opentelemetry-js/issues/1315&quot;&gt;issue&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;Prettier CLI 新版本正在预览，据说会带来显著的性能提升 &lt;a href=&quot;https://prettier.io/blog/2023/11/30/cli-deep-dive&quot;&gt;https://prettier.io/blog/2023/11/30/cli-deep-dive&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;本周加入 GitHub star 的一些有意思的仓库
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/rcbyr/keen-slider&quot;&gt;&lt;code&gt;keen-slider&lt;/code&gt;&lt;/a&gt; 一个顺滑的滑动组件&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;p&gt;完全晚睡的一周。每天的早晨都匆匆忙忙。&lt;/p&gt;
&lt;p&gt;看完了《Legal High》第一季。
看了《情书》。&lt;/p&gt;
&lt;p&gt;周日天气不错，散步到附近的公园晒了晒太阳。&lt;/p&gt;
&lt;p&gt;想放假出去玩。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;周末过得很快的一周。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w47]]></title><description><![CDATA[本周新收藏的歌，有三首。]]></description><link>https://doma.land/weekly/23w47/</link><guid isPermaLink="false">https://doma.land/weekly/23w47/</guid><pubDate>Tue, 28 Nov 2023 22:41:15 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌，有三首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;听歌&lt;/h2&gt;
&lt;p&gt;第一次听到这首《Neon Love》是在两周前，当时并没有收藏，后来又在日推中刷到两遍，终于还是被这奇妙的迪斯科洗脑了。&lt;/p&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;这周参考同事的建议尝试了下 &lt;code&gt;nice-grpc&lt;/code&gt;，比直接 &lt;code&gt;@grpc/grpc-js&lt;/code&gt; 真是好用多了。
支持 Promise 风格的方法调用，还导出了将 proto 中定义的各个枚举类型与 JavaScript 字符串字面量相互转换的工具函数。&lt;/li&gt;
&lt;li&gt;之前不太确定在 Apollo server 中，将 dataSource 调用外部 API 获取的数据结构转换为 GraphQL schema 中定义的数据结构这一步应该发生在 resolver 中还是 dataSource 中。
这周看到 &lt;a href=&quot;https://www.apollographql.com/tutorials/fullstack-quickstart/03-connecting-to-data-sources&quot;&gt;Apollo tutorial 中的一个示例&lt;/a&gt;中，这个转换是在 dataSource 中进行的。
这反映 DataSource 的定位即是 GraphQL schema 中所定义的类型实例的来源。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;开源和 Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;这周写 weekly 的时候 Gatsby 启动一直卡在“compile gatsby files“这一步，似乎是缓存的问题，参考 &lt;a href=&quot;https://github.com/gatsbyjs/gatsby/issues/35606#issuecomment-1307011131&quot;&gt;issue&lt;/a&gt; 使用 &lt;code&gt;gatsby clean&lt;/code&gt; 清除缓存就好了。&lt;/li&gt;
&lt;li&gt;Gatsby 并没有停止维护，相反，他们有 2024 roadmap。相关 &lt;a href=&quot;https://github.com/gatsbyjs/gatsby/issues/38696#issuecomment-1817064739&quot;&gt;issue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;rsuite 正在尝试 CSS relative colors，相关 &lt;a href=&quot;https://github.com/rsuite/rsuite/pull/3428&quot;&gt;PR 3428&lt;/a&gt;、&lt;a href=&quot;https://github.com/rsuite/rsuite/pull/3462&quot;&gt;PR 3462&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;rsuite 中的一条 &lt;a href=&quot;https://github.com/rsuite/rsuite/discussions/3467&quot;&gt;discussion&lt;/a&gt; 指出，DatePicker/DateRangePicker 组件上日历视图中显示的周编号不正确，原本应该显示为 2020 年第 53 周的一周显示为了 2021 年第 1 周。
查了下周编号的 wiki，才知道原来一年的第一周被定义为 1 月 4 日所在的周，而不是 1 月 1 日。&lt;/li&gt;
&lt;li&gt;本周加入 GitHub star 的一些有意思的仓库
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/webpro/knip&quot;&gt;&lt;code&gt;knip&lt;/code&gt;&lt;/a&gt; 一个查找 JavaScript/TypeScript 项目中未被使用的文件、依赖、导出的工具&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;p&gt;这周也有早晨起来跑步。
找到了一个比较适合自己的间歇跑频率，心率不再像原来跑步那么高了。&lt;/p&gt;
&lt;p&gt;最近睡得有点晚，要调整一下。
看到一个视频中说，晚上睡不着的时候就穿上衣服拿上钥匙开车出门散心。
听上去很美妙，可对于第二天不得不起床上班的我来说的确有点奢侈。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;没睡够的一周。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w45+46]]></title><description><![CDATA[这两周新收藏的歌，有三首。]]></description><link>https://doma.land/weekly/23w45+46/</link><guid isPermaLink="false">https://doma.land/weekly/23w45+46/</guid><pubDate>Sun, 19 Nov 2023 18:13:37 GMT</pubDate><content:encoded>&lt;p&gt;这两周新收藏的歌，有三首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;听歌&lt;/h2&gt;
&lt;p&gt;日推刷到一首成龙的 City Pop，评论说“你背着兄弟做元气少女”🤣&lt;/p&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;p&gt;这两周工作节奏有点紧张，加了好几次班，这也是为什么上周的 weekly 没能早点完成。&lt;/p&gt;
&lt;p&gt;两周前的 demo 时，暂定了下次的 demo 时间在一个月后的十一月底。
紧接着却来了另一个与之不相干的开发任务。如今两周过去，这个开发任务终于基本完结。
虽然在此过程中也对相关产品、协作方法有了更多的了解，但 demo 的进度却不容乐观。
为此我感到了一些压力。&lt;/p&gt;
&lt;p&gt;幸好这周后半周的工作还算顺利，我也适当地调整了心态，当下感觉还是能比较从容和自信地完成接下来两周的进度。&lt;/p&gt;
&lt;p&gt;和更多的同事认识并一起吃饭了，开心。&lt;/p&gt;
&lt;h2&gt;开源和 Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Gatsby.js 是否停止维护了？有用户表示了担忧，详见&lt;a href=&quot;https://github.com/gatsbyjs/gatsby/issues/38696&quot;&gt;相关 issue&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;tldraw 推出了 &lt;a href=&quot;https://makereal.tldraw.com/&quot;&gt;makereal.tldraw.com&lt;/a&gt;，利用 GPT-4V 将手绘稿转换为可运行的前端界面，其在 X 上发布的&lt;a href=&quot;https://twitter.com/tldraw/status/1724410017963503894&quot;&gt;演示视频&lt;/a&gt;以及其他用户体验的视频效果令人惊叹。&lt;/li&gt;
&lt;li&gt;本周加入 GitHub star 的一些有意思的仓库
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/walmartlabs/json-to-simple-graphql-schema&quot;&gt;&lt;code&gt;json-to-simple-graphql-schema&lt;/code&gt;&lt;/a&gt; 将 JSON 数据转换为 GraphQL schema 的工具。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ritz078/transform&quot;&gt;transform.tools&lt;/a&gt; 一个提供多种语言转换的在线 GUI 工具，其中的 JSON to GraphQL 功能就由上面这个包实现。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/microsoft/inshellisense&quot;&gt;&lt;code&gt;inshellisense&lt;/code&gt;&lt;/a&gt; 为终端提供类似 IDE 的补全交互。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/pnpm/pacquet&quot;&gt;&lt;code&gt;pacquet&lt;/code&gt;&lt;/a&gt; pnpm 团队正在开发的使用 Rust 编写的 Node.js 包管理器。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/flamrdevs/none&quot;&gt;none&lt;/a&gt; 一个 SVG 生成服务，嵌入在 Markdown 中的效果很不错&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;尝试了几次早上起床后下楼跑步，本以为用不了多少时间，结果加上洗澡的时间，比我预期的还是要久一些。早上的时间还是有些紧张。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;和朋友们去吃了一家新开的羊肉炉，天冷了吃个羊肉很暖和。
周末又去了一家面馆，炒腰花很好吃，切得大块处理得又嫩，调味也很鲜。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b7ccbf0a2d4e9d754783e8b04ca26acf/22b9d/%E8%85%B0%E8%8A%B1.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.31645569620254%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAIDAQT/xAAVAQEBAAAAAAAAAAAAAAAAAAACA//aAAwDAQACEAMQAAABxpLN9BEZ/8QAGhAAAwADAQAAAAAAAAAAAAAAAAIDARIUE//aAAgBAQABBQLFXPTY5FcakzeB2Kp//8QAFxEAAwEAAAAAAAAAAAAAAAAAARAhMf/aAAgBAwEBPwEzV//EABURAQEAAAAAAAAAAAAAAAAAAAAh/9oACAECAQE/Aar/xAAgEAACAQMEAwAAAAAAAAAAAAABAgARISIDEjNRYXGR/9oACAEBAAY/ArtM1Dibkag6malPV5yH5KJp1Hkz/8QAHRAAAgEEAwAAAAAAAAAAAAAAAAERITFBUWFxgf/aAAgBAQABPyFqrV0hrRzRF835KYJNa8hLzrRujsgk/9oADAMBAAIAAwAAABDk7//EABcRAQEBAQAAAAAAAAAAAAAAAAERAFH/2gAIAQMBAT8QFQTJzf/EABgRAQEAAwAAAAAAAAAAAAAAAAEAESFB/9oACAECAQE/EA4YcN3/xAAcEAEAAgIDAQAAAAAAAAAAAAABABFBUTFxgaH/2gAIAQEAAT8Qe8BB1DLRaoxOncpg52DJdxwcuUL5z8g9ukO4GBHcLdFz/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;腰花&quot;
        title=&quot;腰花&quot;
        src=&quot;/static/b7ccbf0a2d4e9d754783e8b04ca26acf/828fb/%E8%85%B0%E8%8A%B1.jpg&quot;
        srcset=&quot;/static/b7ccbf0a2d4e9d754783e8b04ca26acf/ff44c/%E8%85%B0%E8%8A%B1.jpg 158w,
/static/b7ccbf0a2d4e9d754783e8b04ca26acf/a6688/%E8%85%B0%E8%8A%B1.jpg 315w,
/static/b7ccbf0a2d4e9d754783e8b04ca26acf/828fb/%E8%85%B0%E8%8A%B1.jpg 630w,
/static/b7ccbf0a2d4e9d754783e8b04ca26acf/0ede0/%E8%85%B0%E8%8A%B1.jpg 945w,
/static/b7ccbf0a2d4e9d754783e8b04ca26acf/3ac88/%E8%85%B0%E8%8A%B1.jpg 1260w,
/static/b7ccbf0a2d4e9d754783e8b04ca26acf/22b9d/%E8%85%B0%E8%8A%B1.jpg 3154w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;周末又玩起了神力科莎，尝试着把最佳路线引导线给关闭了，视线能放得更远去观察赛道而不是盯着引导线，感觉更有代入感了。
不过没有了刹车提示还是有点难掌握刹车的时机。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;逐渐找回状态的两周。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w44]]></title><description><![CDATA[Weekly 更新一整年了 🎉。]]></description><link>https://doma.land/weekly/23w44/</link><guid isPermaLink="false">https://doma.land/weekly/23w44/</guid><pubDate>Sun, 05 Nov 2023 15:35:56 GMT</pubDate><content:encoded>&lt;p&gt;Weekly 更新一整年了 🎉。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;听歌&lt;/h2&gt;
&lt;p&gt;这周也在认真工作，没顾得上尝试新的歌单，所以也没有新收藏的歌。
如果你有喜欢的歌单请为我推荐一二。&lt;/p&gt;
&lt;p&gt;点一首以前收藏的来自 Sports 的《Panama》，夏日海边与爱人兜风的氛围感，很适合开车的时候听。&lt;/p&gt;
&lt;h2&gt;This weekly&lt;/h2&gt;
&lt;p&gt;今年的 weekly 已经更新到第 44 周了，而第一篇是 &lt;a href=&quot;/weekly/22w45&quot;&gt;22w45&lt;/a&gt;，也就是说刚好更新一整年了，时间过得真快。&lt;/p&gt;
&lt;p&gt;回顾了下之前的 weekly，发现这一年其实发生了不少有意思的小事。
如果未曾记录下来，也许很容易就淡忘了，在年底回顾时，又会觉得自己稀里糊涂地度过了没劲的一年吧。&lt;/p&gt;
&lt;p&gt;有点幸运自己去年尝试养成了这样一个记录的习惯。
也感谢 &lt;a href=&quot;https://www.manjusaka.blog/weekly/2022/index.html&quot;&gt;Manjusaka 的 2022 周报&lt;/a&gt;启发了我 weekly 这样的形式。&lt;/p&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;前几周在做的原型，这周的演示比较成功，开心！&lt;/li&gt;
&lt;li&gt;这周对接了一个小需求，了解了 JSON RPC 的用法。之前我在做 iframe 与父 frame 之间通信的方案时，就设计了一套类似的协议，与之相比 JSON RPC 协议中还包含了协议版本信息。&lt;/li&gt;
&lt;li&gt;公司的上班时间提前了半小时，又得更早地起床了…不过还好这段时间以来，早晨的时间都掌握得不错。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;开源和 Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;新的顶级域名 &lt;code&gt;.ing&lt;/code&gt; 现在可以在 &lt;a href=&quot;https://get.ing/&quot;&gt;get.ing&lt;/a&gt; 注册了。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;VS Code 终于要支持在同一个工作区中分窗口了（&lt;a href=&quot;https://github.com/microsoft/vscode/issues/10121#issuecomment-1790316086&quot;&gt;相关 issue&lt;/a&gt;）。
不过从演示视频中来看，目前似乎只支持将编辑器分离出新窗口，要想在新窗口中用终端估计还得等等。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;MSW v2 前段时间发布了（&lt;a href=&quot;https://github.com/mswjs/msw/releases/tag/v2.0.0&quot;&gt;发布说明&lt;/a&gt;），API 全面向 Fetch API 靠拢，最低支持 Node.js 18。
我个人还是很喜欢这个改动的，无论是尽量使用标准 API，还是鼓励及时从停止支持的 Node.js 版本升级。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在 MSW 的 release notes 中看到这么一个带颜色的提示信息，一开始以为是 HTML 实现的，后来查了下发现原来是 GitHub flavored Markdown 从去年开始支持了为 blockquote 添加高亮的功能，&lt;a href=&quot;https://github.com/orgs/community/discussions/16925&quot;&gt;相关 discussion&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0e8c24f8fc3c5eb29331d4963d6d8662/f6a0e/markdown-alert.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 17.088607594936708%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAADABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHIoWQD/8QAFhABAQEAAAAAAAAAAAAAAAAAEQAQ/9oACAEBAAEFAmc//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQAGPwJ//8QAFhABAQEAAAAAAAAAAAAAAAAAAQAR/9oACAEBAAE/IdStl2//2gAMAwEAAgADAAAAEHQf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGBABAQEBAQAAAAAAAAAAAAAAAREAMaH/2gAIAQEAAT8QIdw1HzIqtd//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Markdown Alert&quot;
        title=&quot;Markdown Alert&quot;
        src=&quot;/static/0e8c24f8fc3c5eb29331d4963d6d8662/828fb/markdown-alert.jpg&quot;
        srcset=&quot;/static/0e8c24f8fc3c5eb29331d4963d6d8662/ff44c/markdown-alert.jpg 158w,
/static/0e8c24f8fc3c5eb29331d4963d6d8662/a6688/markdown-alert.jpg 315w,
/static/0e8c24f8fc3c5eb29331d4963d6d8662/828fb/markdown-alert.jpg 630w,
/static/0e8c24f8fc3c5eb29331d4963d6d8662/0ede0/markdown-alert.jpg 945w,
/static/0e8c24f8fc3c5eb29331d4963d6d8662/3ac88/markdown-alert.jpg 1260w,
/static/0e8c24f8fc3c5eb29331d4963d6d8662/f6a0e/markdown-alert.jpg 1812w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;GitHub 现在支持添加多个账户，可以方便地在多个账户之间切换，详见 &lt;a href=&quot;https://github.blog/changelog/2023-11-03-multi-account-support-on-github-com/&quot;&gt;GitHub Blog&lt;/a&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;本周加入 GitHub star 的一些有意思的仓库&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/gristlabs/grist-core&quot;&gt;grist-core&lt;/a&gt; 开源的关系型电子表格应用。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;p&gt;周六晚上回到家很想喝一瓶养乐多，打开冰箱时才发觉自己似乎很久没有想要喝饮料的心情并且因为喝到而开心了。
看来近期是真的有些精神紧张。&lt;/p&gt;
&lt;p&gt;最近给好多人的朋友圈点了赞，大多是旅游和运动的内容。
平等地羡慕每一个拥有让自己充满能量的事情的人。
又或许是因为这两件其实都是我想做的事，而我在宣泄自己因为种种原因无法尽力地追求它们的烦闷。&lt;/p&gt;
&lt;p&gt;这段时间的天气也令人不那么舒适。
时节已经到了十一月，气温却一天比一天高，每天穿的衣服厚度都跟前一天不一样。
周日的中午已经接近三十度，到了傍晚却忽然狂风大作，气温骤降。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b5e6b6eb9b7c873b21c150acda3c8902/72e01/%E9%99%8D%E6%B8%A9.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 27.848101265822784%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAGABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAL/2gAMAwEAAhADEAAAAc2CpoH/xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAEFAn//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAYEAACAwAAAAAAAAAAAAAAAAAAARDR4f/aAAgBAQAGPwJ1GH//xAAbEAABBAMAAAAAAAAAAAAAAAABABARITFBUf/aAAgBAQABPyGe9MBrKf/aAAwDAQACAAMAAAAQ/wD/AP/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABsQAQACAgMAAAAAAAAAAAAAAAERIQBxMWGR/9oACAEBAAE/EELoFLs82ZUytnFYIJg9DP/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;降温&quot;
        title=&quot;降温&quot;
        src=&quot;/static/b5e6b6eb9b7c873b21c150acda3c8902/828fb/%E9%99%8D%E6%B8%A9.jpg&quot;
        srcset=&quot;/static/b5e6b6eb9b7c873b21c150acda3c8902/ff44c/%E9%99%8D%E6%B8%A9.jpg 158w,
/static/b5e6b6eb9b7c873b21c150acda3c8902/a6688/%E9%99%8D%E6%B8%A9.jpg 315w,
/static/b5e6b6eb9b7c873b21c150acda3c8902/828fb/%E9%99%8D%E6%B8%A9.jpg 630w,
/static/b5e6b6eb9b7c873b21c150acda3c8902/0ede0/%E9%99%8D%E6%B8%A9.jpg 945w,
/static/b5e6b6eb9b7c873b21c150acda3c8902/72e01/%E9%99%8D%E6%B8%A9.jpg 1024w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;晚上再出门时，街道上的落叶已铺了满地。
我也在这忽冷忽热之间果不其然地感冒了。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;仍未能放松的一周。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w43]]></title><description><![CDATA[本周没有新收藏的歌。]]></description><link>https://doma.land/weekly/23w43/</link><guid isPermaLink="false">https://doma.land/weekly/23w43/</guid><pubDate>Mon, 30 Oct 2023 22:43:12 GMT</pubDate><content:encoded>&lt;p&gt;本周没有新收藏的歌。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;听歌&lt;/h2&gt;
&lt;p&gt;有相当一段时间没有新收藏的歌了。
日推的风格都比较接近，工作投入进去的时候常常注意不到音乐。
也许可以换一些歌单听听了。&lt;/p&gt;
&lt;p&gt;这次点一首以前收藏的来自 Rameses B 的《Moonlight》，需要戴耳机听，浩渺太空般的意境很棒。
其中的念白是 Carl Sagan 为《时间简史》所作的序。&lt;/p&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;这周编写了一篇团队内的指导文章并做了分享，得到了同事的正面评价，开心&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 192px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/95b00994611249641944d15ae73fd938/7809d/worth-read-it.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.54430379746836%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHMgSoP/8QAFhABAQEAAAAAAAAAAAAAAAAAACIB/9oACAEBAAEFApS1/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFxAAAwEAAAAAAAAAAAAAAAAAAAExEP/aAAgBAQAGPwKMjz//xAAaEAACAgMAAAAAAAAAAAAAAAAAARFBIZHh/9oACAEBAAE/IZ6hO2whWD//2gAMAwEAAgADAAAAEH//AP/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABoQAQEAAgMAAAAAAAAAAAAAAAERACExQXH/2gAIAQEAAT8QDeTfeQBUzy4mkohRbvP/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;worth read it&quot;
        title=&quot;worth read it&quot;
        src=&quot;/static/95b00994611249641944d15ae73fd938/7809d/worth-read-it.jpg&quot;
        srcset=&quot;/static/95b00994611249641944d15ae73fd938/ff44c/worth-read-it.jpg 158w,
/static/95b00994611249641944d15ae73fd938/7809d/worth-read-it.jpg 192w&quot;
        sizes=&quot;(max-width: 192px) 100vw, 192px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;完成了一个 proposal 的 demo，过程中学到了 GraphQL server 怎么写，如何通过 port forward 在本地访问 GKE 上的 Pod，如何使用 gRPC 的 &lt;code&gt;.proto&lt;/code&gt; 文件构建 Node.js client。
下周要进行演示了，紧张又期待。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;参加了公司的新员工培训，对整个产品业务的概况加深了理解。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;开源和 Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;DefinitelyTyped 仓库近日转换为了 pnpm monorepo &lt;a href=&quot;https://github.com/DefinitelyTyped/DefinitelyTyped/pull/67085&quot;&gt;https://github.com/DefinitelyTyped/DefinitelyTyped/pull/67085&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;ESLint 将从 v8.53.0 起废弃格式化相关的规则，建议用户使用 Prettier 等专用的格式化工具处理代码格式化。&lt;a href=&quot;https://eslint.org/blog/2023/10/deprecating-formatting-rules/&quot;&gt;https://eslint.org/blog/2023/10/deprecating-formatting-rules/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;本周加入 GitHub star 的一些有意思的仓库
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/huozhi/sugar-high&quot;&gt;&lt;code&gt;sugar-high&lt;/code&gt;&lt;/a&gt; 轻量的 JSX 语法高亮库&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/cartant/eslint-plugin-etc&quot;&gt;&lt;code&gt;eslint-plugin-etc&lt;/code&gt;&lt;/a&gt; 一套关注点奇特的 TypeScript ESLint 规则&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Etesam913/react-magic-motion&quot;&gt;&lt;code&gt;react-magic-motion&lt;/code&gt;&lt;/a&gt; 魔法般的 React 动效库&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/vercel/geist-font&quot;&gt;geist-font&lt;/a&gt; Vercel 最近推出的字体&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ekmas/neobrutalism-components&quot;&gt;neobrutalism-components&lt;/a&gt; 粗野主义 React 组件库&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/azat-io/eslint-plugin-perfectionist&quot;&gt;&lt;code&gt;eslint-plugin-perfectionist&lt;/code&gt;&lt;/a&gt; 一套专注于排序的 ESLint 规则&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;p&gt;没什么值得一提的。&lt;/p&gt;
&lt;p&gt;这几周时间精力有些向工作和学习倾斜了，周末甚至还加了班，精神有些紧绷。
突然发现自己 10 月份的心理状态记录在走下坡路，这可不太妙。但愿扛过这一段能轻松一些。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/28586d8867035fb4bdffe66b69b955d4/5510b/%E5%BF%83%E7%90%86%E7%8A%B6%E6%80%81.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 81.64556962025317%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAQABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAABx4AH/8QAFRABAQAAAAAAAAAAAAAAAAAAESD/2gAIAQEAAQUCa//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABYQAQEBAAAAAAAAAAAAAAAAAAEgMf/aAAgBAQABPyFTtf/aAAwDAQACAAMAAAAQsM//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAACAQUAAAAAAAAAAAAAAAABERAAIUFRYf/aAAgBAQABPxASkPYtiS9iv//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;心理状态&quot;
        title=&quot;心理状态&quot;
        src=&quot;/static/28586d8867035fb4bdffe66b69b955d4/828fb/%E5%BF%83%E7%90%86%E7%8A%B6%E6%80%81.jpg&quot;
        srcset=&quot;/static/28586d8867035fb4bdffe66b69b955d4/ff44c/%E5%BF%83%E7%90%86%E7%8A%B6%E6%80%81.jpg 158w,
/static/28586d8867035fb4bdffe66b69b955d4/a6688/%E5%BF%83%E7%90%86%E7%8A%B6%E6%80%81.jpg 315w,
/static/28586d8867035fb4bdffe66b69b955d4/828fb/%E5%BF%83%E7%90%86%E7%8A%B6%E6%80%81.jpg 630w,
/static/28586d8867035fb4bdffe66b69b955d4/0ede0/%E5%BF%83%E7%90%86%E7%8A%B6%E6%80%81.jpg 945w,
/static/28586d8867035fb4bdffe66b69b955d4/5510b/%E5%BF%83%E7%90%86%E7%8A%B6%E6%80%81.jpg 1179w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;即便如此，当周日的窗外阳光明媚时，仍然会自动地心情舒畅起来。&lt;/p&gt;
&lt;p&gt;周末剪了头发。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;有点累的一周。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w42]]></title><description><![CDATA[本周新收藏的歌，有一首。]]></description><link>https://doma.land/weekly/23w42/</link><guid isPermaLink="false">https://doma.land/weekly/23w42/</guid><pubDate>Sun, 22 Oct 2023 21:26:41 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌，有一首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;p&gt;新团队的工作方式相较以前更加有挑战性一些。
团队氛围很尊重每个人的宝贵时间，因此如果你需要在会议上提出方案并发起讨论，需要提前做好比较充分的准备。
我仍在尽快习惯这样的节奏并提高对自己的要求，希望不久能看到自己的进步。&lt;/p&gt;
&lt;h2&gt;开源和 Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;几周前因为一些时区相关的业务代码而对 Date API 感到生气，于是想要写一个考虑时区的 Date-time API，这周终于有空将它完成了 &lt;a href=&quot;https://github.com/SevenOutman/date-z&quot;&gt;https://github.com/SevenOutman/date-z&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;原来可以直接跟 Midjourney Bot 私聊，并非一定要在服务器中进行。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3022e961d9459a1d57843125351683de/5510b/Midjourney.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100.63291139240506%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAMEAgX/xAAXAQEBAQEAAAAAAAAAAAAAAAABAAID/9oADAMBAAIQAxAAAAGNp6uevLaDZbA9iP/EAB0QAQABBAMBAAAAAAAAAAAAAAECERITIgADMTL/2gAIAQEAAQUChu4x5IpLoCp9TNr2Dkbpe//EABYRAQEBAAAAAAAAAAAAAAAAABABEf/aAAgBAwEBPwHCH//EABcRAAMBAAAAAAAAAAAAAAAAAAABERD/2gAIAQIBAT8BpR5//8QAHRAAAgIBBQAAAAAAAAAAAAAAAAEhMQIQERIiof/aAAgBAQAGPwLs9hzRBLMlyL8IKWn/xAAbEAACAgMBAAAAAAAAAAAAAAABEQAhMUFRYf/aAAgBAQABPyEnoSqEe10FZjgWEMMoD455gbUdQDGt30QXMky3P//aAAwDAQACAAMAAAAQb8h//8QAGBEBAQEBAQAAAAAAAAAAAAAAAQAxEUH/2gAIAQMBAT8QB7IdsTt//8QAFxEBAQEBAAAAAAAAAAAAAAAAAQARIf/aAAgBAgEBPxBY4McQNg5f/8QAHhABAQACAgIDAAAAAAAAAAAAAREAITFBcaFRYYH/2gAIAQEAAT8QIEwBAs0axO0xVN9sJnLuTDFav5P3hGcHOWvi8zGVE6YL7wO7GwHvGWvdY/HnGu5phvP/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Midjourney&quot;
        title=&quot;Midjourney&quot;
        src=&quot;/static/3022e961d9459a1d57843125351683de/828fb/Midjourney.jpg&quot;
        srcset=&quot;/static/3022e961d9459a1d57843125351683de/ff44c/Midjourney.jpg 158w,
/static/3022e961d9459a1d57843125351683de/a6688/Midjourney.jpg 315w,
/static/3022e961d9459a1d57843125351683de/828fb/Midjourney.jpg 630w,
/static/3022e961d9459a1d57843125351683de/0ede0/Midjourney.jpg 945w,
/static/3022e961d9459a1d57843125351683de/5510b/Midjourney.jpg 1179w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;试用了一下 &lt;a href=&quot;https://v0.dev&quot;&gt;v0&lt;/a&gt;，对生成的结果并不是很满意。也许我这个用例比较生僻，或者我的 prompt 不够好。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8818fc81c6a569178f3ad6630e6e957d/d0958/v0.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 29.746835443037973%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAGABQDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAX/xAAWAQEBAQAAAAAAAAAAAAAAAAACAAH/2gAMAwEAAhADEAAAAbIZDL//xAAXEAADAQAAAAAAAAAAAAAAAAAAARIR/9oACAEBAAEFAqKNTP/EABURAQEAAAAAAAAAAAAAAAAAABAR/9oACAEDAQE/Aaf/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAgEBPwGI/8QAFhAAAwAAAAAAAAAAAAAAAAAAABAx/9oACAEBAAY/AlD/xAAZEAADAAMAAAAAAAAAAAAAAAAAAREhMVH/2gAIAQEAAT8hhYjJ4zYH/9oADAMBAAIAAwAAABDwH//EABYRAQEBAAAAAAAAAAAAAAAAAAARAf/aAAgBAwEBPxCl1//EABYRAQEBAAAAAAAAAAAAAAAAAAABEf/aAAgBAgEBPxDCw//EABsQAAIBBQAAAAAAAAAAAAAAAAABMREhQVHx/9oACAEBAAE/EK1wHs7BMI8XP//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;v0&quot;
        title=&quot;v0&quot;
        src=&quot;/static/8818fc81c6a569178f3ad6630e6e957d/828fb/v0.jpg&quot;
        srcset=&quot;/static/8818fc81c6a569178f3ad6630e6e957d/ff44c/v0.jpg 158w,
/static/8818fc81c6a569178f3ad6630e6e957d/a6688/v0.jpg 315w,
/static/8818fc81c6a569178f3ad6630e6e957d/828fb/v0.jpg 630w,
/static/8818fc81c6a569178f3ad6630e6e957d/0ede0/v0.jpg 945w,
/static/8818fc81c6a569178f3ad6630e6e957d/3ac88/v0.jpg 1260w,
/static/8818fc81c6a569178f3ad6630e6e957d/d0958/v0.jpg 3858w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;本周加入 GitHub star 的一些有意思的仓库&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/shoelace-style/shoelace&quot;&gt;&lt;code&gt;shoelace&lt;/code&gt;&lt;/a&gt; 一套基于 Web Component 的组件库，马下来作为今后可能提供框架无关的 UI SDK 的参考&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/kucherenko/jscpd&quot;&gt;&lt;code&gt;jscpd&lt;/code&gt;&lt;/a&gt; 一个查找项目中重复代码片段的工具，马下来改进旧代码用&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/signavio/react-mentions&quot;&gt;&lt;code&gt;react-mentions&lt;/code&gt;&lt;/a&gt; 一个实现文本框中 at 人功能的 React 组件库，马下来作为灵感&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;这周睡眠好转了一些，工作日起床时基本上睡眠都是充足的。
现在的通勤时间比原来变长了许多，于是在通勤路上开始看起了书，每天上下班一个来回也能读不少。&lt;/li&gt;
&lt;li&gt;买了一条新的表带。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;认真工作学习的一周。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w41]]></title><description><![CDATA[本周也没有新收藏的歌。]]></description><link>https://doma.land/weekly/23w41/</link><guid isPermaLink="false">https://doma.land/weekly/23w41/</guid><pubDate>Mon, 16 Oct 2023 21:07:57 GMT</pubDate><content:encoded>&lt;p&gt;本周也没有新收藏的歌。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;p&gt;本周入职了新的公司。
这一周以来都在学习新团队的工作内容和方法，
团队的每位同事都很友善，一一为我介绍了各自负责的部分。
Leader 为我计划的接下来的工作内容也令我很感兴趣，我对新的工作充满期待。&lt;/p&gt;
&lt;h2&gt;开源和 Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;由于不再能够继续在上班时间做 rsuite 的内容，我想接下来我应该无法有充足的时间和精力来持续维护 rsuite 了。&lt;/li&gt;
&lt;li&gt;本周加入 GitHub star 的一些有意思的仓库
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/graphql/dataloader&quot;&gt;&lt;code&gt;dataloader&lt;/code&gt;&lt;/a&gt; 把多个单体查询合并成一个列表查询，可用于解决 GraphQL 的 N+1 问题。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/vercel/micro&quot;&gt;&lt;code&gt;micro&lt;/code&gt;&lt;/a&gt; 一个极简的、使用标准 API 的微服务框架。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;p&gt;还有些不习惯新的通勤时间，这周都没太睡好。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;新的开始。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w40]]></title><description><![CDATA[本周没有新收藏的歌。]]></description><link>https://doma.land/weekly/23w40/</link><guid isPermaLink="false">https://doma.land/weekly/23w40/</guid><pubDate>Mon, 09 Oct 2023 23:20:40 GMT</pubDate><content:encoded>&lt;p&gt;本周没有新收藏的歌。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;p&gt;和女朋友去了武汉玩。
生烫牛肉粉吃了，
藕粉吃了，
爷爷不泡茶喝了，
小龙虾吃了，
烧麦吃了，
面窝吃了，
鸡冠饺吃了，
蛋酒喝了，
糊米酒喝了，
三鲜豆皮吃了，
藕汤喝了，
牛杂锅吃了，
黄鹤楼看了，
热干面吃了，
沪渝高速堵了。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;放假真好，还想放假。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w39]]></title><description><![CDATA[本周新收藏的歌，有一首。]]></description><link>https://doma.land/weekly/23w39/</link><guid isPermaLink="false">https://doma.land/weekly/23w39/</guid><pubDate>Sun, 01 Oct 2023 13:50:53 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌，有一首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;听歌&lt;/h2&gt;
&lt;p&gt;日推刷到一首《SWEET MEMORIES》的翻唱，vocal 的声线怎么听都是 ikura，点开一看还真的是。&lt;/p&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;p&gt;本周二离职了。&lt;/p&gt;
&lt;p&gt;上周末已经把工位上大部分东西带回家了，只留下了这周一还要用的电脑、显示器、键盘、触控板。
但忘记留下耳机了，没法听歌，仅仅是摸鱼也感觉真漫长。&lt;/p&gt;
&lt;p&gt;吃过晚饭后，又回到公司来把工位上最后几件东西也收走了。
看着空空如也的工位还是有点感慨。&lt;/p&gt;
&lt;p&gt;节后才入职新公司，在那之前就好好放个假吧。&lt;/p&gt;
&lt;h2&gt;开源和 Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;看了&lt;a href=&quot;https://youtu.be/pQHX-SjgQvQ?si=UMenJq5VkiuxcEXk&quot;&gt;《Medieval Helpdesk》&lt;/a&gt;，确实幽默又形象地表现了可用性设计的难题&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;给车子加油，这次去了一家看起来有点高级的加油站。
没有立式的加油机，而是从天花板悬挂下来的加油枪。
有一个巨大的立面用数码屏显示着各个油枪的油品价格和加油量、金额。
工作人员看上去也很年轻，态度也很热情。
整体体验很好（也很新奇），女朋友说以后都来这加。&lt;/p&gt;
&lt;p&gt;没来得及拍照，从网上找了一张图。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c3566cc4971bf50538a4ca722f81c54d/47311/%E7%AC%AC%E4%B8%80%E5%8A%A0%E6%B2%B9%E7%AB%99.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.32911392405063%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAIFBP/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHW01yoTSz/xAAbEAACAgMBAAAAAAAAAAAAAAAAAQIRAwQQFP/aAAgBAQABBQL0wQ9lCzQquUf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAZEAACAwEAAAAAAAAAAAAAAAAAASAhIjH/2gAIAQEABj8CXTJbh//EABsQAAMBAQADAAAAAAAAAAAAAAABEVEhQWFx/9oACAEBAAE/IU9r6PCVNcujCOD+EOcIsRGH/9oADAMBAAIAAwAAABC7H//EABYRAQEBAAAAAAAAAAAAAAAAAAARAf/aAAgBAwEBPxCJj//EABURAQEAAAAAAAAAAAAAAAAAAAAh/9oACAECAQE/EKr/xAAcEAEBAAICAwAAAAAAAAAAAAABEQAhMUFhcYH/2gAIAQEAAT8QrlTtZmW1d4EfjswMChRfOKqDNmHHo9Z4Wf/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;第一加油站&quot;
        title=&quot;第一加油站&quot;
        src=&quot;/static/c3566cc4971bf50538a4ca722f81c54d/828fb/%E7%AC%AC%E4%B8%80%E5%8A%A0%E6%B2%B9%E7%AB%99.jpg&quot;
        srcset=&quot;/static/c3566cc4971bf50538a4ca722f81c54d/ff44c/%E7%AC%AC%E4%B8%80%E5%8A%A0%E6%B2%B9%E7%AB%99.jpg 158w,
/static/c3566cc4971bf50538a4ca722f81c54d/a6688/%E7%AC%AC%E4%B8%80%E5%8A%A0%E6%B2%B9%E7%AB%99.jpg 315w,
/static/c3566cc4971bf50538a4ca722f81c54d/828fb/%E7%AC%AC%E4%B8%80%E5%8A%A0%E6%B2%B9%E7%AB%99.jpg 630w,
/static/c3566cc4971bf50538a4ca722f81c54d/0ede0/%E7%AC%AC%E4%B8%80%E5%8A%A0%E6%B2%B9%E7%AB%99.jpg 945w,
/static/c3566cc4971bf50538a4ca722f81c54d/47311/%E7%AC%AC%E4%B8%80%E5%8A%A0%E6%B2%B9%E7%AB%99.jpg 1080w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;这次坐高铁回家路上没有像往常一样手机、电脑翻来覆去地玩了。
带上了最近在读的小说在路上看，感觉很放松。&lt;/p&gt;
&lt;p&gt;回到家当晚，趁着睡前把剩余的部分读完了，结局怎么这么刀。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c7279f1cb07d38839b824f5192cc57be/1abae/%E6%AF%95%E4%B8%9A.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 103.79746835443038%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAVABQDASIAAhEBAxEB/8QAGQABAAMBAQAAAAAAAAAAAAAAAAIDBAEF/8QAFwEAAwEAAAAAAAAAAAAAAAAAAAECA//aAAwDAQACEAMQAAAB9Pka5NKB1TmESE6f/8QAGhAAAgMBAQAAAAAAAAAAAAAAAQIAERIDEP/aAAgBAQABBQKKb8siaa4xjk5Tocf/xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAwEBPwFYT//EABURAQEAAAAAAAAAAAAAAAAAABEg/9oACAECAQE/ASP/xAAYEAACAwAAAAAAAAAAAAAAAAAAARAgIf/aAAgBAQAGPwKcqj//xAAbEAADAAIDAAAAAAAAAAAAAAAAAREhMUFhcf/aAAgBAQABPyGvgazpR6gbdEl6Ugh/ZR9GaZP/2gAMAwEAAgADAAAAENzH/P/EABcRAQEBAQAAAAAAAAAAAAAAAAEAERD/2gAIAQMBAT8QweAWwv/EABcRAQEBAQAAAAAAAAAAAAAAAAERABD/2gAIAQIBAT8QtbeKzV3/xAAbEAEAAgMBAQAAAAAAAAAAAAABABEhMUEQUf/aAAgBAQABPxBgMiUKWPfGsFfvJgSbmqYhYs0xLSYufDcTo3ABRVtn/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;毕业&quot;
        title=&quot;毕业&quot;
        src=&quot;/static/c7279f1cb07d38839b824f5192cc57be/828fb/%E6%AF%95%E4%B8%9A.jpg&quot;
        srcset=&quot;/static/c7279f1cb07d38839b824f5192cc57be/ff44c/%E6%AF%95%E4%B8%9A.jpg 158w,
/static/c7279f1cb07d38839b824f5192cc57be/a6688/%E6%AF%95%E4%B8%9A.jpg 315w,
/static/c7279f1cb07d38839b824f5192cc57be/828fb/%E6%AF%95%E4%B8%9A.jpg 630w,
/static/c7279f1cb07d38839b824f5192cc57be/0ede0/%E6%AF%95%E4%B8%9A.jpg 945w,
/static/c7279f1cb07d38839b824f5192cc57be/1abae/%E6%AF%95%E4%B8%9A.jpg 986w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;放假的一周。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w38]]></title><description><![CDATA[本周没有新收藏的歌。]]></description><link>https://doma.land/weekly/23w38/</link><guid isPermaLink="false">https://doma.land/weekly/23w38/</guid><pubDate>Mon, 25 Sep 2023 14:41:04 GMT</pubDate><content:encoded>&lt;p&gt;本周没有新收藏的歌。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;听歌&lt;/h2&gt;
&lt;p&gt;这周没有新增的收藏，给大家点一首以前收藏的《東京の街》，是一首在炎热的夏天听上去会感到清凉的曲子。
虽然夏天已经过去了。&lt;/p&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;p&gt;本周一边把上一期的工作收尾，一边把下一期的工作交接了给了同事，下周准备离职了。&lt;/p&gt;
&lt;p&gt;周末来公司收拾工位。
很快天色暗了下来，写字楼里几乎没有其他人，也不像平时灯火通明，仿佛你是加班到最后的人。
但抬起手腕看一眼时间，才刚刚 18:30，下班后生活才刚刚开始，这感觉令人真开心。
如果这不是周末而是工作日就好了。&lt;/p&gt;
&lt;h2&gt;开源和 Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;这周修一个跨时区的问题深深感到 JS 的 Date API 确实有些问题。
一个“日期时间描述符”（我这样叫它，例如“2023-09-01 00:00:00”）&lt;strong&gt;在没有时区上下文时&lt;/strong&gt;，是不可以与一个“时刻”（例如时间戳）等效的。
但是 JS 的 Date API 默认将其当作本地时间处理，混淆了这而者，这就带来了各种跨时区的问题。
好在 &lt;a href=&quot;https://tc39.es/proposal-temporal/docs/&quot;&gt;Temporal 提案&lt;/a&gt;正是旨在解决这一问题，期待能早日上线。&lt;/li&gt;
&lt;li&gt;本周加入 GitHub star 的一些有意思的仓库
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.automa.site/&quot;&gt;Automa&lt;/a&gt; 一个浏览器自动化扩展，感觉可以作为基础改出很丰富的日常工作流程。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;换 iPhone 15 Pro 啦。手感比原来的 13 Pro 润多了，而且轻了一些，总得来说很舒服。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c20124a832b969ff91d9fa6d95246083/9568a/iPhone15Pro.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGgABAAIDAQAAAAAAAAAAAAAAAAEDAgQFBv/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAG6bOKejYImgNkH/8QAHBABAAICAwEAAAAAAAAAAAAAAgADARIEEBET/9oACAEBAAEFArrkJx7vp0ztghlle4muyn//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/AR//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/AR//xAAbEAABBAMAAAAAAAAAAAAAAAABABARIgIxMv/aAAgBAQAGPwKuMqD09ReXIOg3/8QAHhABAAICAQUAAAAAAAAAAAAAAQARMUEhEFGBodH/2gAIAQEAAT8hJPMdSm8Z9nQ1vG/sT3UwNSjeL3XeEAqXEQAKCif/2gAMAwEAAgADAAAAEHjPgv/EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQMBAT8QH//EABYRAQEBAAAAAAAAAAAAAAAAAAEQMf/aAAgBAgEBPxDSM//EAB8QAQACAgICAwAAAAAAAAAAAAEAESExQVGBsWFxkf/aAAgBAQABPxAOFNi2F1X5Dxs3WvmPqZgcRRsa68PVwO1FkCO11T6gIaQKsKZB5gKlBacuPPcNCDgJ/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;iPhone 15 Pro&quot;
        title=&quot;iPhone 15 Pro&quot;
        src=&quot;/static/c20124a832b969ff91d9fa6d95246083/828fb/iPhone15Pro.jpg&quot;
        srcset=&quot;/static/c20124a832b969ff91d9fa6d95246083/ff44c/iPhone15Pro.jpg 158w,
/static/c20124a832b969ff91d9fa6d95246083/a6688/iPhone15Pro.jpg 315w,
/static/c20124a832b969ff91d9fa6d95246083/828fb/iPhone15Pro.jpg 630w,
/static/c20124a832b969ff91d9fa6d95246083/0ede0/iPhone15Pro.jpg 945w,
/static/c20124a832b969ff91d9fa6d95246083/3ac88/iPhone15Pro.jpg 1260w,
/static/c20124a832b969ff91d9fa6d95246083/9568a/iPhone15Pro.jpg 3024w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《封神》流媒体版上线了，和女朋友又在电视上看了一遍。
感想就是电影果然还是得去电影院看，视听效果的减弱让许多画面的感染力都差了不少。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/141195661890fbb29cfd09c0d3b4ff10/0f98f/%E5%B0%81%E7%A5%9E.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.32911392405063%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAEEA//EABQBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAcJokIIT/8QAGhAAAgIDAAAAAAAAAAAAAAAAAQIAEgMEEP/aAAgBAQABBQLWVCmaoMswhYnn/8QAFREBAQAAAAAAAAAAAAAAAAAAECH/2gAIAQMBAT8Bh//EABURAQEAAAAAAAAAAAAAAAAAABAh/9oACAECAQE/Aaf/xAAZEAACAwEAAAAAAAAAAAAAAAAAARAhIjH/2gAIAQEABj8C0kUo6y3H/8QAHBABAAIBBQAAAAAAAAAAAAAAAQAxIRARQXGB/9oACAEBAAE/IRmRtyQYWRuFQvZZj3p//9oADAMBAAIAAwAAABDYD//EABcRAAMBAAAAAAAAAAAAAAAAAAEQESH/2gAIAQMBAT8QOJF//8QAFhEBAQEAAAAAAAAAAAAAAAAAEQEQ/9oACAECAQE/EIKuf//EABoQAQEBAAMBAAAAAAAAAAAAAAERACExUUH/2gAIAQEAAT8Qu2dWl7zeNaM4mdb64gCDqIm+B/q67//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;封神&quot;
        title=&quot;封神&quot;
        src=&quot;/static/141195661890fbb29cfd09c0d3b4ff10/828fb/%E5%B0%81%E7%A5%9E.jpg&quot;
        srcset=&quot;/static/141195661890fbb29cfd09c0d3b4ff10/ff44c/%E5%B0%81%E7%A5%9E.jpg 158w,
/static/141195661890fbb29cfd09c0d3b4ff10/a6688/%E5%B0%81%E7%A5%9E.jpg 315w,
/static/141195661890fbb29cfd09c0d3b4ff10/828fb/%E5%B0%81%E7%A5%9E.jpg 630w,
/static/141195661890fbb29cfd09c0d3b4ff10/0ede0/%E5%B0%81%E7%A5%9E.jpg 945w,
/static/141195661890fbb29cfd09c0d3b4ff10/3ac88/%E5%B0%81%E7%A5%9E.jpg 1260w,
/static/141195661890fbb29cfd09c0d3b4ff10/0f98f/%E5%B0%81%E7%A5%9E.jpg 1920w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;跟女朋友还有朋友去吃了一家马了挺久的牛排锅，好吃。
这家的红烧水鱼也好吃，水鱼口感真奇妙。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/daf725b914022fbb548f2d48a527bf2e/e8a0d/%E9%BB%84%E7%89%9B%E8%82%89.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.31645569620254%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAwAE/8QAFgEBAQEAAAAAAAAAAAAAAAAAAQID/9oADAMBAAIQAxAAAAFASyvTDB//xAAaEAACAwEBAAAAAAAAAAAAAAACAwABERIT/9oACAEBAAEFAjbzKdeisDrBdPDJT8n/xAAVEQEBAAAAAAAAAAAAAAAAAAAAIf/aAAgBAwEBPwGK/8QAFhEBAQEAAAAAAAAAAAAAAAAAABEC/9oACAECAQE/AYuH/8QAHBAAAgICAwAAAAAAAAAAAAAAAREAAgNBEBIh/9oACAEBAAY/Alrjso6lHYjNhFjHgn//xAAaEAADAQEBAQAAAAAAAAAAAAAAAREhMVFh/9oACAEBAAE/IX4WMvRxLGpxo9ufS52XQVYimxUQu3Ks/9oADAMBAAIAAwAAABCzH//EABcRAQADAAAAAAAAAAAAAAAAAAABEUH/2gAIAQMBAT8QgrD/xAAXEQEBAQEAAAAAAAAAAAAAAAABABEh/9oACAECAQE/EFNhxv/EABwQAQEAAgIDAAAAAAAAAAAAAAERACExcUFRgf/aAAgBAQABPxBmFTUpfPRjai7AR+8jhtSt1cysfCTL2Yra/U0dTC/BRIr7z//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;黄牛肉&quot;
        title=&quot;黄牛肉&quot;
        src=&quot;/static/daf725b914022fbb548f2d48a527bf2e/828fb/%E9%BB%84%E7%89%9B%E8%82%89.jpg&quot;
        srcset=&quot;/static/daf725b914022fbb548f2d48a527bf2e/ff44c/%E9%BB%84%E7%89%9B%E8%82%89.jpg 158w,
/static/daf725b914022fbb548f2d48a527bf2e/a6688/%E9%BB%84%E7%89%9B%E8%82%89.jpg 315w,
/static/daf725b914022fbb548f2d48a527bf2e/828fb/%E9%BB%84%E7%89%9B%E8%82%89.jpg 630w,
/static/daf725b914022fbb548f2d48a527bf2e/0ede0/%E9%BB%84%E7%89%9B%E8%82%89.jpg 945w,
/static/daf725b914022fbb548f2d48a527bf2e/3ac88/%E9%BB%84%E7%89%9B%E8%82%89.jpg 1260w,
/static/daf725b914022fbb548f2d48a527bf2e/e8a0d/%E9%BB%84%E7%89%9B%E8%82%89.jpg 3068w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;过得很快的一周。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w37]]></title><description><![CDATA[本周新收藏的歌，有两首。]]></description><link>https://doma.land/weekly/23w37/</link><guid isPermaLink="false">https://doma.land/weekly/23w37/</guid><pubDate>Tue, 19 Sep 2023 12:19:49 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌，有两首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;听歌&lt;/h2&gt;
&lt;p&gt;这周在网易云 ジミーサム P 的专辑列表中怎么也找不到这首 Calc. 的原版，原来是收录在以 Team MOER 的名义发表的专辑中的。
Jimmy 的歌听上去摇滚，其实词都很深情。&lt;/p&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;DevOps 平台 1.7 终于上线了。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Chrome DevTools 里的“传感器”菜单中可以修改当前时区，会影响代码中 Date API 等的时区。可以用来调试一些支持跨时区的功能。&lt;/p&gt;
&lt;p&gt;不会影响开发工具控制台里的 Date API。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f249ff471a9ca95fb15b351e854d9648/d7ab4/ChromeDevTools.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.77215189873418%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABYlAAAWJQFJUiTwAAABDklEQVQoz42QzW7DIBCE/f7P1ksPPbSKosaxQ/APsItjQwxMhWulaepIHWkEK9iPYQttCEI2+Njt0fYKh7JCrxkvr29435dQxCC2MKvZMqy10MSbLgwxDBGy7OjxWUlIdYE0Vwjt0LMDDR4djTB2gh5mtHRFShEhRsQHF2wH2GFYgHzx2B0EaqkgWg3ZEzpzgejyOkKzgxoihJnxTEWmZqWUkNa9tQPqkwARwzmPs2z+NC73N1zM889rMXzHzsDyWEMpjWmaFvgj5L7+lXCew60IK7DvFcpjhao+gZghzhKTczfImnH7y/fADAshIMUE769o2nYBllWNpu2eptoEPs4iw533yOfjOC2z/A/wC3pPby01UZecAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Chrome DevTools&quot;
        title=&quot;Chrome DevTools&quot;
        src=&quot;/static/f249ff471a9ca95fb15b351e854d9648/f058b/ChromeDevTools.png&quot;
        srcset=&quot;/static/f249ff471a9ca95fb15b351e854d9648/c26ae/ChromeDevTools.png 158w,
/static/f249ff471a9ca95fb15b351e854d9648/6bdcf/ChromeDevTools.png 315w,
/static/f249ff471a9ca95fb15b351e854d9648/f058b/ChromeDevTools.png 630w,
/static/f249ff471a9ca95fb15b351e854d9648/40601/ChromeDevTools.png 945w,
/static/f249ff471a9ca95fb15b351e854d9648/d7ab4/ChromeDevTools.png 1014w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;尝试了一下用 Bun 代替 Node.js 作为 CI 流水线的运行环境。
&lt;code&gt;bun install&lt;/code&gt; 在使用 lockfile 和配合缓存的条件下比 &lt;code&gt;npm ci&lt;/code&gt; 还是快了非常多的，
但使用 &lt;code&gt;bunx&lt;/code&gt; 代替 &lt;code&gt;npx&lt;/code&gt; 运行脚本似乎没有什么明显的速度变化。
例如运行 &lt;code&gt;npx prettier&lt;/code&gt; 和 &lt;code&gt;bunx prettier&lt;/code&gt;，或者 &lt;code&gt;npx eslint&lt;/code&gt; 和 &lt;code&gt;bunx eslint&lt;/code&gt;，又或者 &lt;code&gt;bunx jest&lt;/code&gt; 和 &lt;code&gt;npx jest&lt;/code&gt; 的时间都是差不多的。
也许是项目太小了。&lt;/p&gt;
&lt;p&gt;如果直接用 &lt;code&gt;bun test&lt;/code&gt; 来运行 Jest 看起来的确会更快，可惜的是 &lt;a href=&quot;https://github.com/oven-sh/bun/issues/1825&quot;&gt;&lt;code&gt;bun test&lt;/code&gt; 尚未完全兼容 Jest&lt;/a&gt;。
其中很重要的一点是它暂不支持 &lt;code&gt;expect.extend()&lt;/code&gt;，这使得平时常用的 &lt;code&gt;@testing-library/jest-dom&lt;/code&gt; 等 custom matchers 库无法使用。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;开源和 Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;跟群友聊到了 &lt;code&gt;react-query&lt;/code&gt; 以及 &lt;code&gt;swr&lt;/code&gt; 的 API 设计问题，主要是在每次 &lt;code&gt;useSWR&lt;/code&gt;/&lt;code&gt;useQuery&lt;/code&gt; 时都允许为相同 &lt;code&gt;key&lt;/code&gt;/&lt;code&gt;queryKey&lt;/code&gt; 指定不同的 &lt;code&gt;fetcher&lt;/code&gt;/&lt;code&gt;queryFn&lt;/code&gt; 的问题。
这个问题其实我已经抱有很久了，但是一直就暂且那样用了，这次给 &lt;code&gt;swr&lt;/code&gt; 提了 &lt;a href=&quot;https://github.com/vercel/swr/discussions/2786&quot;&gt;RFC&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Vercel 推出了前端开发 AI 工具 &lt;a href=&quot;https://v0.dev&quot;&gt;https://v0.dev&lt;/a&gt; 。
让我印象深刻的一点是其运用了此前 Vercel 推出的各种开源库和特性（或者反过来说，Vercel 在开发 v0 以及其他项目的过程中推出了这些库和特性）。&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;&lt;a href=&quot;https://t.co/IzUlJQAgug&quot;&gt;https://t.co/IzUlJQAgug&lt;/a&gt; is 100% full-stack &lt;a href=&quot;https://twitter.com/vercel?ref_src=twsrc%5Etfw&quot;&gt;@vercel&lt;/a&gt; &lt;br&gt;◆ Built on &lt;a href=&quot;https://twitter.com/nextjs?ref_src=twsrc%5Etfw&quot;&gt;@nextjs&lt;/a&gt; App Router&lt;br&gt;◆ AI by the &lt;a href=&quot;https://twitter.com/vercel?ref_src=twsrc%5Etfw&quot;&gt;@vercel&lt;/a&gt; `ai` SDK (&lt;a href=&quot;https://t.co/4TxKhPCgEQ&quot;&gt;https://t.co/4TxKhPCgEQ&lt;/a&gt;) &lt;br&gt;◆ RSC &amp;amp; SSR Streaming on &lt;a href=&quot;https://twitter.com/vercel?ref_src=twsrc%5Etfw&quot;&gt;@vercel&lt;/a&gt; Functions&lt;br&gt;◆ Mutations via &lt;a href=&quot;https://twitter.com/nextjs?ref_src=twsrc%5Etfw&quot;&gt;@nextjs&lt;/a&gt; Server Actions&lt;br&gt;◆ Data in &lt;a href=&quot;https://twitter.com/vercel?ref_src=twsrc%5Etfw&quot;&gt;@vercel&lt;/a&gt; KV (&lt;a href=&quot;https://t.co/QaYAvfRzvI&quot;&gt;https://t.co/QaYAvfRzvI&lt;/a&gt;)&lt;br&gt;◆ Flags…&lt;/p&gt;&amp;mdash; Guillermo Rauch (@rauchg) &lt;a href=&quot;https://twitter.com/rauchg/status/1702366144513159451?ref_src=twsrc%5Etfw&quot;&gt;September 14, 2023&lt;/a&gt;&lt;/blockquote&gt;
&lt;p&gt;Shadcn 加入 Vercel 想必也与此项目密切相关。
我很欣赏这些公司的这种态度——你开发我需要的东西，我邀请你加入我（而不是那我也造个轮子）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;本周加入 GitHub star 的一些有意思的仓库：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/cptn-io/el-cptn&quot;&gt;&lt;code&gt;el-cptn&lt;/code&gt;&lt;/a&gt; 一个开源的数据 pipeline 管理平台。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/honojs/hono&quot;&gt;&lt;code&gt;hono&lt;/code&gt;&lt;/a&gt; 一个边缘应用开发框架，看上去写起来很简单。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/aidenybai/million&quot;&gt;&lt;code&gt;million.js&lt;/code&gt;&lt;/a&gt; 一个用于提升 React 性能的库，虽然暂时好像还用不到。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/swup/swup&quot;&gt;&lt;code&gt;swup&lt;/code&gt;&lt;/a&gt; 一个多页网站的过渡效果库。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Minecraft&lt;/h2&gt;
&lt;p&gt;之前一直想试着做地狱交通，这周安排上了。&lt;/p&gt;
&lt;p&gt;女朋友去东南方向跑图，最终会抵达我们上次探索过的林地府邸。
我想那不如就把地狱交通的第一站选在林地府邸吧，这样等女朋友回来的时候就可以直接通过地狱交通回来了。&lt;/p&gt;
&lt;p&gt;不过林地府邸的确还是远，挖隧道的过程神镐的耐久都有点吃不消，还好途中挖一挖石英能补回来。
地狱交通需要非常多的蓝冰，一开始我是打算在一片之前找到的&lt;a href=&quot;https://minecraft.fandom.com/zh/wiki/%E9%9B%AA%E5%8E%9F#%E5%86%B0%E5%88%BA%E4%B9%8B%E5%9C%B0&quot;&gt;冰刺之地&lt;/a&gt;挖浮冰合成蓝冰，不过合成的效率非常低。
恰好女朋友跑图跑到一片&lt;a href=&quot;https://minecraft.fandom.com/zh/wiki/%E5%86%B0%E5%86%BB%E6%B7%B1%E6%B5%B7&quot;&gt;冰冻深海&lt;/a&gt;，于是直接过去挖大蓝冰溜子了。
一个大冰溜子就能装满半个背包，感谢大自然的馈赠。&lt;/p&gt;
&lt;p&gt;一开始准备建三格宽的冰道，不过那样需要的蓝冰太多了。
看了一些视频后发现其实只要一格宽的冰道，两边加护栏就可以了，能够节省 2/3 的蓝冰。
正好上次为了盖悦灵住的玻璃屋，女朋友听说需要许多玻璃于是一口气在沙漠挖了两背包沙子，全都烧成了玻璃，盖完玻璃屋还剩下一大堆，这下可以派上用场全做成玻璃板了。
最终顺利打通了连接家和林地府邸的地狱交通冰道，通行只需一分钟。
（一格宽的冰道跑起来有点像单轨列车。）&lt;/p&gt;
&lt;p&gt;女朋友在抵达林地府邸途中还发现林地府邸的附近就有樱花林（当初找樱花林也&lt;a href=&quot;/weekly/23w31&quot;&gt;找了很久&lt;/a&gt;），于是我们打算把林地府邸当作度假别墅。
现在交通也方便了，可以常常往返。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;忙中有乐的一周。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w35+36]]></title><description><![CDATA[这两周新收藏的歌，有四首。]]></description><link>https://doma.land/weekly/23w35+36/</link><guid isPermaLink="false">https://doma.land/weekly/23w35+36/</guid><pubDate>Mon, 11 Sep 2023 22:46:07 GMT</pubDate><content:encoded>&lt;p&gt;这两周新收藏的歌，有四首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;听歌&lt;/h2&gt;
&lt;p&gt;上周和女朋友把《犬夜叉》动画补完了，犬夜叉的配乐原声带带也从其一到其三，以及选集都听了个遍。&lt;/p&gt;
&lt;p&gt;除此之外，本周还新入手一张 CD：C418《Minecraft Volume Alpha》，
是 Minecraft 的游戏原声带。
我最喜欢的曲目是《Living Mice》，是我觉得 Minecraft 的 BGM 中最具有无人世界的孤独感的一首。
虽然不在本周新收藏之列，但也给大家点上。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/051f11a7ba2cd3bcfba7b1d6515b2195/47311/minecraft.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 105.69620253164558%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAVABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIEBf/EABcBAAMBAAAAAAAAAAAAAAAAAAABAwL/2gAMAwEAAhADEAAAAdk5smH20mZ5AsTt/8QAHRAAAgIBBQAAAAAAAAAAAAAAAQIAAxEQEhMiMf/aAAgBAQABBQJnCnlXS63YwOZmGEdfJ//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQMBAT8BH//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8BH//EABsQAAIBBQAAAAAAAAAAAAAAAAEQABEhQVFx/9oACAEBAAY/AqGZQsD2bLK//8QAHBABAAIDAAMAAAAAAAAAAAAAAQARITFBEGHh/9oACAEBAAE/IRyNvqJ/Hhql4gKUKwCWQjsGBiATOoHHcz//2gAMAwEAAgADAAAAEKzfQv/EABcRAQEBAQAAAAAAAAAAAAAAABEBECH/2gAIAQMBAT8QYB3Jn//EABcRAQADAAAAAAAAAAAAAAAAABEAARD/2gAIAQIBAT8QtXWf/8QAHBABAAMAAgMAAAAAAAAAAAAAAQARITFBYZHw/9oACAEBAAE/EBhAsq33ErbdTcKd3xdUwdbA+Xs9QkgjokPFw7QYqiSiDrYRS4LA4XP/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Minecraft Volume Alpha&quot;
        title=&quot;Minecraft Volume Alpha&quot;
        src=&quot;/static/051f11a7ba2cd3bcfba7b1d6515b2195/828fb/minecraft.jpg&quot;
        srcset=&quot;/static/051f11a7ba2cd3bcfba7b1d6515b2195/ff44c/minecraft.jpg 158w,
/static/051f11a7ba2cd3bcfba7b1d6515b2195/a6688/minecraft.jpg 315w,
/static/051f11a7ba2cd3bcfba7b1d6515b2195/828fb/minecraft.jpg 630w,
/static/051f11a7ba2cd3bcfba7b1d6515b2195/0ede0/minecraft.jpg 945w,
/static/051f11a7ba2cd3bcfba7b1d6515b2195/47311/minecraft.jpg 1080w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;这周开始接手同事的工作了。同事的代码里独特的想法比较多，大多又都藏在脑子里，琢磨起来有时还真的要花些工夫。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;上周排查一个 SDK 上的问题，反馈说某个按钮上的埋点发送的监测请求总是失败。
我去复现下来按钮点一下同时发出六个请求，后三个失败。
看了下失败的错误代码是 &lt;code&gt;net::ERR_HTTP2_SERVER_REFUSED_STREAM&lt;/code&gt;，查了下似乎是 HTTP/2 的并行连接数开小了，在 nginx 中把 &lt;code&gt;http2_max_concurrent_streams&lt;/code&gt; 设大一点应该就好了。
不过对方最后选择关闭了 HTTP/2。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;这周给 rsuite 的 CI 配置更新了下，要在 &lt;code&gt;*.x&lt;/code&gt; 分支上运行测试，结果改完配置 YAML 推上去报了语法错误。
原因是我使用 &lt;code&gt;*.x&lt;/code&gt; 这个字符串时没有用引号包围起来。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; main
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token important&quot;&gt;*.x&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Oops!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;但是 GitHub Actions 的文档中，示例中明明也有带星号的字符串没有引号的情况（并且试了下的确是可行的，不是文档笔误）：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# GitHub Actions 文档&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; v2
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; v1.*&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;难道是仅当星号位于开头的时候必须用引号？
于是搜了下 YAML 究竟什么情况下必须给字符串加引号，搜到的答案都说只要字符串中包含指定的特殊字符（比如星号）就必须用引号包围。&lt;/p&gt;
&lt;p&gt;我想也许是这些回答都比较早了，而 YAML 语法后来又更新了吧，遂去查阅最新的 specification。
篇幅实在太长了，搜关键字也没找到什么清楚的结果，于是去问了 GPT，终于解答了我的疑问——当字符串中的星号可能被当做 YAML 的语法结构时（比如这里用在开头）就必须使用引号。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5498dd5fceafdc32c46729fff11cc178/b9bfc/gpt-yaml.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.54430379746836%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAABYlAAAWJQFJUiTwAAABGklEQVQoz6WQUUvCUBiGz0+q6yBnoU4KqrvoJoS0Aqt/VVjQdRR05zbNkOgX1JXOmTqPtrHtPLGd4X31wcN7eL+P9+N8QinFbwFFkihkoFgEimWokN8JH59TxF8DJzLhxgpp2SF3Tsh1O+ThdYngH5UoiGJFnGjSEk63R8pLr49ldzVOl7blYDv52+5g2R2tudd/e8cbj3WyUvkKhTCKO1TMA0qVPUplTdncZ2t7l4JhYhSrmRaMKpuZmllvbX2D1u19FhNF0eoc4rh2TuPsivrppaZxsdKTelPTaGo/J50/PKrx+PScBcZxnN8WxGA4xPU83NGIkecxcF2GrsvXZIovJTPfx59L5nKBXKQsM28ynREEYf5jtQr8AVGO3+L+hMwYAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;GPT 关于 YAML 字符串的回答&quot;
        title=&quot;GPT 关于 YAML 字符串的回答&quot;
        src=&quot;/static/5498dd5fceafdc32c46729fff11cc178/f058b/gpt-yaml.png&quot;
        srcset=&quot;/static/5498dd5fceafdc32c46729fff11cc178/c26ae/gpt-yaml.png 158w,
/static/5498dd5fceafdc32c46729fff11cc178/6bdcf/gpt-yaml.png 315w,
/static/5498dd5fceafdc32c46729fff11cc178/f058b/gpt-yaml.png 630w,
/static/5498dd5fceafdc32c46729fff11cc178/40601/gpt-yaml.png 945w,
/static/5498dd5fceafdc32c46729fff11cc178/78612/gpt-yaml.png 1260w,
/static/5498dd5fceafdc32c46729fff11cc178/b9bfc/gpt-yaml.png 3112w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;这周 DevOps 平台修了一个流水线表格视图中点“设计流水线”页面 404 的问题。
这个问题其实在流水线列表视图里面也有，已经修过一遍了，这里要再修一遍是因为这两段相同的跳转逻辑分别分散在 RecentPipelineList 和 PipelineTable 组件中了，而没有抽到父组件来。
这里顺便给它重构了一下。之后这种地方该抽象的地方就不要图省事。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;开源和 Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;这两周加入 GitHub star 的一些有意思的仓库
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/stacksjs/stacks&quot;&gt;&lt;code&gt;stacks&lt;/code&gt;&lt;/a&gt; 一个“type-safe full-stack framework for artisans”，现在看上去还在开发早期，收藏下来看看未来是否能带来 Laravel 一样的开发体验&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ai/size-limit&quot;&gt;&lt;code&gt;size-limit&lt;/code&gt;&lt;/a&gt; 一个分析你的库的体积的工具，可以在 PR 中报告体积变化，收藏下来用在 rsuite v6 开发中&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/EvanBacon/pillar-valley&quot;&gt;Pillar Valley&lt;/a&gt; 一个 Web 3D 小游戏，马下来作为灵感&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/measuredco/puck&quot;&gt;&lt;code&gt;puck&lt;/code&gt;&lt;/a&gt; 一个可扩展的拖拽组件搭建界面工具，马下来作为 low code 选型参考&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;和女朋友看了《奥本海默》。
有一句台词我很喜欢，是奥本海默夫妻养不好小孩的时候，一位朋友对他说的：你能看到世界以外的世界，相应地也要付出代价。
结局的立意点让我有点没想到，居然是政客和科学家的格局相比真渺小。&lt;/p&gt;
&lt;p&gt;这次我们坐在 IMAX 的第 8/9 排，荧幕有点向上卷，估计第 7 排应该是最合适的，下次知道了。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;有天突然想到，一周的工作日其实并不是直到周六到来才结束，而应该是从周五下班开始就结束了，随即周末生活也就开始了。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Minecraft&lt;/h2&gt;
&lt;p&gt;这两周 Minecraft 可没少玩。
为了找些新的冒险，我&lt;del&gt;繁殖了一只&lt;/del&gt;招募了一位制图师村民，从他那里购买了一张林地探险家地图，和女朋友一起去寻找林地府邸了。&lt;/p&gt;
&lt;p&gt;经过长途跋涉，跨越了比我们已经探索过的范围还要超出三张地图的距离，终于找到了林地府邸。这也是我玩 Minecraft 以来第一次找到林地府邸。
别人的房子可真大。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/993a7dd58a384c9cf96109018f74a1fb/e5b16/%E6%9E%97%E5%9C%B0%E5%BA%9C%E9%82%B8.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.32911392405063%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMEAf/EABYBAQEBAAAAAAAAAAAAAAAAAAEAAv/aAAwDAQACEAMQAAABROvAoJjV/8QAGxAAAgEFAAAAAAAAAAAAAAAAAQIAAxARE0H/2gAIAQEAAQUCNc5LsW3vbk//xAAWEQEBAQAAAAAAAAAAAAAAAAAAEQH/2gAIAQMBAT8BiY//xAAXEQADAQAAAAAAAAAAAAAAAAAAARIC/9oACAECAQE/Aa0yj//EABkQAAIDAQAAAAAAAAAAAAAAAAAREiAhQf/aAAgBAQAGPwLEiRyn/8QAHBAAAwABBQAAAAAAAAAAAAAAAAERITFRYXHx/9oACAEBAAE/Ib07FMpZ4H5xt3ooSup//9oADAMBAAIAAwAAABCzD//EABgRAAIDAAAAAAAAAAAAAAAAAAABETFh/9oACAEDAQE/EIKjM//EABcRAQEBAQAAAAAAAAAAAAAAAAEAESH/2gAIAQIBAT8QQdZY5rf/xAAcEAEAAwEAAwEAAAAAAAAAAAABABEhMVFhgbH/2gAIAQEAAT8QShlwd/ZYNC48HyXMRBjvAHool2zV+ahC9M//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;林地府邸&quot;
        title=&quot;林地府邸&quot;
        src=&quot;/static/993a7dd58a384c9cf96109018f74a1fb/828fb/%E6%9E%97%E5%9C%B0%E5%BA%9C%E9%82%B8.jpg&quot;
        srcset=&quot;/static/993a7dd58a384c9cf96109018f74a1fb/ff44c/%E6%9E%97%E5%9C%B0%E5%BA%9C%E9%82%B8.jpg 158w,
/static/993a7dd58a384c9cf96109018f74a1fb/a6688/%E6%9E%97%E5%9C%B0%E5%BA%9C%E9%82%B8.jpg 315w,
/static/993a7dd58a384c9cf96109018f74a1fb/828fb/%E6%9E%97%E5%9C%B0%E5%BA%9C%E9%82%B8.jpg 630w,
/static/993a7dd58a384c9cf96109018f74a1fb/0ede0/%E6%9E%97%E5%9C%B0%E5%BA%9C%E9%82%B8.jpg 945w,
/static/993a7dd58a384c9cf96109018f74a1fb/3ac88/%E6%9E%97%E5%9C%B0%E5%BA%9C%E9%82%B8.jpg 1260w,
/static/993a7dd58a384c9cf96109018f74a1fb/e5b16/%E6%9E%97%E5%9C%B0%E5%BA%9C%E9%82%B8.jpg 4480w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;不过林地府邸并没有什么值得一拿的稀罕东西，想要找一朵兰花，却也没遇到花房。只在回程途中的一处海底遗迹扫出了几片之前没收集到的陶片。&lt;/p&gt;
&lt;p&gt;回家后我着手盖一栋新的樱花木房子，女朋友则继续去另一个方向跑图了。
跑图的过程中女朋友遇到了一个掠夺者前哨站，里面关着四只悦灵。这也是我第一次见到悦灵，真的很可爱。而且在我盖房子的过程中也很有用，砍树的时候可以帮忙捡树苗，这样就不用费劲把树叶打光了。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/56e3fbad073c288f1701636bf9e504ea/40e42/%E6%A8%B1%E8%8A%B1%E5%B1%8B.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.69620253164557%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAECBAX/xAAWAQEBAQAAAAAAAAAAAAAAAAABAAL/2gAMAwEAAhADEAAAAePpU5bER//EABkQAAIDAQAAAAAAAAAAAAAAAAISAAEDEP/aAAgBAQABBQIM2tVIYUrn/8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQMBAT8BV//EABURAQEAAAAAAAAAAAAAAAAAABAR/9oACAECAQE/Aaf/xAAaEAACAgMAAAAAAAAAAAAAAAAAEQECICFh/9oACAEBAAY/Ah1l8N4f/8QAGhABAQACAwAAAAAAAAAAAAAAAREAIRAxQf/aAAgBAQABPyHvGZvRIxeY2MxW4QNcv//aAAwDAQACAAMAAAAQ9D//xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAwEBPxAj/8QAGBEAAgMAAAAAAAAAAAAAAAAAAAERITH/2gAIAQIBAT8QupNP/8QAGhABAAIDAQAAAAAAAAAAAAAAAQARITFRQf/aAAgBAQABPxBRDomOnsqjiS9uSzWKje4JpVvjAI3e+wwUT//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;樱花屋&quot;
        title=&quot;樱花屋&quot;
        src=&quot;/static/56e3fbad073c288f1701636bf9e504ea/828fb/%E6%A8%B1%E8%8A%B1%E5%B1%8B.jpg&quot;
        srcset=&quot;/static/56e3fbad073c288f1701636bf9e504ea/ff44c/%E6%A8%B1%E8%8A%B1%E5%B1%8B.jpg 158w,
/static/56e3fbad073c288f1701636bf9e504ea/a6688/%E6%A8%B1%E8%8A%B1%E5%B1%8B.jpg 315w,
/static/56e3fbad073c288f1701636bf9e504ea/828fb/%E6%A8%B1%E8%8A%B1%E5%B1%8B.jpg 630w,
/static/56e3fbad073c288f1701636bf9e504ea/0ede0/%E6%A8%B1%E8%8A%B1%E5%B1%8B.jpg 945w,
/static/56e3fbad073c288f1701636bf9e504ea/3ac88/%E6%A8%B1%E8%8A%B1%E5%B1%8B.jpg 1260w,
/static/56e3fbad073c288f1701636bf9e504ea/40e42/%E6%A8%B1%E8%8A%B1%E5%B1%8B.jpg 2235w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;悦灵放养常常会卡在某个角落里，一时半会找不到的时候还是有点着急。于是盖了一个玻璃屋把悦灵养在里面。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/24743d4fd9c305f822a5208d98db0b49/0695f/%E7%8E%BB%E7%92%83%E5%B1%8B.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 68.35443037974683%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAOABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAQBBf/EABUBAQEAAAAAAAAAAAAAAAAAAAIB/9oADAMBAAIQAxAAAAHlLsMgWiv/xAAaEAACAgMAAAAAAAAAAAAAAAAAAQIDERMh/9oACAEBAAEFAo1ZNQqx8Exn/8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQMBAT8BR//EABYRAQEBAAAAAAAAAAAAAAAAAAAREv/aAAgBAgEBPwGtP//EABYQAQEBAAAAAAAAAAAAAAAAACEAIP/aAAgBAQAGPwJjP//EABoQAAIDAQEAAAAAAAAAAAAAAAARASExUUH/2gAIAQEAAT8hQlEJNaOjPTppRWT/2gAMAwEAAgADAAAAEBT/AP/EABYRAQEBAAAAAAAAAAAAAAAAABEAIf/aAAgBAwEBPxDUhf/EABcRAAMBAAAAAAAAAAAAAAAAAAABIRH/2gAIAQIBAT8QmM0f/8QAGxABAAIDAQEAAAAAAAAAAAAAAQARITFBUWH/2gAIAQEAAT8QciMMLuOaP0ywbBHyK9NbENoUUw9uKkDk/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;玻璃屋&quot;
        title=&quot;玻璃屋&quot;
        src=&quot;/static/24743d4fd9c305f822a5208d98db0b49/828fb/%E7%8E%BB%E7%92%83%E5%B1%8B.jpg&quot;
        srcset=&quot;/static/24743d4fd9c305f822a5208d98db0b49/ff44c/%E7%8E%BB%E7%92%83%E5%B1%8B.jpg 158w,
/static/24743d4fd9c305f822a5208d98db0b49/a6688/%E7%8E%BB%E7%92%83%E5%B1%8B.jpg 315w,
/static/24743d4fd9c305f822a5208d98db0b49/828fb/%E7%8E%BB%E7%92%83%E5%B1%8B.jpg 630w,
/static/24743d4fd9c305f822a5208d98db0b49/0ede0/%E7%8E%BB%E7%92%83%E5%B1%8B.jpg 945w,
/static/24743d4fd9c305f822a5208d98db0b49/3ac88/%E7%8E%BB%E7%92%83%E5%B1%8B.jpg 1260w,
/static/24743d4fd9c305f822a5208d98db0b49/0695f/%E7%8E%BB%E7%92%83%E5%B1%8B.jpg 2477w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;忙中有乐的两周。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w34]]></title><description><![CDATA[本周新收藏的歌，有两首。]]></description><link>https://doma.land/weekly/23w34/</link><guid isPermaLink="false">https://doma.land/weekly/23w34/</guid><pubDate>Wed, 30 Aug 2023 16:26:57 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌，有两首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;听歌&lt;/h2&gt;
&lt;p&gt;有了 CD 播放器后，现在不仅仅在网易上听日推啦，还开始入手一些 CD 了。
不得不说 CD 和流媒体的质感真的不同，完全不会有压缩的感觉，听起来很舒服。&lt;/p&gt;
&lt;p&gt;本周入手的 CD 有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OneRoom《Toy Box》：由我最喜欢 P 主 OneRoom 创作的发行于 2009 年的专辑，这么多年终于补票了。
翻开来自十四年前的歌词本时感觉有点不真实。&lt;/li&gt;
&lt;li&gt;坂本龙一《Playing the piano》：女朋友送的。&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;display: flex; justify-content: center;&quot;&gt;
  &lt;div style=&quot;width: 50%;&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/c5814dbae5572c822bb933e6007fadc5/96287/Toy%20box.jpg&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGQABAAMBAQAAAAAAAAAAAAAAAAIDBAEF/8QAFgEBAQEAAAAAAAAAAAAAAAAAAQIA/9oADAMBAAIQAxAAAAHfRHEnsKks84bomv/EABsQAAMAAgMAAAAAAAAAAAAAAAABAgMQEyJB/9oACAEBAAEFArtQc8azp03Fsl9Wen//xAAVEQEBAAAAAAAAAAAAAAAAAAAQAf/aAAgBAwEBPwEh/8QAGBEAAgMAAAAAAAAAAAAAAAAAAAEQESH/2gAIAQIBAT8Bwscf/8QAGxAAAgEFAAAAAAAAAAAAAAAAAAEQESAhQeH/2gAIAQEABj8CybhUOis//8QAHBAAAgICAwAAAAAAAAAAAAAAAAERITGRQVFx/9oACAEBAAE/IYq99DRxqQKOFW5Ygwn4hrBhsSf/2gAMAwEAAgADAAAAEHvQ/v/EABcRAQADAAAAAAAAAAAAAAAAABEAARD/2gAIAQMBAT8QbYa//8QAFhEBAQEAAAAAAAAAAAAAAAAAESAh/9oACAECAQE/EAbD/8QAGhABAQEBAQEBAAAAAAAAAAAAAREAMUGR0f/aAAgBAQABPxAFh2hFzES85/dCU45ZZKELz3IIwrlV3s35kByKurf/2Q==&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Toy box&quot; title=&quot;Toy box&quot; src=&quot;/static/c5814dbae5572c822bb933e6007fadc5/828fb/Toy%20box.jpg&quot; srcset=&quot;/static/c5814dbae5572c822bb933e6007fadc5/ff44c/Toy%20box.jpg 158w,
/static/c5814dbae5572c822bb933e6007fadc5/a6688/Toy%20box.jpg 315w,
/static/c5814dbae5572c822bb933e6007fadc5/828fb/Toy%20box.jpg 630w,
/static/c5814dbae5572c822bb933e6007fadc5/0ede0/Toy%20box.jpg 945w,
/static/c5814dbae5572c822bb933e6007fadc5/3ac88/Toy%20box.jpg 1260w,
/static/c5814dbae5572c822bb933e6007fadc5/96287/Toy%20box.jpg 3022w&quot; sizes=&quot;(max-width: 630px) 100vw, 630px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
  &lt;/div&gt;
  &lt;div style=&quot;width: 50%;&quot;&gt;
      &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/cd41fe7528d42df79a69e2146010d25f/96287/Playing%20the%20piano.jpg&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGAABAAMBAAAAAAAAAAAAAAAAAAEDBAX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAv/aAAwDAQACEAMQAAAB31zzzoqEr8wSJr//xAAaEAEAAgMBAAAAAAAAAAAAAAABAAIQESES/9oACAEBAAEFAlCe64uxe1eRDc//xAAVEQEBAAAAAAAAAAAAAAAAAAAQAf/aAAgBAwEBPwEh/8QAFxEBAAMAAAAAAAAAAAAAAAAAAAEQEf/aAAgBAgEBPwFqa//EABcQAAMBAAAAAAAAAAAAAAAAABAgITH/2gAIAQEABj8Cr4P/xAAdEAEAAQMFAAAAAAAAAAAAAAABABARIUFRYZGh/9oACAEBAAE/IZJ3PKGCcRWyYccriaS6p//aAAwDAQACAAMAAAAQM/jC/8QAFhEAAwAAAAAAAAAAAAAAAAAAABAR/9oACAEDAQE/ECN//8QAFhEBAQEAAAAAAAAAAAAAAAAAEAEx/9oACAECAQE/EKMn/8QAHRABAAICAgMAAAAAAAAAAAAAAQARIVExQWFxkf/aAAgBAQABPxAwUg6Lm0+phLHEXyi4azFAFbqhuKvfUApC2cniWnL6QwAFE//Z&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Playing the piano&quot; title=&quot;Playing the piano&quot; src=&quot;/static/cd41fe7528d42df79a69e2146010d25f/828fb/Playing%20the%20piano.jpg&quot; srcset=&quot;/static/cd41fe7528d42df79a69e2146010d25f/ff44c/Playing%20the%20piano.jpg 158w,
/static/cd41fe7528d42df79a69e2146010d25f/a6688/Playing%20the%20piano.jpg 315w,
/static/cd41fe7528d42df79a69e2146010d25f/828fb/Playing%20the%20piano.jpg 630w,
/static/cd41fe7528d42df79a69e2146010d25f/0ede0/Playing%20the%20piano.jpg 945w,
/static/cd41fe7528d42df79a69e2146010d25f/3ac88/Playing%20the%20piano.jpg 1260w,
/static/cd41fe7528d42df79a69e2146010d25f/96287/Playing%20the%20piano.jpg 3022w&quot; sizes=&quot;(max-width: 630px) 100vw, 630px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;这周 rsuite 算是正式开始 v6 的开发了，契机是产品上需要 &lt;a href=&quot;https://github.com/rsuite/rsuite/pull/3345&quot;&gt;Sidenav 的设计更新&lt;/a&gt;。
开发在 &lt;code&gt;main&lt;/code&gt; 分支上进行，会优先支持产品的需要，发布一系列仅供内部使用的 alpha 版本。
待面向产品的需求完成后再继续开发面向社区的 beta 版。&lt;/p&gt;
&lt;p&gt;v5 不会因此停止更新，我打算将任何向后兼容的变更（包括特性和修复）都尽可能拣到 v5 上，尽量让 v6 与 v5 的差异保持在少数无法避免的 breaking change，使得升级变得容易。
我所喜爱的 Laravel 框架就做到了这一点，我希望向它看齐。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;这周一位同事离职了，下周开始我得接手他的工作。
这样一来我原本在八月底九月初关于 DevOps 平台和 rsuite 工作项的计划统统会受到影响，要重新安排了 😮‍💨。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;给 DevOps 平台的路由模块重新组织了一下。
原本是参考 bulletproof-react 的 feature-based 的组织方式，将路由页面模块放在各自的 feature 目录中，现在改为了参考 Remix 的路由文件命名组织方式，
使用约定式的具名导出，配合 React Router 的 &lt;code&gt;lazy&lt;/code&gt; 特性，将路由页面模块文件和路由路径一一对应起来。
这样一来减少了很多模块命名的麻烦，而且 feature 目录之间进行引用时也不会再将路由页面模块包含进来。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;给一个使用 Lerna 5.x 的老 monorepo 项目合了一个变更并且要发布版本，没想到 &lt;code&gt;lerna version&lt;/code&gt; 阶段执行的 &lt;code&gt;npm install&lt;/code&gt; 报了 peer 依赖冲突的问题。
在 &lt;code&gt;lerna.json&lt;/code&gt; 里给 &lt;code&gt;lerna version&lt;/code&gt; 命令设置 &lt;code&gt;npmClientArgs: [&apos;--legacy-peer-deps&apos;]&lt;/code&gt; 选项也没有用。
翻了 Issue 才发现偏偏 &lt;code&gt;lerna&lt;/code&gt; 的这些个命令之中偏偏 &lt;a href=&quot;https://github.com/lerna/lerna/issues/3386&quot;&gt;&lt;code&gt;version&lt;/code&gt; 是不支持 &lt;code&gt;npmClientArgs&lt;/code&gt; 的&lt;/a&gt;。
这个问题在 6.3.0 中被修复，于是需要给项目中的 Lerna 升大版本，好在升级没有遇到 breaking change，仅仅更新版本号便解决了问题。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;开源和 Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;看了下 Sentry browser SDK 的源码，这个&lt;a href=&quot;https://github.com/getsentry/sentry-javascript/blob/aab71b1d769b6189c94943ab7639de2ee2801c2e/packages/utils/src/instrument.ts#L393-L394&quot;&gt;劫持 &lt;code&gt;history&lt;/code&gt; 方法&lt;/a&gt;的思路挺有意思，让我想起之前做过一个 &lt;code&gt;use-search-params&lt;/code&gt; 库，是一个获取和操作 &lt;code&gt;URLSearchParams&lt;/code&gt; 的 React hook，兼容不同的 &lt;code&gt;react-router&lt;/code&gt; 版本甚至没有 &lt;code&gt;react-router&lt;/code&gt; 的情况。
但是当时没有想到在没有 &lt;code&gt;react-router&lt;/code&gt; 的情况下该如何实现，现在想想其实可以粗暴一点直接 hack 全局对象的。&lt;/li&gt;
&lt;li&gt;本周加入 GitHub star 的一些有意思的仓库
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/wilmerterrero/Avatartion&quot;&gt;Avatartion&lt;/a&gt; 一个生成 notion 风格 avatar 的网站，有趣。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ethanniser/next-typesafe-url&quot;&gt;&lt;code&gt;next-typesafe-url&lt;/code&gt;&lt;/a&gt; 一个为 Next.js 提供类型安全的路由操作库，收藏下来给接下来 DevOps 平台的路由重构中参数校验的部分做参考。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Yqnn/svg-path-editor&quot;&gt;svg-path-editor&lt;/a&gt; 一个编辑 SVG path 的 GUI，也算是工具 ui 化的灵感。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Inochi2D/inochi2d&quot;&gt;inochi2d&lt;/a&gt; 群友推荐的可以用来做 VTuber 的工具（？）&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/inikulin/parse5&quot;&gt;&lt;code&gt;parse5&lt;/code&gt;&lt;/a&gt; 一个 HTML 解析库&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/sindresorhus/type-fest&quot;&gt;&lt;code&gt;type-fest&lt;/code&gt;&lt;/a&gt; 一个 TypeScript 实用类型集。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;读完了《恶意》，结尾的接连反转让人意想不到，好看。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f3db967b8e29692d7c317afdc71cc202/96287/%E6%81%B6%E6%84%8F.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGQABAAMBAQAAAAAAAAAAAAAAAAIEBQMG/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAEC/9oADAMBAAIQAxAAAAGxU65h6RBmxzirQzf/xAAbEAACAgMBAAAAAAAAAAAAAAABAgADBBESE//aAAgBAQABBQK9mDej9TLOm73ZuW1LYWx0DAz/xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/AR//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/AR//xAAaEAACAwEBAAAAAAAAAAAAAAAAARARIgJB/9oACAEBAAY/AlToWnCOY0ex/8QAHBAAAwEAAgMAAAAAAAAAAAAAAAERITFBUWGB/9oACAEBAAE/Ibr46Joe7Q3p8CXQ+0aE51nhlOuHsdD/2gAMAwEAAgADAAAAELMoAv/EABURAQEAAAAAAAAAAAAAAAAAABAB/9oACAEDAQE/ECH/xAAVEQEBAAAAAAAAAAAAAAAAAAAQAf/aAAgBAgEBPxAp/8QAHBABAAMAAgMAAAAAAAAAAAAAAQARITFBUXGx/9oACAEBAAE/EDQJai52FbDEvIsUWz3d/ZsJQ98WStmEDcoYQRrQl94jn3P/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;恶意&quot;
        title=&quot;恶意&quot;
        src=&quot;/static/f3db967b8e29692d7c317afdc71cc202/828fb/%E6%81%B6%E6%84%8F.jpg&quot;
        srcset=&quot;/static/f3db967b8e29692d7c317afdc71cc202/ff44c/%E6%81%B6%E6%84%8F.jpg 158w,
/static/f3db967b8e29692d7c317afdc71cc202/a6688/%E6%81%B6%E6%84%8F.jpg 315w,
/static/f3db967b8e29692d7c317afdc71cc202/828fb/%E6%81%B6%E6%84%8F.jpg 630w,
/static/f3db967b8e29692d7c317afdc71cc202/0ede0/%E6%81%B6%E6%84%8F.jpg 945w,
/static/f3db967b8e29692d7c317afdc71cc202/3ac88/%E6%81%B6%E6%84%8F.jpg 1260w,
/static/f3db967b8e29692d7c317afdc71cc202/96287/%E6%81%B6%E6%84%8F.jpg 3022w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;和朋友们去吃了一家火锅，特色酥肉豆花锅好吃。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6beffa6b153cd41f6859df45cbd20ed9/39869/%E9%85%A5%E8%82%89%E8%B1%86%E8%8A%B1%E9%94%85.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 74.68354430379746%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAMBAv/EABUBAQEAAAAAAAAAAAAAAAAAAAIB/9oADAMBAAIQAxAAAAGbvSopDf/EABoQAAMAAwEAAAAAAAAAAAAAAAABAgMEEhP/2gAIAQEAAQUC4qx69CUMjPwq25PZo//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABURAQEAAAAAAAAAAAAAAAAAABAh/9oACAECAQE/Aaf/xAAbEAACAgMBAAAAAAAAAAAAAAAAEQFBAiIyYf/aAAgBAQAGPwJY1ZU+HAk4NcZOkf/EABwQAQACAwADAAAAAAAAAAAAAAEAESFBYTFRkf/aAAgBAQABPyEe5TBWCeam2m4BYD7Ms7Ce0+sUodSrn//aAAwDAQACAAMAAAAQo/8A/8QAFhEBAQEAAAAAAAAAAAAAAAAAARAR/9oACAEDAQE/ENGf/8QAFhEAAwAAAAAAAAAAAAAAAAAAAAER/9oACAECAQE/EHBWf//EAB4QAQEAAgICAwAAAAAAAAAAAAERACExQVGBYXGR/9oACAEBAAE/EAsFQT6vOAksFqR75xkKsicvWbFoqsCN3rvFQ+VAD8xCDtUw97m8/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;酥肉豆花锅&quot;
        title=&quot;酥肉豆花锅&quot;
        src=&quot;/static/6beffa6b153cd41f6859df45cbd20ed9/828fb/%E9%85%A5%E8%82%89%E8%B1%86%E8%8A%B1%E9%94%85.jpg&quot;
        srcset=&quot;/static/6beffa6b153cd41f6859df45cbd20ed9/ff44c/%E9%85%A5%E8%82%89%E8%B1%86%E8%8A%B1%E9%94%85.jpg 158w,
/static/6beffa6b153cd41f6859df45cbd20ed9/a6688/%E9%85%A5%E8%82%89%E8%B1%86%E8%8A%B1%E9%94%85.jpg 315w,
/static/6beffa6b153cd41f6859df45cbd20ed9/828fb/%E9%85%A5%E8%82%89%E8%B1%86%E8%8A%B1%E9%94%85.jpg 630w,
/static/6beffa6b153cd41f6859df45cbd20ed9/0ede0/%E9%85%A5%E8%82%89%E8%B1%86%E8%8A%B1%E9%94%85.jpg 945w,
/static/6beffa6b153cd41f6859df45cbd20ed9/3ac88/%E9%85%A5%E8%82%89%E8%B1%86%E8%8A%B1%E9%94%85.jpg 1260w,
/static/6beffa6b153cd41f6859df45cbd20ed9/39869/%E9%85%A5%E8%82%89%E8%B1%86%E8%8A%B1%E9%94%85.jpg 3404w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Minecraft&lt;/h2&gt;
&lt;p&gt;自从上次找到了樱花树、把北极熊牵回家后，暂时还没有想好下一步要探索些什么。
闲来无事想要去许久未去的矿洞里铺铺路，做点景观什么的，没想到沿着一条以前从未走过的岔路连到了又一个巨大的矿洞。
而且没几分钟就一口气挖到了 59 个钻石，意外收获。&lt;/p&gt;
&lt;p&gt;Complementary 光影除了好看之外还有一个功能，就是矿石会发光，在矿洞里看得太清楚了。非常推荐。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;忙碌的一周。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w33]]></title><description><![CDATA[本周新收藏的歌，有四首。]]></description><link>https://doma.land/weekly/23w33/</link><guid isPermaLink="false">https://doma.land/weekly/23w33/</guid><pubDate>Mon, 21 Aug 2023 16:20:24 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌，有四首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://rsuitejs.com&quot;&gt;&lt;code&gt;rsuite&lt;/code&gt;&lt;/a&gt; 时常会添加新的功能，但新功能文档什么时候加一直是个问题。
比如本周为 Calendar 组件新增了一个 &lt;code&gt;cellClassName&lt;/code&gt; prop，我想在文档里加上它的使用示例。
如果在这个新 prop 被发布之前就在文档中添加示例的话，文档站构建是会失败的，因为线上文档站构建所依赖的 &lt;code&gt;rsuite&lt;/code&gt; 是 npm 上的版本。
所以只好在周五发布之后再去另开 PR 添加文档，但是这样很容易忘。
参考此前在 VS Code 迭代计划中得到的启发，我打算尝试在每周一例行地对上一周 &lt;code&gt;rsuite&lt;/code&gt; 发布的新功能进行文档更新。
这样有一个小缺点就是文档是隔了一个周末才更新的，不过这个可以暂且忽略，观察观察再说。&lt;/li&gt;
&lt;li&gt;给 DevOps 平台更新登录页图片用上了 CSS 的 &lt;code&gt;min()&lt;/code&gt; 函数来控制 &lt;code&gt;margin-top&lt;/code&gt;，好玩。&lt;/li&gt;
&lt;li&gt;在开发新功能的接口里尝试使用 Zod schema 来校验响应结果和推断 TypeScript 类型了。
相比原来手写响应的 TypeScript 类型然后用 &lt;code&gt;as&lt;/code&gt; 进行转换变得更安全了。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;开源和 Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;这周给 &lt;a href=&quot;https://chrome.google.com/webstore/detail/keylog/oblkclfpobbmfdfhkgglfphfmpgkkbnb&quot;&gt;KeyLog&lt;/a&gt; 开发新功能的过程中，对 Chrome 扩展中 Content script、Popup 和 Background 三者的 scope 有了更深的认识。
比如，在 Content script 中是不可以使用 &lt;code&gt;chrome.tabs&lt;/code&gt; API 的，而从 Popup/Background 中向 Content script 发送消息则必须指定 &lt;code&gt;tabId&lt;/code&gt;。
Popup 被激活时，并不是在“当前激活的 tab”的 scope 下，如果需要在 Popup 中针对当前激活的 tab 进行操作，需要先通过 &lt;code&gt;chrome.tabs.query&lt;/code&gt; API 查询当前激活的 tab。&lt;/p&gt;
&lt;p&gt;新功能还有些小问题，不过下周应该可以发了。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;本周加入 GitHub star 的一些有意思的仓库&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/hyoban/kodoku&quot;&gt;&lt;code&gt;kodoku&lt;/code&gt;&lt;/a&gt; 一个管理 RSS 订阅的工具，可以自己部署。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/a16z-infra/ai-town&quot;&gt;&lt;code&gt;ai-town&lt;/code&gt;&lt;/a&gt; 一个使用 TypeScript 编写的 AI 小镇应用，可以观察 AI 角色自发的对话。可以自己部署，支持自定义人物设定。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/stylelint-less/stylelint-less&quot;&gt;&lt;code&gt;stylelint-less&lt;/code&gt;&lt;/a&gt; 一组用于检查 Less 文件的 Stylelint 插件。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/JoshuaKGoldberg/TypeStat&quot;&gt;&lt;code&gt;typestat&lt;/code&gt;&lt;/a&gt; 一个可用于将 JavaScript 代码转换为 TypeScript，或者改进 TypeScript 代码的工具。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/tchayen/red-otter&quot;&gt;&lt;code&gt;red-otter&lt;/code&gt;&lt;/a&gt; 一个 webgl flex box 布局引擎，可以帮助你在 canvas 中创建 GUI&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/QuiiBz/next-international&quot;&gt;&lt;code&gt;next-international&lt;/code&gt;&lt;/a&gt; 一个提供类型提示的 React 国际化库&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/orestbida/cookieconsent&quot;&gt;&lt;code&gt;cookieconsent&lt;/code&gt;&lt;/a&gt; 一个 vanilla consent 提示库。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/jakearchibald/svgomg&quot;&gt;&lt;code&gt;svgomg&lt;/code&gt;&lt;/a&gt; 一个 SVGO 的 GUI&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;健身环大冒险每次都会询问当天的训练是要增加、减少还是保持强度，我每次都选保持现状。
这周某天在选完之后弹出了另一个问题：“每次都选择保持现状是否很麻烦”？让我有点意外。
看样子如果选择是的话以后应该就不会询问默认保持，这个设计还挺贴心。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;女朋友送了我一台 CD 播放器。这是我的第一台 CD 播放器，此前因为没有播放器，多年前买下的一张喜欢的 CD 一直没有拆封过。终于第一次听到了它的声音，很开心。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a8d0dd19078677fff8cb1716816300a9/d2602/CD.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.31645569620254%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAQFAgP/xAAVAQEBAAAAAAAAAAAAAAAAAAABAP/aAAwDAQACEAMQAAABX6v5KUVQf//EABwQAQABBAMAAAAAAAAAAAAAAAIBAAMREgQTI//aAAgBAQABBQI8X0uWBjodbRKReQXr/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAHRAAAgEEAwAAAAAAAAAAAAAAAAECEBEjMVGB4f/aAAgBAQAGPwLd4mPZ4JwfQ+HT/8QAGhAAAgMBAQAAAAAAAAAAAAAAAREAITFxYf/aAAgBAQABPyEKO9cTjE2i50g4QcOGNCBlQW1n0z//2gAMAwEAAgADAAAAEOQ//8QAFhEBAQEAAAAAAAAAAAAAAAAAAAER/9oACAEDAQE/EGR//8QAFxEBAAMAAAAAAAAAAAAAAAAAAAERIf/aAAgBAgEBPxDVy//EABoQAQADAQEBAAAAAAAAAAAAAAEAESExUUH/2gAIAQEAAT8QtxjdCFlejkV0BtqeJE8ukcUpHZv9cdJhiIex1vsJmzbsn//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;CD 播放器&quot;
        title=&quot;CD 播放器&quot;
        src=&quot;/static/a8d0dd19078677fff8cb1716816300a9/828fb/CD.jpg&quot;
        srcset=&quot;/static/a8d0dd19078677fff8cb1716816300a9/ff44c/CD.jpg 158w,
/static/a8d0dd19078677fff8cb1716816300a9/a6688/CD.jpg 315w,
/static/a8d0dd19078677fff8cb1716816300a9/828fb/CD.jpg 630w,
/static/a8d0dd19078677fff8cb1716816300a9/0ede0/CD.jpg 945w,
/static/a8d0dd19078677fff8cb1716816300a9/3ac88/CD.jpg 1260w,
/static/a8d0dd19078677fff8cb1716816300a9/d2602/CD.jpg 4032w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在松坂书屋发现一个好玩又没用的柯南周边。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/78b2d52caf11f2d2bd406fa43584b782/9568a/%E5%8F%A3%E7%BA%A2%E6%9E%B6.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGQABAAIDAAAAAAAAAAAAAAAAAAQFAQMG/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAEC/9oADAMBAAIQAxAAAAHMS8q82ndWs2RwkA//xAAcEAACAgIDAAAAAAAAAAAAAAABAgARBBITITH/2gAIAQEAAQUCY9G43rCkxiebQQiwqANP/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAwEBPwEf/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAgEBPwEf/8QAGxAAAQQDAAAAAAAAAAAAAAAAEQABECECMUH/2gAIAQEABj8CKMPXE+NALUGP/8QAGxABAAIDAQEAAAAAAAAAAAAAAQAhETFBEIH/2gAIAQEAAT8hPMWQsUU6m2HhiqHIttHHYs2H5DQdTDBb5//aAAwDAQACAAMAAAAQn/dB/8QAFhEBAQEAAAAAAAAAAAAAAAAAASAh/9oACAEDAQE/EBxj/8QAFhEBAQEAAAAAAAAAAAAAAAAAASBB/9oACAECAQE/EEcj/8QAHRABAAICAgMAAAAAAAAAAAAAAQARITFBUWGBof/aAAgBAQABPxADQAtjY+5oYyDLUCXjt4jZmih06lS5mlWKp+svnlEMKq0ZOIHp7mW9s//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;口红架&quot;
        title=&quot;口红架&quot;
        src=&quot;/static/78b2d52caf11f2d2bd406fa43584b782/828fb/%E5%8F%A3%E7%BA%A2%E6%9E%B6.jpg&quot;
        srcset=&quot;/static/78b2d52caf11f2d2bd406fa43584b782/ff44c/%E5%8F%A3%E7%BA%A2%E6%9E%B6.jpg 158w,
/static/78b2d52caf11f2d2bd406fa43584b782/a6688/%E5%8F%A3%E7%BA%A2%E6%9E%B6.jpg 315w,
/static/78b2d52caf11f2d2bd406fa43584b782/828fb/%E5%8F%A3%E7%BA%A2%E6%9E%B6.jpg 630w,
/static/78b2d52caf11f2d2bd406fa43584b782/0ede0/%E5%8F%A3%E7%BA%A2%E6%9E%B6.jpg 945w,
/static/78b2d52caf11f2d2bd406fa43584b782/3ac88/%E5%8F%A3%E7%BA%A2%E6%9E%B6.jpg 1260w,
/static/78b2d52caf11f2d2bd406fa43584b782/9568a/%E5%8F%A3%E7%BA%A2%E6%9E%B6.jpg 3024w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;周末吃到了好吃的餐厅，开心。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;开心的一周。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w32]]></title><description><![CDATA[本周新收藏的歌，有一首。]]></description><link>https://doma.land/weekly/23w32/</link><guid isPermaLink="false">https://doma.land/weekly/23w32/</guid><pubDate>Mon, 14 Aug 2023 11:09:05 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌，有一首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;听歌&lt;/h2&gt;
&lt;p&gt;本周收藏的一首《I remember you》也是我以前在 QQ 音乐上收藏过的，这周在网易云日推刷到了。
不过这首歌是会员专享，无法在这里播放，所以另外给大家点一首《駅》。&lt;/p&gt;
&lt;h2&gt;This blog&lt;/h2&gt;
&lt;p&gt;上周的 weekly 发布后，居然收获了 RSS 订阅读者！感谢大家愿意阅读和订阅我的 weekly，开心且惶恐 🥹。&lt;/p&gt;
&lt;p&gt;我的博客现在有两个 RSS 订阅地址：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Weekly 订阅：&lt;a href=&quot;https://doma.land/weekly/rss.xml&quot;&gt;https://doma.land/weekly/rss.xml&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;全部文章（包含 weekly 和博文）订阅：&lt;a href=&quot;https://doma.land/rss.xml&quot;&gt;https://doma.land/rss.xml&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;给 DevOps 平台的路由可读性做了一下改进，将一些用数据库 ID 标识的路由改为了用可读的名称标识，这样更方便理解和记忆。
对原来的路径还是保持了兼容，会重定向到新的路径。
React Router 新版本的这个 &lt;code&gt;loader&lt;/code&gt; 还是挺好用的，特别是在其中使用 &lt;code&gt;redirect&lt;/code&gt; 进行重定向要比在路由组件中用 &lt;code&gt;navigate&lt;/code&gt; 关注点更分离，读起来也更清楚。&lt;/li&gt;
&lt;li&gt;修复了几个 &lt;a href=&quot;https://rsuitejs.com/&quot;&gt;&lt;code&gt;rsuite&lt;/code&gt;&lt;/a&gt; 的样式问题，发现 rsuite 的样式实现上还有不少可以改进的地方。
比较突出的问题是有些元素的选择器写得过于复杂，权重过高，导致很难覆盖，需要简化一下。
还有一个问题就是因为要兼容 IE 11 所以有些实现还在用比较老的方法。好想赶紧开始 v6 的开发，移除 IE 支持。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;开源和 Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;上周给 VS Code 提交的 PR 被合并了，下个月开始你们的电脑上就运行着我的代码了（只有一行 😬）。&lt;/li&gt;
&lt;li&gt;本周加入 GitHub star 的一些有意思的仓库
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://7ph.github.io/powerglitch/#/&quot;&gt;&lt;code&gt;powerglitch&lt;/code&gt;&lt;/a&gt; 一个很炫的玩意，为 web 元素添加故障效果。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.vantajs.com/&quot;&gt;&lt;code&gt;vanta&lt;/code&gt;&lt;/a&gt; 一个漂亮的 3D 背景库，使用 THREE.js 或 p5.js 进行渲染。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/microsoft/codetour&quot;&gt;CodeTour&lt;/a&gt; 一个用于在代码库中录制和回放漫游式导览的 VS Code 扩展，看上去对于多人协作项目中新人熟悉代码库很有帮助。由微软推出。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/SukkaW/nolyfill&quot;&gt;&lt;code&gt;nolyfill&lt;/code&gt;&lt;/a&gt; 一组常见的 Polyfill 包的替代方案，通过移除对于过早的 Node.js 版本的支持，减少兼容代码，以节省磁盘空间。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;看到一个很有意思的关于减少使用手机的视频——&lt;a href=&quot;https://b23.tv/UQ9OBQJ&quot;&gt;【每天只看手机 1 小时？戒掉手机之后的奇妙人生！大数据推送给所有沉迷于手机的人！-哔哩哔哩】&lt;/a&gt;。
类似主题的视频以前当然也看到过，但是这一则视频却让我有新的启发。
我意识到早上醒后、日常空闲时、睡前等时候无意识地刷手机浪费时间，之所以难以克服，是因为它从机制上真的是一种上瘾。&lt;/p&gt;
&lt;p&gt;于是也尝试了视频中提到的减少手机使用的方法——在睡前将手机留在卧室外，直到早晨起床、洗漱完成后离开卧室才开始使用手机。
确实让我晚上有了更多的时间，也更容易入睡。早晨也有了更清醒的头脑。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;这周还尝试了几次晨间正念。
我原以为正念就是单纯地闭上眼空想，其实 Apple Watch 上的引导软件会提示一个主题，比如“回想三件让你感激的事情以及感激的原因”。
平时根本不会特意静下心来想这些，仅仅几分钟的正念就有了回顾这些细微的美好念头的机会，让人心情舒畅、心境平和。
我打算把这个习惯保持下去。&lt;/p&gt;
&lt;p&gt;如果你也想尝试体验，我很建议在早晨起床后，第一次拿起手机之前做，这样避免自己的头脑被手机的信息刷屏所占据。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;有点累的一周。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w31]]></title><description><![CDATA[本周新收藏的歌，有两首。]]></description><link>https://doma.land/weekly/23w31/</link><guid isPermaLink="false">https://doma.land/weekly/23w31/</guid><pubDate>Mon, 07 Aug 2023 13:07:52 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌，有两首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;听歌&lt;/h2&gt;
&lt;p&gt;本周收藏的两首歌又都是会员专享，无法在这里播放，所以给大家点一首以前收藏的《Swan Song》。&lt;/p&gt;
&lt;p&gt;你也可以在网易云上收藏&lt;a href=&quot;https://music.163.com/playlist?id=83256808&quot;&gt;我喜欢的音乐&lt;/a&gt;歌单来收听我每周新收藏的歌曲。&lt;/p&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;尝试了一下每天工作的最后半小时用来回顾当日的工作内容，感觉很舒服。
能帮我把一天工作下来产生的零碎想法更完整地梳理一遍，并得到心得和经验。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;这周 &lt;code&gt;rsuite&lt;/code&gt; 发生了一个&lt;a href=&quot;https://github.com/rsuite/rsuite/issues/3318&quot;&gt;紧急故障&lt;/a&gt;。故障出现的原因其实很普通，但是在发现问题之后的修复过程中的体会值得记录。&lt;/p&gt;
&lt;p&gt;这个问题是周一收到 report、分诊并修复的。原本想着像平时一样在周五时随着每周更新发布，但是周三收到了一条不太友好的评论，才让我意识到这似乎是一个紧急故障，修复后应该尽快发一个 hotfix。
于是赶紧合并了 PR 并发了 hotfix 版本，随后在原 issue 中评论说已经发了修复的版本。
这时候我意识到这个 issue 已经开启三天了，当时分诊时只是挂上了 Bug 标签，并没有回复 issue 作者。
如果我是遇到这个问题的用户，这么久没有回应，应该很焦急吧。&lt;/p&gt;
&lt;p&gt;联想到《完美主义扼杀效率》中讲的，有时候即便方案不那么完美，仅仅是迅速回复就能够给人好的印象。
我想这种时候其实应该第一时间先给出回复，比如说建议先降级到正常的版本，同时尽快修复并发 hotfix release。下次可以尝试。&lt;/p&gt;
&lt;p&gt;这次创建了一个 &lt;code&gt;priority: Critical&lt;/code&gt; 标签，用于辅助识别这些紧急的 issue 和 pr 以及时处理。
当然，希望这种紧急故障不再出现。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在 &lt;a href=&quot;https://github.com/rsuite/rsuite-icons&quot;&gt;&lt;code&gt;rsuite/rsuite-icons&lt;/code&gt;&lt;/a&gt; 仓库中使用 Cypress 代替了官方弃用的 Karma 来进行多浏览器测试，算是给 &lt;code&gt;rsuite&lt;/code&gt; 探探路。
Cypress 的本地 TDD 体验很好，能够在 GUI 上完成集成的初始化。官方提供的 GitHub Action 也很好用，配置简单，减少了流水线配置的不少篇幅。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在 &lt;a href=&quot;https://github.com/rsuite/charts&quot;&gt;&lt;code&gt;rsuite/charts&lt;/code&gt;&lt;/a&gt; 仓库中踩了一个本地手动发布的坑，其文档站的构建产物路径恰好跟包源码构建路径一样，构建文档站会覆盖源码的构建产物，结果前两次发布的版本包内容都是文档站构建产物。
为什么发布前没有构建一次源码？说来也巧，项目中同时使用了 turborepo 和 changesets。其实在根目录的 &lt;code&gt;package.json&lt;/code&gt; 中是有 &lt;code&gt;prepublishOnly&lt;/code&gt; 脚本来运行 &lt;code&gt;npm run build&lt;/code&gt; 的，其通过 turborepo 来运行每个包中的 &lt;code&gt;build&lt;/code&gt; 脚本以构建源码。
而 changsets 进行发布时却是进到包目录里去 &lt;code&gt;npm publish&lt;/code&gt; 的，所以没有触发根目录的 &lt;code&gt;prepublishOnly&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;开源和 Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;本周发布了 &lt;a href=&quot;https://aplayer-react.js.org&quot;&gt;&lt;code&gt;aplayer-react&lt;/code&gt;&lt;/a&gt; 1.6 版本。此版本支持了底部固定模式。
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 65.18987341772153%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAd9WQD//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAEFAl//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAXEAEBAQEAAAAAAAAAAAAAAAARAAEQ/9oACAEBAAE/ITIyLGef/9oADAMBAAIAAwAAABATz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABkQAAMBAQEAAAAAAAAAAAAAAAABETEhYf/aAAgBAQABPxDhh5IaLEMmu9LLT//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;aplayer-react 底部固定模式&quot;
        title=&quot;aplayer-react 底部固定模式&quot;
        src=&quot;/static/ce4e354b39a2c4095383673e1084a8ac/828fb/aplayer-react.jpg&quot;
        srcset=&quot;/static/ce4e354b39a2c4095383673e1084a8ac/ff44c/aplayer-react.jpg 158w,
/static/ce4e354b39a2c4095383673e1084a8ac/a6688/aplayer-react.jpg 315w,
/static/ce4e354b39a2c4095383673e1084a8ac/828fb/aplayer-react.jpg 630w,
/static/ce4e354b39a2c4095383673e1084a8ac/0ede0/aplayer-react.jpg 945w,
/static/ce4e354b39a2c4095383673e1084a8ac/3ac88/aplayer-react.jpg 1260w,
/static/ce4e354b39a2c4095383673e1084a8ac/3ba35/aplayer-react.jpg 2956w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Vercel Analytics SDK 暂时不支持在服务端上报 pageview，于是我把 Easy LeetCode 的&lt;a href=&quot;https://ezlc.app/links/daily&quot;&gt;“每日一题”&lt;/a&gt;链接从服务端重定向改为了客户端重定向，过程中会显示一个中转页。
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 65.18987341772153%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAIEBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAYE00HKw/8QAHBAAAgICAwAAAAAAAAAAAAAAAgMABAETBRIV/9oACAEBAAEFAjsu25svlVzDWfH9j86Jq6w//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAHhAAAgAHAQEAAAAAAAAAAAAAAAECERMhMTNREqH/2gAIAQEABj8CiVR5NrG4om7jdTL4bPhL3O/D/8QAHBABAAICAwEAAAAAAAAAAAAAAQARIfAxUXGh/9oACAEBAAE/ISjMGEFFCJj8IGD2O7lO+/YTcy0f/9oADAMBAAIAAwAAABCAD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABsQAQACAwEBAAAAAAAAAAAAAAEAESFBUWGB/9oACAEBAAE/EABMmKAWibM+kKCUF5CwbAcqtuOco1KxfDQd8n//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;每日一题中转页&quot;
        title=&quot;每日一题中转页&quot;
        src=&quot;/static/864854aa91c476c6799c4f406ce70151/828fb/%E6%AF%8F%E6%97%A5%E4%B8%80%E9%A2%98%E4%B8%AD%E8%BD%AC%E9%A1%B5.jpg&quot;
        srcset=&quot;/static/864854aa91c476c6799c4f406ce70151/ff44c/%E6%AF%8F%E6%97%A5%E4%B8%80%E9%A2%98%E4%B8%AD%E8%BD%AC%E9%A1%B5.jpg 158w,
/static/864854aa91c476c6799c4f406ce70151/a6688/%E6%AF%8F%E6%97%A5%E4%B8%80%E9%A2%98%E4%B8%AD%E8%BD%AC%E9%A1%B5.jpg 315w,
/static/864854aa91c476c6799c4f406ce70151/828fb/%E6%AF%8F%E6%97%A5%E4%B8%80%E9%A2%98%E4%B8%AD%E8%BD%AC%E9%A1%B5.jpg 630w,
/static/864854aa91c476c6799c4f406ce70151/0ede0/%E6%AF%8F%E6%97%A5%E4%B8%80%E9%A2%98%E4%B8%AD%E8%BD%AC%E9%A1%B5.jpg 945w,
/static/864854aa91c476c6799c4f406ce70151/3ac88/%E6%AF%8F%E6%97%A5%E4%B8%80%E9%A2%98%E4%B8%AD%E8%BD%AC%E9%A1%B5.jpg 1260w,
/static/864854aa91c476c6799c4f406ce70151/3ba35/%E6%AF%8F%E6%97%A5%E4%B8%80%E9%A2%98%E4%B8%AD%E8%BD%AC%E9%A1%B5.jpg 2956w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;学习了 VS Code 的迭代计划和开发流程，对于我日常维护 rsuite 和制定 DevOps 平台的迭代计划很有启发。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;给 VS Code 提交了一个&lt;a href=&quot;https://github.com/microsoft/vscode/pull/189607&quot;&gt;修复&lt;/a&gt;。调试 VS Code 真有趣。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;本周加入 GitHub star 的一些有意思的仓库&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/rough-stuff/rough-notation&quot;&gt;&lt;code&gt;rough-notation&lt;/code&gt;&lt;/a&gt; 一个手写风格的划重点效果库，效果很可爱&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;这周玩了两次健身环大冒险，感觉强度好像比上周高了一点。第二次甚至感觉真的有点做不动了。&lt;/li&gt;
&lt;li&gt;上周买来想在睡前读的小说，这周终于看起来了。
和预想的一样，相比睡前最后一刻还在刷手机，看书能够给大脑一个从兴奋状态冷却下来的过渡时间，更有助于入睡。&lt;/li&gt;
&lt;li&gt;跟女朋友去三刷了封神。这次选的是 4DX 厅，想体验一下动来动去的座椅。
这种特色厅的画面和音效相比 IMAX 和杜比影院确实差的太远了。
以前没有这样拿同一部影片在不同影厅对比过，现在知道这种视听大片还是得选对厅。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Minecraft&lt;/h2&gt;
&lt;p&gt;这周和女朋友继续跑图终于找到了樱花林！&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d8714cd7da96f85b98af83f422e4f361/d2602/%E6%A8%B1%E8%8A%B1%E6%9E%97.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.31645569620254%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMEAf/EABYBAQEBAAAAAAAAAAAAAAAAAAMAAv/aAAwDAQACEAMQAAABn1DczycFv//EABoQAQACAwEAAAAAAAAAAAAAAAIAAwESEzH/2gAIAQEAAQUCrp2r4qeQ26kWkprGX//EABYRAQEBAAAAAAAAAAAAAAAAAAABIv/aAAgBAwEBPwHSV//EABcRAAMBAAAAAAAAAAAAAAAAAAABIlH/2gAIAQIBAT8BnBo//8QAGRABAQADAQAAAAAAAAAAAAAAAQACETEh/9oACAEBAAY/AjTeNpjEW6ynL//EABoQAQADAAMAAAAAAAAAAAAAAAEAESExUWH/2gAIAQEAAT8hVeeukubBJfsItUq2CLp6eJqqnJ//2gAMAwEAAgADAAAAEOg//8QAFxEBAQEBAAAAAAAAAAAAAAAAAQARQf/aAAgBAwEBPxBeWXDb/8QAGBEAAwEBAAAAAAAAAAAAAAAAAAERMYH/2gAIAQIBAT8QqbXRNcP/xAAaEAEBAAMBAQAAAAAAAAAAAAABEQAhMVGh/9oACAEBAAE/EBj8lSi9+YQrnoMctvk5NRsN13UxdRuqKL3WIm0qJNcz/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;樱花林&quot;
        title=&quot;樱花林&quot;
        src=&quot;/static/d8714cd7da96f85b98af83f422e4f361/828fb/%E6%A8%B1%E8%8A%B1%E6%9E%97.jpg&quot;
        srcset=&quot;/static/d8714cd7da96f85b98af83f422e4f361/ff44c/%E6%A8%B1%E8%8A%B1%E6%9E%97.jpg 158w,
/static/d8714cd7da96f85b98af83f422e4f361/a6688/%E6%A8%B1%E8%8A%B1%E6%9E%97.jpg 315w,
/static/d8714cd7da96f85b98af83f422e4f361/828fb/%E6%A8%B1%E8%8A%B1%E6%9E%97.jpg 630w,
/static/d8714cd7da96f85b98af83f422e4f361/0ede0/%E6%A8%B1%E8%8A%B1%E6%9E%97.jpg 945w,
/static/d8714cd7da96f85b98af83f422e4f361/3ac88/%E6%A8%B1%E8%8A%B1%E6%9E%97.jpg 1260w,
/static/d8714cd7da96f85b98af83f422e4f361/d2602/%E6%A8%B1%E8%8A%B1%E6%9E%97.jpg 4032w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;但这个樱花林也太小了一点，一共只有两棵樱花树。不过总归是拿到樱花树苗了。
女朋友马上回家把房子附近的橡树全都砍掉改种了樱花树，现在村子里有一片美丽的樱花林了。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3aaad6e76ffbb93fbde8a88d2ede70a5/e5b16/%E6%9D%91%E5%AD%90%E6%A8%B1%E8%8A%B1%E6%9E%97.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.32911392405063%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAIDBAX/xAAVAQEBAAAAAAAAAAAAAAAAAAABAP/aAAwDAQACEAMQAAABSuCMdscD/8QAHBAAAQQDAQAAAAAAAAAAAAAAAwABAhMREiEi/9oACAEBAAEFAriKB+2RdFf1lGHDb//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/AYj/xAAWEQEBAQAAAAAAAAAAAAAAAAAAERL/2gAIAQIBAT8BrT//xAAbEAACAQUAAAAAAAAAAAAAAAAAIQEQERJBgf/aAAgBAQAGPwJ4jsOIOUhaP//EABwQAAMAAQUAAAAAAAAAAAAAAAABETFBgZGxwf/aAAgBAQABPyGa8i/VHqPuMhXCTgpNNZMAj//aAAwDAQACAAMAAAAQ3z//xAAWEQEBAQAAAAAAAAAAAAAAAAAAAVH/2gAIAQMBAT8Qwp//xAAXEQADAQAAAAAAAAAAAAAAAAAAASEx/9oACAECAQE/EMwUVH//xAAdEAEAAwABBQAAAAAAAAAAAAABABEhMUFRcZHB/9oACAEBAAE/EK63m6YtASiVY3Y+NAVpw9wNhRB2KP1g/K5WExRZ4zqz/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;村子樱花林&quot;
        title=&quot;村子樱花林&quot;
        src=&quot;/static/3aaad6e76ffbb93fbde8a88d2ede70a5/828fb/%E6%9D%91%E5%AD%90%E6%A8%B1%E8%8A%B1%E6%9E%97.jpg&quot;
        srcset=&quot;/static/3aaad6e76ffbb93fbde8a88d2ede70a5/ff44c/%E6%9D%91%E5%AD%90%E6%A8%B1%E8%8A%B1%E6%9E%97.jpg 158w,
/static/3aaad6e76ffbb93fbde8a88d2ede70a5/a6688/%E6%9D%91%E5%AD%90%E6%A8%B1%E8%8A%B1%E6%9E%97.jpg 315w,
/static/3aaad6e76ffbb93fbde8a88d2ede70a5/828fb/%E6%9D%91%E5%AD%90%E6%A8%B1%E8%8A%B1%E6%9E%97.jpg 630w,
/static/3aaad6e76ffbb93fbde8a88d2ede70a5/0ede0/%E6%9D%91%E5%AD%90%E6%A8%B1%E8%8A%B1%E6%9E%97.jpg 945w,
/static/3aaad6e76ffbb93fbde8a88d2ede70a5/3ac88/%E6%9D%91%E5%AD%90%E6%A8%B1%E8%8A%B1%E6%9E%97.jpg 1260w,
/static/3aaad6e76ffbb93fbde8a88d2ede70a5/e5b16/%E6%9D%91%E5%AD%90%E6%A8%B1%E8%8A%B1%E6%9E%97.jpg 4480w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;从樱花林回家的路上路过冻洋，顺手牵了一头北极熊回家。给它也盖了小屋。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3816e9795b1a084a155415a0242e99f8/e5b16/%E5%8C%97%E6%9E%81%E7%86%8A%E5%B0%8F%E5%B1%8B.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.32911392405063%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAQBAgX/xAAWAQEBAQAAAAAAAAAAAAAAAAACAAP/2gAMAwEAAhADEAAAAZcomZY1A6f/xAAZEAADAQEBAAAAAAAAAAAAAAABAgMEEgD/2gAIAQEAAQUCnmc+APFJHrSzCeajm1gFf//EABcRAAMBAAAAAAAAAAAAAAAAAAABAhH/2gAIAQMBAT8BwUy0f//EABURAQEAAAAAAAAAAAAAAAAAAAEA/9oACAECAQE/ASVv/8QAGhAAAwEBAQEAAAAAAAAAAAAAAAECIRExQf/aAAgBAQAGPwLcl+lOYSZ3R8f0lOnww//EABoQAAMBAQEBAAAAAAAAAAAAAAABESFRMUH/2gAIAQEAAT8h+16vommtyOydGKaelWFsY0aykgkqp//aAAwDAQACAAMAAAAQOx//xAAXEQEBAQEAAAAAAAAAAAAAAAABACEx/9oACAEDAQE/EEcjDt//xAAXEQADAQAAAAAAAAAAAAAAAAAAAREh/9oACAECAQE/EMoRcp//xAAcEAEAAwACAwAAAAAAAAAAAAABABEhQVExYdH/2gAIAQEAAT8QTFYo8h6O5WgEQIGa5+RfSEV0WvOx3cTa6KqM7enhxhkYFAc2z//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;北极熊小屋&quot;
        title=&quot;北极熊小屋&quot;
        src=&quot;/static/3816e9795b1a084a155415a0242e99f8/828fb/%E5%8C%97%E6%9E%81%E7%86%8A%E5%B0%8F%E5%B1%8B.jpg&quot;
        srcset=&quot;/static/3816e9795b1a084a155415a0242e99f8/ff44c/%E5%8C%97%E6%9E%81%E7%86%8A%E5%B0%8F%E5%B1%8B.jpg 158w,
/static/3816e9795b1a084a155415a0242e99f8/a6688/%E5%8C%97%E6%9E%81%E7%86%8A%E5%B0%8F%E5%B1%8B.jpg 315w,
/static/3816e9795b1a084a155415a0242e99f8/828fb/%E5%8C%97%E6%9E%81%E7%86%8A%E5%B0%8F%E5%B1%8B.jpg 630w,
/static/3816e9795b1a084a155415a0242e99f8/0ede0/%E5%8C%97%E6%9E%81%E7%86%8A%E5%B0%8F%E5%B1%8B.jpg 945w,
/static/3816e9795b1a084a155415a0242e99f8/3ac88/%E5%8C%97%E6%9E%81%E7%86%8A%E5%B0%8F%E5%B1%8B.jpg 1260w,
/static/3816e9795b1a084a155415a0242e99f8/e5b16/%E5%8C%97%E6%9E%81%E7%86%8A%E5%B0%8F%E5%B1%8B.jpg 4480w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;因为跑了非常远的距离，家里的墙面已经不够容纳这些地图了，于是将地图展示墙从家里搬到了码头边。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/671960d5b17dfeb0789d2533565e9660/e5b16/%E5%9C%B0%E5%9B%BE%E5%B1%95%E7%A4%BA%E5%A2%99.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.32911392405063%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAIDBP/EABYBAQEBAAAAAAAAAAAAAAAAAAIBA//aAAwDAQACEAMQAAABzNN8lcgU/wD/xAAeEAAABAcAAAAAAAAAAAAAAAAAAQIDBBESFCEzNP/aAAgBAQABBQJJiebgPKOqF6H93//EABcRAAMBAAAAAAAAAAAAAAAAAAABEVH/2gAIAQMBAT8BpVh//8QAFxEBAQEBAAAAAAAAAAAAAAAAAQASIf/aAAgBAgEBPwE6Wm//xAAbEAABBAMAAAAAAAAAAAAAAAAAAQIhQRARcf/aAAgBAQAGPwKVsSdlYYP6f//EABwQAQACAgMBAAAAAAAAAAAAAAEAESFBMaGx0f/aAAgBAQABPyFaSjT7B5qr1BBtLAvBxF3eQAQR/9oADAMBAAIAAwAAABCcP//EABcRAQADAAAAAAAAAAAAAAAAAAEQIWH/2gAIAQMBAT8QbmQP/8QAGBEAAgMAAAAAAAAAAAAAAAAAAAERQWH/2gAIAQIBAT8QmxuzU//EAB0QAQADAAIDAQAAAAAAAAAAAAEAESExQVFhcdH/2gAIAQEAAT8Qs4yXbx9SkMnFF2d5srieLX9m20QD0URnh16e0FwA6Kn/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;地图展示墙&quot;
        title=&quot;地图展示墙&quot;
        src=&quot;/static/671960d5b17dfeb0789d2533565e9660/828fb/%E5%9C%B0%E5%9B%BE%E5%B1%95%E7%A4%BA%E5%A2%99.jpg&quot;
        srcset=&quot;/static/671960d5b17dfeb0789d2533565e9660/ff44c/%E5%9C%B0%E5%9B%BE%E5%B1%95%E7%A4%BA%E5%A2%99.jpg 158w,
/static/671960d5b17dfeb0789d2533565e9660/a6688/%E5%9C%B0%E5%9B%BE%E5%B1%95%E7%A4%BA%E5%A2%99.jpg 315w,
/static/671960d5b17dfeb0789d2533565e9660/828fb/%E5%9C%B0%E5%9B%BE%E5%B1%95%E7%A4%BA%E5%A2%99.jpg 630w,
/static/671960d5b17dfeb0789d2533565e9660/0ede0/%E5%9C%B0%E5%9B%BE%E5%B1%95%E7%A4%BA%E5%A2%99.jpg 945w,
/static/671960d5b17dfeb0789d2533565e9660/3ac88/%E5%9C%B0%E5%9B%BE%E5%B1%95%E7%A4%BA%E5%A2%99.jpg 1260w,
/static/671960d5b17dfeb0789d2533565e9660/e5b16/%E5%9C%B0%E5%9B%BE%E5%B1%95%E7%A4%BA%E5%A2%99.jpg 4480w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;开心的一周。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w30]]></title><description><![CDATA[本周新收藏的歌，有一首。]]></description><link>https://doma.land/weekly/23w30/</link><guid isPermaLink="false">https://doma.land/weekly/23w30/</guid><pubDate>Tue, 01 Aug 2023 17:35:59 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌，有一首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;听歌&lt;/h2&gt;
&lt;p&gt;这首《金魚花火》是大学时候在 QQ 音乐听的，这周在网易云日推听到了。&lt;/p&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;尝试给 rsuite 仓库升级 Stylelint 15。
这个版本中弃用了大量与代码格式相关的规则，作者希望用户自行使用 Prettier 等工具进行代码格式化，而 Stylelint 将更专注于语言本身。
这是我很赞成的一点。从此不再需要 &lt;code&gt;stylelint-config-prettier&lt;/code&gt; 了。&lt;/p&gt;
&lt;p&gt;升级的过程中遇到一条 &lt;code&gt;import-notation&lt;/code&gt; 规则，在其默认配置下，会要求 &lt;code&gt;@import&lt;/code&gt; 指令后面必须跟一个完整的 URL，而不是相对路径。这对于 Less 是不适用的。
在查找修改此规则行为的配置项的过程中，我也向 Stylelint 社区询问在 Less 项目中使用 Stylelint 的推荐方式，得到的答复是使用社区提供、针对 Less 的基础配置（例如 &lt;code&gt;stylelint-config-standard-less&lt;/code&gt;），而非直接使用 &lt;code&gt;stylelint-config-standard&lt;/code&gt; 基础配置（针对 CSS 语言特性设计）。
不过我试用了 &lt;code&gt;stylelint-config-standard-less&lt;/code&gt; 却抛出了许多语法错误，似乎是因为其依赖的 &lt;code&gt;stylelint-config-recommended-less&lt;/code&gt; 库（由另一作者维护）尚未适配 Stylelint 15。
社区也有其他人发现了这一问题，并&lt;a href=&quot;https://github.com/stylelint-less/stylelint-config-standard-less/issues/1&quot;&gt;正在尝试&lt;/a&gt;将这一系列针对 Less 的 Stylelint 相关库整合到同一个 org 中，以保证其维护活跃度。
保持关注。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;时隔 7 个月 DevOps 平台终于要继续新版本迭代了。给 GitLab milestone 加上了 due date，开整！&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;开源和 Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;在 Chrome 应用商店发布了我的第一个 Chrome 扩展 &lt;a href=&quot;https://chrome.google.com/webstore/detail/keylog/oblkclfpobbmfdfhkgglfphfmpgkkbnb&quot;&gt;KeyLog&lt;/a&gt;。
动机源自前段时间我给 xLog 提交&lt;a href=&quot;https://github.com/Crossbell-Box/xLog/pulls?q=is%3Apr+author%3ASevenOutman+is%3Aclosed+a11y&quot;&gt;一些涉及键盘操作的 PR&lt;/a&gt; 时，无法在录屏演示中明确地显示出键盘操作。
不同于社区中流行的 KeyCastr 等软件，KeyLog 以浏览器扩展的方式存在，仅仅显示在当前网页上，当对窗口录屏的时候无需调整显示位置，也不会显示在网页以外发生的键盘操作。&lt;/p&gt;
&lt;p&gt;第一次做浏览器扩展，还有很多不足，欢迎大家提出建议。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;尝试给 Easy LeetCode 的&lt;a href=&quot;https://ezlc.app/links/daily&quot;&gt;“每日一题”&lt;/a&gt;链接添加访问监测，但是 Vercel Analytics SDK 似乎并不支持在服务端发送 pageview 请求。相关 &lt;a href=&quot;https://github.com/vercel/analytics/issues/88&quot;&gt;issue&lt;/a&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/sponsors/SevenOutman&quot;&gt;我的 GitHub Sponsors&lt;/a&gt; 申请通过审核了。接下来就是努力做出有人愿意支持的项目了！&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;试用了下 &lt;a href=&quot;https://github.com/web-infra-dev/oxc/&quot;&gt;&lt;code&gt;oxlint&lt;/code&gt;&lt;/a&gt;，一个使用 Rust 编写的 ESlint alternative。
速度确实很快，令人印象深刻。不过尚不兼容 ESLint config。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;本周加入 GitHub star 的一些有意思的仓库&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://poke-holo.simey.me/&quot;&gt;Pokemon Cards &lt;/a&gt; 一个超酷使用 CSS 实现仿真 pokemon 卡片效果&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/liriliri/chii&quot;&gt;&lt;code&gt;chii&lt;/code&gt;&lt;/a&gt; 一个远程网页调试工具，我收藏下来是因为在一些包含 iframe 的项目中可能会用得到&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/linjc/smooth-signature&quot;&gt;&lt;code&gt;smooth-signature&lt;/code&gt;&lt;/a&gt; 另一个根据鼠标轨迹模拟手写的库，具有笔锋效果&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.leaferjs.com/&quot;&gt;LeaferJS&lt;/a&gt; 一个以高性能为特色的 Canvas 2D 图形渲染引擎&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/emilkowalski/vaul&quot;&gt;&lt;code&gt;vaul&lt;/code&gt;&lt;/a&gt; 一个来自 Vercel 设计师的 Drawer 库，其实现的背景缩放效果比较吸引我。我在 DevOps 平台的移动界面上也做了 Drawer 背景缩放，但是效果不如它自然。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/glennreyes/react-countup&quot;&gt;&lt;code&gt;react-countup&lt;/code&gt;&lt;/a&gt; 一个 countup 组件/hook，可以用在 dashboard 类项目中&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://driverjs.com/&quot;&gt;&lt;code&gt;driver.js&lt;/code&gt;&lt;/a&gt; 一个漫游式引导库，可以用在 DevOps 平台的新版本上用来介绍新功能&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;周一的中午逛书店买了一本书，下班骑车回家时把它放在共享单车的车筐里，但锁车时居然忘记拿走了。
到家快一个小时了才发现，赶紧给客服打了电话帮忙把车子暂时锁定，出门回去看看书还在不在。
回到锁车的地方时，车还在，书已经不见了。
希望捡走的人是拿去读了，不要当破烂卖了。&lt;/p&gt;
&lt;p&gt;第二天重新买了一本。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cbe3a817272b530f67b8828978aa29f9/96287/%E5%85%B1%E4%BA%AB%E5%8D%95%E8%BD%A6.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 133.54430379746836%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAbABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAIDAQT/xAAWAQEBAQAAAAAAAAAAAAAAAAABAAL/2gAMAwEAAhADEAAAAeVWXLoERaFyoIGv/8QAHBAAAgICAwAAAAAAAAAAAAAAAAECEQMSITJB/9oACAEBAAEFArLHRSZrxKbg06cpaiywrxox9f/EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQMBAT8BH//EABURAQEAAAAAAAAAAAAAAAAAABAB/9oACAECAQE/ASH/xAAaEAACAwEBAAAAAAAAAAAAAAABEQAgIQIQ/9oACAEBAAY/AqYYk4yPNHVP/8QAHBABAAMBAAMBAAAAAAAAAAAAAQARITFBUWGB/9oACAEBAAE/Ia1rC3ORB1l5xGpXWn3zKro9pHpF/JkTvfyOERBOJeTRn//aAAwDAQACAAMAAAAQOzSN/8QAFxEAAwEAAAAAAAAAAAAAAAAAAAERIf/aAAgBAwEBPxC6USEsP//EABcRAAMBAAAAAAAAAAAAAAAAAAABEUH/2gAIAQIBAT8QykEG4z//xAAdEAEBAAIDAAMAAAAAAAAAAAABEQAxIUFxYYGR/9oACAEBAAE/EE+p5cBcQG1ZPrICD8ZVlKgp0ZZsnlAbc0cVvEoav5i640iQ15l4x7JBvWL1XUjx7MSLl2G8WAAUvPWEqBi9Z//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;共享单车&quot;
        title=&quot;共享单车&quot;
        src=&quot;/static/cbe3a817272b530f67b8828978aa29f9/828fb/%E5%85%B1%E4%BA%AB%E5%8D%95%E8%BD%A6.jpg&quot;
        srcset=&quot;/static/cbe3a817272b530f67b8828978aa29f9/ff44c/%E5%85%B1%E4%BA%AB%E5%8D%95%E8%BD%A6.jpg 158w,
/static/cbe3a817272b530f67b8828978aa29f9/a6688/%E5%85%B1%E4%BA%AB%E5%8D%95%E8%BD%A6.jpg 315w,
/static/cbe3a817272b530f67b8828978aa29f9/828fb/%E5%85%B1%E4%BA%AB%E5%8D%95%E8%BD%A6.jpg 630w,
/static/cbe3a817272b530f67b8828978aa29f9/0ede0/%E5%85%B1%E4%BA%AB%E5%8D%95%E8%BD%A6.jpg 945w,
/static/cbe3a817272b530f67b8828978aa29f9/3ac88/%E5%85%B1%E4%BA%AB%E5%8D%95%E8%BD%A6.jpg 1260w,
/static/cbe3a817272b530f67b8828978aa29f9/96287/%E5%85%B1%E4%BA%AB%E5%8D%95%E8%BD%A6.jpg 3022w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;玩了女朋友买的健身环大冒险。还挺有意思，而且真的稍微有点运动强度。
以后就省得下楼了，我要在家打败堕拉贡。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;跟女朋友去看了封神，好看！从故事到视效都很棒。建议一定要去 IMAX 或者杜比影院看。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;开心的一周。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w29]]></title><description><![CDATA[本周新收藏的歌，有一首。]]></description><link>https://doma.land/weekly/23w29/</link><guid isPermaLink="false">https://doma.land/weekly/23w29/</guid><pubDate>Mon, 24 Jul 2023 21:27:58 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌，有一首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;尝试用 &lt;a href=&quot;https://ts-morph.com/&quot;&gt;&lt;code&gt;ts-morph&lt;/code&gt;&lt;/a&gt; 编写脚本，一口气把项目中一百多个 mocks 模块的重构解决了。真好用。
然后又加了一个生成 mock 模块的 plop 模板，现在新增一个 mock 接口定义非常轻松。&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;zh&quot; dir=&quot;ltr&quot;&gt;美好生活小妙招：使用 Plop 在项目中生成模板代码。&lt;br&gt;&lt;br&gt;当你的项目中具有一些模块路径约定时尤其好用。&lt;a href=&quot;https://t.co/RSKshFVF59&quot;&gt;https://t.co/RSKshFVF59&lt;/a&gt; &lt;a href=&quot;https://t.co/8XDL94N8G1&quot;&gt;pic.twitter.com/8XDL94N8G1&lt;/a&gt;&lt;/p&gt;&amp;mdash; Doma. (@leishenghao) &lt;a href=&quot;https://twitter.com/leishenghao/status/1682306039260258304?ref_src=twsrc%5Etfw&quot;&gt;July 21, 2023&lt;/a&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;开源和 Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Prettier 3 前几周发布了，一个我很喜欢的改动是现在会默认忽略 &lt;code&gt;.gitignore&lt;/code&gt; 中指定的文件。不过距离完全删除 &lt;code&gt;.prettierignore&lt;/code&gt; 还是差了一步，因为 Prettier 3 仍会格式化 &lt;code&gt;pnpm-lock.yaml&lt;/code&gt;。一位 Prettier 成员在一个 &lt;a href=&quot;https://github.com/prettier/prettier/issues/14517&quot;&gt;issue&lt;/a&gt; 中进行了简短的说明，但我并不认为 &lt;code&gt;pnpm-lock.yaml&lt;/code&gt; 应该像 &lt;code&gt;.next/*&lt;/code&gt; 那样被视为“一些流行框架自有的约定”，而是更接近“npm 生态的基础约定”，就像 Prettier 默认会忽略 &lt;code&gt;node_modules&lt;/code&gt; 目录那样。如果你不认为 &lt;code&gt;pnpm-lock.yaml&lt;/code&gt; 与 &lt;code&gt;node_modules&lt;/code&gt; 足够相似（因为它毕竟是第三方库），另一个事实是 Prettier 也会格式化 &lt;code&gt;package-lock.json&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://aplayer-react.js.org&quot;&gt;&lt;code&gt;aplayer-react&lt;/code&gt;&lt;/a&gt; 这周发了一个新版本 1.5.0，支持设置点击当前播放歌曲的艺术家名称时前往的连接。&lt;/p&gt;
&lt;p&gt;我的 weekly 中也更新了最新的 &lt;code&gt;aplayer-react&lt;/code&gt;。现在如果你听到喜欢的歌，可以点击播放器上正在播放的歌曲的艺术家名称，前往网易云音乐的艺人主页查看其更多作品。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;本周加入 GitHub star 的一些有意思的仓库&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.perfectfreehand.com/&quot;&gt;&lt;code&gt;perfect-freehand&lt;/code&gt;&lt;/a&gt; 一个用于根据鼠标轨迹渲染仿真笔触的库，可用于创建白板类手写交互。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://microsoft.github.io/TypeChat/&quot;&gt;&lt;code&gt;TypeChat&lt;/code&gt;&lt;/a&gt; 一个用于通过编写 TypeScript 类型来为 LLM 提供示例输出结构的库，由微软推出。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;p&gt;最近打算买一台投影仪来用，但是在比较热门的款中间难以选择，看了各种评测视频也是有的说这个好有的说那个好。
最后决定都买回来实际体验一下，留下最合适的其他退货。就这样最后选中了心仪的投影仪，配合幕布，在卧室里看 4K 漫步视频也太舒服了。&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;zh&quot; dir=&quot;ltr&quot;&gt;加了一块幕布，放 4K 片源效果真是绝了🤩其实 1080P 片源效果已经达到对看投影这件事的预期了，但 4K 片源真的不一样，锐利到不觉得自己在看投影。就是我的网速还不能流畅播放 4K（通过镜头拍出来发红是三色激光投影的通病） &lt;a href=&quot;https://t.co/T3pdaO5vaC&quot;&gt;pic.twitter.com/T3pdaO5vaC&lt;/a&gt;&lt;/p&gt;&amp;mdash; Doma. (@leishenghao) &lt;a href=&quot;https://twitter.com/leishenghao/status/1682852418147717121?ref_src=twsrc%5Etfw&quot;&gt;July 22, 2023&lt;/a&gt;&lt;/blockquote&gt;
&lt;h2&gt;Minecraft&lt;/h2&gt;
&lt;p&gt;女朋友去我们已经探索过的地图之外的区域跑图找樱花树，一周过去了也没看见一点粉色的山头。
我则在扩展航线的过程中，在东北方向的沙漠中找到了骆驼。
我突发奇想，与其她一个人跑图，不如我骑着骆驼载她，两个人一块去探索。但是骆驼并不能下水，也不能乘船，该如何运回家去呢？
正在发愁之际，女朋友踩着冰霜行者从大海中走来…&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;轻松愉快的一周。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w27+28]]></title><description><![CDATA[这两周新收藏的歌，有两首。]]></description><link>https://doma.land/weekly/23w27+28/</link><guid isPermaLink="false">https://doma.land/weekly/23w27+28/</guid><pubDate>Sun, 16 Jul 2023 21:57:01 GMT</pubDate><content:encoded>&lt;p&gt;这两周新收藏的歌，有两首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;但我在播放列表里额外加了一首《crazy for you》，它是我许久之前收藏的，在写这篇 weekly 的时候正好听到。
点开评论区被第一条热评戳中了：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;…充满不羁的元气、敢于赌上一切的鲁莽，对美好的全部幻想、最纯净的爱恋之心。所有都源于想要说出的初衷。或许这就是明亮又美好的青春。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;This weekly&lt;/h2&gt;
&lt;p&gt;果然两周并做一篇写只有零次和无数次。&lt;/p&gt;
&lt;p&gt;这一次是因为从上周日开始的行程，整整在外面跑了一周。&lt;/p&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;这周终于解决了流水线上使用 pnpm 的问题。解决的方法很朴素，就是更新了 Jenkins 上的 Node.js 版本。
利用上 pnpm 的缓存机制后，加上之前的其他优化，前端项目的流水线构建时间最终从原来的十分钟降到了四分钟。&lt;/li&gt;
&lt;li&gt;DevOps 平台要做一个下载日志的功能。教会了后端同事响应其实可以用流，一边读取日志文件一边写入响应，而不是先读取完再写入响应。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;开源和 Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;排查了一个 &lt;code&gt;@rsuite/formik&lt;/code&gt; 的组件在 &lt;code&gt;onBlur&lt;/code&gt; 时没有正确将 &lt;code&gt;formik&lt;/code&gt; 的表单项设置为 touched 的问题。
原因出在 &lt;code&gt;@rsuite/formik&lt;/code&gt; 组件的 &lt;code&gt;onBlur&lt;/code&gt; 回调并非绑定在 &lt;code&gt;&amp;#x3C;input&gt;&lt;/code&gt; 元素上，导致 &lt;code&gt;formik&lt;/code&gt; 拿到的 &lt;code&gt;onBlur&lt;/code&gt; 事件上的 &lt;code&gt;target&lt;/code&gt; 不是 &lt;code&gt;&amp;#x3C;input&gt;&lt;/code&gt; 元素，无法正确取到 &lt;code&gt;name&lt;/code&gt; 属性。
看了下其他一些 &lt;code&gt;formik&lt;/code&gt; 绑定库似乎也没有很好地处理这个问题（有些直接将 &lt;code&gt;onBlur&lt;/code&gt; 忽略了），这也是自定义表单控件的一个坑。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;用 GPT 写类型体操还挺好使，发现了新大陆。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;终于成功注册了 Chrome Web Store Developer，正在准备发布一个 Chrome 扩展。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;新栏目：本周加入 GitHub star 的一些有意思的仓库&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/rayepps/radash&quot;&gt;&lt;code&gt;radash&lt;/code&gt;&lt;/a&gt; 一个适应当前时代的工具函数库，指出了&lt;a href=&quot;https://radash-docs.vercel.app/docs/getting-started#love-and-hate&quot;&gt;为什么 lodash 不再适合现代&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://houseform.dev/&quot;&gt;&lt;code&gt;HouseForm&lt;/code&gt;&lt;/a&gt; 一个强调 field 级别校验的表单校验库。
看到它的时候我正好在排查表单校验场景下一些状态同步的问题，以前认为 form error 随时都是 form value + validation schema 的派生状态，这个库的思路给了我新的启发。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/parcel-bundler/lightningcss&quot;&gt;&lt;code&gt;Lightning CSS&lt;/code&gt;&lt;/a&gt; Parcel 团队推出的 CSS 转译和压缩器，使用 Rust 编写，据说是非常的快。
不过我尝试了一下用它代替 cssnano，似乎并没有很明显的区别。也许是我的项目中样式代码量不够多。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ts-morph.com/&quot;&gt;&lt;code&gt;ts-morph&lt;/code&gt;&lt;/a&gt; 一个在 TypeScript Compiler API 封装的 AST 访问/操作库。
看到的时候正好我在参考&lt;a href=&quot;https://juejin.cn/post/7201010542214479927&quot;&gt;《使用文件路径组织 MSW handlers》&lt;/a&gt; 对项目中的 mocks 模块进行重构，重复工作很多，准备试试用它来进行重构。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://react-awesome-reveal.morello.dev/&quot;&gt;&lt;code&gt;react-awesome-reveal&lt;/code&gt;&lt;/a&gt; 一个用于创建进场动画的 React 库。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/promer94/rsc-compatible&quot;&gt;&lt;code&gt;Make your React library RSC compatible&lt;/code&gt;&lt;/a&gt; 一篇关于如何让 React 组件库兼容 React Server Component 的指导文章，由 &lt;a href=&quot;https://twitter.com/yixuanxu94&quot;&gt;@promer94&lt;/a&gt; 编写。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果觉得这个栏目有意思，请关注我的 weekly，也可以&lt;a href=&quot;https://github.com/SevenOutman?tab=stars&quot;&gt;在 GitHub 上关注我&lt;/a&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;搬家以来睡眠到现在也没有倒腾过来，常常不自觉地熬夜，第二天起不来。下周准备全力好好睡觉。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;发现了坚持运动的一个小窍门。有时候觉得懒得去运动，给自己找理由，其实困住的仅仅是“开始去运动”这一个动作而已。
只要稍微努力克服了这个阻力，开始起身换衣服，完成后面的动作并不困难（可能这就是“万事开头难”？）。
这周用这个方法成功地完成了三次运动，虽然不多但是是自己的进步。&lt;/p&gt;
&lt;p&gt;这个思路也可以尝试用在其他事情上，比如按时睡觉起床。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;听朋友们说 MBTI 测试的题目更新了们有些人测出来的结果跟以前不一样了（不过我觉得结果不一样不完全是题目的原因，而是人思考问题的方式本来也会随时间变化）。
闲来无事我也重新测了一遍，仍然是坚定的 ISTJ-A。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;每家兰州牛肉面店的粗细都不太一样，我在一些店习惯吃细的，而在另一些店习惯吃三细。可是我时常会记混，所以总是会点错。
于是现在我拿备忘录记下来了。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;粉红汉堡的孜然鸡柳好吃。取代卡乐星成为我心目中最好吃的鸡柳。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;HomePod mini 全屋联动播放的体验好棒。独自在家的时候会开书房、客厅、卧室的音箱，走到哪里音乐都不会减弱。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;和朋友们驱车出游，开心。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;和女朋友特种兵去了一趟香港，头天晚上订机票酒店第二天一早出发。&lt;/p&gt;
&lt;p&gt;现在港澳通行证办签注好方便，自己在自助终端上就能完成整个流程，几分钟就好了。&lt;/p&gt;
&lt;p&gt;最近的几趟出游以来，香港是第一个我并不感到闲适的地方，反而感到节奏快而忙碌。
就连中环站的扶梯都运行得那么快，催着人加快脚步。&lt;/p&gt;
&lt;p&gt;吃到了好吃的云吞面、烧腊、牛腩。
意外地买到了之前在京东没有抢到的 Starfield 手柄。顺带着买了一台 Switch + 王国之泪。我也来海拉鲁惹。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;山姆的奶油西瓜好吃。但是太大了，两个人吃不完。分给朋友一半，下次可以拼着买。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;终于又有时间玩 Minecraft 了。这次升级到了 1.20，女朋友又出去跑图找樱花树了。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;生活比工作内容多，开心。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w26]]></title><description><![CDATA[本周没有新收藏的歌。]]></description><link>https://doma.land/weekly/23w26/</link><guid isPermaLink="false">https://doma.land/weekly/23w26/</guid><pubDate>Tue, 04 Jul 2023 14:43:51 GMT</pubDate><content:encoded>&lt;p&gt;本周没有新收藏的歌。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;因为上周病休的缘故，收件箱里积累了一大堆待办事项。直到周三才清空。&lt;/li&gt;
&lt;li&gt;测试团队反映前端项目构建到测试环境的流水线用时太久了（接近十分钟），希望优化。
我看了下耗时比较久的步骤主要是构建镜像和安装依赖。先用 &lt;code&gt;.dockerignore&lt;/code&gt; 优化了下 Docker daemon context 的体积，减少了两分钟构建镜像的时间。
而安装依赖慢则是因为在 Docker 环境中运行的流水线里，pnpm 无法利用缓存，每次都要重新下载依赖。
参考 &lt;a href=&quot;https://www.jenkins.io/doc/book/pipeline/docker/#caching-data-for-containers&quot;&gt;Jenkins 文档&lt;/a&gt;在 Jenkinsfile 里用 Docker 的 &lt;code&gt;-v&lt;/code&gt; 参数挂载了一个卷上去，结果 pnpm 并不能对这个挂载的卷创建硬链接，最终还是用不了缓存。
查到了一些相关 issue &lt;a href=&quot;https://github.com/pnpm/pnpm/issues/6338&quot;&gt;pnpm #6338&lt;/a&gt;、&lt;a href=&quot;https://github.com/pnpm/pnpm/issues/6064&quot;&gt;pnpm #6064&lt;/a&gt;，也都没有解决，这个方向只能暂时搁置了。
下周再尝试其他方法。&lt;/li&gt;
&lt;li&gt;在一个验证“禁用早于今日的日期”的单测中学到了 &lt;a href=&quot;https://jestjs.io/docs/jest-object#jestsetsystemtimenow-number--date&quot;&gt;&lt;code&gt;jest.setSystemTime&lt;/code&gt;&lt;/a&gt; 的用法，它可以用来模拟当前日期。
不过在 Jest 中使用 fake timer 会使 &lt;code&gt;@testing-library/user-event&lt;/code&gt; 中的一些异步 API 失效，需要为 &lt;code&gt;userEvent&lt;/code&gt; 设置使用 &lt;code&gt;jest.advanceTimersByTime&lt;/code&gt; 来前进 timer 才行，详见 &lt;a href=&quot;https://github.com/testing-library/user-event/issues/833#issuecomment-1171452841&quot;&gt;相关 issue&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;给 DevOps 平台的流水线列表上增加了一个“复制流水线”操作。
其实原本在新建流水线的时候就可以在表单中选择从其他流水线复制，但当用户明确知道它想要复制列表中某个特定的流水线时，却还是需要在表单中去根据流水线名称搜索。
现在则可以直接在列表上点击他看中的流水线进行复制，虽然可能只是节省了几秒钟的时间，但我觉得这个改动是属于那种能影响用户心情的体验细节。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;开源和 Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;给 &lt;a href=&quot;https://ezlc.app&quot;&gt;Easy LeetCode&lt;/a&gt; 添加了一个“每日一题”链接，会重定向到当日的每日一题。
把链接加到提醒事项中，每次点击就直接打开题目，很方便。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/24fed16e4a32ce3edf9fa24a8f9bcf87/d0cc0/%E6%AF%8F%E6%97%A5%E4%B8%80%E9%A2%98.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 23.417721518987342%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAuUlEQVQY052P2W6DMBBF+fqo35ZuT2kVUdE+gARxsLHxguPAqbBUP7cdaRZdzRzdqeLtBmvgq/lAXK+0bcs8z/w1tm3LvUrpzuI05/cTXddR1zV93xO8z2CtNdZaYowIIYrmnMv6rq3rWqDVPljnGS4CY2aUmtDaMI4jztl8tANCCCil8N5jjMnAPaWUxV0BNs0nh8MDp7czT8+vHB9fcD787+W9LEtkGC5Mk0FKxSgVKaWy+Jv8cfgNWn+C6rfZzagAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;“每日一题”提醒事项&quot;
        title=&quot;“每日一题”提醒事项&quot;
        src=&quot;/static/24fed16e4a32ce3edf9fa24a8f9bcf87/f058b/%E6%AF%8F%E6%97%A5%E4%B8%80%E9%A2%98.png&quot;
        srcset=&quot;/static/24fed16e4a32ce3edf9fa24a8f9bcf87/c26ae/%E6%AF%8F%E6%97%A5%E4%B8%80%E9%A2%98.png 158w,
/static/24fed16e4a32ce3edf9fa24a8f9bcf87/6bdcf/%E6%AF%8F%E6%97%A5%E4%B8%80%E9%A2%98.png 315w,
/static/24fed16e4a32ce3edf9fa24a8f9bcf87/f058b/%E6%AF%8F%E6%97%A5%E4%B8%80%E9%A2%98.png 630w,
/static/24fed16e4a32ce3edf9fa24a8f9bcf87/d0cc0/%E6%AF%8F%E6%97%A5%E4%B8%80%E9%A2%98.png 732w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;需要的读者也可以收藏下。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;美国站：&lt;a href=&quot;https://ezlc.app/links/daily&quot;&gt;https://ezlc.app/links/daily&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;中国站：&lt;a href=&quot;https://ezlc.app/links/daily?region=cn&quot;&gt;https://ezlc.app/links/daily?region=cn&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;rsuite 的&lt;a href=&quot;https://github.com/rsuite/rsuite/discussions/3200&quot;&gt;第一个 good first issue&lt;/a&gt; 中的任务全部被认领完了，开心 🥳。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;这一周以来天气都十分闷热，早上没有再骑车上班了。&lt;/li&gt;
&lt;li&gt;看了下 Carl Pullein 近期的视频，其中提到了 &lt;a href=&quot;https://www.carlpullein.com/podcast/kjlqw03n3jd7mdn3u01xpc4mkdqisk/21/11/2022&quot;&gt;“The 3 unsexy productivity essentials”&lt;/a&gt;，分别是 Sleep, exercise and diet。这令我很有启发，人才是关键，如果人本身不得劲又怎么能做得好事呢？&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;希望大家好好睡觉、好好锻炼、好好吃饭。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w24+25]]></title><description><![CDATA[这两周新收藏的歌，有两首。]]></description><link>https://doma.land/weekly/23w24+25/</link><guid isPermaLink="false">https://doma.land/weekly/23w24+25/</guid><pubDate>Tue, 27 Jun 2023 11:12:20 GMT</pubDate><content:encoded>&lt;p&gt;这两周新收藏的歌，有两首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;在调研表单校验交互方案时，学到了 &lt;a href=&quot;https://en.wikipedia.org/wiki/Robustness_principle&quot;&gt;Robustness principle&lt;/a&gt;。
它可以被解读为：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Be conservative in what you do, be liberal in what you accept from others&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这非常符合我对于 React 组件等模块设计的看法，提供 API 应当保守，同时应当预期用户以及其他模块的行为是不可靠的，因此应当保守地处理这些行为。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;给前端项目添加了 &lt;a href=&quot;https://github.com/plopjs/plop&quot;&gt;Plop&lt;/a&gt; 模板，又能节省一些日常开发的重复工作&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;rsuite 的 CI 突然挂了，原因是 &lt;code&gt;aria-query&lt;/code&gt; 的一个更新引入了 breaking change，导致使用 DOM Testing Library 的 &lt;code&gt;getByRole&lt;/code&gt; 方法查询 &lt;code&gt;img&lt;/code&gt; 元素可能会失败
相关 &lt;a href=&quot;https://github.com/rsuite/rsuite/pull/3247&quot;&gt;PR 3247&lt;/a&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;开源和 Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;在写一个新的应用，现在完成度还很低，之后再公布。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://clerk.com/&quot;&gt;Clerk&lt;/a&gt; 是我近期用过的开发体验最好的平台了，API 简洁、文档友好，从注册平台到为我的应用接入身份认证，前后不到十分钟。&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;zh&quot; dir=&quot;ltr&quot;&gt;多希望自己做出的东西也能让用户有这样的感受，家人们谁懂啊🥹 &lt;a href=&quot;https://t.co/XBSzC8Vn15&quot;&gt;https://t.co/XBSzC8Vn15&lt;/a&gt; &lt;a href=&quot;https://t.co/0ssNrpTf1h&quot;&gt;pic.twitter.com/0ssNrpTf1h&lt;/a&gt;&lt;/p&gt;&amp;mdash; Doma. (@leishenghao) &lt;a href=&quot;https://twitter.com/leishenghao/status/1668827136021663744?ref_src=twsrc%5Etfw&quot;&gt;June 14, 2023&lt;/a&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/jaredpalmer/formik/releases&quot;&gt;Formik&lt;/a&gt; 居然恢复更新了&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;做题&lt;/h2&gt;
&lt;p&gt;在周一的&lt;a href=&quot;https://leetcode.cn/problems/kth-ancestor-of-a-tree-node/submissions/439389352/&quot;&gt;每日一题&lt;/a&gt;中学到了树上倍增，也学到了一个 solution 并不是要二选一地追求空间换时间或时间换空间，而是可以在中间找平衡点。&lt;/p&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;p&gt;上周病倒了，这也是为什么上周没有更新 weekly。
病倒的时候我们正好在搬家，我突然就变成废人一个，剩下女朋友忙前忙后搬来搬去，实在辛苦 😢。&lt;/p&gt;
&lt;p&gt;现在已经康复。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;愿大家都身体健康。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w23]]></title><description><![CDATA[本周新收藏的歌，有一首。]]></description><link>https://doma.land/weekly/23w23/</link><guid isPermaLink="false">https://doma.land/weekly/23w23/</guid><pubDate>Tue, 13 Jun 2023 10:51:38 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌，有一首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;听歌&lt;/h2&gt;
&lt;p&gt;这首《糸》我以前已经收藏了两个版本，分别是 &lt;a href=&quot;https://music.163.com/song?id=514765413&quot;&gt;Aimer&lt;/a&gt; 和&lt;a href=&quot;https://music.163.com/song?id=1464112021&quot;&gt;菅田将晖&lt;/a&gt;的翻唱版本，这次找到原唱了。&lt;/p&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;这周又不得不打开了 Xcode，因为要将之前开发的项目打包成 .ipa 用来测试。
Archive 的时候打包出来总是一个 Xcode generic archive 而不是 .ipa，查看包内容发现是因为打包产物中包含了一个多余的头文件。
两天查了各种相关问题和解决方法，什么 Copy Header 还有 SKIP_INSTALL 都试过了不管用。
最后排查到 Archive Log 发现有两个 Build target，终于从“为什么我只要 build 一个 target Xcode 却给我构建别的 target”的角度终于搜到了&lt;a href=&quot;https://stackoverflow.com/a/16775685&quot;&gt;一个答案&lt;/a&gt;，解决了问题。
跨技术栈 Troubleshooting 真的是一件很痛苦的事情。希望这次关闭 Xcode 之后再也不用打开了。&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;zh&quot; dir=&quot;ltr&quot;&gt;真的再也不想碰 Xcode 了，尤其是易手项目&lt;br&gt;👇我的浏览记录 &lt;a href=&quot;https://t.co/MBMvOtxTQ3&quot;&gt;pic.twitter.com/MBMvOtxTQ3&lt;/a&gt;&lt;/p&gt;&amp;mdash; Doma. (@leishenghao) &lt;a href=&quot;https://twitter.com/leishenghao/status/1666016301402460162?ref_src=twsrc%5Etfw&quot;&gt;June 6, 2023&lt;/a&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;这周给 DevOps 平台加了一个小功能。
越来越多的项目在使用 pnpm 了，之前都是在流水线里 &lt;code&gt;npm i -g pnpm&lt;/code&gt; 来安装 pnpm 的，但随着 pnpm 和 Node.js 的版本更新，已经出现过几次版本冲突的问题。
于是这次加上了在指定 Docker 镜像中运行构建步骤的支持，这样各个流水线就不会互相影响全局安装的 pnpm 了。
不过这带来一个小问题就是 &lt;code&gt;.pnpm-store&lt;/code&gt; 缓存机制就失效了，每次都要重新下载依赖导致构建变慢，之后看看怎么解决。&lt;/p&gt;
&lt;p&gt;在构建这个更新时发生了一件趣事。
在这之前，我构建 DevOps 平台的更新往往都是启动流水线后就不管了，
这次偶然留在了页面上，当构建的流水线完成的同时，之前为 DevOps 平台添加的&lt;a href=&quot;/blog/%E8%A7%A3%E5%86%B3%E5%8D%95%E9%A1%B5%E5%BA%94%E7%94%A8%E4%B8%AD%E7%9A%84%20ChunkLoadError/&quot;&gt;“DevOps 平台有更新”&lt;/a&gt;通知也弹了出来，这种感觉好奇妙。
Dogfooding 爱好者狂喜。&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;zh&quot; dir=&quot;ltr&quot;&gt;当构建 DevOps 平台本身的流水线运行完成的时候，更新通知也弹了出来，这种“我更新我自己”的感觉好奇妙 hhh &lt;a href=&quot;https://t.co/3ohSS9vvIr&quot;&gt;pic.twitter.com/3ohSS9vvIr&lt;/a&gt;&lt;/p&gt;&amp;mdash; Doma. (@leishenghao) &lt;a href=&quot;https://twitter.com/leishenghao/status/1667030509216759813?ref_src=twsrc%5Etfw&quot;&gt;June 9, 2023&lt;/a&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Radar&lt;/h2&gt;
&lt;p&gt;看了 WWDC，Vision Pro 的介绍让我眼前一亮。
它的交互出发点充分承认现实世界的存在，我认为是比 VR 和元宇宙那套东西要更有人性的。
我觉得 Apple 真的总能找到人与机器、世界交互的要义。&lt;/p&gt;
&lt;p&gt;回顾了&lt;a href=&quot;https://www.bilibili.com/video/BV1nJ411P7Xp&quot;&gt;《了不起的设计》宣传片&lt;/a&gt;、2010 年的 &lt;a href=&quot;https://www.bilibili.com/video/BV1xx411W7QG&quot;&gt;iPhone 4 和 FaceTime 发布会&lt;/a&gt;，以及 &lt;a href=&quot;https://www.bilibili.com/video/BV13a411F7xF&quot;&gt;iPhone 无障碍功能的介绍&lt;/a&gt;。
我意识到我总能从 Apple 传达的设计、产品、信息感受到“科技以人为本”这个核，这是我觉得 Apple 与其他厂商不同的地方，也是我认为 Apple 解构人机交互水准之高的源头。
当然也不排除这是一个成功的营销案例。&lt;/p&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;p&gt;周末和朋友们在家吃饭。女朋友尝试着煎了牛排，看上去像模像样的，味道也还不错。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/726ce0065f99bcbda22528b5479b9e2e/96287/%E7%89%9B%E6%8E%92.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 101.8987341772152%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBA//EABYBAQEBAAAAAAAAAAAAAAAAAAIAAf/aAAwDAQACEAMQAAAB2s4BUsjw02oZf//EABwQAQEAAgIDAAAAAAAAAAAAAAIBAAMEERIjMv/aAAgBAQABBQICeAmt16kVfYYF3eRc+itRzqTP/8QAFREBAQAAAAAAAAAAAAAAAAAAASD/2gAIAQMBAT8BI//EABURAQEAAAAAAAAAAAAAAAAAAAEg/9oACAECAQE/AWP/xAAhEAABAgQHAAAAAAAAAAAAAAAAARECIUFxEBIiMUJhkf/aAAgBAQAGPwLNGozKi3wSDkWqS1divMr6bH//xAAcEAEAAwADAQEAAAAAAAAAAAABABEhMUFhcVH/2gAIAQEAAT8hGsXJ7NZriKoadIR37QeHs/YV4lvJLwZHxcopcPcp7b7FEAT/2gAMAwEAAgADAAAAEHjYA//EABcRAQADAAAAAAAAAAAAAAAAAAEAECH/2gAIAQMBAT8QAmxv/8QAFxEBAAMAAAAAAAAAAAAAAAAAAQAQEf/aAAgBAgEBPxBIw2//xAAgEAEBAAIBAwUAAAAAAAAAAAABEQBBMSFRYXGBkbHR/9oACAEBAAE/ECiLoQFHbILeUVTHLs6Bx+4IiEqg6BefbAbdkwmxYZYMEZh7Txq+MHx9GFffOGFGJBk+MZMJevq5/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;牛排&quot;
        title=&quot;牛排&quot;
        src=&quot;/static/726ce0065f99bcbda22528b5479b9e2e/828fb/%E7%89%9B%E6%8E%92.jpg&quot;
        srcset=&quot;/static/726ce0065f99bcbda22528b5479b9e2e/ff44c/%E7%89%9B%E6%8E%92.jpg 158w,
/static/726ce0065f99bcbda22528b5479b9e2e/a6688/%E7%89%9B%E6%8E%92.jpg 315w,
/static/726ce0065f99bcbda22528b5479b9e2e/828fb/%E7%89%9B%E6%8E%92.jpg 630w,
/static/726ce0065f99bcbda22528b5479b9e2e/0ede0/%E7%89%9B%E6%8E%92.jpg 945w,
/static/726ce0065f99bcbda22528b5479b9e2e/3ac88/%E7%89%9B%E6%8E%92.jpg 1260w,
/static/726ce0065f99bcbda22528b5479b9e2e/96287/%E7%89%9B%E6%8E%92.jpg 3022w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;朋友带来了从湖南寄来的虾尾，好吃。
我学会了如何快速地用嘴剥虾尾，要先在背上的壳咬一个洞（大一点），然后就可以把虾肉比较完整地叼出来了。
比以前我用牙先咬掉虾足，再从侧面把虾壳一点点剥干净要方便多了。&lt;/p&gt;
&lt;p&gt;
		&lt;video
			src=./虾尾.mov
			width=&quot;800&quot;
			height=&quot;auto&quot;
			preload=&quot;auto&quot;
			muted=&quot;true&quot;
			title=&quot;./虾尾.mov&quot;
			autoplay
			playsinline
			controls
			loop
		&gt;&lt;/video&gt;
	&lt;/p&gt;
&lt;p&gt;跟朋友们喝酒聊天真开心。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;开心的一周。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w22]]></title><description><![CDATA[本周新收藏的歌，有两首。]]></description><link>https://doma.land/weekly/23w22/</link><guid isPermaLink="false">https://doma.land/weekly/23w22/</guid><pubDate>Tue, 06 Jun 2023 09:47:29 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌，有两首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;听歌&lt;/h2&gt;
&lt;p&gt;这周的收藏又有一个《銀の龍の背に乗って》，是因为上次收藏的那首被网易云替换音源了，变成只有伴奏了（可以回 &lt;a href=&quot;/weekly/23w20/&quot;&gt;23w20&lt;/a&gt; 里面听听看），于是又专门去找了专辑里的原版收藏了一遍。&lt;/p&gt;
&lt;p&gt;第二首歌似乎是会员专享，在我的博客上播放不了。也许我可以给播放器添加个登录网易云账号的功能，这样也许就能播放了吧 🤔️（请留言如果你希望看到这个功能）。&lt;/p&gt;
&lt;p&gt;这周还把播放器更新了下，现在进度条可以拖动了（以前只能点击），感谢 &lt;a href=&quot;https://github.com/raotaohub&quot;&gt;@raotaohub&lt;/a&gt; 为 &lt;a href=&quot;https://aplayer-react.js.org&quot;&gt;&lt;code&gt;aplayer-react&lt;/code&gt;&lt;/a&gt; 添加的这一功能。&lt;/p&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;p&gt;我发现需求文档里有些特定的业务名词，难以通顺地翻译成英文的，在代码里就不要费劲去取英文变量名了，完全可以直接用中文。
直接粘贴需求文档里的原文，既不会引起歧义页不会出错，反而能降低心智负担。&lt;/p&gt;
&lt;h2&gt;开源和 Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;周二 &lt;a href=&quot;https://twitter.com/pengx17&quot;&gt;@pengx17&lt;/a&gt; 提醒我之前提交给 AFFiNE 的 &lt;a href=&quot;https://github.com/toeverything/AFFiNE/pull/2385&quot;&gt;PR 2285&lt;/a&gt; E2E 测试失败，
我本来以为会是 flaky cases，检视后发现真的是我的实现有些问题。修复后这次终于合了 🥳&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/simonguo&quot;&gt;@simonguo&lt;/a&gt; 在 YouTube 上找到一个&lt;a href=&quot;https://www.youtube.com/playlist?list=PLxt4i7QVE689xjFnQkRKFNvrPes2pPS6b&quot;&gt;讲解 rsuite 的 playlist&lt;/a&gt;。
这很有价值，可以让我们从真实用户的视角去审视我们的文档和 API 设计，更准确地找到令用户困惑的地方，有针对性地进行改进。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;周五去见了铃铃 &lt;a href=&quot;https://twitter.com/_foreverbell&quot;&gt;@_foreverbell&lt;/a&gt;，一起去吃了晚饭。&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;ja&quot; dir=&quot;ltr&quot;&gt;和 &lt;a href=&quot;https://twitter.com/_foreverbell?ref_src=twsrc%5Etfw&quot;&gt;@_foreverbell&lt;/a&gt; 周五海底捞🥰 &lt;a href=&quot;https://t.co/stOraVuj5n&quot;&gt;pic.twitter.com/stOraVuj5n&lt;/a&gt;&lt;/p&gt;&amp;mdash; Doma. (@leishenghao) &lt;a href=&quot;https://twitter.com/leishenghao/status/1664587340902195201?ref_src=twsrc%5Etfw&quot;&gt;June 2, 2023&lt;/a&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;给 &lt;a href=&quot;https://ezlc.app&quot;&gt;Easy LeetCode&lt;/a&gt; 的前缀和 &lt;a href=&quot;https://ezlc.app/snippets/prefix-sum&quot;&gt;Prefix Sum&lt;/a&gt; 代码片段添加了对非 &lt;code&gt;number&lt;/code&gt; 类型数组的支持，在周日的&lt;a href=&quot;https://leetcode.cn/problems/count-vowel-strings-in-ranges&quot;&gt;每日一题&lt;/a&gt;中用到了它。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;p&gt;这周体重回到 70kg 以下了，希望保持住。&lt;/p&gt;
&lt;p&gt;公司的空调出风口正好在我的座位头顶。周一午睡起来感觉身上很冷，想吃点东西暖暖身子，又懒得下楼，于是第一次在公司的贩卖机里买了合味道。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fb0b8fbdeb7a023cf86e73d5276815da/d34ab/%E5%90%88%E5%91%B3%E9%81%93.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 133.54430379746836%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAbABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAMEAgH/xAAWAQEBAQAAAAAAAAAAAAAAAAABAAL/2gAMAwEAAhADEAAAAXsmRhqOFT6oQOwG/8QAHBAAAwACAwEAAAAAAAAAAAAAAAECAxEQEiEx/9oACAEBAAEFAsc7OiZ8H5jnJQxsmZ0aWp4//8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAwEBPwEf/8QAFhEAAwAAAAAAAAAAAAAAAAAAAREg/9oACAECAQE/AWI//8QAHRAAAQMFAQAAAAAAAAAAAAAAABARMQEgIUFhof/aAAgBAQAGPwJyGR9mapwj23//xAAbEAADAQEBAQEAAAAAAAAAAAAAARExIUFxEP/aAAgBAQABPyFdsqkQPti4KYr1PhXovYa+9HSNX0hi3jELestkVd/P/9oADAMBAAIAAwAAABCXOM3/xAAXEQEAAwAAAAAAAAAAAAAAAAABABAR/9oACAEDAQE/EEWZf//EABgRAAIDAAAAAAAAAAAAAAAAAAARARAh/9oACAECAQE/EFZI7//EAB4QAQACAgIDAQAAAAAAAAAAAAEAESExQXFRgZGh/9oACAEBAAE/EHNMqq8vmY6Y0INfIKzwjTEOtK3Arf6QtwHg1GFxsFPcAxI5JkEOxvoIp8jFGy4lrHUon//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;合味道&quot;
        title=&quot;合味道&quot;
        src=&quot;/static/fb0b8fbdeb7a023cf86e73d5276815da/828fb/%E5%90%88%E5%91%B3%E9%81%93.jpg&quot;
        srcset=&quot;/static/fb0b8fbdeb7a023cf86e73d5276815da/ff44c/%E5%90%88%E5%91%B3%E9%81%93.jpg 158w,
/static/fb0b8fbdeb7a023cf86e73d5276815da/a6688/%E5%90%88%E5%91%B3%E9%81%93.jpg 315w,
/static/fb0b8fbdeb7a023cf86e73d5276815da/828fb/%E5%90%88%E5%91%B3%E9%81%93.jpg 630w,
/static/fb0b8fbdeb7a023cf86e73d5276815da/0ede0/%E5%90%88%E5%91%B3%E9%81%93.jpg 945w,
/static/fb0b8fbdeb7a023cf86e73d5276815da/3ac88/%E5%90%88%E5%91%B3%E9%81%93.jpg 1260w,
/static/fb0b8fbdeb7a023cf86e73d5276815da/d34ab/%E5%90%88%E5%91%B3%E9%81%93.jpg 2784w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;周六早上写东西的时候突然左手小指根部关节好痛，涂了两天云南白药酊，很明显地缓解了，管用。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;过得飞快的一周。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w21]]></title><description><![CDATA[本周新收藏的歌，有四首。]]></description><link>https://doma.land/weekly/23w21/</link><guid isPermaLink="false">https://doma.land/weekly/23w21/</guid><pubDate>Tue, 30 May 2023 13:01:55 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌，有四首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;听歌&lt;/h2&gt;
&lt;p&gt;日推刷到 C418，老 MC 人落泪 😢。&lt;/p&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;这周终于把 iOS 项目完成了，退出 Xcode 的那一刻感觉终于解脱了。&lt;/li&gt;
&lt;li&gt;回归 Web 开发的工作内容，发现了两个 &lt;code&gt;rsuite&lt;/code&gt; 上的样式问题 &lt;a href=&quot;https://github.com/rsuite/rsuite/pull/3214&quot;&gt;PR 3214&lt;/a&gt;、&lt;a href=&quot;https://github.com/rsuite/rsuite/pull/3215&quot;&gt;PR 3215&lt;/a&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;开源和 Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;rsuite&lt;/code&gt; 的首个 Good first issue 收到了&lt;a href=&quot;https://github.com/rsuite/rsuite/pull/3205&quot;&gt;第一个 PR&lt;/a&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;给 &lt;a href=&quot;https://ezlc.app&quot;&gt;Easy LeetCode&lt;/a&gt; 添加了 Priority Queue 代码片段，并且增加了 Twitter 分享图。&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;zh&quot; dir=&quot;ltr&quot;&gt;Easy LeetCode 添加了 Priority Queue 代码片段&lt;a href=&quot;https://t.co/vRThg2Ahdj&quot;&gt;https://t.co/vRThg2Ahdj&lt;/a&gt;&lt;/p&gt;&amp;mdash; Doma. (@leishenghao) &lt;a href=&quot;https://twitter.com/leishenghao/status/1660880269182910466?ref_src=twsrc%5Etfw&quot;&gt;May 23, 2023&lt;/a&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;一位 &lt;code&gt;rsuite&lt;/code&gt; 的 ex-member 在 &lt;a href=&quot;https://github.com/rsuite/rsuite/pull/3204&quot;&gt;PR 3204&lt;/a&gt; 中演示了使用 Cypress 进行 TDD。
说实话这版本 Cypress UI 还挺讨我喜欢，而且每一个用例都有 DOM 快照，对于 TDD 来说应该比现在的 karma + webpack 好用。
但是 Cypress 采用的异步阻塞模型会使断言失败得比较慢，不知道算是好事还是坏事。
得闲可以更深入地体验一下。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;周六去杭州参加了开源之夏线下 meetup，见到了一众推友。
临时准备了闪电演讲的内容，简短地向大家介绍了一下 &lt;code&gt;rsuite&lt;/code&gt;。
因为准备得仓促，表现得不太令自己满意。下次再有机会的话，要准备得更充分一些才行。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;搜了下我的&lt;a href=&quot;https://github.com/vuejs/vuex/pull/394&quot;&gt;第一个 PR&lt;/a&gt;，居然是提给 Vuex 的。
如今已经离开 Vue 多年了 🫠&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;偶然注意到 GitHub Stats 今年居然不知不觉混到 S 了 🥳
（不过后来发现似乎是因为前两天他们的算法改了 😬
（但这是不是意味着我之前就应该达到 S 了 🤭
&lt;img src=&quot;https://pbs.twimg.com/media/FxIp28_aAAAS4q8?format=jpg&amp;#x26;name=medium&quot; alt=&quot;My GitHub Stats&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;意外地将 &lt;code&gt;rsuite&lt;/code&gt; 安利给了 &lt;a href=&quot;https://twitter.com/Manjusaka_Lee&quot;&gt;@Manjusaka&lt;/a&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;没睡好的一周。不总结了，赶快睡觉去了。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w20]]></title><description><![CDATA[本周新收藏的歌，有四首。]]></description><link>https://doma.land/weekly/23w20/</link><guid isPermaLink="false">https://doma.land/weekly/23w20/</guid><pubDate>Mon, 22 May 2023 11:13:48 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌，有四首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;听歌&lt;/h2&gt;
&lt;p&gt;这周听日推居然刷到了《銀の龍の背に乗って》！我一直都觉得把手术刀比喻为银龙这个意象真的太美了。
另外推荐一下&lt;a href=&quot;https://b23.tv/bIVCgAy&quot;&gt;这个版本的 live&lt;/a&gt;，感觉勇气与希望要溢出来了。&lt;/p&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;p&gt;这周把 iOS 项目原来乱七八糟的界面用 Static Table View 改成了类似 iOS 系统设置页那样的风格，看起来顺眼些了。
只是这一个 ViewController 里面为多个 Static Table View 分别设置 Cell 居然是要在 DataSource/Delegate 的方法里去判断 &lt;code&gt;tableView&lt;/code&gt; 的引用，感觉呆呆的。而如果用单个 Static Table View，为各个 Section 设置 Cell 也是需要在 DataSource/Delegate 的方法里去判断 &lt;code&gt;indexPath.section&lt;/code&gt;，还是呆呆的。&lt;/p&gt;
&lt;h2&gt;开源和 Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;给 AFFiNE 的新版 Sidebar 提了拖拽页面到垃圾箱的交互 &lt;a href=&quot;https://github.com/toeverything/AFFiNE/pull/2385&quot;&gt;PR 2385&lt;/a&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;之前写的&lt;a href=&quot;https://doma.land/blog/%E4%B8%BA%E4%BD%A0%E7%9A%84%20React%20%E5%BA%94%E7%94%A8%E6%B7%BB%E5%8A%A0%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E5%99%A8/&quot;&gt;关于 &lt;code&gt;aplayer-react&lt;/code&gt; 的博文&lt;/a&gt;收到了第一条评论，开心。
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 70.88607594936708%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAABYlAAAWJQFJUiTwAAAB2klEQVQ4y52SW4/aMBCF/f9/zKovfV+pfVmxrbpdLgsJCQkEQu7EdhxfklPZgBa1asXW0idbnvHxHHuIv03x+H0O2rbIixKMd2gpg5QK4zhiGEYM450MA0hyLPB18gNVVYJSir6X4LyD0hp2WNF7hxUlSimkaYqWUvzvGC/VWTfkWkHLOrfxZ9It428MLk8qhebUQhsDEh1yTKYeqrpBmhWoTxRV04J1PaQe0CsDqc15LbVD2Fm9r238RDm6XoKs4hSfvzzjafINszcPSy/AYrV2rMMYfhC5veliiX2aYxMnSNIMfhghjHeIkwMWKx9htIOQCsSa1AMQbRMkhwzrMMI2OcALNgiiLdab2Im9vM6dg6ZlDuvEYisr6xMoF5DKgHiej08PDxexGNt9imNRIytqcCGd5SviYvGWrlcuVjUUXPQgVVliMZ/h5+wNSz9w5a/WobNlBe+l5eJsOdzneJr6yIoS+2PubsqrBi0T7nabfJ3/ho3XJ4ZOSBB/d8Tj8xQvr1PMFkts4p2r0g82rpVuRf9F3bLzL2szuD5aZg38qEaQMvcWVuwjls9vqUHs76R5iUNeoWg4WG/7S116TN2NtSt6BWIPU8bBGIcQPYwZoLSB/gjGuDOWXw3jM655oAS3AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;博文评论&quot;
        title=&quot;博文评论&quot;
        src=&quot;/static/87033967a7a35fe9633b4836ee7292a1/f058b/%E5%8D%9A%E6%96%87%E8%AF%84%E8%AE%BA.png&quot;
        srcset=&quot;/static/87033967a7a35fe9633b4836ee7292a1/c26ae/%E5%8D%9A%E6%96%87%E8%AF%84%E8%AE%BA.png 158w,
/static/87033967a7a35fe9633b4836ee7292a1/6bdcf/%E5%8D%9A%E6%96%87%E8%AF%84%E8%AE%BA.png 315w,
/static/87033967a7a35fe9633b4836ee7292a1/f058b/%E5%8D%9A%E6%96%87%E8%AF%84%E8%AE%BA.png 630w,
/static/87033967a7a35fe9633b4836ee7292a1/40601/%E5%8D%9A%E6%96%87%E8%AF%84%E8%AE%BA.png 945w,
/static/87033967a7a35fe9633b4836ee7292a1/78612/%E5%8D%9A%E6%96%87%E8%AF%84%E8%AE%BA.png 1260w,
/static/87033967a7a35fe9633b4836ee7292a1/3c492/%E5%8D%9A%E6%96%87%E8%AF%84%E8%AE%BA.png 1300w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;这位读者随后向 &lt;code&gt;aplayer-react&lt;/code&gt; 提交了 &lt;a href=&quot;https://github.com/SevenOutman/aplayer-react/pull/17&quot;&gt;PR 17&lt;/a&gt;，在 review 的过程中 ta 所展现出来的认真劲着实有些打动我。我也尽力去做了一个耐心和 helpful 的 reviewer。希望 ta 能够继续保持这样的热情。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;这周为 &lt;code&gt;rsuite&lt;/code&gt; 开启了&lt;a href=&quot;https://github.com/rsuite/rsuite/discussions/3200&quot;&gt;第一个 good first issue&lt;/a&gt;，并且得到了响应，感谢群友们的捧场 🥹 也希望更多的社区成员能够参与进来。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;报名了 5.27 的开源之夏，期待见到推友们。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;做题&lt;/h2&gt;
&lt;p&gt;多做题确实会越来越熟练，这周有&lt;a href=&quot;https://leetcode.cn/problems/minimum-difficulty-of-a-job-schedule/submissions/432800412/&quot;&gt;一道 Hard 的动态规划&lt;/a&gt;，因为联想到之前做过的一道类似思路的题，用了比想象中更少的时间就做出来了。&lt;/p&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;p&gt;周末和女朋友进淄赶烤了。烤五花肉串口感好嫩，跟我想象中韩式烤肉那种烤的焦焦的感觉截然不同，冒着油配上小葱卷在小饼里吃好过瘾。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/116d9197b3c371da9c365f052b72d4de/96287/%E6%B7%84%E5%8D%9A%E7%83%A7%E7%83%A4.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 133.54430379746836%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAbABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAMEAgH/xAAXAQADAQAAAAAAAAAAAAAAAAAAAQID/9oADAMBAAIQAxAAAAGdXZ5YVg34bXmTmCj/xAAbEAACAwEBAQAAAAAAAAAAAAABAgADETIEEv/aAAgBAQABBQIktMGMH3PkGwk5kqB0+ZddFaUd2cVoDX//xAAVEQEBAAAAAAAAAAAAAAAAAAAQAf/aAAgBAwEBPwEh/8QAFxEBAAMAAAAAAAAAAAAAAAAAAAIQIf/aAAgBAgEBPwFqNf/EACAQAAIBAQkAAAAAAAAAAAAAAAARASECEBIxMkFRcYH/2gAIAQEABj8CWwmZSailDgl1gm05Hifl09EM/8QAGhABAQEAAwEAAAAAAAAAAAAAAREAITFBUf/aAAgBAQABPyEiwy/tfZkOQOss+nF63ig1krSWZKE9D5kyx3MsWKcLWSTeE5wCfGNtWfXf/9oADAMBAAIAAwAAABDMAMz/xAAXEQEBAQEAAAAAAAAAAAAAAAABACER/9oACAEDAQE/EM5ZITZNv//EABkRAAMAAwAAAAAAAAAAAAAAAAABESFRYf/aAAgBAgEBPxCtsuhwJ4P/xAAeEAEAAgIDAQEBAAAAAAAAAAABABEhMUFRcWGRsf/aAAgBAQABPxAyq6bG0435FtUC238MwUdiy4iBRqe2Oh79iEHwUkplILTfsNwS6MdC+/sMBTag+1AMdoTDq6igq1ThgOSy1AWdJgv4z//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;淄博烧烤&quot;
        title=&quot;淄博烧烤&quot;
        src=&quot;/static/116d9197b3c371da9c365f052b72d4de/828fb/%E6%B7%84%E5%8D%9A%E7%83%A7%E7%83%A4.jpg&quot;
        srcset=&quot;/static/116d9197b3c371da9c365f052b72d4de/ff44c/%E6%B7%84%E5%8D%9A%E7%83%A7%E7%83%A4.jpg 158w,
/static/116d9197b3c371da9c365f052b72d4de/a6688/%E6%B7%84%E5%8D%9A%E7%83%A7%E7%83%A4.jpg 315w,
/static/116d9197b3c371da9c365f052b72d4de/828fb/%E6%B7%84%E5%8D%9A%E7%83%A7%E7%83%A4.jpg 630w,
/static/116d9197b3c371da9c365f052b72d4de/0ede0/%E6%B7%84%E5%8D%9A%E7%83%A7%E7%83%A4.jpg 945w,
/static/116d9197b3c371da9c365f052b72d4de/3ac88/%E6%B7%84%E5%8D%9A%E7%83%A7%E7%83%A4.jpg 1260w,
/static/116d9197b3c371da9c365f052b72d4de/96287/%E6%B7%84%E5%8D%9A%E7%83%A7%E7%83%A4.jpg 3022w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;吃到了传说中的博山炸肉，好吃！一开始听名字我以为会是类似小酥肉的样子，上桌来看外观又以为应该是炸得焦焦脆脆的口感，结果其实也是非常嫩，而且并不油腻。吃个不停，还打包了一些带回来。淄博人料理猪肉真的有一套。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3826a8e86b00c9ef837183b13ffc3c56/df94c/%E5%8D%9A%E5%B1%B1%E7%82%B8%E8%82%89.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 74.68354430379746%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAIDBAX/xAAXAQADAQAAAAAAAAAAAAAAAAAAAgME/9oADAMBAAIQAxAAAAHdFYZ7dEQU/8QAGhABAQADAQEAAAAAAAAAAAAAAQIAAxETIv/aAAgBAQABBQJTXPod4OVqpyYaT4P/xAAWEQEBAQAAAAAAAAAAAAAAAAABABH/2gAIAQMBAT8BAsL/xAAWEQADAAAAAAAAAAAAAAAAAAAAARH/2gAIAQIBAT8BdKz/xAAaEAACAgMAAAAAAAAAAAAAAAABEQAQEiEy/9oACAEBAAY/Atx40X2qRM//xAAcEAADAAIDAQAAAAAAAAAAAAAAAREhQTFRYXH/2gAIAQEAAT8hwxPCqp9ZEomtjXDHJbRCSvtyDQZp/9oADAMBAAIAAwAAABAfH//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAEDAQE/EGkP/8QAGBEBAQADAAAAAAAAAAAAAAAAAQARIWH/2gAIAQIBAT8QYcurvf/EAB0QAAICAgMBAAAAAAAAAAAAAAERACFBgVFhcbH/2gAIAQEAAT8QPr45BnQjAknQHiBKYBgw8AbQsvWa+QWK80MHkEViOrGup//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;博山炸肉&quot;
        title=&quot;博山炸肉&quot;
        src=&quot;/static/3826a8e86b00c9ef837183b13ffc3c56/828fb/%E5%8D%9A%E5%B1%B1%E7%82%B8%E8%82%89.jpg&quot;
        srcset=&quot;/static/3826a8e86b00c9ef837183b13ffc3c56/ff44c/%E5%8D%9A%E5%B1%B1%E7%82%B8%E8%82%89.jpg 158w,
/static/3826a8e86b00c9ef837183b13ffc3c56/a6688/%E5%8D%9A%E5%B1%B1%E7%82%B8%E8%82%89.jpg 315w,
/static/3826a8e86b00c9ef837183b13ffc3c56/828fb/%E5%8D%9A%E5%B1%B1%E7%82%B8%E8%82%89.jpg 630w,
/static/3826a8e86b00c9ef837183b13ffc3c56/0ede0/%E5%8D%9A%E5%B1%B1%E7%82%B8%E8%82%89.jpg 945w,
/static/3826a8e86b00c9ef837183b13ffc3c56/3ac88/%E5%8D%9A%E5%B1%B1%E7%82%B8%E8%82%89.jpg 1260w,
/static/3826a8e86b00c9ef837183b13ffc3c56/df94c/%E5%8D%9A%E5%B1%B1%E7%82%B8%E8%82%89.jpg 4030w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;开心的一周。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w19]]></title><description><![CDATA[本周新收藏的歌，有三首。]]></description><link>https://doma.land/weekly/23w19/</link><guid isPermaLink="false">https://doma.land/weekly/23w19/</guid><pubDate>Mon, 15 May 2023 10:18:54 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌，有三首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;听歌&lt;/h2&gt;
&lt;p&gt;这周听日推被推到一首坎特雷拉的交响乐版，有点好奇这个乐团还改编过哪些 V 曲，点进去一看居然有 Reboot 🥰。&lt;/p&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Xcode Storyboard 用得稍微比之前熟练点了，其实多用 StackView 的话，用 Storyboard 排界面也并不那么麻烦。
这周做了一个 Container View 内嵌 &lt;code&gt;UIPageViewController&lt;/code&gt; 切换多个子页面的功能，学到了 Swift 中 Delegate 和 DataSource 的概念和用法，配合 &lt;code&gt;extension&lt;/code&gt; 语法，有点 Mixin 的味道，开始觉得 iOS 有意思起来了。
不过也遇到一个没能解决的问题，就是在 &lt;code&gt;UIPageViewController&lt;/code&gt; 中切换子 &lt;code&gt;ViewController&lt;/code&gt; 时，子 &lt;code&gt;ViewController&lt;/code&gt; 的生命周期没有触发。下周得解决这个问题。&lt;/li&gt;
&lt;li&gt;这周还要给一个老项目中的脚本做改动。因为是老项目，所以没有单元测试。突发奇想决定不装测试库了，直接用 Node.js 自带的 Test Runner 试一下，还真的可以。不过 CI 环境中的 Node.js 版本暂时不支持 &lt;code&gt;--experimental-test-coverage&lt;/code&gt;，没法在流水线上显示测试覆盖率了。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;开源和 Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;两周前试用 &lt;a href=&quot;https://affine.pro/&quot;&gt;AFFiNE&lt;/a&gt; 觉得有些需要改进的地方，这次给这些改进项提交了&lt;a href=&quot;https://github.com/pulls?q=is%3Apr+author%3ASevenOutman+archived%3Afalse+org%3Atoeverything&quot;&gt;一些 PR&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;从页面列表中将页面拖拽至收藏夹 &lt;a href=&quot;https://github.com/toeverything/AFFiNE/pull/2216&quot;&gt;PR 2216&lt;/a&gt;，学到了 &lt;a href=&quot;https://dndkit.com/&quot;&gt;&lt;code&gt;dnd-kit&lt;/code&gt;&lt;/a&gt; 的用法&lt;/li&gt;
&lt;li&gt;光标位于末段末行时按 &lt;kbd&gt;ArrowDown&lt;/kbd&gt; 将光标移至行尾 &lt;a href=&quot;https://github.com/toeverything/blocksuite/pull/2418&quot;&gt;blocksuite PR 2418&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;修复了侧边菜单的键盘导航顺序 &lt;a href=&quot;https://github.com/toeverything/AFFiNE/pull/2256&quot;&gt;PR 2256&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;添加了新建页面的快捷键 &lt;a href=&quot;https://github.com/toeverything/AFFiNE/pull/2267&quot;&gt;PR 2267&lt;/a&gt;，学到了 Electron 中的 &lt;a href=&quot;https://www.electronjs.org/docs/latest/tutorial/keyboard-shortcuts#local-shortcuts&quot;&gt;&lt;code&gt;Menu&lt;/code&gt;&lt;/a&gt; 用法以及&lt;a href=&quot;https://www.electronjs.org/docs/latest/tutorial/ipc&quot;&gt;跨进程通信机制&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;应 &lt;a href=&quot;https://twitter.com/ewind1994&quot;&gt;@ewind1994&lt;/a&gt; 老师邀请，进行了视频连线。听雪碧老师亲授 blocksuite 的原理以及 AFFiNE 的展望，收获颇丰。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;修复了 xLog 上一个菜单定位的问题 &lt;a href=&quot;https://github.com/Crossbell-Box/xLog/pull/524&quot;&gt;PR 524&lt;/a&gt;，学到了 &lt;a href=&quot;https://floating-ui.com/&quot;&gt;Floating UI&lt;/a&gt; 的用法。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;这周跟 &lt;a href=&quot;https://twitter.com/suyanhanx&quot;&gt;@suyanhanx&lt;/a&gt; 见了面。还打卡了我的第一张推特合影。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1eb378d18ee43ce73224347948ba6754/72e01/photo-with-suyanhax.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.31645569620254%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAEEA//EABUBAQEAAAAAAAAAAAAAAAAAAAID/9oADAMBAAIQAxAAAAF6TOTlAm//xAAaEAACAwEBAAAAAAAAAAAAAAACAwABERIT/9oACAEBAAEFAlLE7JQUubO688n/xAAXEQEAAwAAAAAAAAAAAAAAAAABAhAR/9oACAEDAQE/AZDtf//EABcRAQADAAAAAAAAAAAAAAAAAAECEBH/2gAIAQIBAT8BgmV//8QAGhAAAQUBAAAAAAAAAAAAAAAAAQACEBESIf/aAAgBAQAGPwJwKea6LgrMf//EABoQAQACAwEAAAAAAAAAAAAAAAEAERAhMWH/2gAIAQEAAT8h5l1K4ODcJoBFv2o3gI1zD//aAAwDAQACAAMAAAAQhx//xAAWEQEBAQAAAAAAAAAAAAAAAAABEBH/2gAIAQMBAT8QRon/xAAXEQEAAwAAAAAAAAAAAAAAAAAAAREh/9oACAECAQE/EMeVv//EABsQAQEBAQEAAwAAAAAAAAAAAAERIQAxQaHB/9oACAEBAAE/EKIsUTEb8PEU9ChxzLOGHEYs+WI305igIPUq5f3rhX77/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;合影&quot;
        title=&quot;合影&quot;
        src=&quot;/static/1eb378d18ee43ce73224347948ba6754/828fb/photo-with-suyanhax.jpg&quot;
        srcset=&quot;/static/1eb378d18ee43ce73224347948ba6754/ff44c/photo-with-suyanhax.jpg 158w,
/static/1eb378d18ee43ce73224347948ba6754/a6688/photo-with-suyanhax.jpg 315w,
/static/1eb378d18ee43ce73224347948ba6754/828fb/photo-with-suyanhax.jpg 630w,
/static/1eb378d18ee43ce73224347948ba6754/0ede0/photo-with-suyanhax.jpg 945w,
/static/1eb378d18ee43ce73224347948ba6754/72e01/photo-with-suyanhax.jpg 1024w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;做题&lt;/h2&gt;
&lt;p&gt;最近在打卡每日一题，这周的题目有些收获&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;BFS 在有些场景下可以用优先队列来代替普通队列，优化搜索顺序&lt;/li&gt;
&lt;li&gt;两数差的绝对值，可转化为两数中的较大者减去两数中的较小者，从而将问题转化为求最大值和最小值的问题&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;做题之余把一些常会复用的数据结构或者算法整理成了 &lt;a href=&quot;https://ezlc.app&quot;&gt;Easy LeetCode&lt;/a&gt;，供自己后续参考，也欢迎读者参考。&lt;/p&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;尝试骑车上下班已经有几周了。在保持日常活动量的基础上，相比步行，骑车通勤用时更短，能够节省时间来专门进行锻炼（比如跳绳），目前来看是更优解。于是买了包月卡，准备以骑车为主要通勤方式了。
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8e4fb138b30e8aa8854ba0f1ba042c09/72e01/%E9%AA%91%E8%A1%8C%E5%8D%A1.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 26.58227848101266%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAFABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHcsJIX/8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQABBQJ//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQAGPwJ//8QAGRABAAIDAAAAAAAAAAAAAAAAAQAREDFB/9oACAEBAAE/IUt3Ao7j/9oADAMBAAIAAwAAABB8D//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAEDAQE/ECf/xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAgEBPxBn/8QAGRABAAMBAQAAAAAAAAAAAAAAAQARITFh/9oACAEBAAE/EH6QvhkyhXq3Azt+z//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;骑行卡&quot;
        title=&quot;骑行卡&quot;
        src=&quot;/static/8e4fb138b30e8aa8854ba0f1ba042c09/828fb/%E9%AA%91%E8%A1%8C%E5%8D%A1.jpg&quot;
        srcset=&quot;/static/8e4fb138b30e8aa8854ba0f1ba042c09/ff44c/%E9%AA%91%E8%A1%8C%E5%8D%A1.jpg 158w,
/static/8e4fb138b30e8aa8854ba0f1ba042c09/a6688/%E9%AA%91%E8%A1%8C%E5%8D%A1.jpg 315w,
/static/8e4fb138b30e8aa8854ba0f1ba042c09/828fb/%E9%AA%91%E8%A1%8C%E5%8D%A1.jpg 630w,
/static/8e4fb138b30e8aa8854ba0f1ba042c09/0ede0/%E9%AA%91%E8%A1%8C%E5%8D%A1.jpg 945w,
/static/8e4fb138b30e8aa8854ba0f1ba042c09/72e01/%E9%AA%91%E8%A1%8C%E5%8D%A1.jpg 1024w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;抽到了麦当劳的 100 块免费麦乐鸡，当了一回麦选之子。周五当作组里的下午茶和同事们分享了。
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2671209e10baeed8338719889b057301/d3883/%E9%BA%A6%E9%80%89%E4%B9%8B%E5%AD%90.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 127.21518987341773%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAZABQDASIAAhEBAxEB/8QAGQABAAIDAAAAAAAAAAAAAAAAAAECAwQF/8QAFwEAAwEAAAAAAAAAAAAAAAAAAAECA//aAAwDAQACEAMQAAAB5UbFc6qyLkkMGv/EABwQAAMAAgMBAAAAAAAAAAAAAAECEQADEiAhMv/aAAgBAQABBQLwM3Ga/lFuaUrATBRgo6//xAAVEQEBAAAAAAAAAAAAAAAAAAAQEv/aAAgBAwEBPwEo/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAgEBPwEf/8QAHBAAAQUAAwAAAAAAAAAAAAAAAQACEBEhICJh/9oACAEBAAY/AtCxsOugu1AIj2d4f//EAB0QAQACAQUBAAAAAAAAAAAAAAEAESEgMUFRYZH/2gAIAQEAAT8hybt/sSz77uDomZlHmo7nFDu8yzYJakiswN+xMqDfsFc7tH//2gAMAwEAAgADAAAAEFgIwf/EABoRAAICAwAAAAAAAAAAAAAAAAEhABEQMZH/2gAIAQMBAT8QAQxZa4ITbn//xAAXEQADAQAAAAAAAAAAAAAAAAABEBFR/9oACAECAQE/EDBTii//xAAfEAEAAgICAgMAAAAAAAAAAAABABEhQVFhIDGRocH/2gAIAQEAAT8Qo1BZfdRQrJ2fpHFuMJKYJZUtPfBLQJmVC3Beu4tFGgJeEitbzCor6jkPkjPxLJRTp8P/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;麦选之子&quot;
        title=&quot;麦选之子&quot;
        src=&quot;/static/2671209e10baeed8338719889b057301/828fb/%E9%BA%A6%E9%80%89%E4%B9%8B%E5%AD%90.jpg&quot;
        srcset=&quot;/static/2671209e10baeed8338719889b057301/ff44c/%E9%BA%A6%E9%80%89%E4%B9%8B%E5%AD%90.jpg 158w,
/static/2671209e10baeed8338719889b057301/a6688/%E9%BA%A6%E9%80%89%E4%B9%8B%E5%AD%90.jpg 315w,
/static/2671209e10baeed8338719889b057301/828fb/%E9%BA%A6%E9%80%89%E4%B9%8B%E5%AD%90.jpg 630w,
/static/2671209e10baeed8338719889b057301/0ede0/%E9%BA%A6%E9%80%89%E4%B9%8B%E5%AD%90.jpg 945w,
/static/2671209e10baeed8338719889b057301/d3883/%E9%BA%A6%E9%80%89%E4%B9%8B%E5%AD%90.jpg 1169w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6f036777ba9837f4e57283b589168c76/46d48/%E9%BA%A6%E4%B9%90%E9%B8%A1x100.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGgABAAIDAQAAAAAAAAAAAAAAAAMEAQIFBv/EABYBAQEBAAAAAAAAAAAAAAAAAAECAP/aAAwDAQACEAMQAAABknk2i6a0Z4voDOQb/8QAGxAAAgIDAQAAAAAAAAAAAAAAAQIAAwQREhD/2gAIAQEAAQUC5baVgG5WZ2AdZxMWxg2vP//EABgRAAIDAAAAAAAAAAAAAAAAAAABEBEx/9oACAEDAQE/AdKFH//EABgRAAIDAAAAAAAAAAAAAAAAAAABEBEx/9oACAECAQE/AcLHH//EABwQAAICAgMAAAAAAAAAAAAAAAABMUEQERIhIv/aAAgBAQAGPwKNCdnmDac3js41n//EABwQAQEAAgIDAAAAAAAAAAAAAAERAEEhURAxgf/aAAgBAQABPyGYv1cfpCaykRw7yE3CRga9hvvOStZFmrTgCzx//9oADAMBAAIAAwAAABCj/wCD/8QAGBEAAgMAAAAAAAAAAAAAAAAAAAEQETH/2gAIAQMBAT8QaFTEf//EABgRAAMBAQAAAAAAAAAAAAAAAAABERAx/9oACAECAQE/EE6KOs//xAAcEAEAAwEAAwEAAAAAAAAAAAABABEhMUFhgbH/2gAIAQEAAT8QtAhOBt5yCKSFwPyDq8TabsxbECvJYF2mC1Xs8TYUrlPCBQrLKyPob+S5/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;麦乐鸡x100&quot;
        title=&quot;麦乐鸡x100&quot;
        src=&quot;/static/6f036777ba9837f4e57283b589168c76/828fb/%E9%BA%A6%E4%B9%90%E9%B8%A1x100.jpg&quot;
        srcset=&quot;/static/6f036777ba9837f4e57283b589168c76/ff44c/%E9%BA%A6%E4%B9%90%E9%B8%A1x100.jpg 158w,
/static/6f036777ba9837f4e57283b589168c76/a6688/%E9%BA%A6%E4%B9%90%E9%B8%A1x100.jpg 315w,
/static/6f036777ba9837f4e57283b589168c76/828fb/%E9%BA%A6%E4%B9%90%E9%B8%A1x100.jpg 630w,
/static/6f036777ba9837f4e57283b589168c76/0ede0/%E9%BA%A6%E4%B9%90%E9%B8%A1x100.jpg 945w,
/static/6f036777ba9837f4e57283b589168c76/46d48/%E9%BA%A6%E4%B9%90%E9%B8%A1x100.jpg 1098w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;这周总的来说效率其实是有点低的，有许多计划的事情没有完成。
感觉刚从假期回来对时间的把握有点松散，下周应该注意。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w18]]></title><description><![CDATA[这周放假。]]></description><link>https://doma.land/weekly/23w18/</link><guid isPermaLink="false">https://doma.land/weekly/23w18/</guid><pubDate>Tue, 09 May 2023 10:35:00 GMT</pubDate><content:encoded>&lt;p&gt;这周放假。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;回家跟爸妈度过了轻松的一周，很开心。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w17]]></title><description><![CDATA[这周没有听日推，也没有新收藏的歌。]]></description><link>https://doma.land/weekly/23w17/</link><guid isPermaLink="false">https://doma.land/weekly/23w17/</guid><pubDate>Mon, 01 May 2023 20:46:18 GMT</pubDate><content:encoded>&lt;p&gt;这周没有听日推，也没有新收藏的歌。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;终于把 &lt;code&gt;rsuite&lt;/code&gt; &lt;a href=&quot;https://github.com/rsuite/rsuite/pull/3079&quot;&gt;PR 3079&lt;/a&gt; 磕完了，离 v6 又近了一步。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;继续重构 Picker 组件原地修改 &lt;code&gt;data&lt;/code&gt; prop 的旧实现（例如 &lt;a href=&quot;https://github.com/rsuite/rsuite/pull/3173&quot;&gt;PR 3173&lt;/a&gt;）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;给 Tree 类组件添加了新的 &lt;code&gt;onSelectItem&lt;/code&gt; 回调（&lt;a href=&quot;https://github.com/rsuite/rsuite/pull/3172&quot;&gt;PR 3172&lt;/a&gt;），在下个 major 版本中准备把旧的 &lt;code&gt;onSelect&lt;/code&gt; 回调给废弃掉，因为参数设计的不好，而且跟原生 DOM 的回调是重名的。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;又向产品经理们反馈了几个体验问题和优化建议，但是否有机会实施仍然只能随缘。
其中一个改进项做了个 demo 发给了设计同事，其评价如下：&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a6b2f15304f45605b2cec603b1dbf0f5/8c557/%E8%AE%BE%E8%AE%A1%E7%9A%84%E8%AF%84%E4%BB%B7.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 18.9873417721519%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAABYlAAAWJQFJUiTwAAAArklEQVQY04WPSQ6CQBREvf9BjBOocQJxiIiAXkDxAqhbCA1NwzPdbo3Wpiq/kvp5naqqoAUpJUIImqZBKUXbtsbrujau+zzPTdbS/Td1wjDGsqdE0Zlbcme+WOG4HpY9Ye1t2e0PuOsNUXwhTR8IUf4eDIKQbm+A7wdcrwlDa8xy5dLrj5gvHDbbPZPpDP944vl8GYqfg/qjxtToRVEYRI2nVIOUn6xvZVmSZf+R31ofK1zi9/OkAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;设计的评价&quot;
        title=&quot;设计的评价&quot;
        src=&quot;/static/a6b2f15304f45605b2cec603b1dbf0f5/f058b/%E8%AE%BE%E8%AE%A1%E7%9A%84%E8%AF%84%E4%BB%B7.png&quot;
        srcset=&quot;/static/a6b2f15304f45605b2cec603b1dbf0f5/c26ae/%E8%AE%BE%E8%AE%A1%E7%9A%84%E8%AF%84%E4%BB%B7.png 158w,
/static/a6b2f15304f45605b2cec603b1dbf0f5/6bdcf/%E8%AE%BE%E8%AE%A1%E7%9A%84%E8%AF%84%E4%BB%B7.png 315w,
/static/a6b2f15304f45605b2cec603b1dbf0f5/f058b/%E8%AE%BE%E8%AE%A1%E7%9A%84%E8%AF%84%E4%BB%B7.png 630w,
/static/a6b2f15304f45605b2cec603b1dbf0f5/8c557/%E8%AE%BE%E8%AE%A1%E7%9A%84%E8%AF%84%E4%BB%B7.png 700w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;跟 iOS 死磕的又一周，Objective-C 真是妖怪语言。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;闰周一有点难受，但周四下班的时候天还亮着（甚至还蓝着），开心。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/748cc92d2a01a447ab607eb9fd8a1da9/df94c/%E5%A4%A9%E4%BA%AE%E4%B8%8B%E7%8F%AD.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 74.68354430379746%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMEAv/EABUBAQEAAAAAAAAAAAAAAAAAAAID/9oADAMBAAIQAxAAAAGhUuqylGhX/8QAGxAAAwACAwAAAAAAAAAAAAAAAAECAwQSExT/2gAIAQEAAQUCcnFmVUrW7SXrR3qj/8QAFhEBAQEAAAAAAAAAAAAAAAAAABES/9oACAEDAQE/Aco//8QAFhEBAQEAAAAAAAAAAAAAAAAAABES/9oACAECAQE/Adq//8QAGhAAAgIDAAAAAAAAAAAAAAAAAAECIRARMf/aAAgBAQAGPwLFI4XFm3E//8QAGhABAQADAQEAAAAAAAAAAAAAAQARIUExgf/aAAgBAQABPyFHXZJ8ZuJYoletu+CGo85v/9oADAMBAAIAAwAAABCs3//EABYRAQEBAAAAAAAAAAAAAAAAAAEAEf/aAAgBAwEBPxBpgv/EABYRAQEBAAAAAAAAAAAAAAAAAAEAEf/aAAgBAgEBPxAc7dv/xAAbEAEBAQACAwAAAAAAAAAAAAABEQAhMUFxgf/aAAgBAQABPxCdFXH3SyT607SD1gNWYEplqEeQi7q3K15Jxv/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;天亮下班&quot;
        title=&quot;天亮下班&quot;
        src=&quot;/static/748cc92d2a01a447ab607eb9fd8a1da9/828fb/%E5%A4%A9%E4%BA%AE%E4%B8%8B%E7%8F%AD.jpg&quot;
        srcset=&quot;/static/748cc92d2a01a447ab607eb9fd8a1da9/ff44c/%E5%A4%A9%E4%BA%AE%E4%B8%8B%E7%8F%AD.jpg 158w,
/static/748cc92d2a01a447ab607eb9fd8a1da9/a6688/%E5%A4%A9%E4%BA%AE%E4%B8%8B%E7%8F%AD.jpg 315w,
/static/748cc92d2a01a447ab607eb9fd8a1da9/828fb/%E5%A4%A9%E4%BA%AE%E4%B8%8B%E7%8F%AD.jpg 630w,
/static/748cc92d2a01a447ab607eb9fd8a1da9/0ede0/%E5%A4%A9%E4%BA%AE%E4%B8%8B%E7%8F%AD.jpg 945w,
/static/748cc92d2a01a447ab607eb9fd8a1da9/3ac88/%E5%A4%A9%E4%BA%AE%E4%B8%8B%E7%8F%AD.jpg 1260w,
/static/748cc92d2a01a447ab607eb9fd8a1da9/df94c/%E5%A4%A9%E4%BA%AE%E4%B8%8B%E7%8F%AD.jpg 4030w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;开源和 Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;做了一个微小的玩意 &lt;a href=&quot;https://github.com/SevenOutman/csbg&quot;&gt;&lt;code&gt;csbg&lt;/code&gt;&lt;/a&gt;，可以在浏览器控制台里打出 shields.io 风格的徽章。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a17d62ddf071882385372ab7d7a70071/487bb/csbg.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 21.51898734177215%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAuElEQVQY05WNTQ7BQBzFezAb13AOCyt7B5A4gKW4hIWdxE5SX2VaRTuqo6aYzk9KJFjxkvdPXt7H3zHmRqoUQbjlICVJtEd4S9ZCoMKQdOqSnzV5HJP5Pich0FlGHkXI8Ri1WHAKAnR+oYRTnuR4JPAFq90BN9a40Znsah4B+05rPzTfnrXPwV6/T7NRp9bqUumMqLaHDObyUTDFq/obnHJVSsna85j5OyZhymSToPS1fIspCoo/eAddiTBA8qxzRQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;csbg&quot;
        title=&quot;csbg&quot;
        src=&quot;/static/a17d62ddf071882385372ab7d7a70071/f058b/csbg.png&quot;
        srcset=&quot;/static/a17d62ddf071882385372ab7d7a70071/c26ae/csbg.png 158w,
/static/a17d62ddf071882385372ab7d7a70071/6bdcf/csbg.png 315w,
/static/a17d62ddf071882385372ab7d7a70071/f058b/csbg.png 630w,
/static/a17d62ddf071882385372ab7d7a70071/487bb/csbg.png 698w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;给 &lt;a href=&quot;https://xlog.app&quot;&gt;xLog&lt;/a&gt; 水起了 &lt;a href=&quot;https://github.com/Crossbell-Box/xLog/pulls?q=is%3Apr+author%3ASevenOutman+&quot;&gt;PR&lt;/a&gt;，贡献一些微小的无障碍经验。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;试用了 &lt;a href=&quot;https://affine.pro&quot;&gt;AFFiNE&lt;/a&gt;。
老实说，其完成度与我基于推友们的评价的想象有些差距。假期过后也准备提交一些微小的 PR。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;看完了&lt;a href=&quot;https://learn.deeplearning.ai/chatgpt-prompt-eng/&quot;&gt;吴恩达的 PE 入门课&lt;/a&gt;，有一些收获。比之前单看文字确实要更容易吸收一点。推荐跟我一样刚入门的读者看一看。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;写作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;周一把 &lt;a href=&quot;/blog/%E8%A7%A3%E5%86%B3%E5%8D%95%E9%A1%B5%E5%BA%94%E7%94%A8%E4%B8%AD%E7%9A%84%20ChunkLoadError&quot;&gt;ChunkLoadError 的文章&lt;/a&gt;同步到了&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzA3ODIxNTU0OQ==&amp;#x26;mid=2455604493&amp;#x26;idx=1&amp;#x26;sn=18244520f80aab5fe627840ef07b6c32&amp;#x26;chksm=88ef0d1fbf9884097e423e53cdb671658bb2f91e9db9d9b94ba576be83aafe45de33f6a21500#rd&quot;&gt;公众号&lt;/a&gt;、&lt;a href=&quot;https://juejin.cn/post/7225424380469264441&quot;&gt;掘金&lt;/a&gt;和 &lt;a href=&quot;https://xlog.doma.land/jie-jue-dan-ye-ying-yong-zhong-de-ChunkLoadError&quot;&gt;xLog&lt;/a&gt;。还为它做了一个简单的封面图。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5557c953390066e7e5a088ea6e9800a0/c1b63/twitter-summary-card.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.89873417721519%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABL0lEQVQoz32S3UqEQBTH5yl6h96v+x6i264TgkCotNqL0lIixWC9WE1vxUC0dnVxyfH8o1Fbd3M6cJjDnDO/OV8MnRAmhIiEju2puzGDjWC0D+sNEPE/IW3bCu2h1L8hNgXZZtL+Iso1F3bTNEJllbBxGaMft9HNB5CeYvV6hNvLc1xd65jN7pCmKZIkQVVVO2AGieR5hs/VF+pEx/LmEKuLAwQPJ/Dmb7CeHuG6LgzDgK7rME0TlmWBcw72k1UYhlgsFoiiCL7vQ9M0KMoZXhwHz7aJuang/f4YfBnv9HCz2SDPcwRBgDjufKwsS9i2DcdxoKoqPM8T4CzLRHBVrTEe49AW2VaIKRdFgbquSVY+tbxTaidXiDoRLjYYkkDZzk1lR8OUae+Xfx/KeMP5DTyJAlp2XWj0AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;封面图&quot;
        title=&quot;封面图&quot;
        src=&quot;/static/5557c953390066e7e5a088ea6e9800a0/f058b/twitter-summary-card.png&quot;
        srcset=&quot;/static/5557c953390066e7e5a088ea6e9800a0/c26ae/twitter-summary-card.png 158w,
/static/5557c953390066e7e5a088ea6e9800a0/6bdcf/twitter-summary-card.png 315w,
/static/5557c953390066e7e5a088ea6e9800a0/f058b/twitter-summary-card.png 630w,
/static/5557c953390066e7e5a088ea6e9800a0/40601/twitter-summary-card.png 945w,
/static/5557c953390066e7e5a088ea6e9800a0/c1b63/twitter-summary-card.png 1200w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;一开始我在 Figma 里是用 Section 来创建 artboard，导出的时候，左上角总是多出来一个小矩形，怎么改都去不掉。
问了设计同事才知道，原来 Figma 里的 Frame 才是对应的 Sketch 的 artboard，用 Frame 的话就可以正常导出画板内容本身了。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;公众号收到了第一条评论，开心。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/38d83bf0964d06a7444c41f353d5dead/ad68d/%E5%85%AC%E4%BC%97%E5%8F%B7%E8%AF%84%E8%AE%BA.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 48.10126582278481%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAceEDKD/xAAYEAACAwAAAAAAAAAAAAAAAAAAEQECIP/aAAgBAQABBQKyeIP/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAWEAEBAQAAAAAAAAAAAAAAAAARICL/2gAIAQEABj8Cylf/xAAaEAACAgMAAAAAAAAAAAAAAAAAEQExECFB/9oACAEBAAE/IYP3cOxInNT/2gAMAwEAAgADAAAAEIDP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAHBAAAgICAwAAAAAAAAAAAAAAAREAIRBBMWFx/9oACAEBAAE/ECiE3wgJ2twkQYThXqNYG/s//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;公众号评论&quot;
        title=&quot;公众号评论&quot;
        src=&quot;/static/38d83bf0964d06a7444c41f353d5dead/828fb/%E5%85%AC%E4%BC%97%E5%8F%B7%E8%AF%84%E8%AE%BA.jpg&quot;
        srcset=&quot;/static/38d83bf0964d06a7444c41f353d5dead/ff44c/%E5%85%AC%E4%BC%97%E5%8F%B7%E8%AF%84%E8%AE%BA.jpg 158w,
/static/38d83bf0964d06a7444c41f353d5dead/a6688/%E5%85%AC%E4%BC%97%E5%8F%B7%E8%AF%84%E8%AE%BA.jpg 315w,
/static/38d83bf0964d06a7444c41f353d5dead/828fb/%E5%85%AC%E4%BC%97%E5%8F%B7%E8%AF%84%E8%AE%BA.jpg 630w,
/static/38d83bf0964d06a7444c41f353d5dead/0ede0/%E5%85%AC%E4%BC%97%E5%8F%B7%E8%AF%84%E8%AE%BA.jpg 945w,
/static/38d83bf0964d06a7444c41f353d5dead/ad68d/%E5%85%AC%E4%BC%97%E5%8F%B7%E8%AF%84%E8%AE%BA.jpg 1170w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;刚发现原来 markdown 的图片是可以跟列表项对齐的。
希望 Day One 和其他 Block-based editor 能学习一个。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;完成了至少跳绳两次的目标 _(:3」∠)_，给自己一点微小的鼓励 👏。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;但是没有完成至少三天按时睡觉的目标，需要继续努力。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;周三和朋友们去吃了小龙虾。
其中有一份叫什么王的个头还挺大，但我拍不出它的大来。
尝试着用网上看来的方法，用人像模式拍，也没有很好的效果（只记住了人像模式没学到如何构图）。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9485b2acd4c8349031748eda83d35fae/d0e0f/%E5%B0%8F%E9%BE%99%E8%99%BE.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 74.68354430379746%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAMEBf/EABYBAQEBAAAAAAAAAAAAAAAAAAIBA//aAAwDAQACEAMQAAABzpyGnSSGf//EABoQAAIDAQEAAAAAAAAAAAAAAAIDAAESISL/2gAIAQEAAQUCDaRplzhQmd2EFnn/xAAWEQEBAQAAAAAAAAAAAAAAAAABABL/2gAIAQMBAT8BELd//8QAFxEBAAMAAAAAAAAAAAAAAAAAAAERIf/aAAgBAgEBPwHVS//EAB4QAAIBAwUAAAAAAAAAAAAAAAABESGBkRIiMlFx/9oACAEBAAY/AquxWGTpwblNzi8nXh//xAAbEAADAQADAQAAAAAAAAAAAAABESEAMUFRYf/aAAgBAQABPyFiBB0YUwFEHmIQxHXSJrc3wxoESHmf/9oADAMBAAIAAwAAABBU/wD/xAAYEQADAQEAAAAAAAAAAAAAAAAAARExUf/aAAgBAwEBPxBBHpHD/8QAFREBAQAAAAAAAAAAAAAAAAAAAQD/2gAIAQIBAT8QRTX/xAAeEAEAAgIBBQAAAAAAAAAAAAABABEhYZExQVGB8P/aAAgBAQABPxBPHdC12rxid5MGNaC4DNCeQO4q3MWUXvqMqTkfBVw6QBQmDkn/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;小龙虾&quot;
        title=&quot;小龙虾&quot;
        src=&quot;/static/9485b2acd4c8349031748eda83d35fae/828fb/%E5%B0%8F%E9%BE%99%E8%99%BE.jpg&quot;
        srcset=&quot;/static/9485b2acd4c8349031748eda83d35fae/ff44c/%E5%B0%8F%E9%BE%99%E8%99%BE.jpg 158w,
/static/9485b2acd4c8349031748eda83d35fae/a6688/%E5%B0%8F%E9%BE%99%E8%99%BE.jpg 315w,
/static/9485b2acd4c8349031748eda83d35fae/828fb/%E5%B0%8F%E9%BE%99%E8%99%BE.jpg 630w,
/static/9485b2acd4c8349031748eda83d35fae/0ede0/%E5%B0%8F%E9%BE%99%E8%99%BE.jpg 945w,
/static/9485b2acd4c8349031748eda83d35fae/3ac88/%E5%B0%8F%E9%BE%99%E8%99%BE.jpg 1260w,
/static/9485b2acd4c8349031748eda83d35fae/d0e0f/%E5%B0%8F%E9%BE%99%E8%99%BE.jpg 3338w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;下周计划&lt;/h2&gt;
&lt;p&gt;因为放假了，也没有再去赶着周日完成 weekly，拖到了周一。&lt;/p&gt;
&lt;p&gt;本该在周日做的下周计划，也拖到了今天。
不过我很喜欢放假期间的周计划，下周计划如下 XD&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2a5a7ae48045a377352ba3eddf2ae916/41d3b/23w18.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 25.316455696202528%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAxklEQVQY04WP2wpFYBCF9ytw48I5IqcLJCWkJIcISXn/F1m7mdqu7L0v1sysVfP9/7x0XUdVVSiKAnVdo2ka9nmec5amKXfKyrKEZVlQFAWapj3qRcC2bbGuK+Z5xjAM3Ldtw3EcuK4L+77jPE/2vu9DluXfQNu2+WWSYRhwHAemaUIQBEiSBFEUb31+p6rqM5AWl2XBNE3oug7jOLLv+57PTZIEWZaxaP4GuoFU6AyS53kIggCu67KnOYoixHGMMAw5+wd8A6zZlqZRxn6bAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;23w18&quot;
        title=&quot;23w18&quot;
        src=&quot;/static/2a5a7ae48045a377352ba3eddf2ae916/f058b/23w18.png&quot;
        srcset=&quot;/static/2a5a7ae48045a377352ba3eddf2ae916/c26ae/23w18.png 158w,
/static/2a5a7ae48045a377352ba3eddf2ae916/6bdcf/23w18.png 315w,
/static/2a5a7ae48045a377352ba3eddf2ae916/f058b/23w18.png 630w,
/static/2a5a7ae48045a377352ba3eddf2ae916/41d3b/23w18.png 774w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;不总结了，去 have good rest 了。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w16]]></title><description><![CDATA[这周新收藏的歌，有六首。]]></description><link>https://doma.land/weekly/23w16/</link><guid isPermaLink="false">https://doma.land/weekly/23w16/</guid><pubDate>Sun, 23 Apr 2023 20:37:40 GMT</pubDate><content:encoded>&lt;p&gt;这周新收藏的歌，有六首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;前两周不得不零基础接手一个 Android 的 demo，好不容易完成了任务，这周又不得不零基础接手一个 iOS 的 demo。我的感想？如下：&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;zh&quot; dir=&quot;ltr&quot;&gt;如果说还有比开发安卓应用更痛苦的事，那就是开发 iOS 应用了🥵 &lt;a href=&quot;https://t.co/6cydsUtTVp&quot;&gt;https://t.co/6cydsUtTVp&lt;/a&gt;&lt;/p&gt;&amp;mdash; Doma. (@leishenghao) &lt;a href=&quot;https://twitter.com/leishenghao/status/1648625789833969664?ref_src=twsrc%5Etfw&quot;&gt;April 19, 2023&lt;/a&gt;&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;在 DevOps 平台上尝试了行内编辑的交互，来代替现有的一些仅有一个表单项的抽屉表单。自我感觉还不错。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Another user-experience improvement by replacing a single-field form in drawer with inline-editing! Comparison at 00:19 &lt;a href=&quot;https://t.co/CoYPo5pV3y&quot;&gt;pic.twitter.com/CoYPo5pV3y&lt;/a&gt;&lt;/p&gt;&amp;mdash; Doma. (@leishenghao) &lt;a href=&quot;https://twitter.com/leishenghao/status/1648972179357450240?ref_src=twsrc%5Etfw&quot;&gt;April 20, 2023&lt;/a&gt;&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;周三的时候，企业微信莫名其妙被封号了，说是有违规行为，永久封禁。跟公司的企业微信管理员一起申诉了好几次，到周五终于解封了。不过始终没有反馈封号的具体原因。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;听歌&lt;/h2&gt;
&lt;p&gt;某天工作的时候突然冒出一些焦虑情绪，并且反常地难以摆脱，没法集中精神工作。
正当我为此烦恼时，突然耳虫起了 &lt;a href=&quot;https://music.163.com/song?id=33937479&quot;&gt;Hand in Hand&lt;/a&gt;。
对啊，livetune 的歌也太能冲破焦虑了。于是在网易云搜起了 livetune，播放！
果然焦虑立即无影无踪了。&lt;/p&gt;
&lt;p&gt;这也是为什么这周的收藏里有这么多 livetune 的歌。&lt;/p&gt;
&lt;p&gt;另外，我将平时日推里刷到的那些十年前我在听的初音未来的歌整理到了&lt;a href=&quot;https://music.163.com/playlist?id=8305474106&quot;&gt;一个歌单&lt;/a&gt;里，持续更新中。&lt;/p&gt;
&lt;h2&gt;Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;上周 &lt;a href=&quot;https://xlog.app&quot;&gt;xLog&lt;/a&gt; 添加了音乐播放器的支持，用的是 &lt;a href=&quot;https://github.com/SevenOutman/aplayer-react&quot;&gt;&lt;code&gt;aplayer-react&lt;/code&gt;&lt;/a&gt;。这周发的宣传贴下面也提及了我，感动 🥹。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;🙌 Huge thanks to &lt;a href=&quot;https://twitter.com/OwOSteveYu?ref_src=twsrc%5Etfw&quot;&gt;@OwOSteveYu&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/leishenghao?ref_src=twsrc%5Etfw&quot;&gt;@leishenghao&lt;/a&gt; for bringing APlayer to xLog &lt;a href=&quot;https://t.co/5k4GWmIIDT&quot;&gt;https://t.co/5k4GWmIIDT&lt;/a&gt;&lt;/p&gt;&amp;mdash; xLog - Write.Own.Earn. (@_xLog) &lt;a href=&quot;https://twitter.com/_xLog/status/1647814645749563392?ref_src=twsrc%5Etfw&quot;&gt;April 17, 2023&lt;/a&gt;&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;这周 &lt;code&gt;aplayer-react&lt;/code&gt; 收到了&lt;a href=&quot;https://github.com/SevenOutman/aplayer-react/pull/14&quot;&gt;第一个外部 PR&lt;/a&gt;，来自 &lt;a href=&quot;https://twitter.com/__oQuery&quot;&gt;@Innei&lt;/a&gt;，开心。&lt;/li&gt;
&lt;li&gt;有一个三周前想到的关于 ChatGPT 应用的独特点子，却一直没有试图腾出时间来做。今天看到推上有人&lt;a href=&quot;https://twitter.com/jdan/status/1649812358791823363&quot;&gt;做出来了&lt;/a&gt;，不免有些沮丧，不知道自己天天瞎忙到底忙了些什么。我想，我需要重新审视我管理时间的方式，让我能更主动地掌握时间来用在想做的事情上。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;周三晚上和朋友们去看了《灌篮高手》电影。电影确实比电视动画的艺术表现空间要大得多，片头五个人的编号 sketch 出来那里真的太令人激动了。比赛最后半分钟的呈现也非常扣人心弦。非常推荐。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.31645569620254%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABAAD/8QAFgEBAQEAAAAAAAAAAAAAAAAAAQAC/9oADAMBAAIQAxAAAAHJImGjxaP/xAAaEAADAAMBAAAAAAAAAAAAAAABAgMAERIj/9oACAEBAAEFAhMAx8ss23kx13j9F//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAEDAQE/ASf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAZEAADAQEBAAAAAAAAAAAAAAAAAQIRECH/2gAIAQEABj8CWUP01ky+M//EABoQAQADAQEBAAAAAAAAAAAAAAEAETFRIYH/2gAIAQEAAT8h9/L2DCytp8lmFWE4gd7Mtb2XZQn/2gAMAwEAAgADAAAAEHAf/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQAhMf/aAAgBAwEBPxBB2y//xAAXEQEBAQEAAAAAAAAAAAAAAAABABEh/9oACAECAQE/ENd5K3//xAAdEAEBAAMAAgMAAAAAAAAAAAABEQAhMUGBUWHh/9oACAEBAAE/ELYdgCB9OIDyAQgp6zeiRPWGS6F7afODvEWrtn5j2sNB8Qz/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;灌篮高手&quot;
        title=&quot;灌篮高手&quot;
        src=&quot;/static/c3cb9dc187e47b8ff4d3dc5e4e004111/828fb/%E7%81%8C%E7%AF%AE%E9%AB%98%E6%89%8B.jpg&quot;
        srcset=&quot;/static/c3cb9dc187e47b8ff4d3dc5e4e004111/ff44c/%E7%81%8C%E7%AF%AE%E9%AB%98%E6%89%8B.jpg 158w,
/static/c3cb9dc187e47b8ff4d3dc5e4e004111/a6688/%E7%81%8C%E7%AF%AE%E9%AB%98%E6%89%8B.jpg 315w,
/static/c3cb9dc187e47b8ff4d3dc5e4e004111/828fb/%E7%81%8C%E7%AF%AE%E9%AB%98%E6%89%8B.jpg 630w,
/static/c3cb9dc187e47b8ff4d3dc5e4e004111/0ede0/%E7%81%8C%E7%AF%AE%E9%AB%98%E6%89%8B.jpg 945w,
/static/c3cb9dc187e47b8ff4d3dc5e4e004111/3ac88/%E7%81%8C%E7%AF%AE%E9%AB%98%E6%89%8B.jpg 1260w,
/static/c3cb9dc187e47b8ff4d3dc5e4e004111/eea4a/%E7%81%8C%E7%AF%AE%E9%AB%98%E6%89%8B.jpg 1280w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;周六去看了车展，没想到偶然见到了 Bronco 实车。我只在墨西哥有一辆，有种次元壁破了的感觉。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;不总结了，赶快睡觉去了。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[解决单页应用中的 ChunkLoadError]]></title><description><![CDATA[公司内部的 DevOps 平台在每周一下班后会进行一次部署更新。在 Sentry 上，我观察到在更新部署的第二天，常常会集中地收到大量 ChunkLoadError 的上报。  本文记录和分享我在 DevOps 平台项目（下文中简称“DevOps…]]></description><link>https://doma.land/blog/解决单页应用中的 ChunkLoadError/</link><guid isPermaLink="false">https://doma.land/blog/解决单页应用中的 ChunkLoadError/</guid><pubDate>Wed, 19 Apr 2023 13:02:00 GMT</pubDate><content:encoded>&lt;p&gt;公司内部的 DevOps 平台在每周一下班后会进行一次部署更新。在 Sentry 上，我观察到在更新部署的第二天，常常会集中地收到大量 &lt;code&gt;ChunkLoadError&lt;/code&gt; 的上报。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/118582dea2a20ed6e8be077bdf644479/8affb/Sentry%E6%88%AA%E5%9B%BE.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 91.13924050632912%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAYAAABb0P4QAAAACXBIWXMAABYlAAAWJQFJUiTwAAACQUlEQVQ4y3VUibKiQAz0/3/xXav45Ia5MgcgvZUMory3a1VXJ1FDz5DOSduI93OPr2LA13Xc489iwHelkWJCZIS4cc5Tmg4587quOIUwoRschpEwao9+cOhHQts7jIoQfBR4CvA+g3NuEDinsH+/3lecKMz4bhxuLaFsvXDVedwah6ojUTKlSfg1nqaXekwS8+dEfkLZWFStRdPTxk6YVT5UiUJ6xln5sS5H5oa32uLGTTvKzTuH79qgbu3hyME/G+UjR2lE5IVFYYgzutGjHwMGHSXuxoBeZfz3yD/jac4KrYs4X0dcvkdRxVzctPC1VLDGCYy2e8wg53OsHbSygmW5s8IFrUrodEIzPtFvOf+In3y/r8K/8KN+8j6hrBSqWqHtrXDdGtSNRtPqg0KjH7GDs/RSz+qloaEJ59KhqAnninAuCX9KQlE5qac0Y5kXuaN5ZizCy7Jg5to05xF63CEHxpCAKMAagnMB1no46+Xt8lvkO2N4enAel2d9GxtlE97OIz4KhY+rwdtF4aPQ+CwU3i9KHhJCPLhExidsc7jP5daQj6RM2BDFfhyPOsDY8PQy+/cV4pDp8J00dDThUhpcK4trzQNtxXZFadD2JEshq4mbh3MuS2GvB4nXdbMeO4P/3PQeNduPHdM6DMojpXTcMvG5bdKjvm2iPDZhEt/Wm5ebrTnng6JdxW/rvWwh8tsdIm8bPiIrKsXLblfINkzxqOyf97kp3q1X3LLNbrXJXGmpVc3rYLuD9Z6D7Q6D/Rcw7mwknaW4wAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Sentry 截图&quot;
        title=&quot;Sentry 截图&quot;
        src=&quot;/static/118582dea2a20ed6e8be077bdf644479/f058b/Sentry%E6%88%AA%E5%9B%BE.png&quot;
        srcset=&quot;/static/118582dea2a20ed6e8be077bdf644479/c26ae/Sentry%E6%88%AA%E5%9B%BE.png 158w,
/static/118582dea2a20ed6e8be077bdf644479/6bdcf/Sentry%E6%88%AA%E5%9B%BE.png 315w,
/static/118582dea2a20ed6e8be077bdf644479/f058b/Sentry%E6%88%AA%E5%9B%BE.png 630w,
/static/118582dea2a20ed6e8be077bdf644479/40601/Sentry%E6%88%AA%E5%9B%BE.png 945w,
/static/118582dea2a20ed6e8be077bdf644479/8affb/Sentry%E6%88%AA%E5%9B%BE.png 1254w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;本文记录和分享我在 DevOps 平台项目（下文中简称“DevOps 平台”）中如何解决用户遇到的 &lt;code&gt;ChunkLoadError&lt;/code&gt; 错误。&lt;/p&gt;
&lt;h2&gt;ChunkLoadError 的原因&lt;/h2&gt;
&lt;p&gt;根据 Sentry 上记录的 &lt;code&gt;ChunkLoadError&lt;/code&gt; 出现时的用户操作日志，发现它主要发生在当用户点击链接跳转到其他页面时，可以分析其成因来自我们在构建单页应用时采取的以下实践：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;为了减少初次下载资源的体积，我们常常会对项目进行代码分割（通过 &lt;code&gt;React.lazy&lt;/code&gt; 和 &lt;code&gt;import()&lt;/code&gt; 函数）。在 DevOps 平台的例子中，所有的功能页面都被分割到了分包中。&lt;/li&gt;
&lt;li&gt;构建应用时，为了更好地利用浏览器缓存，将输出脚本的内容哈希作为脚本文件名称的一部份。以 webpack 为例，可能设置 &lt;code&gt;filename&lt;/code&gt; 为 &lt;code&gt;[name].[contenthash:8].js&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;每次更新部署时，在新一次的构建中，由于代码发生了变化，因此构建出的分包文件名称也会发生了化。
例如，在上一次的构建中，某功能页面 A 构建出的分包脚本名称为 &lt;code&gt;A.oldhash.js&lt;/code&gt;，而在新一次的构建中，其构建出的分包脚本名称变成了 &lt;code&gt;A.newhash.js&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;如果用户在更新部署前访问了你的应用，他的浏览器所运行的就是旧版的应用代码，代码中包含的分包信息也是旧的，链接 A 会请求 &lt;code&gt;A.oldhash.js&lt;/code&gt;。
当部署更新后，服务器上不再存在 &lt;code&gt;A.oldhash.js&lt;/code&gt;，此时如果用户点击链接 A，就会因为请求的分包文件不存在而产生错误。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4cac2fdfd2204679aa96f358c8daf261/30c0b/ChunkLoadError%E7%95%8C%E9%9D%A2%E6%88%AA%E5%9B%BE.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 65.18987341772153%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAABYlAAAWJQFJUiTwAAABxklEQVQ4y52Ry27TQBSGvWdNX4I9TeGNoI4qoUoVb9C04pkQm1I1blGTxo0T0qZCKGlsz9hzOTPj2Ad5HBMj6kqw+PQfnctnWeNcfP7ycjS8fH87f+je38/dmFB3PL1zr28C1w/uXD+YPckomLkDf+Le+FN35E/3/cHt4dfz/ivH867eLlchriKCYRgjpSmGEcHlKkJCGZKEbbNZ0xSXq9jekJAgffiJ/vXgg3PhfevEVABnkCmVaQBjlMqM1pnN5yh3NntSmxyD4Pu+0+9f7nFuZSilLp4CoKJtLqU2AAaDYOpaIWMyKxub4zKxSoMKNHKpMRFVXfb+3LHCrFVYLtbUh49sjUGcY5xuZQ3ahQCm2CQqMMilwTnNccEKXAnECSmQ8Ay1qnbqm1Yh51BwLsu0MAa4pAoTBpgywJCCrauZrNMKyzf4S0hIWsQxxThOfpOQBBePCf5YEKRk248iWmchhLLC8bgSdhq/nCuVFTUAVQppLNCYNalf2Qo972pPCJVrvcbyK23oZ2ZKZevyfjKZdZ2zs/PdKKKCUmYoTRWlTP8rhKQiSTgOh/47p9s9eNHrnbw+Pj7p/D+93dPTT2+Ojj7u/AJtEJkDFYRP8QAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ChunkLoadError 界面截图&quot;
        title=&quot;ChunkLoadError 界面截图&quot;
        src=&quot;/static/4cac2fdfd2204679aa96f358c8daf261/f058b/ChunkLoadError%E7%95%8C%E9%9D%A2%E6%88%AA%E5%9B%BE.png&quot;
        srcset=&quot;/static/4cac2fdfd2204679aa96f358c8daf261/c26ae/ChunkLoadError%E7%95%8C%E9%9D%A2%E6%88%AA%E5%9B%BE.png 158w,
/static/4cac2fdfd2204679aa96f358c8daf261/6bdcf/ChunkLoadError%E7%95%8C%E9%9D%A2%E6%88%AA%E5%9B%BE.png 315w,
/static/4cac2fdfd2204679aa96f358c8daf261/f058b/ChunkLoadError%E7%95%8C%E9%9D%A2%E6%88%AA%E5%9B%BE.png 630w,
/static/4cac2fdfd2204679aa96f358c8daf261/40601/ChunkLoadError%E7%95%8C%E9%9D%A2%E6%88%AA%E5%9B%BE.png 945w,
/static/4cac2fdfd2204679aa96f358c8daf261/78612/ChunkLoadError%E7%95%8C%E9%9D%A2%E6%88%AA%E5%9B%BE.png 1260w,
/static/4cac2fdfd2204679aa96f358c8daf261/30c0b/ChunkLoadError%E7%95%8C%E9%9D%A2%E6%88%AA%E5%9B%BE.png 2956w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;基于此产生原因，本文针对三种引起 &lt;code&gt;ChunkLoadError&lt;/code&gt; 错误的具体情形，逐步解决此问题。&lt;/p&gt;
&lt;h2&gt;解决 ChunkLoadError&lt;/h2&gt;
&lt;p&gt;这个问题的核心在于，应用已经更新了部署，但用户侧运行的仍然是旧的代码。
因此，解决问题的关键就是，在更新了部署后，让用户侧尽可能快地更新到新的代码。为了做到这一点，有如下方法。&lt;/p&gt;
&lt;h3&gt;避免入口缓存&lt;/h3&gt;
&lt;p&gt;通常，我们会为了节省客户端流量，而对大部分的资源，特别是体积较大的资源进行缓存。这使得在更新部署后，当用户再次访问资源，如果缓存尚未过期，客户端就会使用缓存的资源，从而运行旧的代码。
例如，我们的入口脚本名为 &lt;code&gt;app.js&lt;/code&gt;，当更新了部署后，用户再次请求 &lt;code&gt;app.js&lt;/code&gt; 文件时，如果缓存仍未过期，则用户仍会得到旧的 &lt;code&gt;app.js&lt;/code&gt;，也即运行旧版应用，导致加载分包时出现 &lt;code&gt;ChunkLoadError&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;针对这个问题，我们可以在构建阶段，将脚本内容的哈希值作为输出文件名的一部分。这样一来，在脚本内容未发生改变时，浏览器使用缓存的脚本是没有问题的，而当脚本内容发生改变，脚本名称也会改变，也就不存在缓存问题。
例如，旧版本的入口脚本名为 &lt;code&gt;app.oldhash.js&lt;/code&gt;，而新版本的叫做 &lt;code&gt;app.newhash.js&lt;/code&gt;，就能避免缓存问题。&lt;/p&gt;
&lt;p&gt;但是，如果我们对入口 HTML 文件也进行了缓存，那么在更新部署后，如果缓存尚未过期，客户端获得的就仍是旧的 HTML 内容，包括其中引用的脚本名，这样可能会引起错误（但是这里不是 &lt;code&gt;ChunkLoadError&lt;/code&gt; 而是直接入口脚本 404）。因此，我们可以对入口 HTML 文件设置为不缓存，即可解决此问题。并且，因为入口 HTML 文件的体积本身并不大（通常只有几 kB），即使每次都下载，也并没有太大的影响。&lt;/p&gt;
&lt;p&gt;示例 Nginx 配置如下：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;lua&quot;&gt;&lt;pre class=&quot;language-lua&quot;&gt;&lt;code class=&quot;language-lua&quot;&gt;&lt;span class=&quot;token function&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    location &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$request_filename &lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;\&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;htm&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;html&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;$&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            add_header Cache&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Control no&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;cache&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;主动刷新客户端应用&lt;/h3&gt;
&lt;p&gt;第二种情况是，用户在更新部署前就访问了应用，一直没有退出，因此部署更新后仍然处于旧版的应用中，这种情况上面的方法就不适用，因为用户没有去再次请求入口 HTML 文件。
这时我们可以想办法让用户主动进行刷新从而获得新的应用。例如，我们可以在检测到应用更新时，向用户显示一个浮窗提示，让用户主动点击刷新。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9c20f36969d61f326e2e064d2f272011/30c0b/%E6%9B%B4%E6%96%B0%E6%8F%90%E7%A4%BA%E6%88%AA%E5%9B%BE.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 65.18987341772153%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAABYlAAAWJQFJUiTwAAACcElEQVQ4y32SXUsUURjHR9ZKVKKiIKiQLuou+ghtdBMOgXcmBUVa7ZwNdTJ3DDUk6CN00cfwInKnC9GbxSixS28yRFCx2ZnZeTsvc54nzsysrGE+8Oc5nJff8z/POdr5U2cuX7rQN37x2nXzxs1bk1euDph9/WdNravb1LpKRT5epdJps1TqmdS6eyf6zvTP9Pb039ZGRh7dez1t4XTNwrm3b7BmzeKEOY2kOo7V6kSW/ydzcgoXrBq+n5/Fd7U5fPL46by2vLJajhLGEZEjAlP5j+PzMEyKuZO1cYC8voUxIuL6+kZNW/qyVHabLjDGUaQAUURha8eBvX0HPC+AIIghDJNjFQQJbB9Q2NylIo4ZNhprlmZ/tcutVgvSNFVFgFKOYUyRc4EylSilRM45qvX2OFuTEtNUIqIExFQwJrDR+GZptm2XwzAEAEAEBMY5eoGDcZxkh1QIIQ7HClwUR3VGHZQSBBdp4dC2y77fyoGqiUwBmxhHcQZR021HncCs/iFQCs7T3GG9Xi9TSqGomAF9BexwqK7Z6VbpqMN/gIyxo8DQOwQqJ5SxrF9qnAPTk4GUUlnwJGUc/NCFOIrVQ2UXQ8nzjAhcCBBCQGdIKXkOzHt4V23APEA5iZIAWfY1EZMU8bfDcXMvQT/vDLY3d4RQHVlb+z6jLS4u3nEcJw2CAHy/JR2nCTu727C3uw++54Hr+fD5pwOfVh348cuDqOVC03VBfbW2fN/nruvh8vJKTRsaGjpHCLlPCNEJIYOVSkUfff5MHxsd0ysVQ39VJfoHq6J/XCD63BTRX7w0dGIYerE/k2EYg4RUHwwPPxz4C5teSHYAAF6xAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;更新提示截图&quot;
        title=&quot;更新提示截图&quot;
        src=&quot;/static/9c20f36969d61f326e2e064d2f272011/f058b/%E6%9B%B4%E6%96%B0%E6%8F%90%E7%A4%BA%E6%88%AA%E5%9B%BE.png&quot;
        srcset=&quot;/static/9c20f36969d61f326e2e064d2f272011/c26ae/%E6%9B%B4%E6%96%B0%E6%8F%90%E7%A4%BA%E6%88%AA%E5%9B%BE.png 158w,
/static/9c20f36969d61f326e2e064d2f272011/6bdcf/%E6%9B%B4%E6%96%B0%E6%8F%90%E7%A4%BA%E6%88%AA%E5%9B%BE.png 315w,
/static/9c20f36969d61f326e2e064d2f272011/f058b/%E6%9B%B4%E6%96%B0%E6%8F%90%E7%A4%BA%E6%88%AA%E5%9B%BE.png 630w,
/static/9c20f36969d61f326e2e064d2f272011/40601/%E6%9B%B4%E6%96%B0%E6%8F%90%E7%A4%BA%E6%88%AA%E5%9B%BE.png 945w,
/static/9c20f36969d61f326e2e064d2f272011/78612/%E6%9B%B4%E6%96%B0%E6%8F%90%E7%A4%BA%E6%88%AA%E5%9B%BE.png 1260w,
/static/9c20f36969d61f326e2e064d2f272011/30c0b/%E6%9B%B4%E6%96%B0%E6%8F%90%E7%A4%BA%E6%88%AA%E5%9B%BE.png 2956w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;那么，如何检测应用更新呢？这里介绍一种我在&lt;a href=&quot;https://juejin.cn/post/7185451392994115645&quot;&gt;掘金上的一篇文章&lt;/a&gt;中学到的思路。上文中讲到，入口 HTML 文件不进行缓存，并且体积也不大，所以其实轮询入口 HTML 文件，判断是否与当前 HTML 文件不同，就可以检测到应用的更新。
那如何判断入口 HTML 文件是否有不同呢？一种方法是，通过 &lt;code&gt;document.currentScript&lt;/code&gt; 拿到当前脚本所在的 &lt;code&gt;&amp;#x3C;script&gt;&lt;/code&gt; 元素，并取得其 &lt;code&gt;src&lt;/code&gt; 属性，即为当前脚本的文件名。
如果在轮询到的 HTML 文件文本中，不存在这个文件名，说明当前的脚本在新的 HTML 文件中不再被引用了，可以判断为应用有更新。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; currentScriptSrc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentScript&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; hasUpdate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;setInterval&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;hasUpdate&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;html&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentScriptSrc&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          hasUpdate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
          &lt;span class=&quot;token comment&quot;&gt;// 弹窗通知用户有更新&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;边缘情况&lt;/h3&gt;
&lt;p&gt;在进行了上述改进之后，我仍然会在更新部署的第二天集中地收到 &lt;code&gt;ChunkLoadError&lt;/code&gt; 的上报。
我在 Sentry 上查看这些记录，发现它们的用户流程有一个共同的特征，就是某请求响应了 401 状态码，然后发生 &lt;code&gt;ChunkLoadError&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/73ea2d9dc4ad1a1cb5aadaf397d8cafd/8b640/Sentry%E7%94%A8%E6%88%B7%E6%B5%81%E7%A8%8B%E6%88%AA%E5%9B%BE.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 79.74683544303798%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAABYlAAAWJQFJUiTwAAACR0lEQVQ4y0VUibLjIAzj/79x39Ej980VkkD0Rk7odsYDuEbIllqlN4/ZGaSYwE9YA4w2cNbBMoyVM4NnrnrRsi7zIvtl0TiOA+d5Qm19C11X6NoW6dgRnMMyDpj6DnocsQwD9DRKjus89AjO4ggr9tVj8x57WJH2DeexQ53Bwy8zmrIUwG311yXvpJDB3GfvndSd8UDcN9nn4FkRlYVkkEHaskDf1BjaBl1doXw+hHFTFnLu2VFdoa1KVK+nnJviLaEyVaQInAneGlTvl4DwQvH4lXNbFbJ/fP3D+/dHcjyzjsE9HxHAdFNHPIQl2ZFNntvYNvJ6Zs3gnp0MTS3s+3tVwa+w2mAaRuh5wb5tsnrr4CwV1hLeWqTILk6cKeFM8V4TzhglUjyg9q6B44tdhyMEGToZkBXZTV0rQbXNNEqO6jKoNuvjFkQkjk2dW0CwRijzSxZyPl1dSk5EeT1RF++PSGPXynyZy4J5o8UdKh3XDMVL4kOLriovFakqL94CXOpeyhKEgO39PYFJRGVBMuXVWWHCCyymGATlhWwN7s08oSnf8jABCUYhP4Dpts5624btciWL6r4wj/0HNFuL7edubsDw3zYpwulF2F0Miuv11/Pjy2x0gtKPxZ3PdUrapalvY5OhmPkWgsFL9c2WQM/vLwF5/XzL2RktTDkCdcwjtmWGM0Z+8JuzYpN56MQ6y9CLXciYj9llhh4H2HmS/Wq1OIPWYacqMjmN4kP+CfAS2XAmuTUqK7/hppYgG660Sf73oajs9g9Xdb/hOVDqbgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;用户流程截图&quot;
        title=&quot;用户流程截图&quot;
        src=&quot;/static/73ea2d9dc4ad1a1cb5aadaf397d8cafd/f058b/Sentry%E7%94%A8%E6%88%B7%E6%B5%81%E7%A8%8B%E6%88%AA%E5%9B%BE.png&quot;
        srcset=&quot;/static/73ea2d9dc4ad1a1cb5aadaf397d8cafd/c26ae/Sentry%E7%94%A8%E6%88%B7%E6%B5%81%E7%A8%8B%E6%88%AA%E5%9B%BE.png 158w,
/static/73ea2d9dc4ad1a1cb5aadaf397d8cafd/6bdcf/Sentry%E7%94%A8%E6%88%B7%E6%B5%81%E7%A8%8B%E6%88%AA%E5%9B%BE.png 315w,
/static/73ea2d9dc4ad1a1cb5aadaf397d8cafd/f058b/Sentry%E7%94%A8%E6%88%B7%E6%B5%81%E7%A8%8B%E6%88%AA%E5%9B%BE.png 630w,
/static/73ea2d9dc4ad1a1cb5aadaf397d8cafd/40601/Sentry%E7%94%A8%E6%88%B7%E6%B5%81%E7%A8%8B%E6%88%AA%E5%9B%BE.png 945w,
/static/73ea2d9dc4ad1a1cb5aadaf397d8cafd/78612/Sentry%E7%94%A8%E6%88%B7%E6%B5%81%E7%A8%8B%E6%88%AA%E5%9B%BE.png 1260w,
/static/73ea2d9dc4ad1a1cb5aadaf397d8cafd/8b640/Sentry%E7%94%A8%E6%88%B7%E6%B5%81%E7%A8%8B%E6%88%AA%E5%9B%BE.png 1454w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;可以推断场景如下：用户在头一天下午访问了应用，并且没有退出，随后应用更新了部署。到了第二天，用户再次访问时，因为他的登录态失效，应用自动地跳转到登录页（应用中的逻辑），而因为登录页的分包脚本的 hash 已经改变，从而引起 &lt;code&gt;ChunkLoadError&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这与上一种情况的区别是，用户还来不及看到更新通知并做出反应，应用自己就跳转了。这种情况下，只要将登录页这类被动跳转的页面打在主包里，不要分包，就能避免这个问题。这样的页面并不多，&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8653a5493eeee591b6552ecbf0f87b23/55843/%E4%BB%A3%E7%A0%81%E6%88%AA%E5%9B%BE.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 20.88607594936709%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAEABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAMF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAdkSUB//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAEFAn//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAY/An//xAAYEAEBAQEBAAAAAAAAAAAAAAABADERIf/aAAgBAQABPyFOseZGX//aAAwDAQACAAMAAAAQg/8A/8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQMBAT8QJ//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAECAQE/EGf/xAAZEAEAAwEBAAAAAAAAAAAAAAABABExIVH/2gAIAQEAAT8QMLXINpz2Xp1n/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;代码截图&quot;
        title=&quot;代码截图&quot;
        src=&quot;/static/8653a5493eeee591b6552ecbf0f87b23/828fb/%E4%BB%A3%E7%A0%81%E6%88%AA%E5%9B%BE.jpg&quot;
        srcset=&quot;/static/8653a5493eeee591b6552ecbf0f87b23/ff44c/%E4%BB%A3%E7%A0%81%E6%88%AA%E5%9B%BE.jpg 158w,
/static/8653a5493eeee591b6552ecbf0f87b23/a6688/%E4%BB%A3%E7%A0%81%E6%88%AA%E5%9B%BE.jpg 315w,
/static/8653a5493eeee591b6552ecbf0f87b23/828fb/%E4%BB%A3%E7%A0%81%E6%88%AA%E5%9B%BE.jpg 630w,
/static/8653a5493eeee591b6552ecbf0f87b23/55843/%E4%BB%A3%E7%A0%81%E6%88%AA%E5%9B%BE.jpg 928w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;部署了登录页不分割的改动两周后，再去 Sentry 查看时，发现自更新的下一周起，之后的更新部署果然没有再引起 &lt;code&gt;ChunkLoadError&lt;/code&gt; 了。
至此可以认为 &lt;code&gt;ChunkLoadError&lt;/code&gt; 问题被完全解决了。&lt;/p&gt;
&lt;h2&gt;参考链接&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://juejin.cn/post/7185451392994115645&quot;&gt;https://juejin.cn/post/7185451392994115645&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[23w15]]></title><description><![CDATA[本周新收藏的歌，有两首。]]></description><link>https://doma.land/weekly/23w15/</link><guid isPermaLink="false">https://doma.land/weekly/23w15/</guid><pubDate>Sun, 16 Apr 2023 23:15:41 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌，有两首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;This blog&lt;/h2&gt;
&lt;p&gt;我把最新的 weekly 链接贴到&lt;a href=&quot;https://bento.me/doma&quot;&gt;我的 Bento&lt;/a&gt;上了，但是 Bento 不支持 RSS Feed，需要每周手动去更新 weekly 列表，于是去 Discord 提了反馈。
没想到居然有一位路人小哥居然顺着链接来看我的 weekly 了，并且向我反馈了一个音乐播放器交互问题（小哥是位设计师）。这周把它修掉了。&lt;/p&gt;
&lt;p&gt;社区真好。&lt;/p&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;给 DevOps 平台的流水线构建详情页面增加了一个数据缓存的功能。
因为构建结束了之后，它的详情信息是不可能再更新了，把从接口获取的构建详情数据存到了 IndexedDB 中，这样只要加载过一次，以后再来就可以直接显示了。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;之前对于 DevOps 平台的流水线列表页在进行关键字搜索时，数据加载中的提示交互不是很满意——
如果整个列表显示 loading 提示，对页面观感有比较大的扰动；但如果将 loading 放在不那么显眼的地方，如果用户没有注意到，有可能会感觉页面是卡住了。
这周偶然看到 vercel.com 的项目列表在搜索时的 loading 提示交互，觉得很妙。
它将 loading 提示放在了搜索框上，既不会影响到页面的整体观感，又不会被用户忽视——因为用户在进行搜索时，注意力正好就在输入框上。
果断借鉴来给 DevOps 平台做了优化。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;🥳 Another user-experience improvement around a loading search list (comparison at 00:14)&lt;br&gt;&lt;br&gt;Inspired by this clever design from &lt;a href=&quot;https://twitter.com/vercel?ref_src=twsrc%5Etfw&quot;&gt;@vercel&lt;/a&gt; dot com, where the spinner is subtle *yet noticeable* because user&amp;#39;s attention is right on the search box when doing the search. &lt;a href=&quot;https://t.co/o3Qb5z9IBM&quot;&gt;pic.twitter.com/o3Qb5z9IBM&lt;/a&gt;&lt;/p&gt;&amp;mdash; Doma. (@leishenghao) &lt;a href=&quot;https://twitter.com/leishenghao/status/1646417835168763905?ref_src=twsrc%5Etfw&quot;&gt;April 13, 2023&lt;/a&gt;&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;周四收到了组里原来一位已经跳槽的小伙伴的消息，令我备受感动和鼓舞。让我觉得自己所坚持的一些主张是有价值的。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 136.0759493670886%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAbABQDASIAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAAIBAwQF/8QAFwEBAQEBAAAAAAAAAAAAAAAAAAMBAv/aAAwDAQACEAMQAAAB5UpWOIF0bDmGEuNp/8QAGhAAAgMBAQAAAAAAAAAAAAAAAAECERIhE//aAAgBAQABBQJFDXeVZZibPOQ4SRpmmTbv/8QAGBEAAgMAAAAAAAAAAAAAAAAAAAECEBL/2gAIAQMBAT8BrchNn//EABURAQEAAAAAAAAAAAAAAAAAABAR/9oACAECAQE/ASn/xAAZEAACAwEAAAAAAAAAAAAAAAAAMQEgQQL/2gAIAQEABj8CUC5MNohU/8QAHxAAAwAABgMAAAAAAAAAAAAAAAERITFBUWGhceHw/9oACAEBAAE/IUcXeyvcP2PBgSu4VuVuOZNWZYn2aMwHP0c/Q8teh//aAAwDAQACAAMAAAAQ88BM/8QAGBEAAgMAAAAAAAAAAAAAAAAAAAEQEXH/2gAIAQMBAT8QjEbVs//EABcRAQEBAQAAAAAAAAAAAAAAAAEAEBH/2gAIAQIBAT8QzqFv/8QAHxABAAICAgIDAAAAAAAAAAAAAQARIWExUXGRQdHw/9oACAEBAAE/ELXbZ5IA8sutVnwrItEFhZhm1NqHJ0UE4OuZ1/t5iwLkvCMCOHpNf0+o6ZNDtn//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;微信截图&quot;
        title=&quot;微信截图&quot;
        src=&quot;/static/475bcc05e745d61d7e24c79dcdec5e55/828fb/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE.jpg&quot;
        srcset=&quot;/static/475bcc05e745d61d7e24c79dcdec5e55/ff44c/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE.jpg 158w,
/static/475bcc05e745d61d7e24c79dcdec5e55/a6688/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE.jpg 315w,
/static/475bcc05e745d61d7e24c79dcdec5e55/828fb/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE.jpg 630w,
/static/475bcc05e745d61d7e24c79dcdec5e55/b040c/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE.jpg 844w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;rsuite&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;给 Cascader 组件异步加载子级数据的实现做了下重构 (&lt;a href=&quot;https://github.com/rsuite/rsuite/pull/3157&quot;&gt;PR #3157&lt;/a&gt;)。
原本的实现会直接修改传入的 &lt;code&gt;data&lt;/code&gt;，是不安全的。&lt;/li&gt;
&lt;li&gt;修了一个邪门的 bug，rsuite 的 CDN 分发版本里，打包的 lodash 代码居然是错误的。
感兴趣的可以看看 &lt;a href=&quot;https://github.com/rsuite/rsuite/pull/3159&quot;&gt;PR #3159&lt;/a&gt;。最终的修复很简单，但排查的过程还是浅绕了两弯。回头我也打算水篇文章复原下。&lt;/li&gt;
&lt;li&gt;修复了 Toggle 组件在非默认尺寸下的一些样式问题 (&lt;a href=&quot;https://github.com/rsuite/rsuite/pull/3156&quot;&gt;PR #3156&lt;/a&gt;)。
又是想改用 CSS-in-JS 的一天，现在的组件扩展起样式来还是有点麻烦。&lt;/li&gt;
&lt;li&gt;一位俄语老哥提了 &lt;a href=&quot;https://github.com/rsuite/rsuite/pull/3153&quot;&gt;PR #3153&lt;/a&gt; 要更新原有的俄语翻译，我不懂俄语所以请他解释一下前后翻译的区别，老哥好几天没回，另一位俄语老哥主动帮忙 review 了新的翻译。
社区真好 x2。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Radar&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;偶然试了下 Warp 的 AI Command Search，还是比较好用的。
之前记录在 &lt;a href=&quot;https://gist.github.com/SevenOutman/1f8bdd48c19fe357f8458ecce6f4dcab&quot;&gt;My Cheatsheet&lt;/a&gt; 的有些常用命令可以不再需要了。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 49.36708860759494%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAQDBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeWnzLEY/8QAGBAAAwEBAAAAAAAAAAAAAAAAAAEREiD/2gAIAQEAAQUC0ioq4//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABYQAQEBAAAAAAAAAAAAAAAAAAAxIP/aAAgBAQAGPwJVx//EABoQAAICAwAAAAAAAAAAAAAAAAABEDERUWH/2gAIAQEAAT8hxKzahxiY7j//2gAMAwEAAgADAAAAELAP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAHhABAAEDBQEAAAAAAAAAAAAAAQARIVEQMWFx0eH/2gAIAQEAAT8QAaDbJ5B7icL8nH9sShdiVK10/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;warp-ai-command-search&quot;
        title=&quot;warp-ai-command-search&quot;
        src=&quot;/static/098172ea24494853224c0e7891809ddf/828fb/warp-ai-command-search.jpg&quot;
        srcset=&quot;/static/098172ea24494853224c0e7891809ddf/ff44c/warp-ai-command-search.jpg 158w,
/static/098172ea24494853224c0e7891809ddf/a6688/warp-ai-command-search.jpg 315w,
/static/098172ea24494853224c0e7891809ddf/828fb/warp-ai-command-search.jpg 630w,
/static/098172ea24494853224c0e7891809ddf/0ede0/warp-ai-command-search.jpg 945w,
/static/098172ea24494853224c0e7891809ddf/fd879/warp-ai-command-search.jpg 1010w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;这周推上跟几位大佬产生了互动，甚至有几位大佬关注了我，开心。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;这周因为沙尘的原因，没有再步行下班回家了。下周还是要继续保持习惯。&lt;/li&gt;
&lt;li&gt;跟女朋友把《兰亭迷阵》玩完了，还是挺有意思的。个人感觉谜题设计方面比上次玩的《永乐疑阵》更加精巧。
可以推荐给喜欢玩解谜书的读者。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;不总结了，赶快睡觉去了。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w13+14]]></title><description><![CDATA[这两周新收藏的歌，有两首。]]></description><link>https://doma.land/weekly/23w13+14/</link><guid isPermaLink="false">https://doma.land/weekly/23w13+14/</guid><pubDate>Sun, 09 Apr 2023 22:05:26 GMT</pubDate><content:encoded>&lt;p&gt;这两周新收藏的歌，有两首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;This weekly&lt;/h2&gt;
&lt;p&gt;这还是头一回两周并做一周写。
原因是上周六到这周三，趁着清明休假和朋友们出去玩了一趟。
我心想既然是休假，就干脆忘掉所有事务彻底放松吧，于是上周日就跳过了总结和写 weekly 的日常。
令我高兴的是，这的确让我从紧张的日常中解放出来，得到了很好的放松。
不过，休假结束后去填补被堆积的事务，也是挺辛苦的。&lt;/p&gt;
&lt;p&gt;也许生活就是会这样。&lt;/p&gt;
&lt;h2&gt;休假&lt;/h2&gt;
&lt;p&gt;和朋友们去了成都玩。&lt;/p&gt;
&lt;p&gt;因为对川航的独特餐食有所耳闻，于是去程我们选择了川航的航班。
结果上了飞机才发现，好像我们所想象的那种餐食是公务舱才有的，而经济舱的餐食也是普通的“鸡肉或者鱼”（而且同样并不好吃）。&lt;/p&gt;
&lt;p&gt;后来问过空乘才知道，经济舱也可以选择公务舱的餐食，但是需要在起飞前至少提前两天进行预订。下次懂了。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9b3f96281c0c9a1518d0257da5bac383/eea4a/%E5%B7%9D%E8%88%AA%E9%A4%90%E9%A3%9F.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.31645569620254%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAAMBAgQF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAQL/2gAMAwEAAhADEAAAAbJlsOE7ZR//xAAbEAEAAgIDAAAAAAAAAAAAAAABAgMAEgQQEf/aAAgBAQABBQLkh5UFOX0LZM3WG3X/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAVEQEBAAAAAAAAAAAAAAAAAAAQIf/aAAgBAgEBPwGn/8QAGxABAAICAwAAAAAAAAAAAAAAAQAREBIhQXH/2gAIAQEABj8CBY3WrLDjHR5j/8QAGhABAQEAAwEAAAAAAAAAAAAAAREAITFBkf/aAAgBAQABPyFqwO+NQ0D0uarUEhlNX7mmgexh43//2gAMAwEAAgADAAAAEF/v/8QAFhEAAwAAAAAAAAAAAAAAAAAAARAR/9oACAEDAQE/EBF//8QAFxEAAwEAAAAAAAAAAAAAAAAAAAERQf/aAAgBAgEBPxB4RT//xAAdEAEAAgICAwAAAAAAAAAAAAABABEhMUHBUXGR/9oACAEBAAE/EC/J1CvB3KJkrRWPRiMQGWLVdS3rXCWKEPpUv2Vk8T//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;川航餐食&quot;
        title=&quot;川航餐食&quot;
        src=&quot;/static/9b3f96281c0c9a1518d0257da5bac383/828fb/%E5%B7%9D%E8%88%AA%E9%A4%90%E9%A3%9F.jpg&quot;
        srcset=&quot;/static/9b3f96281c0c9a1518d0257da5bac383/ff44c/%E5%B7%9D%E8%88%AA%E9%A4%90%E9%A3%9F.jpg 158w,
/static/9b3f96281c0c9a1518d0257da5bac383/a6688/%E5%B7%9D%E8%88%AA%E9%A4%90%E9%A3%9F.jpg 315w,
/static/9b3f96281c0c9a1518d0257da5bac383/828fb/%E5%B7%9D%E8%88%AA%E9%A4%90%E9%A3%9F.jpg 630w,
/static/9b3f96281c0c9a1518d0257da5bac383/0ede0/%E5%B7%9D%E8%88%AA%E9%A4%90%E9%A3%9F.jpg 945w,
/static/9b3f96281c0c9a1518d0257da5bac383/3ac88/%E5%B7%9D%E8%88%AA%E9%A4%90%E9%A3%9F.jpg 1260w,
/static/9b3f96281c0c9a1518d0257da5bac383/eea4a/%E5%B7%9D%E8%88%AA%E9%A4%90%E9%A3%9F.jpg 1280w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;到成都后就去吃了串串和火锅！吃了辣豆腐脑！吃了老妈蹄花！吃了冰粉和酒酿！&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8fa39708b81e90205c5b13267a3be7a7/eea4a/%E4%B8%B2%E4%B8%B2.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.31645569620254%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIDBP/EABYBAQEBAAAAAAAAAAAAAAAAAAMBAv/aAAwDAQACEAMQAAABisgLDoTX/8QAGxAAAgMAAwAAAAAAAAAAAAAAAQIAERITISL/2gAIAQEAAQUCU9FgZnMdqHKtDzP/xAAWEQEBAQAAAAAAAAAAAAAAAAAAERL/2gAIAQMBAT8BrL//xAAXEQEBAQEAAAAAAAAAAAAAAAABAAIR/9oACAECAQE/AeM6b//EAB0QAAICAQUAAAAAAAAAAAAAAAABESECEiIxcZH/2gAIAQEABj8CuzjwhKUasl3BtxcuiGf/xAAaEAEAAwEBAQAAAAAAAAAAAAABABEhgVGx/9oACAEBAAE/ISPimEuvVsR0pJwYIpI1KGEwwWf/2gAMAwEAAgADAAAAEHgP/8QAGBEBAQADAAAAAAAAAAAAAAAAAQARIUH/2gAIAQMBAT8Qw7ATd//EABYRAQEBAAAAAAAAAAAAAAAAAAEAIf/aAAgBAgEBPxBTSE5f/8QAHRAAAgICAwEAAAAAAAAAAAAAAREAITFBUWFxof/aAAgBAQABPxBoRhba9hotQRZAc3mGqVhwmbMmxSex1A6BgyIHL7gDkIa+T//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;串串&quot;
        title=&quot;串串&quot;
        src=&quot;/static/8fa39708b81e90205c5b13267a3be7a7/828fb/%E4%B8%B2%E4%B8%B2.jpg&quot;
        srcset=&quot;/static/8fa39708b81e90205c5b13267a3be7a7/ff44c/%E4%B8%B2%E4%B8%B2.jpg 158w,
/static/8fa39708b81e90205c5b13267a3be7a7/a6688/%E4%B8%B2%E4%B8%B2.jpg 315w,
/static/8fa39708b81e90205c5b13267a3be7a7/828fb/%E4%B8%B2%E4%B8%B2.jpg 630w,
/static/8fa39708b81e90205c5b13267a3be7a7/0ede0/%E4%B8%B2%E4%B8%B2.jpg 945w,
/static/8fa39708b81e90205c5b13267a3be7a7/3ac88/%E4%B8%B2%E4%B8%B2.jpg 1260w,
/static/8fa39708b81e90205c5b13267a3be7a7/eea4a/%E4%B8%B2%E4%B8%B2.jpg 1280w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b57ad782aa3a98f9db94ee9ddef06b6f/eea4a/%E7%81%AB%E9%94%85.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.31645569620254%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECA//EABUBAQEAAAAAAAAAAAAAAAAAAAID/9oADAMBAAIQAxAAAAHiSVYyR//EABsQAAICAwEAAAAAAAAAAAAAAAECACEDERIT/9oACAEBAAEFAsvKqFAS55DVwgz/xAAWEQEBAQAAAAAAAAAAAAAAAAAAARH/2gAIAQMBAT8BjH//xAAWEQADAAAAAAAAAAAAAAAAAAAAARH/2gAIAQIBAT8BjKz/xAAaEAACAwEBAAAAAAAAAAAAAAAAEQEhMQJx/9oACAEBAAY/At8Nm9KRy7UClFI//8QAHhAAAgEDBQAAAAAAAAAAAAAAAAERIUFRMXGBkeH/2gAIAQEAAT8hWtdNZa8CBaBxYN0qUFd/Ru2rJcm7P//aAAwDAQACAAMAAAAQq/8A/8QAFhEBAQEAAAAAAAAAAAAAAAAAAQAh/9oACAEDAQE/EEOpIv/EABcRAQEBAQAAAAAAAAAAAAAAAAEAETH/2gAIAQIBAT8QQOxhf//EABsQAQACAwEBAAAAAAAAAAAAAAEAESExQVFh/9oACAEBAAE/EHwRYUwfBybCAA5o8zqXNEPQiwj4fi0OauYDcsFBW7E//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;火锅&quot;
        title=&quot;火锅&quot;
        src=&quot;/static/b57ad782aa3a98f9db94ee9ddef06b6f/828fb/%E7%81%AB%E9%94%85.jpg&quot;
        srcset=&quot;/static/b57ad782aa3a98f9db94ee9ddef06b6f/ff44c/%E7%81%AB%E9%94%85.jpg 158w,
/static/b57ad782aa3a98f9db94ee9ddef06b6f/a6688/%E7%81%AB%E9%94%85.jpg 315w,
/static/b57ad782aa3a98f9db94ee9ddef06b6f/828fb/%E7%81%AB%E9%94%85.jpg 630w,
/static/b57ad782aa3a98f9db94ee9ddef06b6f/0ede0/%E7%81%AB%E9%94%85.jpg 945w,
/static/b57ad782aa3a98f9db94ee9ddef06b6f/3ac88/%E7%81%AB%E9%94%85.jpg 1260w,
/static/b57ad782aa3a98f9db94ee9ddef06b6f/eea4a/%E7%81%AB%E9%94%85.jpg 1280w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;去了大熊猫繁育基地，看到了可爱的熊猫。也像所有的景点一样，走了非常多的路（锻炼圆环转爆），最终以烤肠完美收尾。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4b5339dec69097ff2b16635ba8e61836/47311/%E7%83%A4%E8%82%A0.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGAABAAMBAAAAAAAAAAAAAAAAAAIDBAH/xAAXAQADAQAAAAAAAAAAAAAAAAAAAgMB/9oADAMBAAIQAxAAAAHRyEJ00BksgYUgP//EABoQAAMBAQEBAAAAAAAAAAAAAAABEQIDITH/2gAIAQEAAQUCnnO6KdMse6ZcWfukiU//xAAWEQEBAQAAAAAAAAAAAAAAAAABESD/2gAIAQMBAT8BAmP/xAAVEQEBAAAAAAAAAAAAAAAAAAABIP/aAAgBAgEBPwFY/8QAHBAAAQQDAQAAAAAAAAAAAAAAAQAQETECISNB/9oACAEBAAY/ApRm2BxseLnrJbtpgS3/xAAbEAEAAwADAQAAAAAAAAAAAAABABEhMUFxkf/aAAgBAQABPyFgAFFHCElt39EvDo5lKe2wXrqHX3Kh2LmT/9oADAMBAAIAAwAAABAH3/8A/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQAQIf/aAAgBAwEBPxBkTsZ//8QAFhEBAQEAAAAAAAAAAAAAAAAAAQAQ/9oACAECAQE/EEGGc//EAB4QAQEBAAICAwEAAAAAAAAAAAERADFBIXFRYZHR/9oACAEBAAE/EEuiDJfOoEKRKfH5gCBfRrarr0P9zXvDpA9PTkvSLB8/eFouAAGF5m4i8iiPQ7//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;烤肠&quot;
        title=&quot;烤肠&quot;
        src=&quot;/static/4b5339dec69097ff2b16635ba8e61836/828fb/%E7%83%A4%E8%82%A0.jpg&quot;
        srcset=&quot;/static/4b5339dec69097ff2b16635ba8e61836/ff44c/%E7%83%A4%E8%82%A0.jpg 158w,
/static/4b5339dec69097ff2b16635ba8e61836/a6688/%E7%83%A4%E8%82%A0.jpg 315w,
/static/4b5339dec69097ff2b16635ba8e61836/828fb/%E7%83%A4%E8%82%A0.jpg 630w,
/static/4b5339dec69097ff2b16635ba8e61836/0ede0/%E7%83%A4%E8%82%A0.jpg 945w,
/static/4b5339dec69097ff2b16635ba8e61836/47311/%E7%83%A4%E8%82%A0.jpg 1080w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;中途我们去了一趟乐山。
在一家只有我们一桌客人的餐厅吃到了翘脚牛肉。
相比在上海生活的紧张节奏，那顿晚餐让我深深地感受到了惬意。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a37a582ecd079e73c21fa20c29608b76/d7ad1/%E6%9C%8B%E5%8F%8B%E5%9C%88.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 106.32911392405065%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAVABQDASIAAhEBAxEB/8QAGQABAAMBAQAAAAAAAAAAAAAAAAIDBAEF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAfNhpzlTg0VEQFf/xAAaEAACAgMAAAAAAAAAAAAAAAAAAQISEBMh/9oACAEBAAEFAs1RNVZt7J2Z/8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQMBAT8BKf/EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8BH//EABoQAAICAwAAAAAAAAAAAAAAAAERABASITH/2gAIAQEABj8CtZRaNMhzlf/EAB0QAAEEAgMAAAAAAAAAAAAAAAEAITFhEBFBcaH/2gAIAQEAAT8hgQjomPEaKekPSNsGwYBh4cUnRmP/2gAMAwEAAgADAAAAEJAIwP/EABYRAQEBAAAAAAAAAAAAAAAAABEBEP/aAAgBAwEBPxAxWGf/xAAXEQEAAwAAAAAAAAAAAAAAAAAQAREx/9oACAECAQE/ECo0/8QAHRABAAICAwEBAAAAAAAAAAAAAREhAFEQMaFBYf/aAAgBAQABPxCvooaMsAs7duAHsNxGVD0RiIV+5M8B2Ne8Iywwl8YcEbwzLvj/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;朋友圈截图&quot;
        title=&quot;朋友圈截图&quot;
        src=&quot;/static/a37a582ecd079e73c21fa20c29608b76/828fb/%E6%9C%8B%E5%8F%8B%E5%9C%88.jpg&quot;
        srcset=&quot;/static/a37a582ecd079e73c21fa20c29608b76/ff44c/%E6%9C%8B%E5%8F%8B%E5%9C%88.jpg 158w,
/static/a37a582ecd079e73c21fa20c29608b76/a6688/%E6%9C%8B%E5%8F%8B%E5%9C%88.jpg 315w,
/static/a37a582ecd079e73c21fa20c29608b76/828fb/%E6%9C%8B%E5%8F%8B%E5%9C%88.jpg 630w,
/static/a37a582ecd079e73c21fa20c29608b76/0ede0/%E6%9C%8B%E5%8F%8B%E5%9C%88.jpg 945w,
/static/a37a582ecd079e73c21fa20c29608b76/d7ad1/%E6%9C%8B%E5%8F%8B%E5%9C%88.jpg 1001w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;第二天去参观了乐山大佛，又是锻炼圆环爆转的一天，&lt;del&gt;不过那里没有烤肠&lt;/del&gt;。
后来又回到成都去吃了冒烤鸭！吃了粉蒸排骨！吃了锅魁和肥肠粉！&lt;/p&gt;
&lt;p&gt;然后假期就结束了。&lt;/p&gt;
&lt;p&gt;很长时间以来，我对于出游一直是犹豫型选手。即便想出去玩，也总是会觉得要请假、要订票订酒店很麻烦什么的而拖延。
而这次一块出行的朋友们，则是在吃饭时对于成都之行一拍即合后，下了饭桌立刻就凑在一堆开始订票订酒店。
这令我着实感到鼓舞，也是这次休假的收获之一。&lt;/p&gt;
&lt;h2&gt;编程&lt;/h2&gt;
&lt;p&gt;这两周将 DevOps 平台上经常出现的 ChunkLoadError 问题解决了。下周应该可以将文章整理好发出来。&lt;/p&gt;
&lt;p&gt;做题之余做了一个辅助做题的工具。目前还太简陋，随着做题量增加会逐渐完善它并公布出来。&lt;/p&gt;
&lt;p&gt;参加了几次 LeetCode 的周赛模拟，和一次正式的周赛。
成绩很普通，很多题目都是思路正确，但是缺少一些关键的算法知识储备，而无法达到时间限制。&lt;/p&gt;
&lt;p&gt;好消息是，我对做题和习得新知是感受到兴趣和热情的。我想我能够保持下去并且不断进步。&lt;/p&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;p&gt;天气暖和了起来，这两周在步行下班回家，算是初步恢复日常运动量。&lt;/p&gt;
&lt;p&gt;逐渐确定了我的最佳睡眠时间是 8.5 小时。但这两周以来一共只有一天做到了完美地按时就寝，睡满时间后按时起床。
我发现要保证完美的睡眠作息，对于各种外界的扰动要求是很严苛的，比我预期的要更难保持。&lt;/p&gt;
&lt;p&gt;因为休假的时间非常放松，加上有一些拖欠的日常事务要补，休假回来后的几天有点难以保持日常习惯。
好在该补的已经基本都补上了。&lt;/p&gt;
&lt;p&gt;以今天的 weekly 为分界，明天重新回到正轨。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;不总结了，赶快睡觉去了。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w12]]></title><description><![CDATA[本周新收藏的歌，有两首。]]></description><link>https://doma.land/weekly/23w12/</link><guid isPermaLink="false">https://doma.land/weekly/23w12/</guid><pubDate>Sun, 26 Mar 2023 21:32:31 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌，有两首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;This blog&lt;/h2&gt;
&lt;p&gt;周一的时候发了&lt;a href=&quot;/blog/you-may-not-need-usestate-as-often/&quot;&gt;一篇博文&lt;/a&gt;，并为此给博客添加了 Twitter 分享大图的支持。&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;You may not need useState as often as you think&lt;a href=&quot;https://t.co/pVx3NS9Ujp&quot;&gt;https://t.co/pVx3NS9Ujp&lt;/a&gt;&lt;/p&gt;&amp;mdash; Doma. (@leishenghao) &lt;a href=&quot;https://twitter.com/leishenghao/status/1638163467260821504?ref_src=twsrc%5Etfw&quot;&gt;March 21, 2023&lt;/a&gt;&lt;/blockquote&gt;
&lt;p&gt;博客上发的是英文版的，中文版发在了&lt;a href=&quot;https://juejin.cn/post/7212548256944111674&quot;&gt;掘金&lt;/a&gt;和公众号。&lt;/p&gt;
&lt;h2&gt;编程&lt;/h2&gt;
&lt;p&gt;因为之前有些项目里需要做中国地图图表，每次生成样例数据都要去写一遍各个行政区的名字，于是做了个工具专门用来复制这个 &lt;a href=&quot;https://china-ad.vercel.app/&quot;&gt;https://china-ad.vercel.app/&lt;/a&gt; 。&lt;/p&gt;
&lt;p&gt;尝试用了 Svelte，第一印象并没有很惊喜，还是觉得 React 更顺手。
特别是 computed 的部分，一定要把依赖的变量声明成函数参数的形式再传入才能监听变化，总觉得有点刺挠（找不到准确的用词）。
也许这是在鼓励你把计算的逻辑拿到组件外面去，以模块的形式引入使用，这样的话倒是对于组件来说是更好的。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.22784810126582%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAABYlAAAWJQFJUiTwAAABDUlEQVQoz41TS6qFMAx14Y50A4IjceAC3ItOnaioKCriD0Hxl0cC8Vlv78VAaEvPOc2vCtzsPE9aj+Mg5/M0TdD3/YVB3/dd4LApMkEEo/M5z3OI41gQ3Lbt2jMO11eCVVVBmqbSCJ9c5Q76Jtg0DUV5J87zTI44LM8V4bMGMsG6roUIWbDrOhiGgdIXUm7blggYBV7ii28izLIMxnEk/LquxFWYEEURJElCF1yje1PCMBQEl2UBmb1KuSxLeoxHCq0oCtB1HTRNA8MwQFVVsCzrvyk8e28Fsa62bZOI4zhgmia4rvtubDDdIAhANhEfKf8SZPM8D3zfFwaYm3B35Ci/vh4bkrGrz58i+3p/0OCil4WniRsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;截图&quot;
        title=&quot;截图&quot;
        src=&quot;/static/f9744ad995702ff66fa5c3e4d0215d7e/f058b/%E4%B8%AD%E5%9B%BD%E8%A1%8C%E6%94%BF%E5%8C%BA%E5%88%92.png&quot;
        srcset=&quot;/static/f9744ad995702ff66fa5c3e4d0215d7e/c26ae/%E4%B8%AD%E5%9B%BD%E8%A1%8C%E6%94%BF%E5%8C%BA%E5%88%92.png 158w,
/static/f9744ad995702ff66fa5c3e4d0215d7e/6bdcf/%E4%B8%AD%E5%9B%BD%E8%A1%8C%E6%94%BF%E5%8C%BA%E5%88%92.png 315w,
/static/f9744ad995702ff66fa5c3e4d0215d7e/f058b/%E4%B8%AD%E5%9B%BD%E8%A1%8C%E6%94%BF%E5%8C%BA%E5%88%92.png 630w,
/static/f9744ad995702ff66fa5c3e4d0215d7e/40601/%E4%B8%AD%E5%9B%BD%E8%A1%8C%E6%94%BF%E5%8C%BA%E5%88%92.png 945w,
/static/f9744ad995702ff66fa5c3e4d0215d7e/78612/%E4%B8%AD%E5%9B%BD%E8%A1%8C%E6%94%BF%E5%8C%BA%E5%88%92.png 1260w,
/static/f9744ad995702ff66fa5c3e4d0215d7e/db1b2/%E4%B8%AD%E5%9B%BD%E8%A1%8C%E6%94%BF%E5%8C%BA%E5%88%92.png 2900w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;这周还在 LeetCode 上随缘做起了题。&lt;/p&gt;
&lt;p&gt;有一些在矩阵中移动的题目，经常需要在代码中定义上下左右四个移动的方向，我发现我已经习惯了按照 CSS 中的顺序来写 🤣&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 628px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 39.87341772151899%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABYlAAAWJQFJUiTwAAABbklEQVQoz42R2W7bQAxF/f+/lgRo35J4tywr1uyrPKPlFLGLpHnrBQjiksDBJbgapwmdEjkvvL7ObNdwtSNbndmIgYMe2KqBxmS8s5zOR5Zl5l89/HLvq0+gzZlaJ5wHrW40nWHfeg5a01rL5mQRJpNLorm2pBCINaCypAuXH9BVnUZ0TMzTeF/cboFT03M4WGSyuORYryVCBHS2KGvQRiGz4GgP/P74Ra7pO2EZR2SITH+BOQWaVtCcPXYI+BTY7TW9CnxkTX+94rJFZsne7Hhunzi75ivlappnYimMY70PhyE+gI2ndRIZDJuNQplIGjOi7/HJIZNgrd55ubxwG4dvYCmVa++YpgcwBMdu3/G2lvd0J3Hh0jne3gWdVxw/WjpxoU89O7Xl+fxEKpFlnu9XrkqtCBuxzhCip9aCsR6lE6UWfPLEOHDtPbkMhByJKTDUjMkGnRTjVJnn6QH8/HKsFW0kxmp+auH/tHzVH9QlZlEGUQ44AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;LeetCode截图&quot;
        title=&quot;LeetCode截图&quot;
        src=&quot;/static/973a3d301bae7273acb5938961a82844/3d84d/LeetCode%E6%88%AA%E5%9B%BE.png&quot;
        srcset=&quot;/static/973a3d301bae7273acb5938961a82844/c26ae/LeetCode%E6%88%AA%E5%9B%BE.png 158w,
/static/973a3d301bae7273acb5938961a82844/6bdcf/LeetCode%E6%88%AA%E5%9B%BE.png 315w,
/static/973a3d301bae7273acb5938961a82844/3d84d/LeetCode%E6%88%AA%E5%9B%BE.png 628w&quot;
        sizes=&quot;(max-width: 628px) 100vw, 628px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;我喜欢做完题目后，看看题解中各种不同的思路，有时会被其中一些思路妙到。学到东西总是令人开心。&lt;/p&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;p&gt;这周真的去拔牙了。自从小时候换牙之后，这还是第一次拔牙。
虽然打了麻药不会痛，但医生的锤子敲得叮咣作响还是有点难受。&lt;/p&gt;
&lt;p&gt;本来打算两颗一起拔，医生说一起拔恢复期会比较难受，建议先拔一颗感受一下。
拔掉的牙留下的那个坑不知道什么时候才会完全长好，本来拔这颗智齿就是因为经常会有食物残留在牙缝里，现在倒好，直接残留在坑里了，那不是一样么。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 133.54430379746836%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAbABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAQBA//EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAGR3RidZR1zVlB//8QAHBAAAgICAwAAAAAAAAAAAAAAAQIAERIyAxAi/9oACAEBAAEFAsZVFAuLH1yq93CCY1v0+rR9/wD/xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAwEBPwGFP//EABURAQEAAAAAAAAAAAAAAAAAABAR/9oACAECAQE/ASH/xAAcEAADAAEFAAAAAAAAAAAAAAAAAREQAhIxgYL/2gAIAQEABj8CIxVinJu14UI2seToZ//EABsQAAMBAQEBAQAAAAAAAAAAAAABESFBUXGx/9oACAEBAAE/IaWSsbMRH4zqhjkEjcneoxgwb1hE0Pc6yiaXKMWePwz9z//aAAwDAQACAAMAAAAQ7wWw/8QAFxEBAQEBAAAAAAAAAAAAAAAAARARIf/aAAgBAwEBPxAT2aDCf//EABgRAQADAQAAAAAAAAAAAAAAAAEAEBEh/9oACAECAQE/EFDk2ZXWv//EAB4QAQADAAICAwAAAAAAAAAAAAEAESExgUFRYcHR/9oACAEBAAE/EBQEjRRdsfeY2FJlwjUAtX8zc4kRPY4y9jdPh6luBAws1JyUGl1xrAwECoXDBuUdgAChQdQDQp+3+EAAzD2gs//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;拔牙&quot;
        title=&quot;拔牙&quot;
        src=&quot;/static/57d47da6e2b32d889da089e0b1733866/828fb/%E6%8B%94%E7%89%99.jpg&quot;
        srcset=&quot;/static/57d47da6e2b32d889da089e0b1733866/ff44c/%E6%8B%94%E7%89%99.jpg 158w,
/static/57d47da6e2b32d889da089e0b1733866/a6688/%E6%8B%94%E7%89%99.jpg 315w,
/static/57d47da6e2b32d889da089e0b1733866/828fb/%E6%8B%94%E7%89%99.jpg 630w,
/static/57d47da6e2b32d889da089e0b1733866/0ede0/%E6%8B%94%E7%89%99.jpg 945w,
/static/57d47da6e2b32d889da089e0b1733866/3ac88/%E6%8B%94%E7%89%99.jpg 1260w,
/static/57d47da6e2b32d889da089e0b1733866/96287/%E6%8B%94%E7%89%99.jpg 3022w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;天气不像之前那么冷了，也逐渐开始恢复运动。
一开始我想循序渐进，给这周定的目标是至少两天合上锻炼圆环，结果有四天都合上了！开了个不错的头。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 31.0126582278481%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAGABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAEDBf/EABYBAQEBAAAAAAAAAAAAAAAAAAEAAv/aAAwDAQACEAMQAAABxpMtYn//xAAXEAADAQAAAAAAAAAAAAAAAAAAESEB/9oACAEBAAEFAo4aj//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABYQAQEBAAAAAAAAAAAAAAAAAAABMf/aAAgBAQAGPwLa2o//xAAaEAABBQEAAAAAAAAAAAAAAAABABExQWEh/9oACAEBAAE/IQGcKUBhSBnRhf/aAAwDAQACAAMAAAAQAC//xAAWEQEBAQAAAAAAAAAAAAAAAAAAARH/2gAIAQMBAT8QZH//xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAgEBPxCq/8QAHBAAAgIDAQEAAAAAAAAAAAAAAREAQSExYVGx/9oACAEBAAE/EKLIGthm/YQRsyPW9/IIhhY46ez/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;健身圆环&quot;
        title=&quot;健身圆环&quot;
        src=&quot;/static/24bdb23e2bb15837da81f56fb4396ee0/828fb/%E5%81%A5%E8%BA%AB%E5%9C%86%E7%8E%AF.jpg&quot;
        srcset=&quot;/static/24bdb23e2bb15837da81f56fb4396ee0/ff44c/%E5%81%A5%E8%BA%AB%E5%9C%86%E7%8E%AF.jpg 158w,
/static/24bdb23e2bb15837da81f56fb4396ee0/a6688/%E5%81%A5%E8%BA%AB%E5%9C%86%E7%8E%AF.jpg 315w,
/static/24bdb23e2bb15837da81f56fb4396ee0/828fb/%E5%81%A5%E8%BA%AB%E5%9C%86%E7%8E%AF.jpg 630w,
/static/24bdb23e2bb15837da81f56fb4396ee0/0ede0/%E5%81%A5%E8%BA%AB%E5%9C%86%E7%8E%AF.jpg 945w,
/static/24bdb23e2bb15837da81f56fb4396ee0/ad68d/%E5%81%A5%E8%BA%AB%E5%9C%86%E7%8E%AF.jpg 1170w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;但是这周每天都没有按时睡觉，所以睡眠其实是不足的。下周要注意。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;不总结了，赶快睡觉去了。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[You may not need useState as often]]></title><description><![CDATA[useState is one of the most used hooks when we write React applications. However, maybe you don't need it as much as you think.
In this…]]></description><link>https://doma.land/blog/you-may-not-need-usestate-as-often/</link><guid isPermaLink="false">https://doma.land/blog/you-may-not-need-usestate-as-often/</guid><pubDate>Mon, 20 Mar 2023 15:28:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;code&gt;useState&lt;/code&gt; is one of the most used hooks when we write React applications. However, maybe you don&apos;t need it as much as you think.
In this article, we&apos;ll look at situations where you can use other methods instead of &lt;code&gt;useState&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Several types of states&lt;/h2&gt;
&lt;p&gt;In React applications, depending on the form, I would divided states roughly into the following categories.&lt;/p&gt;
&lt;h3&gt;Remote state&lt;/h3&gt;
&lt;p&gt;This is a special category of state, and my introduction to the concept of &quot;remote state&quot; started with the &lt;code&gt;swr&lt;/code&gt; and &lt;code&gt;react-query&lt;/code&gt; libraries.&lt;/p&gt;
&lt;p&gt;Remote state means that the source-of-truth is not actually in your local application runtime, but in a remote service - most of time in a DB. The state you read and update is ultimately reflected in changes to a piece of information stored on the remote service (i.e., a &quot;resource&quot; in REST).
In fact, the core business logic of the front end of most BS architecture applications we develop is reading and updating the remote state (via APIs provided by the server).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// todos is not actually a value declared and maintained by the local application&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// but rather a copy of the previous message on the server side&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;todos&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setTodos&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetchTodos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setTodos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TodoList&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;todos&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;todos&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;onCreateTodo&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createTodo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;onEditTodo&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;updateTodo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;onDeleteTodo&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;todo&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;deleteTodo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The remote state doesn&apos;t really need to be declared by you via &lt;code&gt;useState&lt;/code&gt;. Maybe you used to do this (with &lt;code&gt;useEffect&lt;/code&gt; to update), but now I recommend using libraries like &lt;code&gt;swr&lt;/code&gt; or &lt;code&gt;react-query&lt;/code&gt;, which provide intuitive declarative APIs and handle the request refresh, de-duplication, polling, and other &quot;how to synchronize the remote state with the local copy at the right time &quot; issues are handled very well.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; todos &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;todos&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fetchTodos&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TodoList&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;todos&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;todos&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;onCreateTodo&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createTodo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;onEditTodo&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;updateTodo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;onDeleteTodo&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;todo&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;deleteTodo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Even if you&apos;re developing a small application with just a set of CRUD interfaces, I&apos;d recommend using them instead of updating state yourself in &lt;code&gt;useEffect&lt;/code&gt; (otherwise you have to deal with issues like Promise callbacks when components unmount).&lt;/p&gt;
&lt;p&gt;Other than remote state is local state. Local state can be further broken down into the following categories.&lt;/p&gt;
&lt;h3&gt;Form state&lt;/h3&gt;
&lt;p&gt;From the perspective of the aforementioned &quot;the core business logic of a front-end application is reading and modifying remote state&quot;, forms are the leading step to modifying remote state, providing the input for modifying it (and sometimes doing some client-side validation). Therefore, forms are a very important part of the front-end application.&lt;/p&gt;
&lt;p&gt;The structure and updating logic of form state are often largely the same - structurally, it contains the value of the form fields, the error message of the form fields (considering validation); operationally, it contains the update of the form field value, the update of the error message (form validation), etc. Therefore, the community has wrapped this common logic into libraries such as &lt;code&gt;formik&lt;/code&gt; and &lt;code&gt;react-hook-form&lt;/code&gt;, which provide APIs for managing form state in the form of hooks. If you are going to create more robust forms, I suggest you use these libraries.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  values&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  errors&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  setFieldValue&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  setFieldError&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  handleSubmit&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  isSubmitting&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useFormik&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;initialValues&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;firstName&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;lastName&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token function-variable function&quot;&gt;onSubmit&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;values&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;values&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Also, I don&apos;t recommend using libraries that bind form state logic and form rendering together, claiming to help you write less code. I insist that logic and presentation are best left uncoupled.&lt;/p&gt;
&lt;h3&gt;UI state&lt;/h3&gt;
&lt;p&gt;Besides form state, the rest of the local state is the state that controls the UI.
For example, whether the side navigation menu is expanded/collapsed, which Tab page/list item is currently selected, and so on.&lt;/p&gt;
&lt;p&gt;Most of the time, their values are basic types and the updating logic is very simple.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;isExpanded&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setExpanded&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;selectedTab&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setSelectedTab&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;blog&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I only use &lt;code&gt;useState&lt;/code&gt; at these times. In an application with 70,000+ lines of code, I only used about 20 &lt;code&gt;useState&lt;/code&gt;s.&lt;/p&gt;
&lt;p&gt;Sometimes, the type and update logic of these states is not that simple. I suggest you use a custom hook or reducer to encapsulate this state and update logic to improve predictability and testability. I won&apos;t go into that in this article.&lt;/p&gt;
&lt;h2&gt;Merge related states&lt;/h2&gt;
&lt;p&gt;In some scenarios, if you find that you are using multiple &lt;code&gt;useState&lt;/code&gt;s in a row in the same component (while they are all necessary to implement the component), and often updating multiple states at the same time in a single callback, then the Mutable API may be good for you. Libraries such as &lt;code&gt;use-immer&lt;/code&gt;, &lt;code&gt;use-mutative&lt;/code&gt;, etc. that provide Mutable APIs allow you to update the state in a mutable way. e.g.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setState&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useMutative&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;todo&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button
    onClick&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// set value with draft mutable&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;setState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;draft&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        draft&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;foo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;draft&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;foo&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; 2&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
        draft&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;list&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;todo 2&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    click
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Apart from a preference in API style, Mutable API brings value by allowing us to combine multiple related states as a whole.
For example, if we need to implement a list component with selectable items that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;At most one selected list item at the same time&lt;/li&gt;
&lt;li&gt;When adding a new item, mark the new item as selected&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;we may need two states such as&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setItems&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;selectedItemKey&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setSelectedItemKey&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button
    onClick&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;setItems&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;setSelectedItemKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    Add item
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With Mutable API it&apos;s like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setState&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useMutative&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;selectedItemKey&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button
    onClick&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;setState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selectedItemKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    Add item
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Doesn&apos;t it look more intuitive? And, writing it this way will only do one state update (but that&apos;s not critical, since newer versions of React already merge multiple consecutive state updates to avoid multiple re-renderings).&lt;/p&gt;
&lt;h2&gt;Wrap-up&lt;/h2&gt;
&lt;p&gt;What I&apos;d like to suggest with this article is to use custom hooks, or existing libraries, instead of state scattered inside individual components whenever possible.
This helps reduce coupling of logic and presentation, improves reusability and testability, and in the long run, improves maintainability.&lt;/p&gt;
&lt;p&gt;When you look back at the code you wrote six months ago and get confused by the convoluted state update logic, try the approach in this article to make a change.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I&apos;m not native English speaker and I&apos;m practicing writing in English. Any suggestions that help me write better are welcome!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w11]]></title><description><![CDATA[本周新收藏的歌有一首。]]></description><link>https://doma.land/weekly/23w11/</link><guid isPermaLink="false">https://doma.land/weekly/23w11/</guid><pubDate>Sun, 19 Mar 2023 22:14:48 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌有一首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;This blog&lt;/h2&gt;
&lt;p&gt;上周有群友说希望给 weekly 中每周收藏的歌加上链接，方便点过去收藏。
我想干脆直接把&lt;a href=&quot;https://music.163.com/playlist?id=83256808&quot;&gt;我的“我喜欢的音乐”歌单&lt;/a&gt;链接放出来好了，可以直接收藏它，每周新收藏的歌都在那里。&lt;/p&gt;
&lt;p&gt;上周的 weekly 收到了 saka 的赞，我很开心。毕竟我开始写 weekly 就是因为看了 saka 2022 年的周记而受到的启发。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ae5dcd19ac351b45045bb747c53306aa/ad68d/saka%E5%A4%B8%E6%88%91.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.708860759493675%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAMF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAByJgB/8QAFhABAQEAAAAAAAAAAAAAAAAAAhAS/9oACAEBAAEFAkt3/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFhAAAwAAAAAAAAAAAAAAAAAAEBEh/9oACAEBAAY/Aqj/AP/EABkQAAMAAwAAAAAAAAAAAAAAAAABESExUf/aAAgBAQABPyHak4lhQi6JU//aAAwDAQACAAMAAAAQ88//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAZEAADAAMAAAAAAAAAAAAAAAAAAREhQdH/2gAIAQEAAT8QbgLIayHHkmtP/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;截图&quot;
        title=&quot;截图&quot;
        src=&quot;/static/ae5dcd19ac351b45045bb747c53306aa/828fb/saka%E5%A4%B8%E6%88%91.jpg&quot;
        srcset=&quot;/static/ae5dcd19ac351b45045bb747c53306aa/ff44c/saka%E5%A4%B8%E6%88%91.jpg 158w,
/static/ae5dcd19ac351b45045bb747c53306aa/a6688/saka%E5%A4%B8%E6%88%91.jpg 315w,
/static/ae5dcd19ac351b45045bb747c53306aa/828fb/saka%E5%A4%B8%E6%88%91.jpg 630w,
/static/ae5dcd19ac351b45045bb747c53306aa/0ede0/saka%E5%A4%B8%E6%88%91.jpg 945w,
/static/ae5dcd19ac351b45045bb747c53306aa/ad68d/saka%E5%A4%B8%E6%88%91.jpg 1170w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;工作、开源和雷达&lt;/h2&gt;
&lt;p&gt;这周搞了些杂七杂八的问题，比如这个使用代码分割的情况下，部署更新导致无法找到旧的分包的问题。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8653a5493eeee591b6552ecbf0f87b23/55843/%E6%B3%A8%E9%87%8A.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 20.88607594936709%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAEABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAMF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAdkSUB//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAEFAn//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAY/An//xAAYEAEBAQEBAAAAAAAAAAAAAAABADERIf/aAAgBAQABPyFOseZGX//aAAwDAQACAAMAAAAQg/8A/8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQMBAT8QJ//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAECAQE/EGf/xAAZEAEAAwEBAAAAAAAAAAAAAAABABExIVH/2gAIAQEAAT8QMLXINpz2Xp1n/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;注释&quot;
        title=&quot;注释&quot;
        src=&quot;/static/8653a5493eeee591b6552ecbf0f87b23/828fb/%E6%B3%A8%E9%87%8A.jpg&quot;
        srcset=&quot;/static/8653a5493eeee591b6552ecbf0f87b23/ff44c/%E6%B3%A8%E9%87%8A.jpg 158w,
/static/8653a5493eeee591b6552ecbf0f87b23/a6688/%E6%B3%A8%E9%87%8A.jpg 315w,
/static/8653a5493eeee591b6552ecbf0f87b23/828fb/%E6%B3%A8%E9%87%8A.jpg 630w,
/static/8653a5493eeee591b6552ecbf0f87b23/55843/%E6%B3%A8%E9%87%8A.jpg 928w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;不得不说 Sentry 还是挺好用的，对定位线上问题很有帮助。
要是公司能给 Sentry 升升级就更好了（&lt;/p&gt;
&lt;p&gt;因为杂七杂八的事情有点多所以没能挤出多少时间来做开源项目。
除了照例给 rsuite 修复了一些问题之外，其余只有给 setup-msw 做了一点微小的改进。&lt;/p&gt;
&lt;p&gt;这周终于玩到了 ChatGPT，尝试了几个不同的对话场景，打算之后发博文的时候写摘要就靠它了 😄。&lt;/p&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;p&gt;周五要去医院拔智齿。
拔牙的前一天晚上，女朋友说因为拔完牙要吃好几天流食，趁今天再去吃顿好的吧。于是晚上去吃了收藏夹里的一家涮羊肉，吃到了收藏了很久的羊肉烧麦，好吃！&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4c02194d2070ccb59803be8a04f0a27f/df94c/%E6%B6%AE%E7%BE%8A%E8%82%89.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 74.68354430379746%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAwAE/8QAFQEBAQAAAAAAAAAAAAAAAAAAAgP/2gAMAwEAAhADEAAAARQdU2EMz//EABoQAQEBAAMBAAAAAAAAAAAAAAEDEQACEjL/2gAIAQEAAQUCl9EnWdVama+NevP/xAAXEQADAQAAAAAAAAAAAAAAAAAAAREh/9oACAEDAQE/AaqYf//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAECAQE/AQZ//8QAHhAAAQQBBQAAAAAAAAAAAAAAAAECERIxISKRocH/2gAIAQEABj8C9Jvk0cbUErleiLO5P//EAB0QAQABBAMBAAAAAAAAAAAAAAEAESExQVFhcZH/2gAIAQEAAT8hJel9RUU63PfEeNY+TUHbCnsQy58TSXin/9oADAMBAAIAAwAAABBv3//EABgRAAIDAAAAAAAAAAAAAAAAAAABESFR/9oACAEDAQE/EHegbwf/xAAWEQEBAQAAAAAAAAAAAAAAAAARAAH/2gAIAQIBAT8QIRt//8QAHRABAAICAgMAAAAAAAAAAAAAAQARITFBgVFh0f/aAAgBAQABPxBbcVstkO6ohWRfJlhD05r6owdQTufJlnRGWgKOTnw+QsAA6ET/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;涮羊肉&quot;
        title=&quot;涮羊肉&quot;
        src=&quot;/static/4c02194d2070ccb59803be8a04f0a27f/828fb/%E6%B6%AE%E7%BE%8A%E8%82%89.jpg&quot;
        srcset=&quot;/static/4c02194d2070ccb59803be8a04f0a27f/ff44c/%E6%B6%AE%E7%BE%8A%E8%82%89.jpg 158w,
/static/4c02194d2070ccb59803be8a04f0a27f/a6688/%E6%B6%AE%E7%BE%8A%E8%82%89.jpg 315w,
/static/4c02194d2070ccb59803be8a04f0a27f/828fb/%E6%B6%AE%E7%BE%8A%E8%82%89.jpg 630w,
/static/4c02194d2070ccb59803be8a04f0a27f/0ede0/%E6%B6%AE%E7%BE%8A%E8%82%89.jpg 945w,
/static/4c02194d2070ccb59803be8a04f0a27f/3ac88/%E6%B6%AE%E7%BE%8A%E8%82%89.jpg 1260w,
/static/4c02194d2070ccb59803be8a04f0a27f/df94c/%E6%B6%AE%E7%BE%8A%E8%82%89.jpg 4030w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;不过周五去了医院并没有拔成牙，因为当天只是做检查，拔牙还得改天再来一趟。
不过既然假已经请了，就趁此机会休息了一天。晚上出门去和朋友们吃饭的路上，想到明后天还有两天休息日，简直太开心了。仿佛体会到了做四休三的美妙。
我一直认为一周至少双休一定是必要的，因为至少需要一天来休息、另一天来做自己想做的事，缺一不可。
而如果休三天，就可以再有单独的一天好好陪陪家人，处理家里的事情。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;ChatGPT：本文介绍了本周收藏的音乐、工作和开源方面的经历以及生活琐事。作者分享了自己的一些想法和感受，同时也表达了对假期和休息的重视。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w10]]></title><description><![CDATA[本周新收藏的歌，有九首（喂）。]]></description><link>https://doma.land/weekly/23w10/</link><guid isPermaLink="false">https://doma.land/weekly/23w10/</guid><pubDate>Sun, 12 Mar 2023 11:27:33 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌，有九首（喂）。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;我也不知道为什么一下收藏了这么多，可能是因为好久没听了吧。&lt;/p&gt;
&lt;h2&gt;This blog&lt;/h2&gt;
&lt;p&gt;给博客添加了嵌入推文的支持，可以在 &lt;a href=&quot;/weekly/23w09&quot;&gt;23w09&lt;/a&gt; 中看到效果。&lt;/p&gt;
&lt;h2&gt;开源&lt;/h2&gt;
&lt;p&gt;最近 GitHub 日历不知不觉绿了起来。
去年我甚至曾专门把“填满 7x7 日历”当作一个挑战来完成，尽管 49 天过后确实完成了，但没过多久日历又变得稀稀拉拉起来。
如今虽然和开源大佬们还有差距，但是在日常中不知不觉地就达到了原来卯着劲做才能完成的水平，让我在为自己的进步高兴的同时，其实也有点惊讶。
我的变化其实仅仅是养成每天处理收件箱的习惯，以及每周安排固定的日子用于做 rsuite 和开源项目（通过一周工作表的方式）。
看来习惯真的很重要。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 528px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/debe0d7e926b71f1f4580e458d5d001f/c64a7/github.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 98.73417721518987%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAMEBf/EABYBAQEBAAAAAAAAAAAAAAAAAAIAA//aAAwDAQACEAMQAAABxVjcje560vErqgr/xAAbEAACAwEBAQAAAAAAAAAAAAABAgMEEhEiI//aAAgBAQABBQIh3IHisTmE/Q55Vk5G1ddmsu4YVjT/xAAWEQEBAQAAAAAAAAAAAAAAAAABESD/2gAIAQMBAT8BVuP/xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/AR//xAAdEAADAQABBQAAAAAAAAAAAAABAiEAERIxMkGS/9oACAEBAAY/AmbqPcwau/1nBvDcXNc1GMJuLAkX1vJruBv/xAAcEAACAwADAQAAAAAAAAAAAAABEQAhQTFRcWH/2gAIAQEAAT8hBvIIclIgHb8ryBclwNypWGmAn6IhzbpjowrhJdvyP4+7iGgCXRcVd22Z/9oADAMBAAIAAwAAABCDwAL/xAAWEQEBAQAAAAAAAAAAAAAAAAABABH/2gAIAQMBAT8QJiByyL//xAAWEQEBAQAAAAAAAAAAAAAAAAABACH/2gAIAQIBAT8QcyUIZv/EABwQAQEAAwEAAwAAAAAAAAAAAAERACExQWFx8P/aAAgBAQABPxCW6ER6IAE9wtFIGMsyeqcxQkaqEDFQXrhA4awAvkL3HwmYO+hl7cPWEvZnV+sbudAALtNaygg1DVb8YXSFJar+M//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;GitHub&quot;
        title=&quot;GitHub&quot;
        src=&quot;/static/debe0d7e926b71f1f4580e458d5d001f/c64a7/github.jpg&quot;
        srcset=&quot;/static/debe0d7e926b71f1f4580e458d5d001f/ff44c/github.jpg 158w,
/static/debe0d7e926b71f1f4580e458d5d001f/a6688/github.jpg 315w,
/static/debe0d7e926b71f1f4580e458d5d001f/c64a7/github.jpg 528w&quot;
        sizes=&quot;(max-width: 528px) 100vw, 528px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;p&gt;上周去吃了一家新开的店，有一道麻婆豆腐很好吃，这周五又去吃了一次。
为了早点吃到，周五的早上提前了半小时去下班，下午也提前了半小时下班。
下班的时候天还没有黑，令人开心。（上次下班时天还没黑还是去年夏天）&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/822f4794bb6cbe04dd74168eb6957242/eea4a/%E9%BA%BB%E5%A9%86%E8%B1%86%E8%85%90.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.31645569620254%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAIDBP/EABYBAQEBAAAAAAAAAAAAAAAAAAIDBP/aAAwDAQACEAMQAAABzPGma8hxn//EABkQAQADAQEAAAAAAAAAAAAAAAEAAgMSEP/aAAgBAQABBQIOpyeGhHQJWtrz/8QAFxEBAAMAAAAAAAAAAAAAAAAAAAESIv/aAAgBAwEBPwHK0P/EABgRAAIDAAAAAAAAAAAAAAAAAAABAhES/9oACAECAQE/AakZZ//EABkQAAIDAQAAAAAAAAAAAAAAAAABEBEhMf/aAAgBAQAGPwK2dcasiz//xAAaEAEAAwADAAAAAAAAAAAAAAABABEhQWFx/9oACAEBAAE/IUdz7HiJ7lIpRkFlKcgNhsFShs//2gAMAwEAAgADAAAAEDsP/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQAhQf/aAAgBAwEBPxAcTwv/xAAWEQADAAAAAAAAAAAAAAAAAAABEEH/2gAIAQIBAT8QMU//xAAbEAEBAQACAwAAAAAAAAAAAAABEQAxYSGx4f/aAAgBAQABPxDh5eCK3mF4MkjQ7HrLTRRz9FgWwe806JX5ibKiJv/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;麻婆豆腐&quot;
        title=&quot;麻婆豆腐&quot;
        src=&quot;/static/822f4794bb6cbe04dd74168eb6957242/828fb/%E9%BA%BB%E5%A9%86%E8%B1%86%E8%85%90.jpg&quot;
        srcset=&quot;/static/822f4794bb6cbe04dd74168eb6957242/ff44c/%E9%BA%BB%E5%A9%86%E8%B1%86%E8%85%90.jpg 158w,
/static/822f4794bb6cbe04dd74168eb6957242/a6688/%E9%BA%BB%E5%A9%86%E8%B1%86%E8%85%90.jpg 315w,
/static/822f4794bb6cbe04dd74168eb6957242/828fb/%E9%BA%BB%E5%A9%86%E8%B1%86%E8%85%90.jpg 630w,
/static/822f4794bb6cbe04dd74168eb6957242/0ede0/%E9%BA%BB%E5%A9%86%E8%B1%86%E8%85%90.jpg 945w,
/static/822f4794bb6cbe04dd74168eb6957242/3ac88/%E9%BA%BB%E5%A9%86%E8%B1%86%E8%85%90.jpg 1260w,
/static/822f4794bb6cbe04dd74168eb6957242/eea4a/%E9%BA%BB%E5%A9%86%E8%B1%86%E8%85%90.jpg 1280w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;这周读完了《给大人们的情绪使用说明书》，对于平常难以感知别人细腻情绪的我来说，确实有不少启发。
这本书也很适合（其实更适合）平时会因为自己的负面情绪所困扰的人来读。
推荐一下。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/17ccab3b6c400b9686551e99fcc87577/eea4a/%E7%BB%99%E5%A4%A7%E4%BA%BA%E4%BB%AC%E7%9A%84%E6%83%85%E7%BB%AA%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E%E4%B9%A6.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGQABAQADAQAAAAAAAAAAAAAAAAQBAwUG/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAc3S1R5prHUnLIwv/8QAHBAAAQQDAQAAAAAAAAAAAAAAAAECAwQQERIx/9oACAEBAAEFAqrUcLGzkq+7XHboxbUps//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQMBAT8BH//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8BH//EABoQAAEFAQAAAAAAAAAAAAAAAAEAAhARIjL/2gAIAQEABj8CdYtHIhyeDGV1H//EABwQAAMAAgMBAAAAAAAAAAAAAAABESExQVFh8f/aAAgBAQABPyFtFE2eY3wNmE+kVWVNMaHY3l8PlIo//9oADAMBAAIAAwAAABB870D/xAAVEQEBAAAAAAAAAAAAAAAAAAARIP/aAAgBAwEBPxBj/8QAFhEBAQEAAAAAAAAAAAAAAAAAAREg/9oACAECAQE/EIuP/8QAHRAAAgICAwEAAAAAAAAAAAAAAREAMSHwQVFxgf/aAAgBAQABPxACJqdLgj3AwIpiDJ+QUNDgYAhoTBEI4dd7YGvYJhFtxAa1P//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;《给大人们的情绪使用说明书》&quot;
        title=&quot;《给大人们的情绪使用说明书》&quot;
        src=&quot;/static/17ccab3b6c400b9686551e99fcc87577/828fb/%E7%BB%99%E5%A4%A7%E4%BA%BA%E4%BB%AC%E7%9A%84%E6%83%85%E7%BB%AA%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E%E4%B9%A6.jpg&quot;
        srcset=&quot;/static/17ccab3b6c400b9686551e99fcc87577/ff44c/%E7%BB%99%E5%A4%A7%E4%BA%BA%E4%BB%AC%E7%9A%84%E6%83%85%E7%BB%AA%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E%E4%B9%A6.jpg 158w,
/static/17ccab3b6c400b9686551e99fcc87577/a6688/%E7%BB%99%E5%A4%A7%E4%BA%BA%E4%BB%AC%E7%9A%84%E6%83%85%E7%BB%AA%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E%E4%B9%A6.jpg 315w,
/static/17ccab3b6c400b9686551e99fcc87577/828fb/%E7%BB%99%E5%A4%A7%E4%BA%BA%E4%BB%AC%E7%9A%84%E6%83%85%E7%BB%AA%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E%E4%B9%A6.jpg 630w,
/static/17ccab3b6c400b9686551e99fcc87577/0ede0/%E7%BB%99%E5%A4%A7%E4%BA%BA%E4%BB%AC%E7%9A%84%E6%83%85%E7%BB%AA%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E%E4%B9%A6.jpg 945w,
/static/17ccab3b6c400b9686551e99fcc87577/3ac88/%E7%BB%99%E5%A4%A7%E4%BA%BA%E4%BB%AC%E7%9A%84%E6%83%85%E7%BB%AA%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E%E4%B9%A6.jpg 1260w,
/static/17ccab3b6c400b9686551e99fcc87577/eea4a/%E7%BB%99%E5%A4%A7%E4%BA%BA%E4%BB%AC%E7%9A%84%E6%83%85%E7%BB%AA%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E%E4%B9%A6.jpg 1280w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;这周有在练字了，随便录了录&lt;a href=&quot;https://b23.tv/vlmBaYG&quot;&gt;发在了 B 站上&lt;/a&gt;。
可以当&lt;del&gt;解压&lt;/del&gt;升压视频看（&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;内容好像不多，就听听歌吧。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w09]]></title><description><![CDATA[本周没有新收藏的歌。]]></description><link>https://doma.land/weekly/23w09/</link><guid isPermaLink="false">https://doma.land/weekly/23w09/</guid><pubDate>Sun, 05 Mar 2023 21:36:16 GMT</pubDate><content:encoded>&lt;p&gt;本周没有新收藏的歌。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;p&gt;这周试着在每天工作开始前，制定一个当日的工作内容表，列出今天要做/想做哪些任务。
一天的工作结束后，对工作表进行回顾。
这样一方面对每天的工作成果更加地清楚，另一方面对自己一天能够处理多少工作更有了解。&lt;/p&gt;
&lt;p&gt;除此之外，还尝试了制定每天的工作表预设，将自己工作中关注的各个工作方向分配到每一天。
例如，周二工作以 rsuite 为主、周三周四以 DevOps 平台和组内公共库为主，等等。
这样一来，避免一周下来某个方向的工作被疏忽，并且可以推自己处理一些因为觉得麻烦而迟迟不动手导致被拖延的工作项。&lt;/p&gt;
&lt;h2&gt;This blog&lt;/h2&gt;
&lt;p&gt;上周的 weekly 收获了我的博客的第一条评论（来自 &lt;a href=&quot;https://github.com/suyanhanx&quot;&gt;@suyanhanx&lt;/a&gt;），令我很开心。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.139240506329116%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB3aAD/8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQABBQJ//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQAGPwJ//8QAGBAAAgMAAAAAAAAAAAAAAAAAARARITH/2gAIAQEAAT8hi9L/AP/aAAwDAQACAAMAAAAQgA//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAZEAEAAwEBAAAAAAAAAAAAAAABABEhMUH/2gAIAQEAAT8QtddWzcIFerOz/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;第一条评论截图&quot;
        title=&quot;第一条评论截图&quot;
        src=&quot;/static/58185d8287425e80b1161a6039e9196f/828fb/first-comment.jpg&quot;
        srcset=&quot;/static/58185d8287425e80b1161a6039e9196f/ff44c/first-comment.jpg 158w,
/static/58185d8287425e80b1161a6039e9196f/a6688/first-comment.jpg 315w,
/static/58185d8287425e80b1161a6039e9196f/828fb/first-comment.jpg 630w,
/static/58185d8287425e80b1161a6039e9196f/0ede0/first-comment.jpg 945w,
/static/58185d8287425e80b1161a6039e9196f/d3883/first-comment.jpg 1169w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;接到反馈说 weekly 中的播放器在 Safari 上一点播放就白屏了，这周也修好了这个问题。&lt;/p&gt;
&lt;h2&gt;开源&lt;/h2&gt;
&lt;p&gt;播放器导致白屏的问题，其实是 &lt;a href=&quot;https://github.com/SevenOutman/aplayer-react&quot;&gt;&lt;code&gt;aplayer-react&lt;/code&gt;&lt;/a&gt; 的 bug。
这个 bug 只在 Safari 上出现了，根据定位的问题，推测原因可能是 Safari 性能太好 😅
（详情见 &lt;a href=&quot;https://github.com/SevenOutman/aplayer-react/pull/10%EF%BC%89%E3%80%82&quot;&gt;https://github.com/SevenOutman/aplayer-react/pull/10）。&lt;/a&gt;&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Just came across this issue that only happens on &lt;a href=&quot;https://twitter.com/hashtag/Safari?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#Safari&lt;/a&gt; possibly because Safari is too fast 😐 &lt;a href=&quot;https://t.co/c9FkEmNTtS&quot;&gt;https://t.co/c9FkEmNTtS&lt;/a&gt;&lt;/p&gt;&amp;mdash; Doma. (@leishenghao) &lt;a href=&quot;https://twitter.com/leishenghao/status/1630527710094327809?ref_src=twsrc%5Etfw&quot;&gt;February 28, 2023&lt;/a&gt;&lt;/blockquote&gt;
&lt;p&gt;这周还发布了 &lt;a href=&quot;https://github.com/SevenOutman/setup-msw&quot;&gt;&lt;code&gt;setup-msw&lt;/code&gt;&lt;/a&gt; 1.1，它现在可以根据你所使用的框架来推断静态资源目录路径了。&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;setup-msw is smarter now! It can guess you static assets folder based on the framework you use and generates boilerplates with .ts extension if you&amp;#39;re using TypeScript.&lt;a href=&quot;https://t.co/2t99Ht1gnZ&quot;&gt;https://t.co/2t99Ht1gnZ&lt;/a&gt; &lt;a href=&quot;https://t.co/6n3j8cb9hd&quot;&gt;pic.twitter.com/6n3j8cb9hd&lt;/a&gt;&lt;/p&gt;&amp;mdash; Doma. (@leishenghao) &lt;a href=&quot;https://twitter.com/leishenghao/status/1631228962537082880?ref_src=twsrc%5Etfw&quot;&gt;March 2, 2023&lt;/a&gt;&lt;/blockquote&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;p&gt;这周因为各种原因，从周一开始连续好几天都没有睡好觉。
不按时作息的连带效应真的很明显，一天不好好睡觉很容易后面几天的作息安排都受影响。
好在周日的上午起床时终于感觉精神又回来了。&lt;/p&gt;
&lt;p&gt;这周开始练字了。
女朋友送了我一支练字用的钢笔，很好用。
第一本字帖是最基本的控笔练习，每天都要画直线。&lt;/p&gt;
&lt;p&gt;这周做的不错的是每天都合上了站立圆环（甚至包括周末！）。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 32.911392405063296%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAQD/2gAMAwEAAhADEAAAAcQDIj//xAAWEAEBAQAAAAAAAAAAAAAAAAAhABD/2gAIAQEAAQUCML//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAXEAADAQAAAAAAAAAAAAAAAAAAMaEQ/9oACAEBAAY/AnBzP//EABkQAAIDAQAAAAAAAAAAAAAAAAABESExof/aAAgBAQABPyHUlK5DxZ//2gAMAwEAAgADAAAAEHg//8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQMBAT8Qiv/EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAECAQE/EEf/xAAbEAADAAIDAAAAAAAAAAAAAAABESEAMUFhgf/aAAgBAQABPxAETY6NNATHkNbQD3JLxs7Of//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;站立圆环&quot;
        title=&quot;站立圆环&quot;
        src=&quot;/static/db3407bf4d569cd007e77ebdbea0c8ee/828fb/%E5%81%A5%E8%BA%AB%E5%9C%86%E7%8E%AF.jpg&quot;
        srcset=&quot;/static/db3407bf4d569cd007e77ebdbea0c8ee/ff44c/%E5%81%A5%E8%BA%AB%E5%9C%86%E7%8E%AF.jpg 158w,
/static/db3407bf4d569cd007e77ebdbea0c8ee/a6688/%E5%81%A5%E8%BA%AB%E5%9C%86%E7%8E%AF.jpg 315w,
/static/db3407bf4d569cd007e77ebdbea0c8ee/828fb/%E5%81%A5%E8%BA%AB%E5%9C%86%E7%8E%AF.jpg 630w,
/static/db3407bf4d569cd007e77ebdbea0c8ee/0ede0/%E5%81%A5%E8%BA%AB%E5%9C%86%E7%8E%AF.jpg 945w,
/static/db3407bf4d569cd007e77ebdbea0c8ee/ad68d/%E5%81%A5%E8%BA%AB%E5%9C%86%E7%8E%AF.jpg 1170w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Minecraft&lt;/h2&gt;
&lt;p&gt;因为觉得村子里有点拥挤，于是把家后面的地平了一片，做了个小公园。
参考了&lt;a href=&quot;https://b23.tv/OV5kFYe&quot;&gt;【MC 我的世界如何建造一个公园-哔哩哔哩】 &lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.32911392405063%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGQAAAQUAAAAAAAAAAAAAAAAAAAECAwQF/8QAFgEBAQEAAAAAAAAAAAAAAAAAAwAC/9oADAMBAAIQAxAAAAHMbaTFGArf/8QAGRAAAgMBAAAAAAAAAAAAAAAAAAEDEyEQ/9oACAEBAAEFAqNpFEN7z//EABcRAQADAAAAAAAAAAAAAAAAAAABAxH/2gAIAQMBAT8BRXr/xAAYEQADAQEAAAAAAAAAAAAAAAAAASEDEf/aAAgBAgEBPwGnc1Gj/8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGhABAAIDAQAAAAAAAAAAAAAAAQARITFBUf/aAAgBAQABPyG2F0wd7gpuI5vYT2f/2gAMAwEAAgADAAAAEGz/AP/EABYRAQEBAAAAAAAAAAAAAAAAAAEAEf/aAAgBAwEBPxByQDf/xAAXEQADAQAAAAAAAAAAAAAAAAAAAREx/9oACAECAQE/EE4psFP/xAAbEAEBAQACAwAAAAAAAAAAAAABEQAhMUGBsf/aAAgBAQABPxBtY+Gs0MelC62nvw5qIlnrNYrhbyTl6d//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;公园&quot;
        title=&quot;公园&quot;
        src=&quot;/static/3b6a620b2beca9a49babf1581c331b36/828fb/%E5%85%AC%E5%9B%AD.jpg&quot;
        srcset=&quot;/static/3b6a620b2beca9a49babf1581c331b36/ff44c/%E5%85%AC%E5%9B%AD.jpg 158w,
/static/3b6a620b2beca9a49babf1581c331b36/a6688/%E5%85%AC%E5%9B%AD.jpg 315w,
/static/3b6a620b2beca9a49babf1581c331b36/828fb/%E5%85%AC%E5%9B%AD.jpg 630w,
/static/3b6a620b2beca9a49babf1581c331b36/0ede0/%E5%85%AC%E5%9B%AD.jpg 945w,
/static/3b6a620b2beca9a49babf1581c331b36/3ac88/%E5%85%AC%E5%9B%AD.jpg 1260w,
/static/3b6a620b2beca9a49babf1581c331b36/eea4a/%E5%85%AC%E5%9B%AD.jpg 1280w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w08]]></title><description><![CDATA[本周新收藏的歌有一首。]]></description><link>https://doma.land/weekly/23w08/</link><guid isPermaLink="false">https://doma.land/weekly/23w08/</guid><pubDate>Sun, 26 Feb 2023 20:25:43 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌有一首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;p&gt;本周照例发布了 DevOps 平台 1.6 的每周更新。
由于自从上次更新以来，已经在开发一些 1.7 版本的功能，代码库和依赖版本相较 1.6 版本已有比较多的不同，所以这次使用 &lt;code&gt;git worktree&lt;/code&gt; 给 1.6 稳定分支单独建了个目录来维护。
不得不说 &lt;code&gt;git worktree&lt;/code&gt; 非常适合在上游优先的分支策略下维护历史发布分支。再配合 GUI 工具进行 cherry-pick 操作，每周更新的分支准备工作轻松如喝水。&lt;/p&gt;
&lt;h2&gt;开源&lt;/h2&gt;
&lt;p&gt;MSW 的作者对 &lt;a href=&quot;https://github.com/SevenOutman/setup-msw&quot;&gt;&lt;code&gt;setup-msw&lt;/code&gt;&lt;/a&gt; 表达了肯定，并邀请将其作为官方的启动工具，我为此很受鼓舞。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3e54764fc8a7ea4a4c24ce7cf10d9200/71c1d/comment-from-mswjs-author.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 17.72151898734177%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAtUlEQVQY042Qy1LDMAxFs+tQJpS2JHb9dhzHSYFu2v//tcPYMMMSFmejK13pqnPzBzosDCpwFhbtF1za2L9Kdv3Arh/ZH0b6o/gXnfK5GV5sQpoJExa0z5Rk2NKF96yRUvL0MvB8+JuuDlcTnzZsLPh0xU0rt+vM/TPxuM3EGDgOhpMwnMZfau0sHW/St3SVTrlvQxMLys3tWhMKwmRGkxF2QfnSltlpbX1V1z9JbFzbi6pW+QK42mjG9tCd2AAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;MSW 作者的评论&quot;
        title=&quot;MSW 作者的评论&quot;
        src=&quot;/static/3e54764fc8a7ea4a4c24ce7cf10d9200/f058b/comment-from-mswjs-author.png&quot;
        srcset=&quot;/static/3e54764fc8a7ea4a4c24ce7cf10d9200/c26ae/comment-from-mswjs-author.png 158w,
/static/3e54764fc8a7ea4a4c24ce7cf10d9200/6bdcf/comment-from-mswjs-author.png 315w,
/static/3e54764fc8a7ea4a4c24ce7cf10d9200/f058b/comment-from-mswjs-author.png 630w,
/static/3e54764fc8a7ea4a4c24ce7cf10d9200/40601/comment-from-mswjs-author.png 945w,
/static/3e54764fc8a7ea4a4c24ce7cf10d9200/78612/comment-from-mswjs-author.png 1260w,
/static/3e54764fc8a7ea4a4c24ce7cf10d9200/71c1d/comment-from-mswjs-author.png 1536w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;不过 &lt;code&gt;setup-msw&lt;/code&gt; 目前还只是一个最简单的原型，与我设想的使用体验还有一定距离，这周我会继续完善它。&lt;/p&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;p&gt;尝试养成早起习惯约有一个月了，基本已经稳定地保持住了。我发觉当掌握了方法后，保持早起习惯比我过去想象的要容易。
现在每天早晨起床后，到出发上班前，固定有至少半个小时的时间可以做自己想做的事。&lt;/p&gt;
&lt;p&gt;说到读书，一周前偶然去逛了逛书店，看到一本《起床后的黄金 1 小时》，适逢我正在培养早起习惯的途中，便买来读。
这周已经将它读完，确实有不少收获。特别是第七章，解答了我关于早起的一些具体的疑惑，对我关于早起和时间规划的方法论有所补充，也增加了我保持习惯的信心。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/81a03c1d32211f87185161d521913bdf/eea4a/my-morning-routine.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGQABAAIDAAAAAAAAAAAAAAAAAAQFAQIG/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAc2ESwjm0ZVloRBFf//EAB0QAAICAQUAAAAAAAAAAAAAAAABAgMEEBETFDL/2gAIAQEAAQUCxkpSdcdmzF9jOSVZ2rdP/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAwEBPwEf/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAgEBPwEf/8QAHBAAAQMFAAAAAAAAAAAAAAAAAQAQMQIRInGR/9oACAEBAAY/AjcKGq0+Kkcb/8QAGhABAQEBAAMAAAAAAAAAAAAAAQAhETFR0f/aAAgBAQABPyEQ7AXS+4VhyC4mye2lWvssGHm//9oADAMBAAIAAwAAABCkCID/xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAwEBPxAhP//EABYRAQEBAAAAAAAAAAAAAAAAAAERIP/aAAgBAgEBPxBW4//EAB0QAQACAgIDAAAAAAAAAAAAAAEAESFRMWFxgbH/2gAIAQEAAT8QoqS1nfUuxVrenmXNgPM0fssoZDBUzBBwNNDG4C9Js8s//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;《起床后的黄金 1 小时》&quot;
        title=&quot;《起床后的黄金 1 小时》&quot;
        src=&quot;/static/81a03c1d32211f87185161d521913bdf/828fb/my-morning-routine.jpg&quot;
        srcset=&quot;/static/81a03c1d32211f87185161d521913bdf/ff44c/my-morning-routine.jpg 158w,
/static/81a03c1d32211f87185161d521913bdf/a6688/my-morning-routine.jpg 315w,
/static/81a03c1d32211f87185161d521913bdf/828fb/my-morning-routine.jpg 630w,
/static/81a03c1d32211f87185161d521913bdf/0ede0/my-morning-routine.jpg 945w,
/static/81a03c1d32211f87185161d521913bdf/3ac88/my-morning-routine.jpg 1260w,
/static/81a03c1d32211f87185161d521913bdf/eea4a/my-morning-routine.jpg 1280w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;在读书做笔记的过程中，发觉自己的书写不够工整，心想如果可以写得更好看一些，应该也会让自己更愉悦，于是下周开始准备用这半个小时来练字。&lt;/p&gt;
&lt;p&gt;这周还给自己换了个新的表盘，将我需要快速查看的信息和操作的入口都摆在了上面，它使用起来非常方便。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/54286ae9be3ca7f6850c36bb3255cd5b/eea4a/new-watch-face.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAMEBQL/xAAWAQEBAQAAAAAAAAAAAAAAAAACAQD/2gAMAwEAAhADEAAAAazjkK6bUi5/omsI/wD/xAAdEAACAgEFAAAAAAAAAAAAAAACAwABEgQREyMx/9oACAEBAAEFAmAqoKFlOOqmqPcUEQGLKun+K7Bxxn//xAAWEQEBAQAAAAAAAAAAAAAAAAAQAhH/2gAIAQMBAT8Bwk//xAAVEQEBAAAAAAAAAAAAAAAAAAAQAf/aAAgBAgEBPwEp/8QAHBAAAgICAwAAAAAAAAAAAAAAABECIQEQIkGB/9oACAEBAAY/AnKMLHCWPDsSpj3yso//xAAcEAEAAgMAAwAAAAAAAAAAAAABABEhMUFRYbH/2gAIAQEAAT8hBBXbD9j6e6TSLaKR6K6TxonSrljmefDExA4a9RKpNT//2gAMAwEAAgADAAAAECjHwP/EABgRAAIDAAAAAAAAAAAAAAAAAAAREDFh/9oACAEDAQE/ELIWT//EABkRAAEFAAAAAAAAAAAAAAAAABEAARBBUf/aAAgBAgEBPxCii+y//8QAGxABAAIDAQEAAAAAAAAAAAAAAQARITFhQdH/2gAIAQEAAT8QXrmTVcBXWzbbNtkWd8c5hliiyJk87HGXkoC4SunEmV1cRg4FdvXJUL22fJ//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;新的表盘&quot;
        title=&quot;新的表盘&quot;
        src=&quot;/static/54286ae9be3ca7f6850c36bb3255cd5b/828fb/new-watch-face.jpg&quot;
        srcset=&quot;/static/54286ae9be3ca7f6850c36bb3255cd5b/ff44c/new-watch-face.jpg 158w,
/static/54286ae9be3ca7f6850c36bb3255cd5b/a6688/new-watch-face.jpg 315w,
/static/54286ae9be3ca7f6850c36bb3255cd5b/828fb/new-watch-face.jpg 630w,
/static/54286ae9be3ca7f6850c36bb3255cd5b/0ede0/new-watch-face.jpg 945w,
/static/54286ae9be3ca7f6850c36bb3255cd5b/3ac88/new-watch-face.jpg 1260w,
/static/54286ae9be3ca7f6850c36bb3255cd5b/eea4a/new-watch-face.jpg 1280w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Minecraft&lt;/h2&gt;
&lt;p&gt;上周把高速熔炉做好了之后，还是觉得总是需要去仙人掌农场取仙人掌再拿去烧太麻烦，于是干脆在仙人掌农场下又建了一个小的自动熔炉。
目前的仙人掌农场产量无法充分供应两个熔炉，于是又将仙人掌农场向上扩展了几层，现在是一座相当高的塔了。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b94e907bdc9e6d05e907a10f6ca9e783/eea4a/%E4%BB%99%E4%BA%BA%E6%8E%8C%E5%A1%94.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.32911392405063%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMEBf/EABYBAQEBAAAAAAAAAAAAAAAAAAABAv/aAAwDAQACEAMQAAABwG1MyzS8X//EABgQAAMBAQAAAAAAAAAAAAAAAAABAhEh/9oACAEBAAEFAsLRjF2qWpH/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAgEBPwGq/8QAFhAAAwAAAAAAAAAAAAAAAAAAAAIg/9oACAEBAAY/AhZ//8QAGhABAQACAwAAAAAAAAAAAAAAAQARQSExYf/aAAgBAQABPyFRqxmBq8mImSDkLpf/2gAMAwEAAgADAAAAENjP/8QAFxEAAwEAAAAAAAAAAAAAAAAAAAERIf/aAAgBAwEBPxC4JH//xAAWEQEBAQAAAAAAAAAAAAAAAAABERD/2gAIAQIBAT8QLZn/xAAbEAEAAgMBAQAAAAAAAAAAAAABABEhMUFRkf/aAAgBAQABPxDbIl+2VaOwQs+UESc+QJQ0YhOSf//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;仙人掌塔&quot;
        title=&quot;仙人掌塔&quot;
        src=&quot;/static/b94e907bdc9e6d05e907a10f6ca9e783/828fb/%E4%BB%99%E4%BA%BA%E6%8E%8C%E5%A1%94.jpg&quot;
        srcset=&quot;/static/b94e907bdc9e6d05e907a10f6ca9e783/ff44c/%E4%BB%99%E4%BA%BA%E6%8E%8C%E5%A1%94.jpg 158w,
/static/b94e907bdc9e6d05e907a10f6ca9e783/a6688/%E4%BB%99%E4%BA%BA%E6%8E%8C%E5%A1%94.jpg 315w,
/static/b94e907bdc9e6d05e907a10f6ca9e783/828fb/%E4%BB%99%E4%BA%BA%E6%8E%8C%E5%A1%94.jpg 630w,
/static/b94e907bdc9e6d05e907a10f6ca9e783/0ede0/%E4%BB%99%E4%BA%BA%E6%8E%8C%E5%A1%94.jpg 945w,
/static/b94e907bdc9e6d05e907a10f6ca9e783/3ac88/%E4%BB%99%E4%BA%BA%E6%8E%8C%E5%A1%94.jpg 1260w,
/static/b94e907bdc9e6d05e907a10f6ca9e783/eea4a/%E4%BB%99%E4%BA%BA%E6%8E%8C%E5%A1%94.jpg 1280w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;不过仍然供不上两个熔炉，还需改进。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w07]]></title><description><![CDATA[本周新收藏的歌有两首。]]></description><link>https://doma.land/weekly/23w07/</link><guid isPermaLink="false">https://doma.land/weekly/23w07/</guid><pubDate>Thu, 16 Feb 2023 15:06:57 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌有两首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;开源&lt;/h2&gt;
&lt;p&gt;这周在给 rsuite 写 PR 的时候，因为给组件文档添加新 props 的说明时，需要保持 props 表格按字母序排列，有点麻烦，于是写了一个 &lt;a href=&quot;https://github.com/SevenOutman/prettier-plugin-sort-markdown-table&quot;&gt;Prettier 插件&lt;/a&gt;来自动给表格整理排序。&lt;/p&gt;
&lt;p&gt;另外，上周在写 MSW handlers 的文章时，因为要建立一个 demo 仓库总是需要重复 msw 文档上的 setup 步骤，所以编写了一个 &lt;a href=&quot;https://github.com/SevenOutman/setup-msw&quot;&gt;CLI 工具&lt;/a&gt;来自动完成这些步骤。&lt;/p&gt;
&lt;p&gt;在这过程中也逐步完善了自己的&lt;a href=&quot;https://github.com/SevenOutman/ts-lib-starter&quot;&gt;仓库模板&lt;/a&gt;（起源是从 @EGOIST 的 ts-lib-starter fork 而来）。&lt;/p&gt;
&lt;h2&gt;工作和生活&lt;/h2&gt;
&lt;p&gt;这周感觉对于时间的掌控感更强了，逐渐不再总是觉得因为还有事情没做完而紧张焦虑。
也逐渐开始有效地每周产生一点进步了，这种感觉让我很高兴和有信心。&lt;/p&gt;
&lt;h2&gt;Minecraft&lt;/h2&gt;
&lt;p&gt;由于黏液球的收集是在太慢了，还是将地毯复制机寄希望于蜂蜜块。
女朋友去帮我搞了三个蜂巢回来，我盖了一间蜂房，在里面开蜂巢盲盒。一共开出来五只蜜蜂。
很快就凑齐了蜂蜜块，改进了地毯复制机，最终高速熔炉得以开机（目前是 12 倍速）。烧仙人掌和沙子变得更方便了。&lt;/p&gt;
&lt;p&gt;自从几位慷慨的村民入职，以及几台机器开机以来，绿宝石已经不那么紧缺了。我们甚至在空地上拿绿宝石块盖起了撤硕。
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4709342ddf937320bd65a98d1e13c5e8/4e8c3/%E7%BB%BF%E5%AE%9D%E7%9F%B3%E5%A1%94.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.32911392405063%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC80lEQVQozxXIS08jBQDA8fkCHtSkCYQVgWIfM1Om7fQ5HaevgW7plE7f3ZZSILWl5RFQMOyKguC64prlIBrdbNbERA+bjYmJ69HEeNPEsyYmHjzvxQ/wN/6OP6E76SAf85CNeMjFvKiJMHJURVrQkP0aoj+GJEeRxAhSIIH0/y2EkQMaHl+MYFhnfPQO3u0LZganCLdefpGWLtFcDtJYlFiqS4RyPl7zLuBZiCK7NTy5OK7y64jeOG4piKgkcEtxXME0rb1j9u8+YKp/xszgDKGnaVRTCs28j3rFzULGQbvfoVBLk0wbeNdNnPeXmDiJoVTTmOYKihohm4pyc3TA5vkV3dvnSP3bhDePEAoxN3l9nnI6QDXjI52ZZHjLZmPUJHonx/z7y0w/yDLz8SLOCxOjs0g+nWSwrrIxsmh1ytjVIplChkpZQ2jbPqycl35dp7+apGqqZHs6obMUE+cJbpwluXGmMfWexktbPl45jmOcWjTrBjsdg4stk4P1mwxXs2zaCsKjkwIfHVdpjJPUtqNYbRV5oDJxGGZqpPLqdRbv4yKzX2QQH1aZ+aGGeJWg3onQK4U5HaZ4eJLnyxOLo0EV4c7IYsNSKHRjlHYilEZxrDUf2ZZIrCEyvSbheEvFcRpG/LTC1OcZXCMFvSTTXw+wtuznYDVGrdai0T1EsFeKtHLTvL07SWPbT3k7hD1WKe2FKAwDJJtBFMvJbGUOR9/NC2+KzLXmGO8WufdJn1rOQynjRzeylGtdBMPIcvnuIk8fF6kNg9g7QarjIG90DPrtJP2mSaURIGnPoS/NIhVmiRSd/PjNJc//+Jnfnl2z1zVRw1HskoVgmmn2O0l2OwYbRxqFTT/lXpS2pdLKh+kVolRqCoVWiJVylHopSLWs8d3Xl/zz+zOe//kT3171WLN1UmYRoZw32KoEuRgk+eDDHLotkkj7EP0eXB4Xok9EVpwo8jzhuIyhOUnoMk++usffv37Pv3/9wmfX29zdT3D/sMJ/PA6oZxZVvUoAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;绿宝石塔&quot;
        title=&quot;绿宝石塔&quot;
        src=&quot;/static/4709342ddf937320bd65a98d1e13c5e8/f058b/%E7%BB%BF%E5%AE%9D%E7%9F%B3%E5%A1%94.png&quot;
        srcset=&quot;/static/4709342ddf937320bd65a98d1e13c5e8/c26ae/%E7%BB%BF%E5%AE%9D%E7%9F%B3%E5%A1%94.png 158w,
/static/4709342ddf937320bd65a98d1e13c5e8/6bdcf/%E7%BB%BF%E5%AE%9D%E7%9F%B3%E5%A1%94.png 315w,
/static/4709342ddf937320bd65a98d1e13c5e8/f058b/%E7%BB%BF%E5%AE%9D%E7%9F%B3%E5%A1%94.png 630w,
/static/4709342ddf937320bd65a98d1e13c5e8/40601/%E7%BB%BF%E5%AE%9D%E7%9F%B3%E5%A1%94.png 945w,
/static/4709342ddf937320bd65a98d1e13c5e8/78612/%E7%BB%BF%E5%AE%9D%E7%9F%B3%E5%A1%94.png 1260w,
/static/4709342ddf937320bd65a98d1e13c5e8/4e8c3/%E7%BB%BF%E5%AE%9D%E7%9F%B3%E5%A1%94.png 4480w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;而牧羊人虽然终于升级到了专家，他却并不收购绿色染料。无奈只好又招聘了一位牧羊人，用羊毛机的库存将他迅速喂到了专家级，但他也不收购绿色染料。
如此反复，现在正在筹备招聘第五位牧羊人。&lt;/p&gt;
&lt;p&gt;我们已经有一整套满配的钻石装备了。女朋友不知道从哪里看到了一种“紫色的、看上去就很高级的装备”，我告诉她是下界合金装备，她就钻进下界找残骸去了。
在用床炸开了数十个区块之后，终于凑够了一套下界合金装备。现在女朋友要去整第二套满配装备了。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w06]]></title><description><![CDATA[本周新收藏的歌有一首。]]></description><link>https://doma.land/weekly/23w06/</link><guid isPermaLink="false">https://doma.land/weekly/23w06/</guid><pubDate>Sun, 12 Feb 2023 20:17:24 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌有一首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;工作和生活&lt;/h2&gt;
&lt;p&gt;说快也快，第六周已经过去了，也就是今年已过了 10%。
这周感觉对生活节奏的掌握感更强了，更积极地早起，更清醒地应对拖延。希望能保持下去。&lt;/p&gt;
&lt;p&gt;这周工作时听歌的时间变得比以前要少了，因为在尝试一些让自己工作时更专注的方法，发现专注地工作时听歌其实会有一些干扰，尤其是有歌词的歌曲。&lt;/p&gt;
&lt;p&gt;我现在将每天的上班时间分割成 45 分钟的节，每节之间休息十分钟左右，就像上学时候的课间那样。这样来让自己不要连续坐太久。
确实是有效果的，后两天甚至合上了 12 次站立的圆环。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/edb8e9571f8ee17a75398589692ea040/ad68d/%E5%81%A5%E8%BA%AB%E5%9C%86%E7%8E%AF.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.708860759493675%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAABwwMhP//EABQQAQAAAAAAAAAAAAAAAAAAABD/2gAIAQEAAQUCf//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABgQAAIDAAAAAAAAAAAAAAAAAAAQMaHh/9oACAEBAAY/Apoxf//EABkQAAMBAQEAAAAAAAAAAAAAAAABESFBUf/aAAgBAQABPyFSrAz1cDh//9oADAMBAAIAAwAAABB4H//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/EIr/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAEAAgMBAQAAAAAAAAAAAAABESEAMUHB8P/aAAgBAQABPxATHiesWI/ZfMTiHRdbvP/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;健身圆环&quot;
        title=&quot;健身圆环&quot;
        src=&quot;/static/edb8e9571f8ee17a75398589692ea040/828fb/%E5%81%A5%E8%BA%AB%E5%9C%86%E7%8E%AF.jpg&quot;
        srcset=&quot;/static/edb8e9571f8ee17a75398589692ea040/ff44c/%E5%81%A5%E8%BA%AB%E5%9C%86%E7%8E%AF.jpg 158w,
/static/edb8e9571f8ee17a75398589692ea040/a6688/%E5%81%A5%E8%BA%AB%E5%9C%86%E7%8E%AF.jpg 315w,
/static/edb8e9571f8ee17a75398589692ea040/828fb/%E5%81%A5%E8%BA%AB%E5%9C%86%E7%8E%AF.jpg 630w,
/static/edb8e9571f8ee17a75398589692ea040/0ede0/%E5%81%A5%E8%BA%AB%E5%9C%86%E7%8E%AF.jpg 945w,
/static/edb8e9571f8ee17a75398589692ea040/ad68d/%E5%81%A5%E8%BA%AB%E5%9C%86%E7%8E%AF.jpg 1170w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Minecraft&lt;/h2&gt;
&lt;p&gt;这周也建造了很多机器。&lt;/p&gt;
&lt;p&gt;上周有了羊毛机后，原先地面上的羊圈就拆除了，给村民交易所留出场地。
但女朋友觉得把村民关在 1x1 的单元里资本家看了都流泪，强烈要求村民至少得有床可以睡觉。于是在空地盖起了小商铺（参考了&lt;a href=&quot;https://www.bilibili.com/video/BV1Mu411m78E&quot;&gt;【【我的世界建筑教程】小小杂货铺~-哔哩哔哩】】&lt;/a&gt;），让每个村民有 3x2 的空间。&lt;/p&gt;
&lt;p&gt;造出了刷铁轨机（参考了&lt;a href=&quot;https://b23.tv/8c4nNCm&quot;&gt;【[我的世界]无粘液球地毯机-哔哩哔哩】&lt;/a&gt;），运输村民派上了用场。&lt;/p&gt;
&lt;p&gt;女朋友说钓鱼要是可以不用一直抛竿收竿就好了，于是这周造了钓鱼机（参考了&lt;a href=&quot;https://b23.tv/TcT8xQ3&quot;&gt;【[教程]1.19 宝藏钓鱼机-哔哩哔哩】&lt;/a&gt; 和&lt;a href=&quot;https://b23.tv/hn2IvHm&quot;&gt;【我的世界：java 版自动钓鱼机，可钓宝藏，无高难红石-哔哩哔哩】&lt;/a&gt;）。最近女朋友的任务是打一套满配附魔的钻石装备，钓鱼机在获取经验和一些附魔书上还是有不错的作用。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/27095f00827b01a2002f0382cfc38786/4e8c3/%E9%92%93%E9%B1%BC%E6%9C%BA.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.32911392405063%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAACpUlEQVQoz52OzW8SCRjGZxjEGoYtpa20U2jL0OGrfDgwVKYUWqTQD/kQBUu1FkGspq2e9KIe9rBqs9n9C3Yvmz2YmHjRg3o28WaMdxOPnvwbfsbGGD148fDmyZvneX55BMlqQRAEBEH8qr9y33VHlAFEi/jNsNokrEcs3wKSJGGVvv6i+FOQbLeRDY0hTAZljtikQ+jw+DHGpu0oqszg8ABWScJul1GmwgyNKMgOx48gUUQakPBPOamkp1lPTiJ4A/bDgG3AimdGxqvJ+CIOtIQbl2eWYf8yofktNLPHsBJEFAQsFguiKGI7aiUZOM6q7iXtH0VXR74sdPLboAPZdRTnhI2xCTsBzUkwFUA11lATK0TNdSJLHbR0g6FRGYfLxpBbRg+4WYqME1AGmQu6aZgqghryktBLTGohXNMufF4X81GF2fwUqVKEQqXAme09FpeLRDNxpsNujo87cCtO/MoI8clRot4hCrqHjUUNYcLvxxddInwyTXYtRm55hpWyRu9Shv1elr3+FrXuPt1mge62SbuRZmE9yFxRJZb1kj81QzI4yqzHST3jQ8gUNeoXVHZ33Jy7HKF+NUHreor+fp5be2Ue7Ne5vVvibr/M3zcabN04SXUnxu27dR4cbHN25wQrzRCGOUHO8CAUW2Hu/b7APwdlGp04tX6cM1cS9DZzbDczNPNBerUUd7qnuHjJYLUdorIR5vn/D/n09iXPHt2n2omx2o6wUPIh1DoxrrUM1soxKlfiVLcSVNtJ2pUUGxXjUFvndSoX5qhvpFmtGtRr8zz974APr5/w8c0zdm4WKTY1VlphhM2OTnctTr6k0tzVKZyOkMlFCJ0IMBPX0PQA5lycnGlgmLMkkmHMbIIn//7B+1eP+fTuBX/92WexoXJ6M8pnbLpX2Gx+2iwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;钓鱼机&quot;
        title=&quot;钓鱼机&quot;
        src=&quot;/static/27095f00827b01a2002f0382cfc38786/f058b/%E9%92%93%E9%B1%BC%E6%9C%BA.png&quot;
        srcset=&quot;/static/27095f00827b01a2002f0382cfc38786/c26ae/%E9%92%93%E9%B1%BC%E6%9C%BA.png 158w,
/static/27095f00827b01a2002f0382cfc38786/6bdcf/%E9%92%93%E9%B1%BC%E6%9C%BA.png 315w,
/static/27095f00827b01a2002f0382cfc38786/f058b/%E9%92%93%E9%B1%BC%E6%9C%BA.png 630w,
/static/27095f00827b01a2002f0382cfc38786/40601/%E9%92%93%E9%B1%BC%E6%9C%BA.png 945w,
/static/27095f00827b01a2002f0382cfc38786/78612/%E9%92%93%E9%B1%BC%E6%9C%BA.png 1260w,
/static/27095f00827b01a2002f0382cfc38786/4e8c3/%E9%92%93%E9%B1%BC%E6%9C%BA.png 4480w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;为了买价格 56 绿宝石的效率 V 附魔书，需要卖纸给图书管理员来挣绿宝石，所以做了甘蔗机（参考了&lt;a href=&quot;https://b23.tv/p45F5Zx&quot;&gt;【我的世界：自动甘蔗机，全版本通用，我看谁还学不会？-哔哩哔哩】&lt;/a&gt;）。不过效率并不算高，纸的价格又很低，勉强能慢慢攒一点。
查了百科发现农民收南瓜也太实惠了，于是又做了一台南瓜机（参考了&lt;a href=&quot;https://b23.tv/n2hJhnY&quot;&gt;【我的世界：西南瓜农场，双版本通用，掏干村民的绿宝石-哔哩哔哩】&lt;/a&gt;），不得不说真的香。（再后来又发现制箭师和石匠简直是大善人）&lt;/p&gt;
&lt;p&gt;流浪商人又带着粘液球来了，现在有 10 个粘液球了，距离地毯复制机还差 8 个。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[MSW Handlers with Filesystem Route]]></title><description><![CDATA[When we use Mock Service Worker (MSW) for api mocking, as the number of handlers grows, it's common we come into these problems. First, we…]]></description><link>https://doma.land/blog/msw-handlers-with-fs-route/</link><guid isPermaLink="false">https://doma.land/blog/msw-handlers-with-fs-route/</guid><pubDate>Sat, 11 Feb 2023 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When we use &lt;a href=&quot;https://mswjs.io/&quot;&gt;Mock Service Worker (MSW)&lt;/a&gt; for api mocking, as the number of handlers grows, it&apos;s common we come into these problems.&lt;/p&gt;
&lt;p&gt;First, we often have to write the same API path multiple times for register handlers for each method it supports as REST API.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// mocks/handlers.js&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; handlers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  rest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/api/todos&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  rest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/api/todos&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  rest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/api/todos/:id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  rest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/api/todos/:id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  rest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/api/todos/:id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// It&apos;s even worse when paths are nested&lt;/span&gt;
  rest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/api/todos/:id/attachments&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  rest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/api/todos/:id/attachments&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  rest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/api/todos/:id/attachments/:name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  rest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/api/todos/:id/attachments/:name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  rest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/api/todos/:id/attachments/:name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And second, it&apos;s heavy to put all these handlers in a single file, so we usually separate them as modules.
Most of time we separate modules based on features, which often couple with the REST API paths.
And again that&apos;s repetition.&lt;/p&gt;
&lt;p&gt;Is there a way to define modules directly using API paths, just like &lt;a href=&quot;https://nextjs.org/docs/api-routes/introduction&quot;&gt;Next.js API Routes&lt;/a&gt;?
For example, can we create an &lt;code&gt;api/todos.js&lt;/code&gt; and export a &lt;code&gt;{ post, get }&lt;/code&gt; object, and it generates &lt;code&gt;POST /api/todos&lt;/code&gt; and &lt;code&gt;GET /api/todos&lt;/code&gt; handlers for us?&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// mocks/api/todos.js&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; handlers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function-variable function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; json &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token function-variable function&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; status&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; json &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;201&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; handlers&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Fortunately, if you are building with webpack, you can do this with &lt;a href=&quot;https://webpack.js.org/guides/dependency-management/#requirecontext&quot;&gt;&lt;code&gt;require.context&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;require.context&lt;/code&gt; allow us to dynamicly &lt;code&gt;require&lt;/code&gt; files under a directory, like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Search for all files ending with .js under ./api directory,&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// including files in sub-directories&lt;/span&gt;
require&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./api&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;\.js$&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That enables us to implement API Routes with it:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// mocks/handlers.js&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; requireMockHandler &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; require&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./api&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;\.js$&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; handlers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; requireMockHandler
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;allHandlers&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; handlers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;requireMockHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;default

    &lt;span class=&quot;token comment&quot;&gt;// map &quot;./path/[to]/handlers.js&quot; -&gt; &quot;/api/path/:to/handlers&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; apiPath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;/api/&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;
      path
        &lt;span class=&quot;token comment&quot;&gt;// trim &quot;./&quot; at start&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;^\.\/&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// trim &quot;.js&quot; at end&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;\.[^/.]+$&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// replace &quot;[param]&quot; with &quot;:param&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;\[([^/]*)\]&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-flags&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;:$1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; pathHandlers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getOwnPropertyNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;handlers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; handler &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; handlers&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;method&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

      &lt;span class=&quot;token comment&quot;&gt;// for every method in handlers, register an msw handler&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;method &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; rest&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; rest&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;method&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;apiPath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handler&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;allHandlers&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;pathHandlers&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally, all we have to do is to create files according to API paths,
and get msw handlers without repeatedly writing down API paths.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;src/mocks/
├── api/
|   ├── todos.js
|   └── todos/
|       ├── [id].js
|       └── [id]/
|           ├── attachments.js
|           └── attachments/
|               └── [name].js
└── handlers.js&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The example code snippets demoed in this article can be found at &lt;a href=&quot;https://github.com/SevenOutman/msw-require-context-demo&quot;&gt;https://github.com/SevenOutman/msw-require-context-demo&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w05]]></title><description><![CDATA[本周新收藏的歌有一首。]]></description><link>https://doma.land/weekly/23w05/</link><guid isPermaLink="false">https://doma.land/weekly/23w05/</guid><pubDate>Mon, 06 Feb 2023 13:49:25 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌有一首。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;p&gt;把 GitHub 的站内通知关掉了，因为和邮件是重复的。现在用邮件来处理 GitHub 上的任务感觉还挺顺畅。每天上班首先把收件箱过一遍，如果是需要我处理的事情就移入 Action Today 文件夹，账单什么的就归档，营销邮件就删掉等等，直到把收件箱清空。然后正式开始工作 session 时针对 Action Today 文件夹里的邮件一条条进行处理，直到清空 Action Today 文件夹。这样一来就不漏不拖地把任务完成了。&lt;/p&gt;
&lt;p&gt;稍微整理了下 &lt;a href=&quot;https://github.com/rsuite/rsuite&quot;&gt;&lt;code&gt;rsuite&lt;/code&gt; 仓库&lt;/a&gt; 的标签，添加了几个表示 Issue 当前处理状态的&lt;a href=&quot;https://github.com/rsuite/rsuite/labels&quot;&gt;标签&lt;/a&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Needs Triage&lt;/strong&gt; 表示仅仅是知晓了 Issue 的存在，还没有针对 Issue 开展工作。在每日处理 Action Today 任务时，对于新创建的 Issue，我会阅读主体内容，确定与哪个 API 相关，然后添加对应 API 的标签，并且添加 Needs Triage 标签。后续会专门筛选 Needs Triage 标签来集中处理。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Needs More Info&lt;/strong&gt; 表示当前提供的信息不足以复现或定位 Issue 所描述的问题。等待作者补充更多的信息。具有 Needs More Info 的 Issue 将被暂时搁置直到有新的回复。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;标签多起来后，为了更好地归类，又给标签分别加了前缀，比如&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;status: Needs Triage, status: Needs More Info&lt;/strong&gt; 用来标记 Issue 当前处理的状态&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;type: Bug, type: Feature Request, type: Enhancement&lt;/strong&gt; 用来标记 Issue 的类型，对应不同的处理方式&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;topic: Style, topic: TypeScript, topic:UI/UX&lt;/strong&gt; 用来标记 Issue 所关心的话题&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;有了这些标签后，处理 rsuite issues 的条理变得更加清晰了。配合更好的时间/任务管理方法，能够更及时高效地处理 rsuite 的问题了。&lt;/p&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;p&gt;转阴已经不短的时间了，晚上睡觉还是常常会梦醒，然后难以继续入睡。另一方面，早上总是没法按时起床，闹钟响后习惯性地按掉去睡懒觉，结果早晨节奏还是有些拖沓。&lt;/p&gt;
&lt;p&gt;之前买的《永乐疑阵》到了，周末和女朋友开始玩了。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c2a6c3388161b5f678f0ff69316247c6/eea4a/%E6%B0%B8%E4%B9%90%E7%96%91%E9%98%B5.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.31645569620254%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAIDBP/EABcBAAMBAAAAAAAAAAAAAAAAAAABAgP/2gAMAwEAAhADEAAAAcrvPK6kgf8A/8QAGhABAAIDAQAAAAAAAAAAAAAAAQACERMhI//aAAgBAQABBQJONsV8o9NaWn//xAAVEQEBAAAAAAAAAAAAAAAAAAAAEv/aAAgBAwEBPwGUv//EABcRAQADAAAAAAAAAAAAAAAAAAABAhL/2gAIAQIBAT8BizT/xAAbEAADAAIDAAAAAAAAAAAAAAAAARECEDFRcf/aAAgBAQAGPwK2YkXhwkLFotvWv//EABsQAQACAwEBAAAAAAAAAAAAAAEAESExQVFh/9oACAEBAAE/ITaQPpv5H9Y4meVnQJTEfEY32msmbn//2gAMAwEAAgADAAAAECzf/8QAFxEAAwEAAAAAAAAAAAAAAAAAAAERIf/aAAgBAwEBPxB7UWEH/8QAFhEBAQEAAAAAAAAAAAAAAAAAAREQ/9oACAECAQE/EJjcf//EAB0QAQEAAgMAAwAAAAAAAAAAAAERADEhQVFhcYH/2gAIAQEAAT8QBFQm59x94VkcCRxbjMXTYl71gRaTRI++Y2eqo1+ZNhh8Gf/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;永乐疑阵&quot;
        title=&quot;永乐疑阵&quot;
        src=&quot;/static/c2a6c3388161b5f678f0ff69316247c6/828fb/%E6%B0%B8%E4%B9%90%E7%96%91%E9%98%B5.jpg&quot;
        srcset=&quot;/static/c2a6c3388161b5f678f0ff69316247c6/ff44c/%E6%B0%B8%E4%B9%90%E7%96%91%E9%98%B5.jpg 158w,
/static/c2a6c3388161b5f678f0ff69316247c6/a6688/%E6%B0%B8%E4%B9%90%E7%96%91%E9%98%B5.jpg 315w,
/static/c2a6c3388161b5f678f0ff69316247c6/828fb/%E6%B0%B8%E4%B9%90%E7%96%91%E9%98%B5.jpg 630w,
/static/c2a6c3388161b5f678f0ff69316247c6/0ede0/%E6%B0%B8%E4%B9%90%E7%96%91%E9%98%B5.jpg 945w,
/static/c2a6c3388161b5f678f0ff69316247c6/3ac88/%E6%B0%B8%E4%B9%90%E7%96%91%E9%98%B5.jpg 1260w,
/static/c2a6c3388161b5f678f0ff69316247c6/eea4a/%E6%B0%B8%E4%B9%90%E7%96%91%E9%98%B5.jpg 1280w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;打算尝试今年每个月都进行一个新的家庭娱乐项目，一月放假跳过了，二月是这个永乐疑阵，三月已经订购了乐高新品，等到了后再贴照片。&lt;/p&gt;
&lt;h2&gt;Minecraft&lt;/h2&gt;
&lt;p&gt;这周 Minecraft 生存进展还不错，起点是刷出了出售经验修补附魔书的图书管理员村民，虽然几本经验修补就掏空了钱包，但这样一来就可以大胆地使用附魔钻石工具了。为了可持续发展，把村民运了回来下崽。因为女朋友每天都会去钓鱼、村子里有羊圈、我每天都需要砍树，所以优先安排了一位渔夫、一位牧羊人和一位制箭师，回收一些绿宝石。&lt;/p&gt;
&lt;p&gt;有了效率附魔的镐、锹和斧后，开始在屋子下面挖起了地下室，准备放些红石机械进去（参考了&lt;a href=&quot;https://b23.tv/QYpV1GD&quot;&gt;【山洞:&quot;下次不许再塞了！&quot;-哔哩哔哩】&lt;/a&gt;）。第一个放进去的是十六色羊毛机（参考了&lt;a href=&quot;https://b23.tv/9XnGOvb&quot;&gt;【💗 萌新家的 16 色全自动羊毛农场建筑教程 💗-哔哩哔哩】&lt;/a&gt;），可以卖些羊毛给牧羊人。&lt;/p&gt;
&lt;p&gt;由于斧子上没有耐久附魔，很快耐久值就不太够用了。虽然有经验修补附魔，但是和平生存里获取经验的方式很有限。在海边的空地上建了三层仙人掌农场（参考了&lt;a href=&quot;https://b23.tv/TwBabL9&quot;&gt;【我的世界：仙人掌农场，生存前期可做，全版本通用-哔哩哔哩】&lt;/a&gt;），目前供应速度还是足够支持这几件工具的。&lt;/p&gt;
&lt;p&gt;准备做地毯复制机，来给未来的自动熔炉供应燃料。黏液块可以用蜂蜜块代替，但是家附近的几个蜂巢似乎都没有蜜蜂，很奇怪。几番种树之后也只刷出了一个蜂巢，但只有一只蜜蜂，还是没办法繁殖，一边继续种树一边暂且拿这一个孤单的蜂巢攒蜂蜜。虽然黏液块解决了，但没有黏液球的话还是没法做黏性活塞。想起之前有一趟帮女朋友取箱子的路上遇到过一个丛林神庙，但当时并没想起里面有黏性活塞（以及财宝）可以拿，只拿走了发射器 🤣。更糟的是没把它的坐标记下来…只能骑上马去跑图，凭模糊的记忆去找。最后虽然没找到当时的那座丛林神庙，但是在家正南方不远处发现了一个新的丛林神庙，终于拿到了黏性活塞！结果第二天流浪商人造访就带了黏液球 😐。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;稳中向好。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w04]]></title><description><![CDATA[开工了。]]></description><link>https://doma.land/weekly/23w04/</link><guid isPermaLink="false">https://doma.land/weekly/23w04/</guid><pubDate>Mon, 30 Jan 2023 14:54:21 GMT</pubDate><content:encoded>&lt;p&gt;开工了。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;最近发现每周到了写 weekly 的时候，这周发生的事情还有想法有一些就已经忘记了。还是应该每天安排个固定的时间来做回顾（maybe 其实就是写日记？），还可以顺便检查一下习惯养成情况，今天有没有起床后喝一杯水、有没有刷 b 站超过一个小时什么的。&lt;/p&gt;
&lt;h2&gt;辞旧迎新&lt;/h2&gt;
&lt;p&gt;初五回到上海来了，跟女朋友轮流开了五个多小时车，从天亮开到天黑，还是有些累人。之后花了两天时间给屋子做了清扫，铺上了新的门垫。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6b1207bf3f7513ce7306071f5f2c8727/eea4a/%E5%B0%8F%E5%85%94%E5%AD%90%E9%97%A8%E5%9E%AB.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.31645569620254%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAQFA//EABUBAQEAAAAAAAAAAAAAAAAAAAMB/9oADAMBAAIQAxAAAAHSewmaVhQO/wD/xAAdEAAABQUAAAAAAAAAAAAAAAAAAQIDEQQSEyIx/9oACAEBAAEFAqk9OBqMa13gwl6C/8QAFhEBAQEAAAAAAAAAAAAAAAAAEQIQ/9oACAEDAQE/ASTP/8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQIBAT8Bqv/EABkQAQADAQEAAAAAAAAAAAAAAAEAEBExQf/aAAgBAQAGPwIz2jJvKAJ//8QAGhABAQEAAwEAAAAAAAAAAAAAAREAITFRQf/aAAgBAQABPyGnoaQ81xHzS7qiDmPutMDuAb//2gAMAwEAAgADAAAAENQ//8QAFhEBAQEAAAAAAAAAAAAAAAAAEQEA/9oACAEDAQE/EIhXSQ3/xAAXEQEBAQEAAAAAAAAAAAAAAAABABEx/9oACAECAQE/EBSt5f/EABwQAQADAQADAQAAAAAAAAAAAAEAESExQWGBsf/aAAgBAQABPxALnAr0EApFGF1NFi0u1XVfsuCSK011/ImVQ12DwMdO+Z//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;小兔子门垫&quot;
        title=&quot;小兔子门垫&quot;
        src=&quot;/static/6b1207bf3f7513ce7306071f5f2c8727/828fb/%E5%B0%8F%E5%85%94%E5%AD%90%E9%97%A8%E5%9E%AB.jpg&quot;
        srcset=&quot;/static/6b1207bf3f7513ce7306071f5f2c8727/ff44c/%E5%B0%8F%E5%85%94%E5%AD%90%E9%97%A8%E5%9E%AB.jpg 158w,
/static/6b1207bf3f7513ce7306071f5f2c8727/a6688/%E5%B0%8F%E5%85%94%E5%AD%90%E9%97%A8%E5%9E%AB.jpg 315w,
/static/6b1207bf3f7513ce7306071f5f2c8727/828fb/%E5%B0%8F%E5%85%94%E5%AD%90%E9%97%A8%E5%9E%AB.jpg 630w,
/static/6b1207bf3f7513ce7306071f5f2c8727/0ede0/%E5%B0%8F%E5%85%94%E5%AD%90%E9%97%A8%E5%9E%AB.jpg 945w,
/static/6b1207bf3f7513ce7306071f5f2c8727/3ac88/%E5%B0%8F%E5%85%94%E5%AD%90%E9%97%A8%E5%9E%AB.jpg 1260w,
/static/6b1207bf3f7513ce7306071f5f2c8727/eea4a/%E5%B0%8F%E5%85%94%E5%AD%90%E9%97%A8%E5%9E%AB.jpg 1280w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;在尝试用一种新的方式整理 Reminders 和 Notes，将堆积在脑中的想法和事务倾倒出去，确实感觉更放松一些了。新的一年准备注重一下 wlb，尽量不要让头脑和时间被工作过多地占据，而忽略了生活和健康。&lt;/p&gt;
&lt;h2&gt;上班第一天&lt;/h2&gt;
&lt;p&gt;我有两副眼镜，一副平时每天都戴，另一副因为镜框比较重戴久了耳朵被压得很痛所以不怎么戴。而这次回来上班我却选择戴了重的那一副，因为它可以提醒我连续工作一段时间要摘下眼镜休息下 🤣，这可比闹钟要有效。&lt;/p&gt;
&lt;p&gt;以前总是将 GitHub 通知 inbox 当作自己的待办事项列表，将不需要自己处理的就删掉，需要处理的就留在里面等着以后处理，但这样久而久之 inbox 就越堆越多，加上还有很多 bot 通知，逐渐就处理不动了。看了 Carl Pullein 的视频受到一些启发，明白了 inbox 的由来以及它在任务处理流程中实际扮演的角色。这回专门用一下午一口气清空了 inbox，基本上对每一条通知都做了处理，将新的 issue 打好 label，写好回复或者 assign 好成员，不需要处理的 bot 通知什么的就标记为 done，总之就是让 inbox 里什么都不要留。然后再针对自己被 assign 的任务去逐条处理，不再像以前一样一团乱麻了。接下来打算每天都对 inbox 做一次清空。不过 GitHub 通知没有分目录的功能，得研究下怎么配合邮件来使用，或者设计一套 label，来更好地组织需要处理的任务。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w03]]></title><description><![CDATA[本周放假回家来了。]]></description><link>https://doma.land/weekly/23w03/</link><guid isPermaLink="false">https://doma.land/weekly/23w03/</guid><pubDate>Mon, 23 Jan 2023 19:46:14 GMT</pubDate><content:encoded>&lt;p&gt;本周放假回家来了。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;This blog&lt;/h2&gt;
&lt;p&gt;把 aplayer-react 的文章整理好发掉了，想起来自己居然还有一个订阅号来着，就也在订阅号发了一遍。把 markdown 编辑成微信公众号图文真是费劲啊。&lt;/p&gt;
&lt;p&gt;给博客加了个 plop generator，创建新 weekly 的时候不用手动写周号和创建时间了。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ npx plop weekly
? 哪一周？ 23w03
✔  ++ /content/weekly/23w03/index.md&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;放假&lt;/h2&gt;
&lt;p&gt;爸妈都还没阳，所以回来这么些天基本没出过门。
幸好带了电脑回来，可以偶尔写点代码。&lt;/p&gt;
&lt;p&gt;在 &lt;a href=&quot;https://github.com/rsuite/rsuite/discussions/2932&quot;&gt;rsuite v6 thoughts&lt;/a&gt; 下写了一段比较长的&lt;a href=&quot;https://github.com/rsuite/rsuite/discussions/2932#discussioncomment-4749139&quot;&gt;评论回复&lt;/a&gt;，评论是一位用户关于 rsuite v6 可能采用 CSS in JS 提出的反对意见，我回复他来解释为什么考虑采用 CSS in JS。
由于回复的是英文，写的过程中一度因为担心自己表达地不够有条理而想要干脆放弃不写了。但是一方面觉得这样表现地像是忽视了用户的意见，一方面反思自己怎么连克服这么一点困难的毅力都没有，最后还是静下心来写完了回复。算是新年伊始自己一点微小的进步。&lt;/p&gt;
&lt;p&gt;假期还有三天，需要恢复一下作息时间了。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w02]]></title><description><![CDATA[本周新收藏的歌有四首]]></description><link>https://doma.land/weekly/23w02/</link><guid isPermaLink="false">https://doma.land/weekly/23w02/</guid><pubDate>Tue, 17 Jan 2023 17:17:00 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌有四首&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;终于又在通勤路上和工作时候听歌了，收藏了一大堆。&lt;/p&gt;
&lt;h2&gt;aplayer-react&lt;/h2&gt;
&lt;p&gt;这周写了介绍 &lt;code&gt;aplayer-react&lt;/code&gt; 的文章，但总感觉内容有点空泛，心想着再改改再改改，最后就还拖着没有发出来。趁着今天更新 weekly 一鼓作气把它改好发出来吧，切不可拖到年后。&lt;/p&gt;
&lt;h2&gt;Minecraft&lt;/h2&gt;
&lt;p&gt;女朋友让我在码头旁边盖了个小屋，放上箱子和烟熏炉，这样她钓鱼就不用总是装满了背包往家跑了。这次盖的还是木质的，下一个屋子想试试盖那种现代风格的 multi 火柴盒。&lt;/p&gt;
&lt;p&gt;钓鱼这么久也只钓上来过一本经验修补，不太够用，还是准备走交易这条路。去最近的村庄里把一个无业游民变成了卖经验修补的图书管理员（参考了 MC 百科和&lt;a href=&quot;https://b23.tv/41Cwbcs&quot;&gt;【3 分钟教你跟村民交换顶级附魔书-哔哩哔哩】&lt;/a&gt;)，买了 4 本就花光了之前挖到的所有绿宝石。因为开的和平的缘故没办法打骨折了，准备做个自动甘蔗机靠卖纸挣钱。&lt;/p&gt;
&lt;h2&gt;新年&lt;/h2&gt;
&lt;p&gt;上完了今年最后一天班，看完了今年最后一场演出。贴好了春联，下周就要放假回家过年了。&lt;/p&gt;
&lt;p&gt;往年没有正经做过什么年度计划，有不少想做的事情不知不觉就一直拖着。回过头来总觉得嗯今年虽然不是白过，但也说不上有什么令自己满意的收获。今年准备尝试着做一个年度计划看看一年之后是否会觉得更充实。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[为你的 React 应用添加音乐播放器]]></title><description><![CDATA[本文介绍 aplayer-react 库，以及我如何使用 aplayer-react 在我的 Gatsby…]]></description><link>https://doma.land/blog/为你的 React 应用添加音乐播放器/</link><guid isPermaLink="false">https://doma.land/blog/为你的 React 应用添加音乐播放器/</guid><pubDate>Tue, 10 Jan 2023 17:49:00 GMT</pubDate><content:encoded>&lt;p&gt;本文介绍 aplayer-react 库，以及我如何使用 aplayer-react 在我的 Gatsby 博客中播放网易云音乐歌曲。&lt;/p&gt;
&lt;!-- !more --&gt;
&lt;p&gt;最近在培养每周在&lt;a href=&quot;https://doma.land&quot;&gt;我的博客&lt;/a&gt;更新周记的习惯。
在周记的开头，我会分享本周我在网易云音乐上新收藏的歌曲，有时是一首，有时也会有多首。
相比仅仅是将歌曲的链接贴在文中，我想干脆在周记中显示一个音乐播放器，将歌曲播放出来。&lt;/p&gt;
&lt;p&gt;播放器的选型上我选择 &lt;a href=&quot;https://github.com/DIYgod&quot;&gt;@DIYgod&lt;/a&gt; 创建的 &lt;a href=&quot;https://github.com/DIYgod/APlayer&quot;&gt;&lt;code&gt;APlayer&lt;/code&gt;&lt;/a&gt; 库，因为我很喜欢它的外观。由于我的博客使用 React 编写，我编写了 &lt;code&gt;aplayer-react&lt;/code&gt; 库，使得可以以 React 组件的形式使用 APlayer。&lt;/p&gt;
&lt;h2&gt;aplayer-react&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;aplayer-react&lt;/code&gt; 的实现上其实并未调用 APlayer 的 API。事实上，它只是使用了 APlayer 的样式表，功能逻辑则完全使用 React 重写。可以将其理解为“APlayer 原型的 React 实现”。&lt;/p&gt;
&lt;p&gt;示例效果如下：&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e32328f9609fe8271d38c54f8b38107b/c929c/screenshot.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 38.60759493670886%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABYlAAAWJQFJUiTwAAABKklEQVQoz5VRy0rDQBSdD/cDpG7EjVsfCOLGgjtduFBUECoIrqJgpk2TNjMJaR5NMmmOnKGpqFFw4HAJmTmvK54v3jDcv8TZ6RDG1IjjGEmSYLFYIM9z1HWNpmnQd9q2/YLVagVxs3OI861tHO8doCgLTKdTaK0RRRGklPDW3xSgUJqmdhZF8UPAEu4OBrg6OYLz9LKW/YSpamRZtgHJurlcLmGMQVVVKMtyk0Q83I1we32P0eMIYynhvrtQoYJWGpGObAV9YIIwDC1msxlc17UphNYhnFcHcizh+779SQddxL/AOwQFgiCwnQs+9jzPXqBlRmGs/xxGJqmNTFa6IgntTyYTa5/9dJvr2yYnCdhh55A9CjojAZ1939hvIBEJ+WY+n1tDSilr4gMmSGFk2J6Z3wAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot for aplayer-react&quot;
        title=&quot;Screenshot for aplayer-react&quot;
        src=&quot;/static/e32328f9609fe8271d38c54f8b38107b/f058b/screenshot.png&quot;
        srcset=&quot;/static/e32328f9609fe8271d38c54f8b38107b/c26ae/screenshot.png 158w,
/static/e32328f9609fe8271d38c54f8b38107b/6bdcf/screenshot.png 315w,
/static/e32328f9609fe8271d38c54f8b38107b/f058b/screenshot.png 630w,
/static/e32328f9609fe8271d38c54f8b38107b/40601/screenshot.png 945w,
/static/e32328f9609fe8271d38c54f8b38107b/c929c/screenshot.png 1218w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;主要的特性包括：&lt;/p&gt;
&lt;ul&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;/ul&gt;
&lt;h3&gt;基础用例&lt;/h3&gt;
&lt;p&gt;一个最基本的用例如下，为 APlayer 组件的 &lt;code&gt;audio&lt;/code&gt; 属性传入一个包含歌曲信息的对象。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; APlayer &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aplayer-react&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aplayer/dist/APlayer.min.css&quot;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;APlayer&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;audio&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Dancing with my phone&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;artist&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;HYBS&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://music.163.com/song/media/outer/url?id=1969744125&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;cover&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;https://p1.music.126.net/tOtUdKjS9rktAFRamcomWQ==/109951167748733958.jpg&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/197d8a2e66db58bc56158b5361b22cc5/cd138/screenshot-without-playlist.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 14.556962025316455%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAhklEQVQI123PvQoCMQwH8L67o09ws7OTgos3CS73FHfY3qQW+v35lwYqChf4DU1SkrBxuOJ8vCCXBGMMsdbCOYcYI2lRa930G+3NDrs9bqcRz/cLyzxDcA7OOYQQkFJCa02NpZQ/PZdS+so5g033CevyoGLfqAshwHtP225pl7SBjVKK/nwAYBfl9fIcLp8AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;aplayer-react 截图&quot;
        title=&quot;aplayer-react 截图&quot;
        src=&quot;/static/197d8a2e66db58bc56158b5361b22cc5/f058b/screenshot-without-playlist.png&quot;
        srcset=&quot;/static/197d8a2e66db58bc56158b5361b22cc5/c26ae/screenshot-without-playlist.png 158w,
/static/197d8a2e66db58bc56158b5361b22cc5/6bdcf/screenshot-without-playlist.png 315w,
/static/197d8a2e66db58bc56158b5361b22cc5/f058b/screenshot-without-playlist.png 630w,
/static/197d8a2e66db58bc56158b5361b22cc5/40601/screenshot-without-playlist.png 945w,
/static/197d8a2e66db58bc56158b5361b22cc5/cd138/screenshot-without-playlist.png 1220w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;滚动歌词&lt;/h3&gt;
&lt;p&gt;在 &lt;code&gt;audio&lt;/code&gt; 对象的 &lt;code&gt;lrc&lt;/code&gt; 字段设置 LRC 格式的歌词，即可在界面上显示跟随歌曲进度滚动的歌词。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;APlayer&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;audio&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Dancing with my phone&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;artist&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;HYBS&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://music.163.com/song/media/outer/url?id=1969744125&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;cover&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;https://p1.music.126.net/tOtUdKjS9rktAFRamcomWQ==/109951167748733958.jpg&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;lrc&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[00:00.000] 作词 : James Alyn Wee/Kasidej Hongladaromp\n[00:01.000] 作曲 : James Alyn Wee/Kasidej Hongladaromp\n[00:28.836] I&apos;m just laying on the floor again\n[00:33.124] Can&apos;t be bothered to get up now\n[00:36.345] I wouldn’t care\n[00:38.348] If I never get up again\n[00:41.363] I don’t want to\n[00:47.388] Then our song comes on the radio\n[00:51.906] Makes me wanna start to dance, oh\n[00:55.163] I wanna know\n[00:56.997] If you feel the same way as me\n[01:00.097] Why would you go?\n[01:02.695]\n[01:05.780] Dancing, I&apos;m all alone\n[01:09.163] Figuring out how I can get you home\n[01:15.129] Dancing with my phone\n[01:18.393] Thinking about you\n[01:22.292]\n[01:25.154] On my feet and now I&apos;m out the door\n[01:29.741] Walking by the places that we used to go\n[01:34.478] I remember all your favorite stores\n[01:37.743] I won&apos;t lie\n[01:43.345] I don&apos;t think I even know myself anymore\n[01:52.741] You&apos;re the one who knew me ****ing well\n[01:58.914] Yeah, you know\n[02:00.129]\n[02:02.177] Dancing, I&apos;m all alone\n[02:05.652] Figuring out how I can get you home\n[02:11.617] Dancing with my phone\n[02:14.687] Thinking about you\n[02:20.993] Dancing, I&apos;m all alone (I&apos;m dancing all alone)\n[02:24.218] Figuring out how I can get you home (How I can get you home)\n[02:30.291] Dancing with my phone (I&apos;m dancing with my phone)\n[02:33.626] Thinking about you\n[02:37.376]\n[02:39.724] Dancing all alone, dancing all alone (I&apos;m dancing all alone)\n[02:44.589] Dancing all alone, dancing all alone (I&apos;m dancing with my phone)\n[02:49.284] Dancing with my phone\n[02:52.504] Thinking about you\n[02:58.522] Dancing all alone, dancing all alone\n[03:03.262] Dancing all alone, dancing all alone (Thinking about you)\n[03:08.094] Dancing with my phone\n[03:11.402] Thinking about you\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/73ad952fe5930f1ba8d4ffa7949e633f/e9140/screenshot-with-lyrics.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 18.9873417721519%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAu0lEQVQY022PzQqCQBSFfesewoIeIFzUItraRmhVu6ggCMPFBCWCLlyo4/g345y4A0JWFz4u3J9zz7Xuuye81R6u60FDoyxLVFWFpmnQti2klOj7Hv9Caz2C5qyDvcF6YsOZLcAFR/gKkSQJoigCYwxxHCNNU3DOkWUZiqIw5HkOpdTogBGc21Nslw6C4/XHAbkkt0II45yEhkx0XTeCvrEupxv8s49HwCCVQl3XRmh4+Xvpc5n6dGyA6m+4fy6ZdanIkwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;APlayer displying scrolling lyrics&quot;
        title=&quot;APlayer displying scrolling lyrics&quot;
        src=&quot;/static/73ad952fe5930f1ba8d4ffa7949e633f/f058b/screenshot-with-lyrics.png&quot;
        srcset=&quot;/static/73ad952fe5930f1ba8d4ffa7949e633f/c26ae/screenshot-with-lyrics.png 158w,
/static/73ad952fe5930f1ba8d4ffa7949e633f/6bdcf/screenshot-with-lyrics.png 315w,
/static/73ad952fe5930f1ba8d4ffa7949e633f/f058b/screenshot-with-lyrics.png 630w,
/static/73ad952fe5930f1ba8d4ffa7949e633f/40601/screenshot-with-lyrics.png 945w,
/static/73ad952fe5930f1ba8d4ffa7949e633f/e9140/screenshot-with-lyrics.png 1226w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;播放列表&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;audio&lt;/code&gt; 属性除了接收单个歌曲信息对象外，也可以接收包含多个歌曲信息的数组。当 &lt;code&gt;audio&lt;/code&gt; 为数组时，会显示一个播放列表。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;APlayer&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;audio&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Dancing with my phone&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;artist&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;HYBS&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://music.163.com/song/media/outer/url?id=1969744125&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;cover&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;https://p1.music.126.net/tOtUdKjS9rktAFRamcomWQ==/109951167748733958.jpg&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;lrc&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[00:00.000] 作词 : James Alyn Wee/Kasidej Hongladaromp\n[00:01.000] 作曲 : James Alyn Wee/Kasidej Hongladaromp\n[00:28.836] I&apos;m just laying on the floor again\n[00:33.124] Can&apos;t be bothered to get up now\n[00:36.345] I wouldn’t care\n[00:38.348] If I never get up again\n[00:41.363] I don’t want to\n[00:47.388] Then our song comes on the radio\n[00:51.906] Makes me wanna start to dance, oh\n[00:55.163] I wanna know\n[00:56.997] If you feel the same way as me\n[01:00.097] Why would you go?\n[01:02.695]\n[01:05.780] Dancing, I&apos;m all alone\n[01:09.163] Figuring out how I can get you home\n[01:15.129] Dancing with my phone\n[01:18.393] Thinking about you\n[01:22.292]\n[01:25.154] On my feet and now I&apos;m out the door\n[01:29.741] Walking by the places that we used to go\n[01:34.478] I remember all your favorite stores\n[01:37.743] I won&apos;t lie\n[01:43.345] I don&apos;t think I even know myself anymore\n[01:52.741] You&apos;re the one who knew me ****ing well\n[01:58.914] Yeah, you know\n[02:00.129]\n[02:02.177] Dancing, I&apos;m all alone\n[02:05.652] Figuring out how I can get you home\n[02:11.617] Dancing with my phone\n[02:14.687] Thinking about you\n[02:20.993] Dancing, I&apos;m all alone (I&apos;m dancing all alone)\n[02:24.218] Figuring out how I can get you home (How I can get you home)\n[02:30.291] Dancing with my phone (I&apos;m dancing with my phone)\n[02:33.626] Thinking about you\n[02:37.376]\n[02:39.724] Dancing all alone, dancing all alone (I&apos;m dancing all alone)\n[02:44.589] Dancing all alone, dancing all alone (I&apos;m dancing with my phone)\n[02:49.284] Dancing with my phone\n[02:52.504] Thinking about you\n[02:58.522] Dancing all alone, dancing all alone\n[03:03.262] Dancing all alone, dancing all alone (Thinking about you)\n[03:08.094] Dancing with my phone\n[03:11.402] Thinking about you\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;僕は今日も&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;artist&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Vaundy&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://music.163.com/song/media/outer/url?id=1441997419&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;cover&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;https://p1.music.126.net/AnR2ejcBgGnOJXPsytivBQ==/109951164922366027.jpg&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;lrc&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[00:00.000] 作词 : Vaundy\n[00:00.002] 作曲 : Vaundy\n[00:00.04]僕は今日も - Vaundy\n[00:15.00]母さんが言ってたんだ\n[00:22.31]お前は才能があるから\n[00:29.06]「芸術家にでもなりな」と\n[00:35.98]また根拠の無い夢を語る\n[00:49.77]父さんが言ってたんだ\n[00:56.58]お前は親不孝だから\n[01:03.45]１人で生きていきなさい\n[01:08.69]また意味もわからず罵倒する\n[01:16.24]１人ではないと暗示をして\n[01:19.89]２人ではないとそう聞こえて\n[01:23.13]思ってるだけじゃ\n[01:24.88]そう 辛くてでも\n[01:26.72]そうする他にすべはなくて\n[01:29.85]愉快な日々だと暗示をして\n[01:33.38]不協和音が 聞こえてきた\n[01:36.80]抑えてるだけじゃ そう 辛くて\n[01:40.08]だから この気持ちを弾き語るよ\n[01:43.83]もしも僕らが生まれてきて\n[01:50.68]もしも僕らが大人になっても\n[01:57.48]もしも僕らがいなくなっていても\n[02:04.37]そこに僕の歌があれば\n[02:09.83]それでいいさ\n[02:18.90]彼女が言ってたんだ\n[02:25.69]あなたはカッコイイから\n[02:32.60]イケメンじゃなくていいんだよ\n[02:37.87]また元も子も無い言葉を君は言う\n[02:45.55]僕はできる子と暗示をして\n[02:48.87]心が折れる音が聞こえた\n[02:52.25]思ってるだけじゃ\n[02:54.02]そう 辛くてでも\n[02:55.86]そうする他にすべはなくて\n[02:59.12]明日は晴れると暗示をして\n[03:02.50]次の日は傘を持って行った\n[03:06.00]抑えてるだけじゃ そう 辛くて\n[03:09.10]だから この気持ちを弾き語るよ\n[03:12.96]もしも僕らが生まれてきて\n[03:19.77]もしも僕らが大人になっても\n[03:26.59]もしも僕らがいなくなっていても\n[03:33.52]そこに僕の歌があれば\n[03:38.92]それでいいさ\n[03:41.08]ピアノの音が聞こえる\n[03:47.91]ガラガラの声が聞こえる\n[03:54.01]枯れてく僕らの音楽に\n[03:57.08]飴をやって もう少しと\n[04:01.57]その気持ちを弾き語るよ\n[04:07.75]もしも僕らが生まれてきて\n[04:14.66]もしも僕らが大人になっても\n[04:21.58]もしも僕らがいなくなっていても\n[04:28.40]そこに僕の歌があれば\n[04:33.72]それでいいさ\n[04:35.48]もしも僕らに才能がなくて\n[04:42.13]もしも僕らが親孝行して\n[04:49.00]もしも僕らがイケていたら\n[04:54.74]ずっとそんなことを思ってさ\n[05:01.31]弾き語るよ\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e32328f9609fe8271d38c54f8b38107b/c929c/screenshot.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 38.60759493670886%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABYlAAAWJQFJUiTwAAABKklEQVQoz5VRy0rDQBSdD/cDpG7EjVsfCOLGgjtduFBUECoIrqJgpk2TNjMJaR5NMmmOnKGpqFFw4HAJmTmvK54v3jDcv8TZ6RDG1IjjGEmSYLFYIM9z1HWNpmnQd9q2/YLVagVxs3OI861tHO8doCgLTKdTaK0RRRGklPDW3xSgUJqmdhZF8UPAEu4OBrg6OYLz9LKW/YSpamRZtgHJurlcLmGMQVVVKMtyk0Q83I1we32P0eMIYynhvrtQoYJWGpGObAV9YIIwDC1msxlc17UphNYhnFcHcizh+779SQddxL/AOwQFgiCwnQs+9jzPXqBlRmGs/xxGJqmNTFa6IgntTyYTa5/9dJvr2yYnCdhh55A9CjojAZ1939hvIBEJ+WY+n1tDSilr4gMmSGFk2J6Z3wAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of playlist&quot;
        title=&quot;Screenshot of playlist&quot;
        src=&quot;/static/e32328f9609fe8271d38c54f8b38107b/f058b/screenshot.png&quot;
        srcset=&quot;/static/e32328f9609fe8271d38c54f8b38107b/c26ae/screenshot.png 158w,
/static/e32328f9609fe8271d38c54f8b38107b/6bdcf/screenshot.png 315w,
/static/e32328f9609fe8271d38c54f8b38107b/f058b/screenshot.png 630w,
/static/e32328f9609fe8271d38c54f8b38107b/40601/screenshot.png 945w,
/static/e32328f9609fe8271d38c54f8b38107b/c929c/screenshot.png 1218w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;以上是几个常用功能的简介，完整的使用说明见 &lt;a href=&quot;https://aplayer-react.js.org%E3%80%82&quot;&gt;https://aplayer-react.js.org。&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;源码仓库见 &lt;a href=&quot;https://github.com/SevenOutmanm/aplayer-react%E3%80%82&quot;&gt;https://github.com/SevenOutmanm/aplayer-react。&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;我如何使用 aplayer-react&lt;/h2&gt;
&lt;p&gt;我的博客使用 Gatsby 搭建，使用 Markdown 编写文章，并可以在 frontmatter 中存放一些信息，通过 GraphQL 查询。
在周记中，我将本周收藏的歌曲链接放到 frontmatter 中的 &lt;code&gt;songs&lt;/code&gt; 字段。&lt;/p&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;markdown&quot;&gt;&lt;pre class=&quot;language-markdown&quot;&gt;&lt;code class=&quot;language-markdown&quot;&gt;&lt;span class=&quot;token front-matter-block&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token front-matter yaml language-yaml&quot;&gt;title: 22w47
date: 2022-11-25
songs:
  - https://music.163.com/song?id=1969744125
  - https://music.163.com/song?id=1441997419&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;在文章页面组件中，使用 &lt;code&gt;gatsby-transformer-remark&lt;/code&gt; 插件提供的 &lt;code&gt;markdownRemark&lt;/code&gt; GraphQL 查询可以读取出 frontmatter 中的数据&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; pageQuery &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;graphql
  query {
    markdownRemark(id: { eq: $id }) {
      frontmatter {
        title
        date(formatString: &quot;MMMM DD, YYYY&quot;)
        songs
      }
    }
  }
&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Weekly&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; markdownRemark &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;NeteaseMusicPlayer&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;songUrls&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;markdownRemark&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;frontmatter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;songs&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这里的 &lt;code&gt;NeteaseMusicPlayer&lt;/code&gt; 组件，是用于根据 &lt;code&gt;songUrls&lt;/code&gt; 属性中的歌曲链接来获取歌曲的名称、歌手、封面图、歌词等信息。大致逻辑如下：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; APlayer &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aplayer-react&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;NeteaseMusicPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; songUrls &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 从歌曲分享链接中提取歌曲 id&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; songIds &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useMemo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; songUrls&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getSongId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;songUrls&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 根据歌曲 id 查询歌曲详细信息、歌词&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; songInfos &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSongInfos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;songIds&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;APlayer&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;audio&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;songInfos&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;theme&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;auto&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autoPlay&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;useSongInfos&lt;/code&gt; 钩子中通过请求 &lt;a href=&quot;https://github.com/Binaryify/NeteaseCloudMusicApi&quot;&gt;NeteaseCloudMusicApi&lt;/a&gt; 来查询歌曲的详细信息和歌词。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSongInfos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;songIds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 初始返回歌曲的媒体播放地址，从而播放器可以先开始播放&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;songInfos&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setSongInfos&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
    songIds&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;composeMediaUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 获取歌曲详细信息&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;ncm.api/song/detail?ids=&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;songIds&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;,&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; songs &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;setSongInfos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
          songs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;songInfo&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; songInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;token literal-property property&quot;&gt;artist&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; songInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ar&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;artist&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; artist&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;token literal-property property&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;https://music.163.com/song/media/outer/url?id=&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;songInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;token literal-property property&quot;&gt;cover&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; songInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;al&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;picUrl&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        songs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;songInfo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token comment&quot;&gt;// 获取歌曲歌词&lt;/span&gt;
          &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;ncm.api/lyric?id=&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;songInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;lrc&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; lyric &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;token function&quot;&gt;setSongInfos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;prev&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; song &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; prev&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
                  &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;prev&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; index&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;song&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token literal-property property&quot;&gt;lrc&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; lyric&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;prev&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;songIds&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; songInfos
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;效果如下：&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4948ec975973bafb95cae4770abea478/252a4/22w47.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAABYlAAAWJQFJUiTwAAABPElEQVQoz5WSy0rDQBSG87S+ibhQpLvuBHHXnUhVLNgWSUvfIJWqSGxJm7mkSZpMJpP8cqZaUIo2Bz7mLIZvzmWcfr+PVquFTqeDdruNyWQCiqqqUNc1mobjeR6GwyFG7giu62K+WOyEFCRtglMag7IuYWoDYyrESQrG+EHCvRVmqQabJ3h/WUBI+UPyX+ytcDp+xfjsHudHx7i76SFXCstVCCEjhCGDEAJpmkJrDaUUiqLY8ftRK+xeP+Dq9ASDywsEb9v51dX2IkmyLEOSJFYax7HNic1mA2OMldBJIyIcz5vhtvuIQe8Js+cZVqsQjDGs1+s/IbmU0kJdBEFgH3F0UYCFIXz/A8FyaeGcI4qigyGp7/tfQq3tVmlucZzYGZalafRlqOU8z7ctU8I4twghwbiAUkXjP/i9lE/zaP47xHsuHwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of 22w47&quot;
        title=&quot;Screenshot of 22w47&quot;
        src=&quot;/static/4948ec975973bafb95cae4770abea478/f058b/22w47.png&quot;
        srcset=&quot;/static/4948ec975973bafb95cae4770abea478/c26ae/22w47.png 158w,
/static/4948ec975973bafb95cae4770abea478/6bdcf/22w47.png 315w,
/static/4948ec975973bafb95cae4770abea478/f058b/22w47.png 630w,
/static/4948ec975973bafb95cae4770abea478/40601/22w47.png 945w,
/static/4948ec975973bafb95cae4770abea478/78612/22w47.png 1260w,
/static/4948ec975973bafb95cae4770abea478/252a4/22w47.png 1314w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;但是到目前为止，这样的实现存在一个小问题。在歌曲信息加载完成之前，播放器会短暂地显示缺省状态，观感上还是有些奇怪。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ca619c850f97c94954a50671c5c0edcd/71b12/22w47-empty.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 45.56962025316456%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA4ElEQVQoz52Su46DMBRE/f8N4i9o+QsUKhq63RWChGS9fsQvCMbMyl5RbBcY6XS+4ztjk7IskWUZiqJAnudomgZRIQRs24ajIm3boqoqXC416roGpTQZnYW8lgVumhPza4FSGvSH4axIjOb9inX9Y495esPdIIQNjHFwIWGMgdYa3vtD2yXD1Xs452CtxeRcMnuHeF4plZBSQgiBeZ5BrNHo+x4fn18Yxzs452CMHWIYBnRdly4isTchJR7fFMbaFDOyd/oOsZ64ZZwjz6fCcL3hehuT6Xh/wLnp31888ii/bRm5izgHANkAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of 22w47&quot;
        title=&quot;Screenshot of 22w47&quot;
        src=&quot;/static/ca619c850f97c94954a50671c5c0edcd/f058b/22w47-empty.png&quot;
        srcset=&quot;/static/ca619c850f97c94954a50671c5c0edcd/c26ae/22w47-empty.png 158w,
/static/ca619c850f97c94954a50671c5c0edcd/6bdcf/22w47-empty.png 315w,
/static/ca619c850f97c94954a50671c5c0edcd/f058b/22w47-empty.png 630w,
/static/ca619c850f97c94954a50671c5c0edcd/40601/22w47-empty.png 945w,
/static/ca619c850f97c94954a50671c5c0edcd/78612/22w47-empty.png 1260w,
/static/ca619c850f97c94954a50671c5c0edcd/71b12/22w47-empty.png 1308w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;既然每篇周记中包含的歌曲，在周记创建的时候就已经确定了，能否提前将歌曲的详细信息获取完直接静态地写进页面呢？刚好 Gatsby 提供了相关的能力。&lt;/p&gt;
&lt;p&gt;Gatsby 允许在页面中通过编写 GraphQL 的形式查询数据用于展示，并且这个查询发生在构建阶段，查询到的数据直接以静态数据的形式写进页面。于是我们可以通过 Gatsby 提供的创建自定义 GraphQL 的能力，创建一个能够读取网易云音乐详情的 GraphQL 查询。&lt;/p&gt;
&lt;p&gt;在 &lt;code&gt;gatsby-node.js&lt;/code&gt; 中，添加 &lt;code&gt;createSchemaCustomization&lt;/code&gt; 方法，来添加自定义的 GraphQL 类型声明。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// gatsby-node.js&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/**
 * @type {import(&apos;gatsby&apos;).GatsbyNode[&apos;createSchemaCustomization&apos;]}
 */&lt;/span&gt;
exports&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;createSchemaCustomization&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; createTypes &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;createTypes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
    # Netease Cloud Music songs&apos; info
    type NeteaseCloudMusicSong {
      id: Int
      name: String
      mediaUrl: String
      ar: [NeteaseCloudMusicArtist]
      al: NeteaseCloudMusicAlbum
      lrc: String
    }

    type NeteaseCloudMusicArtist {
      name: String
    }

    type NeteaseCloudMusicAlbum {
      picUrl: String
    }

    type MarkdownRemark implements Node {
      frontmatter: Frontmatter
    }

    type Frontmatter {
      songs: [NeteaseCloudMusicSong]
    }
  &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这里我根据我所需要的歌曲详情信息，创建了歌曲信息类型 &lt;code&gt;NeteaseCloudMusicSong&lt;/code&gt;。并扩展了 &lt;code&gt;gatsby-transformer-remark&lt;/code&gt; 提供的 &lt;code&gt;MarkdownRemark&lt;/code&gt; 类型，使得 &lt;code&gt;frontmatter&lt;/code&gt; 中增加一个 &lt;code&gt;songs&lt;/code&gt; 字段，来查询我们的 &lt;code&gt;NeteaseCloudMusicSong&lt;/code&gt; 信息。&lt;/p&gt;
&lt;p&gt;接着，添加 &lt;code&gt;createResolvers&lt;/code&gt; 方法，来声明如何解析 &lt;code&gt;songs&lt;/code&gt; 字段。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// gatsby-node.js&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ncmApi &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;NeteaseCloudMusicApi&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/**
 * @type {import(&apos;gatsby&apos;).GatsbyNode[&apos;createResolvers&apos;]}
 */&lt;/span&gt;
exports&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;createResolvers&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; createResolvers &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; resolvers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;Frontmatter&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;songs&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;NeteaseCloudMusicSong&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;source&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;songs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; source&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;songs

          &lt;span class=&quot;token comment&quot;&gt;// 从歌曲分享链接中提取歌曲 id&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; songIds &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; source&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;songs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getSongId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

          &lt;span class=&quot;token comment&quot;&gt;// 根据歌曲 id 查询歌曲详细信息&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; ncmApi
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;song_detail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;ids&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; songIds&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;,&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;songs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;NeteaseCloudMusicSong&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;mediaUrl&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;String&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;composeMediaUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;source&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;lrc&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;String&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; ncmApi
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;lyric&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; source&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lrc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lyric&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;createResolvers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resolvers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;接着，在文章页面组件中，修改 GraphQL 查询，直接读取 songs 的各个详情字段。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; pageQuery &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;graphql
  query {
    markdownRemark(id: { eq: $id }) {
      frontmatter {
        title
        date(formatString: &quot;MMMM DD, YYYY&quot;)
        songs {
          name
          ar {
            name
          }
          mediaUrl
          al {
            picUrl
          }
          lrc
        }
      }
    }
  }
&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Weekly&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; markdownRemark &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;NeteaseMusicPlayer&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;songs&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;markdownRemark&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;frontmatter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;songs&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;最后，从 &lt;code&gt;NeteaseMusicPlayer&lt;/code&gt; 组件移除请求 API 的逻辑，仅仅将 GraphQL 查询结果的结构转为 &lt;code&gt;aplayer-react&lt;/code&gt; 接收的结构即可。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;NeteaseMusicPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; songs &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;APlayer&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;audio&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;songs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;songInfo&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; songInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;artist&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; songInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ar&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;artist&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; artist&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; songInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mediaUrl&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;cover&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; songInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;al&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;picUrl&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;lrc&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; songInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lrc&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;theme&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;auto&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;autoPlay&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这样一来，首次加载时播放器就已经具有完整的歌曲详细信息，不会再显示缺省状态。&lt;/p&gt;
&lt;h2&gt;结语&lt;/h2&gt;
&lt;p&gt;最后，欢迎大家 star 收藏 &lt;a href=&quot;https://github.com/SevenOutman/aplayer-react&quot;&gt;aplayer-react GitHub 仓库&lt;/a&gt; ，也欢迎来&lt;a href=&quot;https://doma.land&quot;&gt;我的博客&lt;/a&gt;留言。&lt;/p&gt;
&lt;p&gt;祝过年好。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[23w01]]></title><description><![CDATA[新年快乐。]]></description><link>https://doma.land/weekly/23w01/</link><guid isPermaLink="false">https://doma.land/weekly/23w01/</guid><pubDate>Mon, 09 Jan 2023 11:55:00 GMT</pubDate><content:encoded>&lt;p&gt;新年快乐。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;本周仍然没有新收藏的歌，大抵是因为自从居家办公以来就没怎么听过歌了。&lt;/p&gt;
&lt;p&gt;去看了海源的《坐在角落的人 2》，真的很爽。是会想要再看一遍的那种。不仅仅是“愿意”再看一遍，而是“想要”再看一遍。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/29b876ae258ca00bf99292f00aeec088/eea4a/%E7%BA%AA%E5%BF%B5%E7%A5%A8%E5%B0%81%E9%9D%A2.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.31645569620254%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAQFA//EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAFejFfhUzI//8QAGxAAAwACAwAAAAAAAAAAAAAAAQIDAAQREyL/2gAIAQEAAQUC10FGMEOP5edOt13cJ5b/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAcEAEBAAEFAQAAAAAAAAAAAAABABEDEiExUaH/2gAIAQEABj8CRUgMkmercXOn9l9v/8QAGxAAAwACAwAAAAAAAAAAAAAAAAERIUFRYYH/2gAIAQEAAT8hgHFYti5h/NtMDZpoPUqzRlxeDvlT/9oADAMBAAIAAwAAABD7/wD/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAcEAEBAAICAwAAAAAAAAAAAAABEQAhMUFRcdH/2gAIAQEAAT8Q18LrOW8FINxT2uCkBK8scKwwimCJiuwnf2M1VKx4rc//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;纪念票封面&quot;
        title=&quot;纪念票封面&quot;
        src=&quot;/static/29b876ae258ca00bf99292f00aeec088/828fb/%E7%BA%AA%E5%BF%B5%E7%A5%A8%E5%B0%81%E9%9D%A2.jpg&quot;
        srcset=&quot;/static/29b876ae258ca00bf99292f00aeec088/ff44c/%E7%BA%AA%E5%BF%B5%E7%A5%A8%E5%B0%81%E9%9D%A2.jpg 158w,
/static/29b876ae258ca00bf99292f00aeec088/a6688/%E7%BA%AA%E5%BF%B5%E7%A5%A8%E5%B0%81%E9%9D%A2.jpg 315w,
/static/29b876ae258ca00bf99292f00aeec088/828fb/%E7%BA%AA%E5%BF%B5%E7%A5%A8%E5%B0%81%E9%9D%A2.jpg 630w,
/static/29b876ae258ca00bf99292f00aeec088/0ede0/%E7%BA%AA%E5%BF%B5%E7%A5%A8%E5%B0%81%E9%9D%A2.jpg 945w,
/static/29b876ae258ca00bf99292f00aeec088/3ac88/%E7%BA%AA%E5%BF%B5%E7%A5%A8%E5%B0%81%E9%9D%A2.jpg 1260w,
/static/29b876ae258ca00bf99292f00aeec088/eea4a/%E7%BA%AA%E5%BF%B5%E7%A5%A8%E5%B0%81%E9%9D%A2.jpg 1280w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2d4f1065d47ff860eb7146b0e46e3e66/eea4a/%E7%BA%AA%E5%BF%B5%E7%A5%A8%E5%86%85%E5%AE%B9.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.31645569620254%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMEBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAGXSWwxCkj/xAAaEAACAwEBAAAAAAAAAAAAAAACAwABExEj/9oACAEBAAEFAlAyyaHl24toCGwR2dl//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAHRAAAgEEAwAAAAAAAAAAAAAAAQIAEBESISIyYf/aAAgBAQAGPwJTY4xsV35QKW2J2nGf/8QAGhABAAMBAQEAAAAAAAAAAAAAAQARIVFxkf/aAAgBAQABPyF7s08qMgXNNm1K3KBA04yxKPxgG8bXZ//aAAwDAQACAAMAAAAQk8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAVEQEBAAAAAAAAAAAAAAAAAAAQIf/aAAgBAgEBPxCH/8QAGxABAAMBAAMAAAAAAAAAAAAAAQARITFBkaH/2gAIAQEAAT8QY2gqivabNoqAtImgg0l8YHIUxKTHxEUC3jt8lhQJ6BpvpP/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;纪念票内容&quot;
        title=&quot;纪念票内容&quot;
        src=&quot;/static/2d4f1065d47ff860eb7146b0e46e3e66/828fb/%E7%BA%AA%E5%BF%B5%E7%A5%A8%E5%86%85%E5%AE%B9.jpg&quot;
        srcset=&quot;/static/2d4f1065d47ff860eb7146b0e46e3e66/ff44c/%E7%BA%AA%E5%BF%B5%E7%A5%A8%E5%86%85%E5%AE%B9.jpg 158w,
/static/2d4f1065d47ff860eb7146b0e46e3e66/a6688/%E7%BA%AA%E5%BF%B5%E7%A5%A8%E5%86%85%E5%AE%B9.jpg 315w,
/static/2d4f1065d47ff860eb7146b0e46e3e66/828fb/%E7%BA%AA%E5%BF%B5%E7%A5%A8%E5%86%85%E5%AE%B9.jpg 630w,
/static/2d4f1065d47ff860eb7146b0e46e3e66/0ede0/%E7%BA%AA%E5%BF%B5%E7%A5%A8%E5%86%85%E5%AE%B9.jpg 945w,
/static/2d4f1065d47ff860eb7146b0e46e3e66/3ac88/%E7%BA%AA%E5%BF%B5%E7%A5%A8%E5%86%85%E5%AE%B9.jpg 1260w,
/static/2d4f1065d47ff860eb7146b0e46e3e66/eea4a/%E7%BA%AA%E5%BF%B5%E7%A5%A8%E5%86%85%E5%AE%B9.jpg 1280w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;读了 &lt;a href=&quot;https://github.com/readme/guides/document-success&quot;&gt;&lt;em&gt;Brag now, remember later: Document your accomplishments&lt;/em&gt;&lt;/a&gt;
，感觉有一些启发。我平常就会在一段时间后做回顾的时候想不起来很多事情的细节，可能当时为了解决一个问题去查找、尝试了各种方法最终得以解决并学得新知，但这些细节到最后无法被我复述出来，以至于自己总是觉得阶段总结和汇报很平淡。现在想想应该更及时地记录下来才对。&lt;/p&gt;
&lt;h2&gt;This blog&lt;/h2&gt;
&lt;p&gt;给博客加上了评论区。参考 &lt;a href=&quot;https://tkdodo.eu/blog&quot;&gt;TkDodo 的博客&lt;/a&gt;，用了 &lt;a href=&quot;https://giscus.app&quot;&gt;giscus&lt;/a&gt;。集成起来非常容易。好的开源软件真的会让人开心。&lt;/p&gt;
&lt;p&gt;每周收藏的歌原来是在浏览时根据歌曲 id 去查询歌曲名称、歌手、封面图、歌词这些信息，再填充到 aplayer-react 里的，于是播放器一开始会显示一个缺省状态，感觉怪怪的。于是把获取信息的过程改到了构建时，这样生成的页面上歌曲信息就是静态的了，不用在浏览时再去请求，也不需要单独部署一个查询服务了。&lt;/p&gt;
&lt;h2&gt;aplayer-react&lt;/h2&gt;
&lt;p&gt;这周把 &lt;code&gt;aplayer-react&lt;/code&gt; 发了正式版，文档站部署在 &lt;a href=&quot;https://aplayer-react.js.org%EF%BC%8C%E9%80%89%E7%94%A8%E7%9A%84%E8%BF%98%E6%98%AF&quot;&gt;https://aplayer-react.js.org，选用的还是&lt;/a&gt; Nextra。准备下周写篇文章简单介绍下。&lt;/p&gt;
&lt;h2&gt;Minecraft&lt;/h2&gt;
&lt;p&gt;这周完成了不少事。给家门前面的树林里挖了个小池塘，去繁茂洞穴捞了三只美西螈养了进去。还砍了两个蜂巢回来，终于有蜜脾可以做蜡烛了。&lt;/p&gt;
&lt;p&gt;女朋友跑图的过程中开了不少宝箱，收获了一批附魔金工具、钓鱼竿，还有好多海洋之心。打了一把满配钓鱼竿准备钓鹦鹉螺壳来做个潮涌核心看看。&lt;/p&gt;
&lt;p&gt;女朋友把从家为中心周边的 9 张地图都画完了，中间好几次背包满了直接原地做个箱子让我去取。虽然家里有马，但是从家出发三面环水马过不去，于是在西面和东北方向修了两座桥，暂且能将马的足迹延伸到前 6 张图了。跑完图的女朋友现在打开游戏就是钓鱼，一钓就一个小时。&lt;/p&gt;
&lt;p&gt;砍掉了家门前的树林，虽然一开始就是因为觉得门前太空旷了所以种的树。现在再看觉得太空旷应该加装饰，于是铺了小径，做了一些路灯，现在既不空旷也不拥挤了。&lt;/p&gt;
&lt;p&gt;下一步准备捆村民回来帮忙种地 🤔。&lt;/p&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;p&gt;下周要回公司上班了，该做团队年终总结了。下周应该有新收藏的歌了吧。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[22w52]]></title><description><![CDATA[转眼就到最后一周了。]]></description><link>https://doma.land/weekly/22w52/</link><guid isPermaLink="false">https://doma.land/weekly/22w52/</guid><pubDate>Tue, 27 Dec 2022 17:39:00 GMT</pubDate><content:encoded>&lt;p&gt;转眼就到最后一周了。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;新冠&lt;/h2&gt;
&lt;p&gt;这周转阴了，去医院拍了肺部 CT 也没有感染，还算康复得比较顺利。不过还在一直咳嗽。
尝试着恢复工作，不过恰好这周没什么工作任务，感觉比较闲，就当是又休息了一个星期。&lt;/p&gt;
&lt;h2&gt;2022&lt;/h2&gt;
&lt;p&gt;转眼最后一周也过去了。
因为在休息的缘故，博客和 &lt;code&gt;aplayer-react&lt;/code&gt; 都没有更新。加上工作也比较闲，一周下来基本没什么产出，周报也因此感觉无事可写。
虽说可以用病休的理由说服自己，但是这空闲的一周恰好是今年最后一周，有种一整年都这样松懈度过了的错觉。&lt;/p&gt;
&lt;p&gt;今年其实没有什么年终总结，自己的周报其实也才刚刚开始写而已。就当作又是平常的一周吧。
暂时也没有 2023 年的明确计划，目前只有一条要养成锻炼身体的习惯。这样看来不做计划也不验收真的会让自己有点无聊。&lt;/p&gt;
&lt;p&gt;前几周的周报草稿中其实都包含了一些下周要做的事，比如上周就有写“Weekly 现在是从 frontmatter 里读 title 的，其实可以改成直接用文件路径”。
但是它们最终没有出现在周报正文里，因为我还是想当一件事完成了再记录到周报中。但有时上周立的 flag 下周并没做完，久而久之就会有些想做的事被遗忘在了过去的草稿中。看来立了 flag 还是得及时回收，也更严格地按周迭代自己。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[22w51]]></title><description><![CDATA[本周阳了。]]></description><link>https://doma.land/weekly/22w51/</link><guid isPermaLink="false">https://doma.land/weekly/22w51/</guid><pubDate>Sun, 25 Dec 2022 22:33:00 GMT</pubDate><content:encoded>&lt;p&gt;本周阳了。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;This blog&lt;/h2&gt;
&lt;p&gt;周一给博客买了个域名，算是本周唯一的好事了。夸一下 Vercel 上的买域名体验，一搜、一付款、一分配，完事。真滴一百分。&lt;/p&gt;
&lt;p&gt;给 &lt;code&gt;aplayer-react&lt;/code&gt; 加了跳过无效媒体，现在 22w49 应该不会卡在第一首会员歌了。&lt;/p&gt;
&lt;h2&gt;新冠&lt;/h2&gt;
&lt;p&gt;周一女朋友发烧嗓子痛全身酸痛，周三一测抗原果然阳了。当天傍晚我也毫不意外地发起烧来，头痛的要命，身上倒是没有传说中那么痛，只是腰腿有些酸，贴了发热贴敷一敷。高烧两天低烧一天，到周六下午才算完全退烧。周日还是继续休息，既没有工作也没有玩游戏。这周眨眼就结束了，只想赶紧恢复健康。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[22w50]]></title><description><![CDATA[本周没有新收藏的歌。]]></description><link>https://doma.land/weekly/22w50/</link><guid isPermaLink="false">https://doma.land/weekly/22w50/</guid><pubDate>Mon, 19 Dec 2022 13:42:00 GMT</pubDate><content:encoded>&lt;p&gt;本周没有新收藏的歌。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;This blog&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;aplayer-react@v1.0.0-alpha.3&lt;/code&gt; 在博客的 production 环境会导致包含它的页面链接报错，一方面是因为 Gatsby 5 的页面加载有 bug &lt;a href=&quot;https://github.com/gatsbyjs/gatsby/discussions/36609#discussioncomment-4375572&quot;&gt;https://github.com/gatsbyjs/gatsby/discussions/36609#discussioncomment-4375572&lt;/a&gt; ，另一方面是 &lt;code&gt;aplayer-react&lt;/code&gt; 会导致所在页面加载时抛出 ChunkLoadError，原因是 APlayer 组件渲染会引起 &lt;code&gt;Invalid left-hand side in assignment&lt;/code&gt; 错误。打开 source map 一查居然是个非常蠢的问题，就是图省事在 ts 里绕过了空值检查导致的错误 &lt;a href=&quot;https://github.com/SevenOutman/react-aplayer/commit/34ef3478a3d19bd552d4a66e489a5d02f12aef21&quot;&gt;https://github.com/SevenOutman/react-aplayer/commit/34ef3478a3d19bd552d4a66e489a5d02f12aef21&lt;/a&gt; 。被自己菜哭。&lt;/p&gt;
&lt;p&gt;现在播放器还有一些小问题，比如网易会员歌曲大概是不能直接访问播放地址的，现在收藏的歌里如果有会员歌曲会播放不了，需要做成自动跳过无效歌曲。还有云音乐 API 好像一会就会更新自己的 CORS header，不知道是特性还是 bug。&lt;/p&gt;
&lt;p&gt;总之，总算是成功给博客加上播放器了，还是很开心的。还给文章加上了 tag，以及按 tag 查看文章列表。接下来要整理一下文章的摘要，现在的摘要是按原来 hexo 的 more 注释格式写的，要改成新的格式，或者让博客支持 more 注释。&lt;/p&gt;
&lt;p&gt;现在 blog 列表已经很久远了，最近又好像没有很清晰的要写什么文章的点子，打算先把后来发在掘金上的文章搬回来看看。&lt;/p&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;p&gt;周一晚上 DevOps 1.6 RC 上线，有些小 bug，周二上午在家修掉了。后来又陆陆续续添加了一些 UI 改进，下周一 routine 每周更新的时候发。因为没有新的迭代在做，master 就相当于 latest 的维护分支，感觉不用一个一个 cherry-pick，可以直接全合到 1.6 上。&lt;/p&gt;
&lt;p&gt;周二下午去公司路上感觉阳光很舒服，一想到要呆在工位上度过下午就感觉很没劲。没有人会用写字楼这种意向来形容温暖对吗。&lt;/p&gt;
&lt;p&gt;周三组里有同事阳了，其他部门阳的同事也越来越多，打算最近一段时间先不去公司了。&lt;/p&gt;
&lt;p&gt;被 semantic-release 又折腾了一天。原本有一个 monorepo 项目，因为 lerna 实在太烦了，总是把 devDeps 的更新也算做 patch 变更，最后干脆把那个 devDeps 包从 monorepo 里分离出去先。新建仓库的时候保留了原来 monorepo 的提交历史和 tag，但是切了个 alpha 分支出来往里添加 semantic-release 的时候它死活识别不出以前的 tag。上网查了各种可能的原因，检查了 tag 的命名格式也是对的，又检查了 gitlab ci 上也是拉到 tag 了的，忙活了一天最后&lt;a href=&quot;https://github.com/semantic-release/semantic-release/discussions/2238#discussioncomment-1620557&quot;&gt;找到一条 discussion&lt;/a&gt; 说在 prerelease channel 上 semantic-release 是通过 note 来识别上一个发布 tag 的，而我的仓库以前没有用 semantic-release 所以 tag 上没有 note，所以识别不出来。最后的解决方法是给原来的发布 tag 上手动添加了 note，终于是能正常发出来了。加入到了 &lt;a href=&quot;https://gist.github.com/SevenOutman/1f8bdd48c19fe357f8458ecce6f4dcab#fix-semantic-release-not-detecting-existing-releases-on-prerelease-channels-when-adding-semantic-release-to-an-existing-repo&quot;&gt;My cheatsheet&lt;/a&gt; 里。&lt;/p&gt;
&lt;p&gt;后来用 &lt;a href=&quot;https://nextra.site&quot;&gt;nextra&lt;/a&gt; 给这个 monorepo 里的库搭建了文档站，终于不用寡看 README 了。不得不说 &lt;a href=&quot;https://nextra.site/docs/docs-theme/start&quot;&gt;&lt;code&gt;nextra-theme-docs&lt;/code&gt;&lt;/a&gt; 确实好用。&lt;/p&gt;
&lt;h2&gt;aplayer-react&lt;/h2&gt;
&lt;p&gt;完成了音量控制和 playlist 顺序控制，下周再修复一些小问题，整理一下代码，基本上是可以 beta 的状态了。&lt;/p&gt;
&lt;h2&gt;Minecraft&lt;/h2&gt;
&lt;p&gt;到了周末终于抽出时间玩 Minecraft 了。建好了猪圈和马厩，还搭了一个小花坛用来插花。女朋友在外面探险了这么久终于遇上了第一个雪原地形，一直盖房子的我终于有云杉木可以用了。&lt;/p&gt;
&lt;p&gt;话说 1.18 之后这洞穴生成什么鬼算法，在铺路的时候看到地上有个 1x4 的小缝，想着填上之前先看看里面有没有煤吧，结果一进去到现在已经先后连接了三四个峡谷和洞穴了，废弃矿道都遇到了三个，甚至不缺木头，东西多到已经设下四个暂存点了……这还仅仅是“遇到岔口先探右边”的头几个右手边，感觉好像亲身体会到了 dfs 的内存压力。&lt;/p&gt;
&lt;p&gt;第四个暂存点设下后不久就没木头用了，决定暂时从洞里出来，把挖到的东西带回家，之后换钻石工具再来。幸好进来的一路上做好了路牌，回来一路跟着路牌走不怕错。感觉跟写了良好的文档有点像捏。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[22w49]]></title><description><![CDATA[本周收藏的歌，有两首]]></description><link>https://doma.land/weekly/22w49/</link><guid isPermaLink="false">https://doma.land/weekly/22w49/</guid><pubDate>Sun, 11 Dec 2022 23:14:00 GMT</pubDate><content:encoded>&lt;p&gt;本周收藏的歌，有两首&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;&lt;a href=&quot;https://music.163.com/song?id=22825753&quot;&gt;https://music.163.com/song?id=22825753&lt;/a&gt;
&lt;a href=&quot;https://music.163.com/song?id=1331298993&quot;&gt;https://music.163.com/song?id=1331298993&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;This blog&lt;/h2&gt;
&lt;p&gt;这周把博客首页区分成 blog 列表和 weekly 列表了，也修好了上一篇/下一篇链接地址的错误。写久了 TypeScript 来写无类型的 JavaScript 好难受 😂 回头还是得把博客代码改成 TypeScript 的。
在看前几篇 weekly 的时候感觉好像写得有点太随意太零碎了，应该改成像周报那样的，整理出来一周的 achievements，不要直接把碎片笔记直接誊上去了。&lt;/p&gt;
&lt;p&gt;尝试给 weekly 加上音乐播放器，来播放每周新收藏的歌。选型上第一反应是 &lt;code&gt;react-aplayer&lt;/code&gt;，因为在还没有转 react 的时候我维护的 &lt;code&gt;vue-aplayer&lt;/code&gt;，对 APlayer 比较了解也很喜欢。
但是试用下来 &lt;code&gt;react-aplayer&lt;/code&gt; 不像 &lt;code&gt;vue-aplayer&lt;/code&gt; 那样支持 &lt;code&gt;audio&lt;/code&gt; 属性的更新，这样就没法先把 url 放进去播放，等从 API 获取完歌曲信息再更新上去（除非用 key 重新渲染，但那样就会把正在播放的歌打断）；也不支持自动根据专辑封面提取主题色；不支持 SSR，在 Gatsby 构建时会报错。
只好自己重新写了一个 &lt;code&gt;aplayer-react&lt;/code&gt;，现在已经把 working copy 完成了，发了第一个 alpha 版本。周日晚上本想冲一冲把 bug 在这周全修掉，结果 bug 修掉了却在 Gatsby 生产构建的时候出了点奇怪的问题，调到两点多没能解决。实在太晚了得休息了，周一一定把这些问题解决，让 weekly 正经播出声儿来。&lt;/p&gt;
&lt;h2&gt;DevOps 平台&lt;/h2&gt;
&lt;p&gt;DevOps 平台 1.6 版本需要做流水线路由的简化，也就是用项目组的 namespace 和流水线的 name 直接作为访问路径，就像 GitHub 的 org/repo 那样。这需要服务端的 REST API 把获取项目组详情和获取流水线详情两个接口从原来的只支持传入 id 改为支持传入 namespace 和 name。可服务端说不好实现，换了一个折衷的方案，另外加了一个根据 namespace 获取项目组和根据 name 获取流水线的接口，我在前端通过这两个接口转换成 id 再去用原来的接口读取详情。本来想着就这样做算了，到了周一是在还是忍不了这种徒增复杂性的事情，翻了翻 Mybatis 的文档，给服务端提了 MR。描述里弱弱地写了“第一次用 mybatis 不知道写的对不对 😷”，本来想着只有相关的 reviewer 能看到，结果怎么在群里推送了一个 MR 通知啊喂直接公开处刑了。
不过总的来说这周为止 DevOps 平台 1.6 版本这周终于开发的差不多了，坐等下周一发布上线。&lt;/p&gt;
&lt;h2&gt;Minecraft&lt;/h2&gt;
&lt;p&gt;最近在和女朋友玩 Minecraft 生存了，这几天把房子和农田搭了搭好，今天又盖好了羊厩和牛棚。但是 Mac 是真带不动光影啊，很想用的 Complementary 光影跑起来 3 FPS😅，即便是换更轻量的 Slidurs 甚至是主打 lagless 的 SORA 也并不能达到能玩的帧率。大概是因为没有独显罢。换到女朋友的电脑上运行 Complementary 流畅 90 帧，确实漂亮。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[22w48]]></title><description><![CDATA[本周好像没有新收藏的歌]]></description><link>https://doma.land/weekly/22w48/</link><guid isPermaLink="false">https://doma.land/weekly/22w48/</guid><pubDate>Thu, 01 Dec 2022 13:00:00 GMT</pubDate><content:encoded>&lt;p&gt;本周好像没有新收藏的歌&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;Day 1&lt;/h2&gt;
&lt;p&gt;约了周一下午的安装服务来装伞和桌子，请好了假下午在家，结果到点的时候突然下起了大雨，觉得室外工作太危险了就把安装服务改期了。后来一下了一个小时就停了，也没有要再下的意思，几番犹豫之后决定干脆自己动手安装。从下午搞到天黑终于拥有了阳台餐桌～今年的十一月底算是平稳度过&lt;/p&gt;
&lt;p&gt;一点小收获就是有点体会到 just do it。但愿这一点领悟能帮我克服一些拖延。&lt;/p&gt;
&lt;h2&gt;Day 2&lt;/h2&gt;
&lt;p&gt;明天起连续三天都要加班帮小朋友修 bug 了。&lt;/p&gt;
&lt;h2&gt;Day 3&lt;/h2&gt;
&lt;p&gt;想做 batch api 方案。&lt;/p&gt;
&lt;h2&gt;Day 4&lt;/h2&gt;
&lt;p&gt;终于把 blog 重新部署好了，下一步就是搞明白怎么在 gatsby 里给 weekly 创建专门的路由。参考 &lt;a href=&quot;https://weknowinc.com/blog/create-gatsby-site-multiple-types-markdown-pages&quot;&gt;https://weknowinc.com/blog/create-gatsby-site-multiple-types-markdown-pages&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Day 5&lt;/h2&gt;
&lt;p&gt;今天把 blog 和 weekly 路由分开了，这下可以把 weekly 搬上去了。
下一步就是整理首页，把 blog 和 weekly 分成不同的目录。还有就是要在 weekly 中支持音乐播放器。&lt;/p&gt;
&lt;p&gt;Day 6&lt;/p&gt;
&lt;p&gt;DevOps 平台新版本的某个功能需要后端修改一个 API，私以为并不难后端却说做起来很麻烦，决定只好亲自动手。去看了 mybatis，第一印象是还是 eloquent 好用。&lt;/p&gt;
&lt;p&gt;我觉得 16 型人格中的 I 和 E 与其简单地解释为内向外向，倒不如说 I 是更在意别人对自己建立的印象，E 没那么在意别人对自己的印象。这也能解释为什么平时印象中的 I 在有些场合看起来也很自如甚至显得外向，特别是在一群与自己比较熟的人当中时，或者是在网络上时——因为他不那么在意这些人对他的印象了，或者是印象已经稳定，或者是根本不会更多地认识这些人。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[22w47]]></title><description><![CDATA[本周新收藏的歌 有两首]]></description><link>https://doma.land/weekly/22w47/</link><guid isPermaLink="false">https://doma.land/weekly/22w47/</guid><pubDate>Fri, 25 Nov 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌 有两首&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;&lt;a href=&quot;https://music.163.com/song?id=1969744125&quot;&gt;https://music.163.com/song?id=1969744125&lt;/a&gt;
&lt;a href=&quot;https://music.163.com/song?id=1441997419&quot;&gt;https://music.163.com/song?id=1441997419&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;上周的 weekly 结果还是只有草稿，没有正式整理。&lt;/p&gt;
&lt;p&gt;这周又开始焦虑了，还是没法忽视十一月底这个存在。&lt;/p&gt;
&lt;p&gt;DevOps 平台 Milestone 1.6:
35% complete
Start date: Aug 5, 2022&lt;/p&gt;
&lt;p&gt;rsuite 6 真得开始做了，不能一直拖。写了 thoughts memo。&lt;/p&gt;
&lt;p&gt;好想上漂移课了又，但是最近经济状况好差。&lt;/p&gt;
&lt;p&gt;我希望与我共事的人、协作的上下游，水平是高于我的，这样能让我继续学习进步，在我还卷的动的时候。与无法在同一水平理解事情的人反复沟通简直就是浪费生命。比较让人欣慰的是这段时间去沟通对接的一些人还是比较 ok 的。&lt;/p&gt;
&lt;p&gt;平时一直是心静自然凉的一个人最近却常常因为思绪很乱而燥热。就像现在，脑袋里面有一团东西如果不写下来恐怕难以入睡。我想我的写作习惯还需加强，很多时候解决了问题并没有把过程和思路记录下来。偶尔想到时我可能觉得这些问题太小了不值得一记，可是久而久之回过头来看我又记了什么“值得一记”的吗？其实也没有。认真想想其实这种“感觉不值得一记”的想法就是没有良好的写作习惯的表现。&lt;/p&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; 本周必须把 weekly 发表起来了。不能再拖了。别人不会等你，不进则退。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但是也不能乱了阵脚，还是要足够笃定和专注。说到底，既要专注又能求胜的水平，我还尚未达到。&lt;/p&gt;
&lt;p&gt;出于好奇在微信搜索搜了我的 github id，居然还是有几篇公众号文章提到了我。内容都是分享以前做的那个椰树生成器- -。回想起来那是就是想到什么就做做好了就拿出来晒。现在我做到了保持热血保持中二吗？似乎没有。&lt;/p&gt;
&lt;p&gt;试着和 saka 打了招呼。当你遇到和你相信着同样的事的人而热泪盈眶时，你不觉得这是命中注定吗。&lt;/p&gt;
&lt;p&gt;尝试着有意地传达善意。有时候即便你相信自己的路是正确的，偶尔也需要一些窍门来走得更顺。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[22w46]]></title><description><![CDATA[本周新收藏的歌 https://music.163.com/song?id=1923210613]]></description><link>https://doma.land/weekly/22w46/</link><guid isPermaLink="false">https://doma.land/weekly/22w46/</guid><pubDate>Sun, 20 Nov 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;本周新收藏的歌 &lt;a href=&quot;https://music.163.com/song?id=1923210613&quot;&gt;https://music.163.com/song?id=1923210613&lt;/a&gt;&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;实现了 api-bridge 协议
有同学用 @hy/form 了，我还挺高兴的。因为这个包有一些 API 我还没能敲定，我也不确定目前具有的 API 是否是最佳实践，所以一直是 beta 状态，也没有正式跟大家宣讲。即便如此我还是对它的易用性比较有信心的，也希望大家来试用体验。今天真的有同学在用我很开心。不过被他发现少了某个组件的文档，赶紧补上了。
尝试了路由参数绑定，但还不是很好用
月度例会上 review 季度待办事项和公共库版本跟踪更新&lt;/p&gt;
&lt;p&gt;本来上周就想写的，创建了 22w45 的草稿，往草稿里加了一首上周新收藏的歌，其他的不知道该写些什么，结果拖到最后忘了&lt;/p&gt;
&lt;p&gt;api-bridge 感觉可以写成一个公共的包啊，还可以写篇文章介绍思路啥的&lt;/p&gt;
&lt;p&gt;还有路由绑定啊，也可以写篇文章&lt;/p&gt;
&lt;p&gt;太不常写文章了，这样不好&lt;/p&gt;
&lt;p&gt;有点不确定以周编号的形式来写周记是否真的能摆脱月的周期来按周迭代自己&lt;/p&gt;
&lt;p&gt;这周记写的像卡片笔记一样&lt;/p&gt;
&lt;p&gt;看着 w46 这个编号意识到今年只有 6 周了。感觉有点奇妙，好像真的和按月来看时间的心情有所不同。最近因为 11 月底女朋友的生日不知道买什么礼物而一直焦虑，但刚才看到今年只有 6 周的时候好像直接把“11 月底”这个事抛到脑后去了，感到很放松。虽然还什么都没做，但仅仅是换个尺度来看时间就会有不一样的感受。&lt;/p&gt;
&lt;p&gt;这周带团队小朋友们去聚餐了，并且这次终于记得拍合影了。之前几次聚餐没有留下 snapshot，待到人员变动的时候才想起来怪可惜的。&lt;/p&gt;
&lt;p&gt;现在是周日，下周要继续更新 rsuite 代码库了，这次要尽量把测试脚本中用 css selector 进行的 dom 访问给改掉。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[22w45]]></title><description><![CDATA[本周的新加入收藏的音乐
https://music.163.com/song?id=1954146690]]></description><link>https://doma.land/weekly/22w45/</link><guid isPermaLink="false">https://doma.land/weekly/22w45/</guid><pubDate>Tue, 08 Nov 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;本周的新加入收藏的音乐
&lt;a href=&quot;https://music.163.com/song?id=1954146690&quot;&gt;https://music.163.com/song?id=1954146690&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[React 中的副作用和远程状态]]></title><description><![CDATA[本文只讨论 React 16.8 以来的函数组件和 hook 模型，不讨论历史的 class 组件和生命周期模型 在编写 React 应用时，我们常常会需要发送网络请求以从服务端获取我们渲染界面所需要的数据。以往一种常见的实践是在 useEffect…]]></description><link>https://doma.land/blog/React 中的副作用和远程状态/</link><guid isPermaLink="false">https://doma.land/blog/React 中的副作用和远程状态/</guid><pubDate>Fri, 28 Jan 2022 14:21:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;本文只讨论 React 16.8 以来的函数组件和 hook 模型，不讨论历史的 class 组件和生命周期模型&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在编写 React 应用时，我们常常会需要发送网络请求以从服务端获取我们渲染界面所需要的数据。以往一种常见的实践是在 &lt;code&gt;useEffect&lt;/code&gt; 钩子中去发送网络请求，一个简单的示例如下：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setContent&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;fetchPostContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;在上面的示例中，&lt;code&gt;&amp;#x3C;Post&gt;&lt;/code&gt; 组件接收一个 &lt;code&gt;id&lt;/code&gt; 属性，并渲染对应 ID 的博文内容。实现上，使用一个状态 (state) 来存放博文的内容，其初始值为空字符串，然后在 &lt;code&gt;useEffect&lt;/code&gt; 中根据 &lt;code&gt;id&lt;/code&gt; 的变化发送网络请求来获取博文的内容，更新这个状态，引起组件的重新渲染，从而显示出博文内容。&lt;/p&gt;
&lt;p&gt;这样的实践很常见，但有一些显而易见的缺陷。例如竞态问题：设想 &lt;code&gt;id&lt;/code&gt; 属性初始为 1，&lt;code&gt;fetchPostContent(1)&lt;/code&gt; 网络请求发出，此时 &lt;code&gt;id&lt;/code&gt; 属性变更为 2，&lt;code&gt;fetchPostContent(2)&lt;/code&gt; 请求发出，此二请求谁先返回并不能保证，因此可能导致 &lt;code&gt;id&lt;/code&gt; 属性为 2 而显示的是 ID 为 1 的博文内容。&lt;/p&gt;
&lt;p&gt;解决这一问题也并不难，例如可以在 &lt;code&gt;fetchPostContent&lt;/code&gt; 方法中做处理，当参数变化时中断上一个请求等等，当然这样的方式又需要与其他的错误情况做区分等等更多的处理，实践过的朋友一定都有体验。&lt;/p&gt;
&lt;p&gt;在拆东墙补西墙之前，我们不妨想一想这样的流程为何会导致这样的问题。&lt;/p&gt;
&lt;h2&gt;副作用&lt;/h2&gt;
&lt;p&gt;首先我想对齐一个前提：&lt;strong&gt;&lt;code&gt;useEffect&lt;/code&gt; 不是监听器&lt;/strong&gt;，而是用来声明在组件渲染过程中运行的的副作用 (effect) 的。如果你常将 &lt;code&gt;useEffect&lt;/code&gt; 当作监听器来用，不妨阅读 React 官网对 &lt;code&gt;useEffect&lt;/code&gt; 依赖的解释——&lt;a href=&quot;https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects&quot;&gt;用于跳过副作用运行&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;在我看来，所谓副作用即应当是在 &lt;code&gt;ui=f(prop,state)&lt;/code&gt; 的等式中不影响 &lt;code&gt;ui&lt;/code&gt; 输出的存在。换句话说，如果移除一个 &lt;code&gt;useEffect&lt;/code&gt; 声明，渲染结果应当不受影响。例如：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; content &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; title
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; children
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;在上面的示例中，我们使用 &lt;code&gt;useEffect&lt;/code&gt; 来设置页面标题。这一动作不作用于组件输出的 UI，而是对组件外部产生影响，因此是一个副作用。如果我们移除这段 &lt;code&gt;useEffect&lt;/code&gt;，组件输出的 UI 将不受影响。而在我们在 &lt;code&gt;useEffect&lt;/code&gt; 中请求博文内容的示例中，组件输出的 UI 却严重依赖了一个副作用的结果，这正是导致输出结果不可靠的原因。找到了问题的原因，我们重新来审视 &lt;code&gt;&amp;#x3C;Post id&gt;&lt;/code&gt; 组件。我们想要输出的“对应 ID 的博文内容”不应该是副作用的结果，那么它是什么呢？这里我想引入“远程状态”这一概念。&lt;/p&gt;
&lt;h2&gt;远程状态&lt;/h2&gt;
&lt;p&gt;在 React 应用中，我们会使用 &lt;code&gt;useState&lt;/code&gt; 钩子来声明一个状态，我称作本地状态。远程状态就像本地状态一样，它有一个确定的值，只不过本地状态的值保存在本地，远程状态的值保存在服务端。这一概念并不难理解，其实 &lt;strong&gt;Web 应用的本质就是读取和修改远程状态&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;我们看如下引入了远程状态概念的示例：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; content &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;usePostContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;此示例中，我们拥有一个 &lt;code&gt;usePostContent&lt;/code&gt; 钩子，它接收博文的 ID，返回其内容。这一 &lt;code&gt;usePostContent&lt;/code&gt; 钩子其实并不难实现，实际上，它看起来仅仅是把刚才的 &lt;code&gt;useEffect&lt;/code&gt; 那些东西包装了起来。不过这次我们增加了竞态处理，使用一个 &lt;code&gt;posts&lt;/code&gt; 对象来根据 &lt;code&gt;id&lt;/code&gt; 返回对应的博文内容。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;usePostContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;posts&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setPosts&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;fetchPostContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;setPosts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;previous&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;previous&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; content&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; posts&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;仅仅是一个简单的封装，让我们的 &lt;code&gt;&amp;#x3C;Post id&gt;&lt;/code&gt; 组件形成了一个干干净净的 &lt;code&gt;ui=f(props)&lt;/code&gt; 范式。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如果你恰好觉得“既然知道封装是怎么实现的了那就跟没封装没区别了”，那你可以不看了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;社区实践&lt;/h2&gt;
&lt;p&gt;目前的封装比较简陋，如果要应对如下一些情况，实现就会变得复杂起来，比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果要接收的参数不仅仅是一个 &lt;code&gt;id&lt;/code&gt;，而是多个参数，如何存放保证根据参数返回正确的结果&lt;/li&gt;
&lt;li&gt;错误处理&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些还仅仅是对于某一个远程状态的封装，而系统中往往存在许多远程状态，每一个都实现一遍更是繁琐。但聪明的你一定想到了，可以把重复的部分再封装一个通用的钩子，每个远程状态里面调这个钩子。这件事已经有人替你做了，社区非常流行的 &lt;code&gt;react-query&lt;/code&gt; 和 &lt;code&gt;swr&lt;/code&gt; 库正是起到这个作用。以 &lt;code&gt;react-query&lt;/code&gt; 为例改造我们的 &lt;code&gt;usePostContent&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; useQuery &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;react-query&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;usePostContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;posts&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchPostContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;其他的放结语里&lt;/h2&gt;
&lt;p&gt;引入“远程状态”的视角还能够帮助我们看清本地状态的作用——存放 UI 相关的状态。例如：对话框的开闭、表单状态等等。而诸如列表数据这些，应该视作远程状态，如果发现在设计组件时使用了本地状态来存放，可以多斟酌一下。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Suspense & SWR]]></title><description><![CDATA[这篇是我在组内做的一次技术分享的讲稿。没怎么修改就直接分享出来了。 Suspense 前言 React 16.6 添加了一个 <Suspense> 组件，可以用来在 lazy load 的时候显示加载中的状态。 后来 React 想，这 Suspense 既然能用来等待 lazy…]]></description><link>https://doma.land/blog/Suspense &amp; SWR/</link><guid isPermaLink="false">https://doma.land/blog/Suspense &amp; SWR/</guid><pubDate>Fri, 24 Apr 2020 19:52:00 GMT</pubDate><content:encoded>&lt;p&gt;这篇是我在组内做的一次技术分享的讲稿。没怎么修改就直接分享出来了。&lt;/p&gt;
&lt;h2&gt;Suspense&lt;/h2&gt;
&lt;h3&gt;前言&lt;/h3&gt;
&lt;p&gt;React 16.6 添加了一个 &lt;code&gt;&amp;#x3C;Suspense&gt;&lt;/code&gt; 组件，可以用来在 lazy load 的时候显示加载中的状态。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ProfilePage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;lazy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./ProfilePage&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Lazy-loaded&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Show a spinner while the profile is loading&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Suspense&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fallback&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Spinner&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ProfilePage&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Suspense&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;后来 React 想，这 Suspense 既然能用来等待 lazy load 的 Promise，其实也可以用来等待其他东西，比如请求数据的 Promise，因此就有了 Suspense for Data Fetching 这个特性。目前它仍是一个实验特性，官网上的文档也主要面向的是请求库的开发者（比如 &lt;code&gt;swr&lt;/code&gt; 现在就适配 Suspense 了），对于大多数用户，React 官方文档仍然推荐&lt;a href=&quot;https://www.robinwieruch.de/react-hooks-fetch-data&quot;&gt;使用 hooks 来请求数据&lt;/a&gt;。&lt;/p&gt;
&lt;h3&gt;是什么？不是什么？能干什么？&lt;/h3&gt;
&lt;p&gt;Suspense 是一种“等待”机制，它作为一个组件，可以让你显式地声明当等待时应该渲染什么。&lt;/p&gt;
&lt;p&gt;Suspense 不是一个请求库。它本身并不负责创建和管理请求。&lt;/p&gt;
&lt;p&gt;Suspense 可以让请求库与 React 深度集成，请求库可以直接“告诉” React 它正在等待响应，而无需用户手动管理 loading 状态。&lt;/p&gt;
&lt;h3&gt;怎么用？&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; post &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getPost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Suspense&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fallback&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Loading...&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Post&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Suspense&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;这个 &lt;code&gt;getPost(id)&lt;/code&gt; 是…同步的?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;也许你感到这个写法符合直觉却又有些困惑，这就要说到数据获取的方式。&lt;/p&gt;
&lt;h3&gt;获取数据的方式&lt;/h3&gt;
&lt;p&gt;也许你在 React 文档中看过这部分的内容，我会用与官方文档稍有不同的方式讲解。这里有两种获取数据的方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;渲染后获取（传统的方式）&lt;/li&gt;
&lt;li&gt;渲染即获取（Suspense 的方式）&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;渲染后获取&lt;/h5&gt;
&lt;p&gt;渲染后获取就是我们最常写的方式，在 &lt;code&gt;componentDidMount&lt;/code&gt; 或者等效的 &lt;code&gt;useEffect&lt;/code&gt; 中进行数据获取&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setPost&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;fetchPost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setPost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Loading...&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这种方式中，&lt;code&gt;&amp;#x3C;Post&gt;&lt;/code&gt; 组件首先进行首次渲染，渲染完成后（&lt;code&gt;componentDidMount&lt;/code&gt; 阶段）开始发出请求。我们再来看另一种。&lt;/p&gt;
&lt;h4&gt;渲染即获取&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; post &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getPost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// just works&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Suspense&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fallback&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Loading...&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Post&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Suspense&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这种方式中 &lt;code&gt;&amp;#x3C;Post&gt;&lt;/code&gt; 渲染的同时，&lt;code&gt;getPost&lt;/code&gt; 的请求就发出了。目前看起来渲染即获取的方式只是比渲染后获取早了一点点时间，实际上里面的区别远不仅此。&lt;/p&gt;
&lt;h3&gt;两种方式的不同&lt;/h3&gt;
&lt;h4&gt;放弃了 state&lt;/h4&gt;
&lt;p&gt;你会注意到渲染即获取的方案里，是没有组件内部 state 的。这就是为什么它给人的第一印象是干净、清爽、符合直觉。&lt;code&gt;&amp;#x3C;Post&gt;&lt;/code&gt; 的 UI 真正的是它 props 的映射了，而不是某个 state 的映射。&lt;/p&gt;
&lt;p&gt;可难道也就只有更纯净了这种乌托邦式的区别吗？也并不是。放弃 state 同样能够避免一些问题，说不定你也曾遇到过。&lt;/p&gt;
&lt;h5&gt;1. Waterfall&lt;/h5&gt;
&lt;p&gt;请求瀑布。看如下使用内部 state 的例子：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Profile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setUser&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;fetchUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Loading profile...&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Posts&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Posts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;posts&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setPosts&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;fetchPosts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setPosts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;posts&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Loading posts...&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;posts&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这个例子里，&lt;code&gt;posts&lt;/code&gt; 要等到 &lt;code&gt;user&lt;/code&gt; 响应之后才开始获取。&lt;/p&gt;
&lt;h5&gt;2. Race condition&lt;/h5&gt;
&lt;p&gt;考虑如下渲染后获取的例子：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setPost&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;fetchPost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setPost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Loading...&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setId&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;currentId&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; currentId &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        Next post
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Post&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这个例子中 &lt;code&gt;&amp;#x3C;App&gt;&lt;/code&gt; 有一个 state，表示当前要看的文章 id，还有一个按钮用来修改这个 state。设想我们快速点击两次按钮，让 &lt;code&gt;id&lt;/code&gt; 变成 2 再变成 3，&lt;code&gt;&amp;#x3C;Post&gt;&lt;/code&gt; 最终显示的是哪篇文章？由于异步请求的响应顺序无法确定，我们并不能保证 &lt;code&gt;id&lt;/code&gt; 为 3 的时候，我们看到的就是文章 3。而没有了多余的 state，一切都变得顺畅而正确。&lt;/p&gt;
&lt;h4&gt;更早地获取数据&lt;/h4&gt;
&lt;p&gt;再讲一种情况，渲染即获取可以更早的获取数据。刚才不是讲过更早获取数据了么？这是一种不同的情况。&lt;/p&gt;
&lt;p&gt;考虑如下例子，还是一个按钮修改 id，我们用渲染即获取的 &lt;code&gt;&amp;#x3C;Post&gt;&lt;/code&gt; 组件：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Post.js&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; post &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// App.js&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; Post &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;lazy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./Post&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setId&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;currentId&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; currentId &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        Next post
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Post&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这个例子中，我们可以同时获取 &lt;code&gt;post&lt;/code&gt; 的内容和 &lt;code&gt;&amp;#x3C;Post&gt;&lt;/code&gt; 的代码，而不用等到 &lt;code&gt;&amp;#x3C;Post&gt;&lt;/code&gt; 下载完成才开始发起请求 &lt;code&gt;post&lt;/code&gt; 内容。&lt;/p&gt;
&lt;h3&gt;这个 &lt;code&gt;getPost(id)&lt;/code&gt; 是…同步的?&lt;/h3&gt;
&lt;p&gt;终于回到这个问题了，我们讲了这么多关于 Suspense for Data Fetching 所推崇的的“渲染即获取”，可究竟该怎么实现呢？&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; post &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getPost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;虽然在 &lt;code&gt;&amp;#x3C;Suspense&gt;&lt;/code&gt; 的源码中没有找到它的实现，我们在官方示例的源码中大致推测出了它的机制：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 不是示例的源码，但是是内意思&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; post
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; promise

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getPost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; post
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;promise&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; promise

  promise &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchPost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;post &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; promise
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;没想到吧，这个 &lt;code&gt;getPost(id)&lt;/code&gt; 方法多次调用可能返回不同的结果。它或者返回 &lt;code&gt;post&lt;/code&gt; 的内容，或者抛出一个 &lt;code&gt;Promise&lt;/code&gt;。经过试验，最终得出这样一个结论：当你的组件抛出一个 &lt;code&gt;Promise&lt;/code&gt; 时，&lt;code&gt;&amp;#x3C;Suspense&gt;&lt;/code&gt; 就会认为，请求正在进行，并渲染 &lt;code&gt;fallback&lt;/code&gt;。然后 React 调度器会适时地再次尝试渲染组件。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;但这写法也太奇葩了&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这就是为什么 React 关于这部分的文档是面向请求库作者，而非 React 用户的。&lt;/p&gt;
&lt;h2&gt;SWR&lt;/h2&gt;
&lt;h3&gt;前言&lt;/h3&gt;
&lt;p&gt;日常开发中有时会遇到这些场景和处理方式：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;相同的 URL （以及参数），缓存上一次的结果&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;设置一些常量来标识不同的请求，请求的结果根据标识放在 redux 里缓存，取数据的时候从 redux 取。这样来提高页面的响应效率，减少看到空页面的时间。&lt;/p&gt;
&lt;p&gt;实际上，&lt;code&gt;swr&lt;/code&gt; 已经帮你做好了。&lt;/p&gt;
&lt;h3&gt;是什么？不是什么？&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;swr&lt;/code&gt; 得名于 &lt;code&gt;stale-while-revalidate&lt;/code&gt; 的缩写，它是 &lt;a href=&quot;https://tools.ietf.org/html/rfc5861&quot;&gt;HTTP RFC 5861&lt;/a&gt; 中描述的一种 Cache-Control 扩展。大致意思是先返回缓存的响应，与此同时在后台请求新的响应，以提高响应速度，减少等待时间。虽然得名于此，&lt;code&gt;swr&lt;/code&gt; 只是借用了它的概念，实际实现与 &lt;code&gt;stale-while-revalidate&lt;/code&gt; 指令并无关系。&lt;/p&gt;
&lt;p&gt;SWR 并不是一个十足的“请求库”。它主要针对的是数据&lt;strong&gt;获取&lt;/strong&gt;的管理，而数据的更新、删除，它不管。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;SWR is a React Hooks library for remote data fetching.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;用法&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; useSWR &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;swr&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// fetch current user&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; data &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSWR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/api/user&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;也许你见过 SWR 这样的用法示例，心想这和普通的请求库做个 hook 没啥区别。那么我们来细细看下到底 SWR 是个什么东西。&lt;/p&gt;
&lt;h4&gt;API&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; error&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isValidating&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; mutate &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSWR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fetcher&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h5&gt;参数&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;key&lt;/code&gt;: 请求的标识&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fetcher&lt;/code&gt;: 返回请求数据的异步方法&lt;/li&gt;
&lt;li&gt;&lt;code&gt;options&lt;/code&gt;: 更多配置项&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;返回值&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;data&lt;/code&gt;: 标识 &lt;code&gt;key&lt;/code&gt; 对应的数据&lt;/li&gt;
&lt;li&gt;&lt;code&gt;error&lt;/code&gt;: 加载数据过程中抛出错误&lt;/li&gt;
&lt;li&gt;&lt;code&gt;isValidating&lt;/code&gt;: 是否正在请求或重新验证数据&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mutate(data?, shouldRevalidate)&lt;/code&gt;: 用于修改缓存数据&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;useSWR&lt;/h3&gt;
&lt;h4&gt;Data Fetching&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; useSWR &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;swr&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchCurrentUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; data &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;axios&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/api/user&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; data
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Profile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; user &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSWR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;currentUser&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fetchCurrentUser&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Loading profile...&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;useSWR&lt;/code&gt; 方法会为你返回已缓存的标识为 &lt;code&gt;currentUser&lt;/code&gt; 的数据，并且通过 &lt;code&gt;fetchCurrentUser&lt;/code&gt; 获取更新的数据并存入这个标识中。&lt;/p&gt;
&lt;p&gt;注意，SWR 并非请求库，它并非接收 URL 作为第一个参数并向其地址发送请求。从上面例子就可看出 &lt;code&gt;key&lt;/code&gt; 并非 URL，只是用于标识一个你需要的资源。不过，比起给你的每一个资源取名字，直接用 URL 来作为标识既方便又准确。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; user &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSWR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/api/user&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fetchCurrentUser&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;SWR 会将 &lt;code&gt;key&lt;/code&gt; 作为参数传给 &lt;code&gt;fetcher&lt;/code&gt;。既然我们使用了 URL 作为 &lt;code&gt;key&lt;/code&gt;，那么封装一个通用的 &lt;code&gt;request&lt;/code&gt; 方法作为 &lt;code&gt;fetcher&lt;/code&gt; 也是个不错的选择。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; data &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;axios&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; data
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; user &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSWR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/api/user&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;再加上 SWR 支持全局配置默认 &lt;code&gt;fetcher&lt;/code&gt;，最终就变成了&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; user &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSWR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/api/user&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Emmm，有内味了。看起来就像 SWR 是一个请求库一样，但你一定要清楚，其实并不是这样 😂。这一点很重要，对于你理解 SWR 的更多用法会有帮助。&lt;/p&gt;
&lt;h4&gt;Conditional Fetching &amp;#x26; Dependent Fetching&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;key&lt;/code&gt; 传入 &lt;code&gt;null&lt;/code&gt; 即代表不请求数据。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; posts &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSWR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/api/users/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/posts&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;如果你觉得这样有些反直觉，为什么 URL 为 &lt;code&gt;null&lt;/code&gt; 就代表不发送请求？那么你就掉进了上面提到的误区。我们知道 &lt;code&gt;key&lt;/code&gt; 是获取数据的标识，传入 &lt;code&gt;null&lt;/code&gt; 表示我现在不取数据。&lt;/p&gt;
&lt;p&gt;如果还是绕不清楚，换一种写法看看&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchUserPosts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; userId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;^posts by user (\d+)$&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; data &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;axios&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/api/users/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;userId&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/posts&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; data
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; posts &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSWR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  user &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;posts by user &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  fetchUserPosts
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;至此你应该不会再混淆这个概念了。&lt;/p&gt;
&lt;p&gt;花了这么大工夫搞清楚这个概念之后，我们继续来看 SWR 的用法。刚才讲了 &lt;code&gt;key&lt;/code&gt; 可以是一个字符串，其实 &lt;code&gt;key&lt;/code&gt; 还接受一些其他类型。比如当它是一个函数时，SWR 会用它的返回值作为存储标识。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// function as key&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; user &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSWR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/api/user&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// conditional&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; posts &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSWR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
  user &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/api/users/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/posts&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;SWR 对于函数 &lt;code&gt;key&lt;/code&gt; 有一个特殊的处理，使得 dependent fetching 可以变得更美观流畅：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; user &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSWR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/api/user&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; posts &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSWR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/api/users/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/posts&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;当 &lt;code&gt;user&lt;/code&gt; 还没有加载完时，&lt;strong&gt;&lt;code&gt;posts&lt;/code&gt; 的 &lt;code&gt;key&lt;/code&gt; 函数&lt;/strong&gt;会抛出异常。这时 SWR 就会不加载数据，就像 &lt;code&gt;key&lt;/code&gt; 值为 &lt;code&gt;null&lt;/code&gt; 一样。而当 &lt;code&gt;user&lt;/code&gt; 加载完成，&lt;code&gt;posts&lt;/code&gt; 就会开始加载。&lt;/p&gt;
&lt;h4&gt;Multiple Arguments&lt;/h4&gt;
&lt;p&gt;除了函数，&lt;code&gt;key&lt;/code&gt; 还可以接收一个数组。就像 &lt;code&gt;useCallback&lt;/code&gt; 或 &lt;code&gt;useEffect&lt;/code&gt; 的 &lt;code&gt;deps&lt;/code&gt; 数组那样，&lt;code&gt;key&lt;/code&gt; 数组的各个值依次相等则为相同的标识。&lt;code&gt;key&lt;/code&gt; 数组的值会依次传入 &lt;code&gt;fetcher&lt;/code&gt; 作为参数。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchUserPosts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;_&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; userId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;axios&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/api/users/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;userId&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/posts&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; posts &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSWR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;posts by user&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; userId&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fetchPostsByUser&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Mutate&lt;/h3&gt;
&lt;h4&gt;Manually Revalidate&lt;/h4&gt;
&lt;p&gt;前面的示例都是初次获取数据，那怎么手动再次获取某个标识的数据呢？SWR 提供了 &lt;code&gt;mutate&lt;/code&gt; 方法。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; useSWR&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; mutate &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;swr&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Profile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; user &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSWR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;currentUser&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token function&quot;&gt;mutate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;currentUser&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        Refresh
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;当调用了 &lt;code&gt;mutate(key)&lt;/code&gt;，SWR 就会再次请求它对应的数据，并更新缓存。要注意，这里的 &lt;code&gt;data: user&lt;/code&gt; 总是缓存中的内容。&lt;/p&gt;
&lt;p&gt;或者，你也可以直接使用 &lt;code&gt;useSWR&lt;/code&gt; 中返回的 &lt;code&gt;mutate&lt;/code&gt; 方法，这样可以省去再次书写 &lt;code&gt;key&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Profile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; mutate &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSWR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;currentUser&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token function&quot;&gt;mutate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        Refresh
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Mutation&lt;/h4&gt;
&lt;p&gt;我们前面说 SWR 本身不管更新数据。但是他允许我们修改缓存的数据。用的还是 &lt;code&gt;mutate&lt;/code&gt; 方法，它接收第二个参数，用于在重新请求之前先修改缓存的数据。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Todo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; todo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; mutate &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSWR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/api/todos/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;markTodoAsDone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; axios&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/api/todos/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;mutate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 更新缓存数据，同时重新请求数据&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Loading...&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;markTodoAsDoen&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Mark as done&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;很多时候，更新数据的请求会直接返回更新后的资源，这时我们可能希望更新缓存的同时不用再去重新验证资源。&lt;code&gt;mutate&lt;/code&gt; 接收第三个参数来允许控制是否重新验证资源。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; updated &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; axios&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/api/todos/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;mutate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;updated&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// shouldRevalidate=false 表示无需重新验证资源&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;或者，你也可以用 &lt;code&gt;Promise&lt;/code&gt; 来更新缓存，这也表示你不需要重新验证。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;updateTodo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; updated &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; axios&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/api/todos/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; updated&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;mutate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;updateTodo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Optimistic UI&lt;/h4&gt;
&lt;p&gt;也许你听说过 Optimistic UI 的概念。它描述的是当我请求更新/删除数据时，可以假设请求是成功的，并据此更新 UI；待请求完成，再根据实际结果更新 UI，这样提高页面响应速度。结合上面 &lt;code&gt;mutate&lt;/code&gt; 的用法，其实就得到了 Optimistic UI。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token function&quot;&gt;mutate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 先乐观更新本地缓存，且不重新验证&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;mutate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;updateTodo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 再用请求结果更新缓存&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;不止这些&lt;/h3&gt;
&lt;p&gt;上面只是介绍了 SWR 的一些 API 的常见用法，而 SWR 的能力可不止于此。&lt;/p&gt;
&lt;h4&gt;Focus Revalidation&lt;/h4&gt;
&lt;p&gt;当你重新聚焦到页面的时候，SWR 会自动重新验证数据。比如你在两个标签页打开了一个应用，然后在其中一个标签页修改了你的头像，当你切换到另一个标签页中时，新的头像已经加载好了。而这一机制无需你编写任何多余的代码。&lt;/p&gt;
&lt;h4&gt;Refetch on Interval&lt;/h4&gt;
&lt;p&gt;通过一个配置项开启周期性重新验证。这听起来自己实现也并不难，但别忘了，手动更新数据后重启定时器、页面离屏时暂停定时器，这些 SWR 都已帮你处理好。&lt;/p&gt;
&lt;h3&gt;从 request 到 SWR&lt;/h3&gt;
&lt;h4&gt;key 的复用&lt;/h4&gt;
&lt;p&gt;我自己开发应用的时候习惯把请求按照 API 或者业务逻辑封装成一个个函数来调用，以便复用。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; fetchUsers&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; updateUser &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;@/services/users&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;UserList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;users&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setUsers&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; data &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchUsers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;setUsers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserTable&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;users&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;onEditUser&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;openEditUserDrawer&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;而在使用 SWR 的应用中，则应将 key 管理起来进行复用。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; resources &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;@/constants/swr&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;UserList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; users &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSWR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resources&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;users&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserTable&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;users&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;onEditUser&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;openEditUserDrawer&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;在前面讲到的 Conditional Fetching 和 Data Fetching 的用法中可以看出，当使用函数作为 &lt;code&gt;key&lt;/code&gt; 等情况下时，往往会依赖组件内的变量/常量，这就要求可复用的 key 允许传入参数，来返回正确的 key。这样一来，一些 key 是字符串，一些是函数，使得 key 的管理变得不是很优雅。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; user &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/api/user&quot;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// conditional&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;userPosts&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/api/users/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/posts&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// dependent&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;userPosts&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/api/users/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/posts&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; user &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSWR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resources&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;users&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; posts &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSWR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resources&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;userPosts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;fetcher 的复用&lt;/h4&gt;
&lt;p&gt;当你处理好了 key 的复用，你会发现他们并没能完全代替原来复用的异步方法。你仍需要管理 fetcher，因为全局 fetcher 并没能应对你所有的 key。而在 Multiple Arguments 的例子中，像查询参数这类请求的参数应当展开成数组，这就需要 fetcher 的单独支持。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;users&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;page&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pageSize&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; orderColumn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; orderType&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; groupId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;/api/users&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  page&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  pageSize&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  orderColumn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  orderType&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  groupId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;queryUsers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token parameter&quot;&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  page&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  pageSize&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  orderColumn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  orderType&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  groupId&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;axios&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    page&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    pageSize&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    orderColumn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    orderType&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    groupId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;UserList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; users &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSWR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    resources&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;page&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pageSize&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; orderColumn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; orderType&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; groupId &lt;span class=&quot;token comment&quot;&gt;/*, ...*/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    queryUsers
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserTable&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;users&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这里 fetcher 的 url 参数耦合度也很怪。&lt;/p&gt;
&lt;p&gt;当你又最终想办法处理好了 key 和 fetcher 的复用，你发现，&lt;code&gt;@/services&lt;/code&gt; 目录并没有消失，更新资源的异步函数仍然在里面。到头来这部分复用并没有帮你更高效更优雅地编写应用代码。&lt;/p&gt;
&lt;h4&gt;有没有 workaround？&lt;/h4&gt;
&lt;p&gt;也不是没有。我们来看 Multiple Arguments 的参数列表耦合问题。为什么会出现这个问题？是因为在函数中无法知道 &lt;code&gt;key&lt;/code&gt; 数组各项分别对应什么参数。如何解决，就是不把参数拆开。可是传一个对象又不行，会重复触发更新，怎么办？&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;users&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/api/users&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;queryUsers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; params&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;axios&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;UserList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; users &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSWR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resources&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; queryUsers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserTable&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;users&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;用 &lt;code&gt;JSON.stringify&lt;/code&gt; 将对象变成字符串就行了。这时你发现 &lt;code&gt;queryUsers&lt;/code&gt; 的参数列表已经完全和调用者结耦，这部分处理 &lt;code&gt;params&lt;/code&gt; 的机制可以被整合到全局 fetcher 里去了，不再需要单独的 fetcher。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;users&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/api/users&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;UserList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; users &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSWR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resources&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserTable&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;users&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;机灵的你也许已经注意到，我的 key 里先进行一次 &lt;code&gt;JSON.stringify&lt;/code&gt;，fetcher 里再进行一次 &lt;code&gt;JSON.parse&lt;/code&gt;，到头来 axios 还要把它再 stringify 后拼到 URL 上去，是不是有点啰嗦？&lt;/p&gt;
&lt;p&gt;我们可以用 &lt;code&gt;qs&lt;/code&gt; 代替 &lt;code&gt;JSON.stringify&lt;/code&gt;，这样我们的 key 数组就形成了漂亮的 &lt;code&gt;[url, querystring]&lt;/code&gt; 范式，而这恰恰是区分资源最好的标识。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetcher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; querystring&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;axios&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;url&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;querystring&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;users&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/api/users&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; qs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;UserList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; users &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSWR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resources&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UserTable&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;users&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;但是使用范式会带来一点点代价。URL 相同而参数不同的情况下，会被认为是不同的资源，因此我们在表格页修改查询参数的时候，原先的查询结果无法留在界面上。&lt;/p&gt;
&lt;h3&gt;Suspense&lt;/h3&gt;
&lt;p&gt;讲了半天 Suspense，又讲了半天 SWR，他俩到底有啥关系呢？让我们回看这个问题：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这个 &lt;code&gt;getPost(id)&lt;/code&gt; 是…同步的?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; post &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getPost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;你可能已经注意到，SWR 的 API 就实现了 Suspense 中推行的这一范式，让你摆脱 state 管理异步数据。并且，SWR 通过配置项支持了 Suspense 模式：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; post &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSWR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/api/posts/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;suspense&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Suspense&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fallback&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Loading...&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Post&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Suspense&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;当你开启了 &lt;code&gt;suspense: true&lt;/code&gt;，SWR 就会在数据初次加载中抛出 &lt;code&gt;Promise&lt;/code&gt;，触发 &lt;code&gt;&amp;#x3C;Suspense&gt;&lt;/code&gt; 的 &lt;code&gt;fallback&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;这次介绍 Suspense 和 SWR，并非推销这两个技术，推行大家去使用它们。把它们放在一起讲，是因为它们引入了异步数据使用的思想上的更新。换个角度看问题，有时问题便不再是问题。&lt;/p&gt;
&lt;h2&gt;参考链接&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://zh-hans.reactjs.org/docs/concurrent-mode-suspense.html&quot;&gt;Suspense for Data Fetching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/zeit/swr&quot;&gt;zeit/swr: React Hooks library for remote data fetching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/dance2die/useswr-react-hooks-for-remote-data-fetching-1nlo&quot;&gt;&quot;useSWR&quot; - React Hooks for Remote Data Fetching&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[缓存控制中的 stale-while-revalidate]]></title><description><![CDATA[stale-while-revalidate 是 HTTP RFC 5861 中描述的一种 Cache-Control…]]></description><link>https://doma.land/blog/缓存控制中的 stale-while-revalidate/</link><guid isPermaLink="false">https://doma.land/blog/缓存控制中的 stale-while-revalidate/</guid><pubDate>Fri, 24 Apr 2020 17:04:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;code&gt;stale-while-revalidate&lt;/code&gt; 是 &lt;a href=&quot;https://tools.ietf.org/html/rfc5861&quot;&gt;HTTP RFC 5861&lt;/a&gt; 中描述的一种 Cache-Control 扩展，属于对缓存到期的控制。&lt;/p&gt;
&lt;h2&gt;缓存控制&lt;/h2&gt;
&lt;p&gt;很多人都了解浏览器的缓存机制，这里简单温习一下。为了提高响应速度，浏览器会帮我们把一些请求的响应缓存下来，在下次请求相同的资源时，直接返回缓存的结果。但业务中有些资源是不应该缓存的，应该总是请求最新的结果，浏览器怎么判断哪些资源可以缓存，缓存多久这些信息呢？HTTP 缓存文档中允许服务端设置一些响应头 (如 Cache-control) 来告诉浏览器如何缓存这个响应。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Cache-Control: max-age=600&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;max-age&lt;/code&gt; 就是 &lt;code&gt;Cache-control&lt;/code&gt; 包含的一个指令，用于设置缓存存储的最大周期，超过这个时间缓存被认为过期(单位秒)。比如上面的例子，在 600 秒内，再次请求这个资源，浏览器就会返回缓存的响应，超过这个时间后，请求这个资源，浏览器就会请求新的结果，应用就需要等待这个请求。&lt;/p&gt;
&lt;h2&gt;含义&lt;/h2&gt;
&lt;p&gt;那我们回到 &lt;code&gt;stale-while-revalidate=&amp;#x3C;seconds&gt;&lt;/code&gt;。它表明客户端愿意接受陈旧 (stale) 的响应，同时在后台异步检查新的响应 (revalidate)。秒值指示客户愿意接受陈旧响应的时间长度。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;怎么样的响应算是陈旧的响应？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;stale-while-revalidate&lt;/code&gt; 指令应当与 &lt;code&gt;max-age&lt;/code&gt; 配合使用，超过 &lt;code&gt;max-age&lt;/code&gt; 指定的时间的响应就是陈旧的响应。与之相对的，没有超过时间的就是新鲜的 (fresh) 响应。如果一个缓存的响应没有超过 &lt;code&gt;max-age&lt;/code&gt; 指定的时间（仍是新鲜的），按照上面讲的缓存机制，此时请求这个资源，浏览器会直接返回缓存的结果。如果缓存的结果已经陈旧了呢？按照前面讲的缓存机制，浏览器应该去请求新的响应了，但是如果存在 &lt;code&gt;stale-while-revalidate&lt;/code&gt; 指令就不一样了，浏览器会检查这个陈旧的响应是否超过了 &lt;code&gt;stale-while-revalidate&lt;/code&gt; 规定的时间窗口。如果没有超过，那么浏览器仍然会直接返回缓存的结果，同时在后台请求新的结果用来更新缓存。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;有点绕…&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们看 RFC 5861 给出的示例用法。&lt;/p&gt;
&lt;h2&gt;用法&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Cache-Control: max-age=600, stale-while-revalidate=30&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这个响应头规定了缓存的周期为 600 秒，可接受 30 秒内的陈旧响应。如果在 600 秒之内请求这个资源，浏览器就会直接返回缓存的响应。如果在 600 秒之后请求，浏览器会检查是否已经超过可接受的陈旧时间，也就是总共是否超过了 630 秒。如果没有超过的话，仍然返回缓存的响应，同时在后台请求新的响应。如果超过了 630 秒，就直接请求新的响应，应用将等待这个请求。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这和直接设置 &lt;code&gt;max-age=630&lt;/code&gt; 有什么不一样？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们设想这样一个步骤，比较两种方式的异同。&lt;/p&gt;
&lt;p&gt;设置 &lt;code&gt;max-age=630&lt;/code&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;初次请求，应用等待请求，得到新鲜的响应，存入缓存&lt;/li&gt;
&lt;li&gt;在 600 秒内再次请求，不用等，得到缓存响应&lt;/li&gt;
&lt;li&gt;在 610 秒时再次请求，不用等，得到缓存响应&lt;/li&gt;
&lt;li&gt;在 640 秒时再次请求，应用等待请求，得到新鲜的响应，存入缓存&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;设置 &lt;code&gt;max-age=600, stale-while-revalidate=30&lt;/code&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;初次请求，应用等待请求，得到新鲜响应，存入缓存&lt;/li&gt;
&lt;li&gt;在 600 秒内再次请求，不用等，得到缓存响应&lt;/li&gt;
&lt;li&gt;在 610 秒时再次请求，不用等，得到缓存响应，同时后台请求了新的响应，存入缓存&lt;/li&gt;
&lt;li&gt;在 640 秒时再次请求，不用等，得到 610 秒时刷新的缓存响应&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;可以看到我们在 640 秒时的这个请求，即不用等，也保证了新鲜度。实际上，我们在超过 &lt;code&gt;max-age&lt;/code&gt; 的周期之后，在 &lt;code&gt;stale-while-revalidate&lt;/code&gt; 指定的时间窗口之内发出的请求，都会得到这个作用。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如果没有恰好在那 30 秒内请求不还是没用么&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;说对了。600 和 30 这两个数值的搭配可能并不能让你想到实际使用场景，我们来举一个实例。&lt;/p&gt;
&lt;h2&gt;实例&lt;/h2&gt;
&lt;p&gt;假设我有一个 HTTP API，它返回现在是当前小时的第几分钟。它具有如下缓存控制响应头：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Cache-Control: max-age=1, stale-while-revalidate=59&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;如果在 1 秒钟内重新请求，那么分钟数和原来是一样的，之前缓存的结果完全是新鲜的；如果在 1-60 秒内请求，需要请求一个新的结果了，但原来的结果也不是不能用；如果在超过 60 秒后请求，则一定需要新的结果。&lt;/p&gt;
&lt;p&gt;从这个例子可以看出，&lt;code&gt;stale-while-revalidate&lt;/code&gt; 比较适用的场景是，我们查询的信息需要被刷新，但一定程度的陈旧是可以接受的。通常来讲，这种场景对应的业务是，我们请求的资源会在已知的或者可预见的周期内定时更新，同时我们会多次请求这个资源。在这样的场景下，&lt;code&gt;stale-while-revalidate&lt;/code&gt; 可以在提供新鲜度有保证的响应结果的同时，减少重复请求的等待时间。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;等等，这听起来有点像…&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们在开发应用时，为了减少请求的等待时间，减少空白页的出现，有时会在请求完成后，按照请求的 URL 等标识，将请求结果存储在 Redux/Vuex 等介质中，在页面上从 store 中读取数据来显示，而不是直接在 state 中维护和显示请求结果。这样一来，当需要重新请求相同的资源时，我们可以在界面上看到上次请求的结果，随后看到新的结果，减少空白页的出现。&lt;/p&gt;
&lt;p&gt;如果你也有这样的操作，或者想要有这样的操作，不妨看看 zeit 的 &lt;code&gt;swr&lt;/code&gt; 库，它已经帮你做了。&lt;code&gt;swr&lt;/code&gt; 即 &lt;code&gt;stale-while-revalidate&lt;/code&gt; 的缩写，虽然得名于此，&lt;code&gt;swr&lt;/code&gt; 只是借用了它的思想，实际实现与 &lt;code&gt;stale-while-revalidate&lt;/code&gt; 指令并无关系。&lt;code&gt;swr&lt;/code&gt; 在早读课的另一篇文章有介绍。这里不多赘述。&lt;/p&gt;
&lt;h2&gt;浏览器兼容性&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;stale-while-revalidate&lt;/code&gt; 不是缓存标准文档的一部分，而是扩展内容，属于实验性质。目前桌面浏览器只有 Chrome 75、Firefox 68、Edge 79 起对其有支持。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;缓存控制是应用优化中一个通用的、常用的方法，它不是单纯的前端技术或知识，也并非一夜之间就能一把梭的方法。很多时候缓存控制应该结合实际业务需求，对各个资源有针对性地使用，以在信息的准确性和响应的时间上达到最佳的平衡。&lt;/p&gt;
&lt;p&gt;最后分享一句格言，它是 Google Web 指南里的一个章节标题。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Never load the same resource twice.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;参考链接&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tools.ietf.org/html/rfc5861&quot;&gt;RFC 5861 - HTTP Cache-Control Extensions for Stale Content&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control&quot;&gt;Cache-Control - HTTP | MDN&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/stale-while-revalidate/&quot;&gt;Keeping things fresh with stale-while-revalidate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#stale-while-revalidate&quot;&gt;https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#stale-while-revalidate&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Laravel Pjax 总是 abort(422)]]></title><description><![CDATA[使用 spatie/laravel-pjax 的时候遇到总是 abort(422) 的情况，查了一圈大多说原因是渲染的页面里找不到监听的 pjax container。
可检查过后我的页面里都是有 id="pjax-container" 的，请求头里也有 X-PJAX…]]></description><link>https://doma.land/blog/laravel-pjax-abort-422/</link><guid isPermaLink="false">https://doma.land/blog/laravel-pjax-abort-422/</guid><pubDate>Thu, 15 Nov 2018 18:15:55 GMT</pubDate><content:encoded>&lt;p&gt;使用 &lt;code&gt;spatie/laravel-pjax&lt;/code&gt; 的时候遇到总是 &lt;code&gt;abort(422)&lt;/code&gt; 的情况，查了一圈大多说原因是渲染的页面里找不到监听的 pjax container。
可检查过后我的页面里都是有 &lt;code&gt;id=&quot;pjax-container&quot;&lt;/code&gt; 的，请求头里也有 &lt;code&gt;X-PJAX-CONTAINER: #pjax-container&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;查源码加调试输出找到问题的原因：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;spatie/laravel-pjax&lt;/code&gt; 里使用了 &lt;code&gt;Symfony\Component\DomCrawler\Crawler&lt;/code&gt; 来检查渲染的 DOM 中是否存在指定的 &lt;code&gt;X-PJAX-CONTAINER&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Middleware/FilterIfPjax.php&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;filterResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Response&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$container&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$crawler&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCrawler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token variable&quot;&gt;$response&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;makeTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$crawler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt;
       &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetchContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$crawler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$container&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;abort(422)&lt;/code&gt; 发生在 &lt;code&gt;fetchContainer()&lt;/code&gt; 中：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;php&quot;&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;fetchContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Crawler&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$crawler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$container&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$content&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$crawler&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$container&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$content&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;422&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$content&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;注意到在 &lt;code&gt;filterResponse()&lt;/code&gt; 中是把当前的 &lt;code&gt;$response&lt;/code&gt; 传给了 &lt;code&gt;$crawler&lt;/code&gt; 的，那么如果当前的 &lt;code&gt;$response&lt;/code&gt; 其实是个错误页面呢？这里就会发生找不到 &lt;code&gt;#pjax-container&lt;/code&gt; 的情况了。&lt;/p&gt;
&lt;p&gt;输出一看果然，&lt;code&gt;$response&lt;/code&gt; 是个异常信息页面。我遇到的问题是忘了 &lt;code&gt;migrate&lt;/code&gt;。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[处理 Chrome 请求错误 net::ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION]]></title><description><![CDATA[疑难杂症。]]></description><link>https://doma.land/blog/error-response-headers-multiple-content-disposition/</link><guid isPermaLink="false">https://doma.land/blog/error-response-headers-multiple-content-disposition/</guid><pubDate>Wed, 08 Aug 2018 20:03:06 GMT</pubDate><content:encoded>&lt;p&gt;疑难杂症。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;在 OTV Intelligence 里有导出报表的功能。前端发送 POST 请求给后端，后端输出二进制文件给浏览器。&lt;/p&gt;
&lt;p&gt;其中一个导出接口报错 &lt;code&gt;net::ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION&lt;/code&gt;，google 了下找到几个链接&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=vAuZwirKjWs&quot;&gt;https://www.youtube.com/watch?v=vAuZwirKjWs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://forums.asp.net/t/1941627.aspx?How+to+fix+the+problem+of+Error+code+ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION+&quot;&gt;https://forums.asp.net/t/1941627.aspx?How+to+fix+the+problem+of+Error+code+ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION+&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;（关键）&lt;a href=&quot;http://www.gangarasa.com/lets-Do-GoodCode/tag/err_response_headers_multiple_content_disposition/&quot;&gt;http://www.gangarasa.com/lets-Do-GoodCode/tag/err_response_headers_multiple_content_disposition/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;总结下来原因是，服务端返回的 &lt;code&gt;Content-disposition&lt;/code&gt; header 中，&lt;code&gt;filename&lt;/code&gt; 包含逗号。解决方案是将 &lt;code&gt;filename&lt;/code&gt; 部分使用双引号括住。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[hubble]]></title><link>https://doma.land/blog/hubble/</link><guid isPermaLink="false">https://doma.land/blog/hubble/</guid><pubDate>Sat, 16 Jun 2018 01:43:15 GMT</pubDate><content:encoded></content:encoded></item><item><title><![CDATA[Hubble 见证 Vue 与 React 突破 10 万 GitHub Stars!]]></title><description><![CDATA[昨天 19 时 9 分前后，Vue 与 React 两个著名现代前端框架双双突破 10 万 GitHub Stars。
并且在同日，晚于 React 诞生的 Vue 首次在 GitHub Star 数量上超越 React，最终率先达到 10 万。]]></description><link>https://doma.land/blog/hubble-congrats-react-and-vue-100k-stars/</link><guid isPermaLink="false">https://doma.land/blog/hubble-congrats-react-and-vue-100k-stars/</guid><pubDate>Sat, 16 Jun 2018 00:07:42 GMT</pubDate><content:encoded>&lt;p&gt;昨天 19 时 9 分前后，Vue 与 React 两个著名现代前端框架双双突破 10 万 GitHub Stars。
并且在同日，晚于 React 诞生的 Vue 首次在 GitHub Star 数量上超越 React，最终率先达到 10 万。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9143358544ed87494fe807b2e13be083/61583/react-and-vue-both-reach-100k-stars.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.86075949367089%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA8UlEQVQoz51S0W7CMAzM/38lmjY0JhaiwZrEPp8nN1CK6Paw06lq7Tu71yZRp2e6TjgfptO+ljfWTK2bsuRbAOySy+5lv3t9bwKYbcqSk+5OdxvXeHIR/Tjm42fOp5JLAeDR4iBnS5hFhGQ3thsHRKRJAMCoNEjVoEBHJY2p/0OyOQ8X0vm7GmalnYc+zArlk4WRzQZBU5oYmumXyqHVsQK0pIDS1KxBp5k1iIVzEdUgRpDfdt0TmbuGocWI+JKYOd6E/leEe+YNcFnA9ZT1uNR75yMQh8LWyqVlZuv7639er1RVGsqln6oDEJGH1k2sqj/xvsZTq9S2wwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;React and Vue both reach 100k stars&quot;
        title=&quot;React and Vue both reach 100k stars&quot;
        src=&quot;/static/9143358544ed87494fe807b2e13be083/f058b/react-and-vue-both-reach-100k-stars.png&quot;
        srcset=&quot;/static/9143358544ed87494fe807b2e13be083/c26ae/react-and-vue-both-reach-100k-stars.png 158w,
/static/9143358544ed87494fe807b2e13be083/6bdcf/react-and-vue-both-reach-100k-stars.png 315w,
/static/9143358544ed87494fe807b2e13be083/f058b/react-and-vue-both-reach-100k-stars.png 630w,
/static/9143358544ed87494fe807b2e13be083/40601/react-and-vue-both-reach-100k-stars.png 945w,
/static/9143358544ed87494fe807b2e13be083/78612/react-and-vue-both-reach-100k-stars.png 1260w,
/static/9143358544ed87494fe807b2e13be083/61583/react-and-vue-both-reach-100k-stars.png 1616w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h3&gt;Hubble&lt;/h3&gt;
&lt;p&gt;Hubble 取名自哈勃空间望远镜，寓意观测星星的历史。
主要功能是查询一个 GitHub 仓库的 star 历史，以及查看&lt;a href=&quot;https://hubble.js.org/my-stars-this-year&quot;&gt;自己今年获得了多少 star&lt;/a&gt;。
&lt;img src=&quot;https://i.loli.net/2018/05/29/5b0d1c3190a01.png&quot; alt=&quot;Screenshot&quot;&gt;
做这个项目的起因其实只是我想要知道自己今年获得了多少 star 而已。顺带想起用过的几个查 star 历史的工具都不是很好看或者好用，于是就想也做上查询 star 历史的功能。
在此过程中对 GitHub API 的使用也有了更加深入的学习。
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.26582278481012%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABYUlEQVQoz42SPS9EQRSG718RCi0/QKIkCrUfQOIX6EQj0RAaCcluBJVCiEpBhEJC6EQhPhLsWja79t7s/ZpzHrl3XLtXLCZ5M2cy73lm5sxxAPRT2VC1KyOCW6/z9FzmqVTB95uomJzHxnYOYnCSDSNWohb9BTSGph8QhCFIxPVLzE1V0r3Eq5p5W7OjKm131Jwxk4iACl4IXtgC/CSn1hTmjmFqXzl5sObI/AzODu0ES4HP78JAAboXlMKF5m77W2JHYKkhDG9A/7KydqksnSpjW6RxpxL8CRxah75lpXihTOwqXfPK9IEFZp+V1vI/wOTJg0XoXYLVc2VyD3oWYeawvZHIlYK2lvkeO6+eMr4jjG4atq+U2SNlZF1YOVO8QLivRrx5BjeEx7pQdpVYlCAyqUQhjA1+aBBR24dhLPiRSZ+X/LAfSRpXXOGuGlNpRLzWXG5LNcq1ZgrJA21+UpIPqJT8H6RMfHAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;How many stars have I earned this year?&quot;
        title=&quot;How many stars have I earned this year?&quot;
        src=&quot;/static/5fbe881782c9fe11c09ad887cdc9774a/f058b/my-stars-this-year.png&quot;
        srcset=&quot;/static/5fbe881782c9fe11c09ad887cdc9774a/c26ae/my-stars-this-year.png 158w,
/static/5fbe881782c9fe11c09ad887cdc9774a/6bdcf/my-stars-this-year.png 315w,
/static/5fbe881782c9fe11c09ad887cdc9774a/f058b/my-stars-this-year.png 630w,
/static/5fbe881782c9fe11c09ad887cdc9774a/40601/my-stars-this-year.png 945w,
/static/5fbe881782c9fe11c09ad887cdc9774a/78612/my-stars-this-year.png 1260w,
/static/5fbe881782c9fe11c09ad887cdc9774a/21cdd/my-stars-this-year.png 1680w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;关于 Hubble 的更多内容将在我的&lt;a href=&quot;https://seven.ooo/hubble&quot;&gt;另一篇博客&lt;/a&gt;中记录。&lt;/p&gt;
&lt;h3&gt;React vs. Vue 100k star race LIVE&lt;/h3&gt;
&lt;p&gt;Hubble 上线之后，有用户提议增加同时查看两个 repo 历史的功能，看看两个仓库互相追逐的样子。我觉得听起来很有趣，就把『Repo race』这个功能列入了计划当中。
作为 React 和 Vue 的用户和粉丝，听到这样的功能第一个想到的就是它俩，于是我在 Repo race 中加入了一个彩蛋：
因为要区分两个仓库的图线，所以需要用户来为两个仓库选择各自图线的颜色，而当输入的 repo 是 &lt;code&gt;facebook/react&lt;/code&gt; 和 &lt;code&gt;vuejs/vue&lt;/code&gt; 时，将会自动使用它们的主题色(&lt;code&gt;#61dafb&lt;/code&gt; 和 &lt;code&gt;#41b883&lt;/code&gt;)。
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c8655e149b31e0a55f802914f6216f32/cf8e5/react-vue-repo-race.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 59.49367088607595%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABNUlEQVQoz5WS62rDMAyF8/6vshfYU+zXGIP9LaWXDNY2rW1JR6fYidN4ZZcaDnYU/OnIUsfFcnc2C8ah77lab7nZ7QnU/353J+9ZXT3UHwcBv6IQZnSA20PPVb/jMFxIGt/3zqdX5/OHM6oX+NJI9z1TMOdZlICV2Fkjo0m1zM3J+bIm33ak2M0tHPyMJ3aqysal37LOmT1fqN8LuZd43pMZN5cDOzNrgH8pAwxe3rMmFDgHHat4GLisJPcoAjyrlJJz7GFgLTP3IxoYTKb42IcOwL8gMwhOAZlgTBNsqV+BXLybTCDz/GZKMZ1dNcBaMr3tXt7yZZ1kpRkorhTWDPOdQzQuRgUzBlMKrABSOes4Jj/ACjDP4TEEHlMsgKxTvDDlzgEFFmIs51piSuk2RgBFZI5fAYsUtFeCfrVxAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Vue is gaining faster&quot;
        title=&quot;Vue is gaining faster&quot;
        src=&quot;/static/c8655e149b31e0a55f802914f6216f32/f058b/react-vue-repo-race.png&quot;
        srcset=&quot;/static/c8655e149b31e0a55f802914f6216f32/c26ae/react-vue-repo-race.png 158w,
/static/c8655e149b31e0a55f802914f6216f32/6bdcf/react-vue-repo-race.png 315w,
/static/c8655e149b31e0a55f802914f6216f32/f058b/react-vue-repo-race.png 630w,
/static/c8655e149b31e0a55f802914f6216f32/40601/react-vue-repo-race.png 945w,
/static/c8655e149b31e0a55f802914f6216f32/78612/react-vue-repo-race.png 1260w,
/static/c8655e149b31e0a55f802914f6216f32/cf8e5/react-vue-repo-race.png 1402w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;在开发 Repo race 的过程中，我自己已经看过完整的 React 和 Vue star 历史。我注意到很长时间以来，Vue 的增长速度比 React 要快很多，并且最近差距已经缩小到 1000 多。
我就突发奇想，想要做一个实况，亲眼见证 Vue 赶超 React 的时刻。就这样，React vs. Vue 在三周前上线了。
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3b65dd956202b1afaaa1ec9057aca5f3/d5bfb/live-is-on.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 64.55696202531645%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAABYlAAAWJQFJUiTwAAACvklEQVQ4y3WTyU9TURTG+VtcuHKAju+1j5aWVl6LHaA0VFMHbNCFiQujCSaujC7ZmbhiaxyibJSFJsY4YK0GldcyuCgmxAGklPaVqX3wM+/SkrDwJicn+c653/3Ouee0ub1BZKeMLHsIRxJ4/SoOlxdZ8WOXPdicnftmbZnkwSZ5sMveA2ZibeFwlIQawCkpJNMZIv0pAif7CYTjqNEEPjVGdyiGX40SDMUIqBFhHr8qyG1N8tYDbQJ0KNhcPtqDfVgDMey9g3T0JLCqA1hDSexqAntPH84TccyKpM5unIoPm+zB4eo6qNAEOhwKvmCYbDaHVpjlyzeN2XyB+UKBGU3jfTbHZDZHNveZvGbic8zPzJPPzzBdKPDp85S432F37xG221wE1CgbG5uYpwZMbsG7TfjDwbMDfFwr8nplju8bSwJrGAbBUFTwtJkfYDKbfflbWhUJU5U6ffkterUtHi3VBc12oyFixcoSXRO3OPT4CiO5+wIrVyrifrutpbBJWCqXRYJWM7ix0OBascHzkqlpl8bOjoj90JdJv7lL36tRbn8dF1hV10WF+4QthaXVJqFuMFKsc71YZ6JkCMK6YYjYQnWZyx/GGHp7j1HtmcAqevUg4Z7CKOVKVSRMr0NyziA2a/C0tCuw3WYPF9dLBF7c4fD4VW5OPRSYvr7eJDR7KDd72BPh56/f1Dc3+VHWGV+s8mSxyvSKTq2qU15bo6bX+F1e4cHcW8a0l7xa+EZ9a5s/S8vivvjl1vxIip9UeoizQxc5d36Yi5lhLmWGuTA0zKmzGdLnMsQTKSLxJIMDaVIDZ0j0n+ZkPElvbEDMo5hDu+QVA2r6HpsHWfHhNtfOF0ZSU1gcXVjsbiwORZR0zCpz1CJxxOIU/rhVFra/KS2FpnfJXTjdPrEFTqUbyXMCh8u3vwmmiv9Za1P+AagRh70Jxz5LAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;React vs. Vue 100k stars race is LIVE now.&quot;
        title=&quot;React vs. Vue 100k stars race is LIVE now.&quot;
        src=&quot;/static/3b65dd956202b1afaaa1ec9057aca5f3/f058b/live-is-on.png&quot;
        srcset=&quot;/static/3b65dd956202b1afaaa1ec9057aca5f3/c26ae/live-is-on.png 158w,
/static/3b65dd956202b1afaaa1ec9057aca5f3/6bdcf/live-is-on.png 315w,
/static/3b65dd956202b1afaaa1ec9057aca5f3/f058b/live-is-on.png 630w,
/static/3b65dd956202b1afaaa1ec9057aca5f3/40601/live-is-on.png 945w,
/static/3b65dd956202b1afaaa1ec9057aca5f3/d5bfb/live-is-on.png 1072w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;几周以来我听取一些访问者的意见和建议，逐步优化性能和视觉效果，让访客能清晰的体验这场竞赛的走势（尤其感谢 &lt;a href=&quot;https://github.com/gustojs&quot;&gt;@gusto&lt;/a&gt; 的许多建设性意见）。除了原本的柱状图外，还增加了折线图，显示最近 400 天两者的 star 趋势。并且为了让更多人能正常访问，同时结合使用了 GitHub REST API v3 和 GitHub GraphQL API v4。
这三周以来大约每天分差都会缩小几十到一百左右，预计在 6 月 20 日前后 Vue 将追平 React。到了前天晚上，差距缩小到 500，我增加了 Facebook 和 Twitter 分享按钮，希望更多的粉丝能一起观看。并且打算过几天即将追平的时候，把折线图换成每秒趋势图，这样可以实时看到走势变化，并且最终赶超时将会看到一个 &quot;X&quot; 形。&lt;/p&gt;
&lt;p&gt;结果一觉醒来，Vue 已经领先 React。
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bb0d47b25565bcbd94409b413b41e963/d53ff/boost-overnight.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 76.58227848101265%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAABYlAAAWJQFJUiTwAAAB1UlEQVQ4y6WUTWsTURSG83tEq2kzk49pMolTJ02mbSQLESwIWqtjsYoLsfUDsbWxCq60S7Hgzi5F3CuIoFXEaqoSmiY6mYlxUmptExMeSWqgC9ExvXDufReX57zncs51xbUEyaiCV/QzkDxIT28/Hl8IwS/jCyp4/DKCL7R5OgiXTwrTLYXwBxXOj19mMnWDq5PXmbg2zdRUil4twR5BcgRrAhtbR1cARe2jVDShtsH6d5t6dQ1zpcLg0RF27hYQpYgzoBiQcQsSe9U+ioZB2TL5mMny1TJ4WwD1wAjuTgExEHHusAW0bZuSXebDUo7slwKNNXT8FDs6PHil/wZqfDZNVus1VjbW+fZjjSowrLcBbDy60gAWTGYMmLXgVu4xE5mHnNBPtw/MmxYX83D2eZ5Ds5cYenUX/eSZ9oA9qsZyweKOCdMLFucezXDh/RzD+uj2HI4tQSoHo+n7HFm4h76dko1ikZu5OreXq1x594Cxxbn2gO7fwFJ5tdkqlUqVn7V6U28tuTWS/wR2it3ISowX869Jpxd58vQZL+ff8CmTZfDwMXa5vYiBsHOHrcxqLEFU248aT7AvNtDU8f4kwUiULm+Q1t2/hetPWTbdhLdo2fHn8AtQAmiJ7zXlOgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Boost overnight&quot;
        title=&quot;Boost overnight&quot;
        src=&quot;/static/bb0d47b25565bcbd94409b413b41e963/f058b/boost-overnight.png&quot;
        srcset=&quot;/static/bb0d47b25565bcbd94409b413b41e963/c26ae/boost-overnight.png 158w,
/static/bb0d47b25565bcbd94409b413b41e963/6bdcf/boost-overnight.png 315w,
/static/bb0d47b25565bcbd94409b413b41e963/f058b/boost-overnight.png 630w,
/static/bb0d47b25565bcbd94409b413b41e963/40601/boost-overnight.png 945w,
/static/bb0d47b25565bcbd94409b413b41e963/d53ff/boost-overnight.png 1068w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;也就是说一夜之间分差的缩小速度从 100 猛增到了 500 多。社区的粉丝提醒我，不仅是 Vue 突然开始猛增，React 其实也突然开始猛涨。事实上，如果去看看 Repo race 就会发现，两个仓库这一天的增长速度都是各自创建以来前所未有的。
当然这也让我不得不手忙脚乱紧急开始上线每秒趋势图，幸好还是赶上了。有了每秒趋势图，可以清楚地看到两条图线分分合合缠绕在一起，不停地相互超越，非常精彩刺激。
这下我不光看到了 &quot;X&quot;，甚至看到了一条 DNA…
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c0d0e9d76a26327da52f67a7c9a55bd4/c2d13/final-stage.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.89873417721519%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA40lEQVQoz31Si47CMAzb/3/owQkhJDjYmsTxKekeZXRUspRulmM3Gdgc3yGOiPDn9Mvz5UoRXXlwT7TcYuQQhZnRAbo7FRVR050GsIikMID8FkJimkheiHqVHpbLET4SeOup3otpdeuzw+iML6J7WDiEcTLjy5Q3KTRHNvhwyNaVcxYANaIDnGApprMJtnTfRV7eJ8iSAtVF1JtA07TzPGvkpXvBJrA1YlfAjwQ9nYHHpw6iHVJvcGtkVSWsOsoVagdgxqIg5lWKND1OrlRvKO97Rb7Gkbf7k/fHH8dpevvXi/wPb9IZIrVn/0MAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Final stage&quot;
        title=&quot;Final stage&quot;
        src=&quot;/static/c0d0e9d76a26327da52f67a7c9a55bd4/f058b/final-stage.png&quot;
        srcset=&quot;/static/c0d0e9d76a26327da52f67a7c9a55bd4/c26ae/final-stage.png 158w,
/static/c0d0e9d76a26327da52f67a7c9a55bd4/6bdcf/final-stage.png 315w,
/static/c0d0e9d76a26327da52f67a7c9a55bd4/f058b/final-stage.png 630w,
/static/c0d0e9d76a26327da52f67a7c9a55bd4/40601/final-stage.png 945w,
/static/c0d0e9d76a26327da52f67a7c9a55bd4/78612/final-stage.png 1260w,
/static/c0d0e9d76a26327da52f67a7c9a55bd4/c2d13/final-stage.png 2560w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;最终的结果也是十分戏剧性，两个仓库在同一分钟双双达到 10 万 star，皆大欢喜。一些粉丝在 &lt;a href=&quot;https://hubble.js.org/react-vs-vue&quot;&gt;Hubble&lt;/a&gt; 全程关注了这一精彩的实况，其中包括 Vue 核心团队成员（也是上图的提供者）。&lt;/p&gt;
&lt;p&gt;最后贴一张截图，是昨天 Vue 冲线时一位 Hubble 访客的评论。我深有同感。
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b2ec08c56fa7b8f41d979202e84be450/91f10/comments.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 18.9873417721519%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAw0lEQVQY012PSw6CQBBEuY5xYVRAZGCGr/JJIAICAVQSN97/AmW6RRcuXipTPemq1lSYwLQ9uP4ZXpTCcgIYR/VDtySzP7g//We3zOi/VlUX5EWFup0wP184ZSUvpiBSoWIchA+hIg623RBHN4QtI56RUhnyaKm2Wm9gWBJZUaPpJtTtiG54oB3uTD/N7PXjjGrRa39DnBQcHqcF8rKBDBJuq5nCh24pTnK8EzehtK+S/0UGZ24lZPQ51XSYrSH4TSe/ATvBjha3C/1rAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Comments from community&quot;
        title=&quot;Comments from community&quot;
        src=&quot;/static/b2ec08c56fa7b8f41d979202e84be450/f058b/comments.png&quot;
        srcset=&quot;/static/b2ec08c56fa7b8f41d979202e84be450/c26ae/comments.png 158w,
/static/b2ec08c56fa7b8f41d979202e84be450/6bdcf/comments.png 315w,
/static/b2ec08c56fa7b8f41d979202e84be450/f058b/comments.png 630w,
/static/b2ec08c56fa7b8f41d979202e84be450/40601/comments.png 945w,
/static/b2ec08c56fa7b8f41d979202e84be450/91f10/comments.png 992w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;总之大家都对喜爱的东西抱有热情，开开心心的就好啦。也欢迎大家关注 Hubble 以及我的其他项目。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[预渲染 Vue SPA（踩坑记录）]]></title><description><![CDATA[使用 prerender-spa-plugin 在 Netlify 上对 Vue SPA 项目进行预渲染以优化 SEO。]]></description><link>https://doma.land/blog/prerender-vue-spa/</link><guid isPermaLink="false">https://doma.land/blog/prerender-vue-spa/</guid><pubDate>Thu, 14 Jun 2018 02:09:06 GMT</pubDate><content:encoded>&lt;p&gt;使用 &lt;code&gt;prerender-spa-plugin&lt;/code&gt; 在 Netlify 上对 Vue SPA 项目进行预渲染以优化 SEO。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;最近一直在更新 Hubble 上的 &lt;a href=&quot;https://hubble.js.org/react-vs-vue&quot;&gt;React vs. Vue 100k star race LIVE&lt;/a&gt; 忙的不亦乐乎。
昨天 Vue 与 React 的差距缩小到 500 以内，想着终于到了加上 Facebook 和 Twitter 分享按钮的时候了，可以吸引一些观众。&lt;/p&gt;
&lt;p&gt;按照 Facebook 和 Twitter 各自的文档，配好了相应的链接和 &lt;code&gt;&amp;#x3C;meta&gt;&lt;/code&gt; 标签（使用 &lt;code&gt;vue-meta&lt;/code&gt;），然而实际效果却与预期有些差距。
Hubble 主站的 title 和 description 分别是 &quot;Hubble&quot; 和 &quot;Travel through GitHub Stars&apos; History&quot;，而在 React vs. Vue 的页面，本应已经分别设为 &quot;React vs. Vue · Hubble&quot; 和 &quot;100k stars race between React and Vue is LIVE now.&quot;。
然而按照 Facebook 的文档配好了 title 和 description 后，分享出去的链接里仍然显示原来的文字。
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 527px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0b7b4202a01d7ea030e9678f9479e745/44385/facebook-share.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 32.911392405063296%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAsElEQVQoz62O2QrCMBBF8/9/JIJ/kYpoG9NVuqVp0yUp9ErT6oMoaPHCYWbgMhwSxQkch4JSCo8xKKUwZ5ombAkpRQVRzUgMg34+2wrBn0OMGa1Z1/fQ2kCbV0aYHyD0eALjARj3UUmFRnWom5l23VvIWn3F3CW7/QFBlKAQEmlWIMtLS14Ie5dC2mLdqHW+R9bLJH4YWcOLd4V35RaXcfAgxNlliG+pNX0YfH64GN4BL6sdwnffcYIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Facebook share&quot;
        title=&quot;Facebook share&quot;
        src=&quot;/static/0b7b4202a01d7ea030e9678f9479e745/44385/facebook-share.png&quot;
        srcset=&quot;/static/0b7b4202a01d7ea030e9678f9479e745/c26ae/facebook-share.png 158w,
/static/0b7b4202a01d7ea030e9678f9479e745/6bdcf/facebook-share.png 315w,
/static/0b7b4202a01d7ea030e9678f9479e745/44385/facebook-share.png 527w&quot;
        sizes=&quot;(max-width: 527px) 100vw, 527px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
问题就出在 SEO，解决的方法就是使用 SSR 或者进行预渲染。&lt;/p&gt;
&lt;p&gt;那么首先看看 &lt;a href=&quot;https://ssr.vuejs.org/zh/#%E4%B8%BA%E4%BB%80%E4%B9%88%E4%BD%BF%E7%94%A8%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%AB%AF%E6%B8%B2%E6%9F%93-ssr-%EF%BC%9F&quot;&gt;Vue 的文档怎么说&lt;/a&gt;。
文档里说如果只是为了 SEO，那么可以不必使用 SSR，只需要配合 webpack 使用 &lt;a href=&quot;https://github.com/chrisvfritz/prerender-spa-plugin&quot;&gt;&lt;code&gt;prerender-spa-plugin&lt;/code&gt;&lt;/a&gt; 就可以。
按照插件的文档给出的&lt;a href=&quot;https://github.com/chrisvfritz/prerender-spa-plugin/blob/master/examples/vue2-webpack-router/webpack.config.js&quot;&gt;示例&lt;/a&gt;来配置 webpack&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrerenderSPAPlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;staticDir&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../dist&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;routes&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/react-vs-vue&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;renderer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Renderer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;inject&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;headless&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;renderAfterDocumentEvent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;render-event&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;要注意 Vue-Router 只有在 history 模式下才支持 SSR 和预渲染，因此要做一些调整，并且把原来代码里的一些链接改掉。
修改完后，提交代码。&lt;/p&gt;
&lt;p&gt;接着要在 Netlify 上开启预渲染，具体位置在 &lt;code&gt;Settings-&gt;Build &amp;#x26; deploy&lt;/code&gt;。
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/22117f348d3b39e68faa6e2d2074d1cf/eb390/enable-prerendering-on-netlify.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 29.746835443037973%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA10lEQVQY05WRTUoEMRCF+8YuvIGLuYJXEheDI+qICOKiu8XpxJmedJNUJfkkwR/UEfHBRy0K3qvkNW6aGIzBWItqpCjnXEkpVeI30iFyJqjSOO/ZWMswGOZ5RlURkcpfeg8uFGmMNM/B82AH2r7nsWtp+47uqa/4EGrAJMKsnwQRYvz6miIphh9xMUHKpBAo31AvVUFFqqGT8DYFL+GgYb1waTccX56zuF1xtDpjPW4hw945Usr8R9Xw6sWwWF9wen/DyfWSu922bNiNIyJKjD9L+a2YUsora1fSGGiMJ8cAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Enable prerendering on Netlify&quot;
        title=&quot;Enable prerendering on Netlify&quot;
        src=&quot;/static/22117f348d3b39e68faa6e2d2074d1cf/f058b/enable-prerendering-on-netlify.png&quot;
        srcset=&quot;/static/22117f348d3b39e68faa6e2d2074d1cf/c26ae/enable-prerendering-on-netlify.png 158w,
/static/22117f348d3b39e68faa6e2d2074d1cf/6bdcf/enable-prerendering-on-netlify.png 315w,
/static/22117f348d3b39e68faa6e2d2074d1cf/f058b/enable-prerendering-on-netlify.png 630w,
/static/22117f348d3b39e68faa6e2d2074d1cf/eb390/enable-prerendering-on-netlify.png 935w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;推送分支，让 Netlify 开始构建。&lt;/p&gt;
&lt;p&gt;然后报错。:)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:30:14 AM: &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; build/build.js
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:31:20 AM: Error: Failed to launch chrome&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:31:20 AM: &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;0613/173120.242750:ERROR:nacl_helper_linux.cc&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;310&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; NaCl helper process running without a sandbox&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:31:20 AM: Most likely you need to configure your SUID sandbox correctly
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:31:20 AM: TROUBLESHOOTING: https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:31:20 AM:     at onClose &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;/opt/build/repo/node_modules/puppeteer/lib/Launcher.js:285:14&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:31:20 AM:     at Interface.helper.addEventListener &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;/opt/build/repo/node_modules/puppeteer/lib/Launcher.js:274:50&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:31:20 AM:     at emitNone &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;events.js:111:20&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:31:20 AM:     at Interface.emit &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;events.js:208:7&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:31:20 AM:     at Interface.close &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;readline.js:368:8&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:31:20 AM:     at Socket.onend &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;readline.js:147:10&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:31:20 AM:     at emitNone &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;events.js:111:20&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:31:20 AM:     at Socket.emit &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;events.js:208:7&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:31:20 AM:     at endReadableNT &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_stream_readable.js:1064:12&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:31:20 AM:     at _combinedTickCallback &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;internal/process/next_tick.js:138:11&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:31:20 AM:     at process._tickCallback &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;internal/process/next_tick.js:180:9&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:31:20 AM: &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Prerenderer - PuppeteerRenderer&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Unable to start Puppeteer
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:31:20 AM: &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node:1246&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; UnhandledPromiseRejectionWarning: TypeError: Cannot &lt;span class=&quot;token builtin class-name&quot;&gt;read&lt;/span&gt; property &lt;span class=&quot;token string&quot;&gt;&apos;close&apos;&lt;/span&gt; of null
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:31:20 AM:     at PuppeteerRenderer.destroy &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;/opt/build/repo/node_modules/@prerenderer/renderer-puppeteer/es6/renderer.js:139:21&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:31:20 AM:     at Prerenderer.destroy &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;/opt/build/repo/node_modules/@prerenderer/prerenderer/es6/index.js:87:20&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:31:20 AM:     at PrerendererInstance.initialize.then.then.then.then.then.then.then.then.catch.err &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;/opt/build/repo/node_modules/prerender-spa-plugin/es6/index.js:145:29&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:31:20 AM:     at &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;anonymous&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:31:20 AM:     at process._tickCallback &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;internal/process/next_tick.js:188:7&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;看起来很明显是 &lt;code&gt;puppeteer&lt;/code&gt; 出了什么问题。
跟着错误信息里给出的&lt;a href=&quot;https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md&quot;&gt;链接&lt;/a&gt;，找到相关的文档，里面提到了类似的问题。
解决方法是启动 &lt;code&gt;puppeteer&lt;/code&gt; 时传入 &lt;code&gt;--no-sandbox --disable-setuid-sandbox&lt;/code&gt; 两个 flag。&lt;/p&gt;
&lt;p&gt;那么就需要去查看 &lt;code&gt;prerender-spa-plugin&lt;/code&gt; 的文档了，因为 &lt;code&gt;puppeteer&lt;/code&gt; 是由它启动的。
&lt;a href=&quot;https://github.com/chrisvfritz/prerender-spa-plugin#prerendererrenderer-puppeteer-options&quot;&gt;在 README 中可以看到&lt;/a&gt;，可以向 &lt;code&gt;PuppeteerRenderer&lt;/code&gt; 的构造函数传入任何 &lt;code&gt;puppeteer.launch()&lt;/code&gt; 所接受的选项。据此修改 webpack 配置如下&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;new PrerenderSPAPlugin({
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   staticDir: path.join(__dirname, &apos;../dist&apos;),
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   routes: [ &apos;/react-vs-vue&apos; ],
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   renderer: new Renderer({
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       inject: {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       },
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       headless: false,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       renderAfterDocumentEvent: &apos;render-event&apos;,
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;       args: [&apos;--no-sandbox&apos;, &apos;--disable-setuid-sandbox&apos;],
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   })
&lt;/span&gt;})&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;提交代码并推送，让 Netlify 再次构建。&lt;/p&gt;
&lt;p&gt;并报错。:)&lt;/p&gt;
&lt;p&gt;打开错误日志，发现和上次一样，说明同样的问题并没解决。那么只好在 &lt;code&gt;prerenderer-spa-plugin&lt;/code&gt; 的 issues 里面搜下。
一搜果然还就&lt;a href=&quot;https://github.com/chrisvfritz/prerender-spa-plugin/issues/200&quot;&gt;搜到了&lt;/a&gt;。在评论中一位 collaborator 指出，如果出现这个问题，要去掉 Renderer 选项中的 &lt;code&gt;headless: false&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;new PrerenderSPAPlugin({
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   staticDir: path.join(__dirname, &apos;../dist&apos;),
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   routes: [ &apos;/react-vs-vue&apos; ],
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   renderer: new Renderer({
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       inject: {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       },
&lt;/span&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;       headless: false,
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       renderAfterDocumentEvent: &apos;render-event&apos;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       args: [&apos;--no-sandbox&apos;, &apos;--disable-setuid-sandbox&apos;],
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   })
&lt;/span&gt;})&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;再提交推送试一次。这次成功了，虽然并不懂为什么。:)&lt;/p&gt;
&lt;p&gt;现在再点击 Facebook 分享按钮，分享的链接已经可以爬取正确的 title 和 description 了。
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 514px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/897071f83bb7f6d6053ad6106eab8b05/dea13/facebook-share-prerendered.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 86.0759493670886%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAAsTAAALEwEAmpwYAAACvElEQVQ4y5WTy08TQRzH+9foSS8+oonxYDxqIj6iHkjEixcDEWOIilUSFS74OqAmPjA+oyFBREEDJCLlrQexT0rZ3bbYFli2u9t2Z2dnvmZ3Wyi0HPxtvpmdx+8zv99vZjzxZAqpdAbRaBThcASJZBKEGLCNc/7f8hgGQcEwoKoaZFkBpXQVVg1aMl7xueZBFasGKJ8DBzbz8zDGQIsiliuTMVicw7IsyCsKlmUFmaVl2NnYZloUBjVXZfctxlyg7Zi3OEzG4W69JsY5DOKWxIbZG1SLjljUkT3nYdx1/ks4nqYoXqUpnqUo+mULG71LvY/iNO77+/AoNOC0kr7kjDPO3Ahtm9EZDvkJToUIDvsJvKLplr7KoZzzPcHWrovY/akZW7oa4UuHnXE77SKQI5BjDuxsxMTpEEFbnG4KvDT1Gnt6vTjYfwt7e72YXIxWAv05hrqIicaY6UBvS3ZNqgMbxl9g/5cW1Azewc6eq5jIzK4BWXHRH53hukjRsUDRGqd4kKRFQCXw8vQ7nPnxENd+vsexobsYTUdcIN8QYcOciWbBjbIErIiQA95fH1Az2I7a4Q4cH7qH8fIIS4di1/BEkKAuQnAySNyUN6nhhYmX2NXTjAN9N7Gj50q1GropHwkQ1IZNHA0StIh03XUpB54fe47t3U3Y9/kGtnU3YSxTlrJTQ84gFBjaJBPtkoFWkeBt2gSzLOcyl4szjsfBAdSPdqJp8g3qxzoRkBMOkFoWPKHILGKCCEGUIMZiUJUs9KwKXdWgarl10nS3zecMGDkDBb0AI0egaXlkVR26noen/+s3jPjG8XvGj9GJKQSCYQyP+DAw9B2LyyvOQnklCyWrOW9aVlQoqr5O2aI0GyhIcczNC5gXJAhiHLNzMbdf/BfEBGLzIqR4AqKUREyQkFhIOc42RNV0J+oS8B9Yi+kaLwtNKAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Facebook share with prerendering&quot;
        title=&quot;Facebook share with prerendering&quot;
        src=&quot;/static/897071f83bb7f6d6053ad6106eab8b05/dea13/facebook-share-prerendered.png&quot;
        srcset=&quot;/static/897071f83bb7f6d6053ad6106eab8b05/c26ae/facebook-share-prerendered.png 158w,
/static/897071f83bb7f6d6053ad6106eab8b05/6bdcf/facebook-share-prerendered.png 315w,
/static/897071f83bb7f6d6053ad6106eab8b05/dea13/facebook-share-prerendered.png 514w&quot;
        sizes=&quot;(max-width: 514px) 100vw, 514px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;最近可能再写一篇关于 Hubble 以及 React vs. Vue 本身的博客。&lt;/p&gt;
&lt;p&gt;耶。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[使用 GPG key 签名你的 Git 提交]]></title><description><![CDATA[偶然在 GitHub 上注意到有些 commit 记录上有一个绿色的 "Verified" 标记，感觉酷酷的，就给自己也弄了一个。
过程和遇到的一些小坑做下记录。]]></description><link>https://doma.land/blog/sign-git-commits-with-gpg-key/</link><guid isPermaLink="false">https://doma.land/blog/sign-git-commits-with-gpg-key/</guid><pubDate>Sun, 27 May 2018 14:48:10 GMT</pubDate><content:encoded>&lt;p&gt;偶然在 GitHub 上注意到有些 commit 记录上有一个绿色的 &quot;&lt;strong&gt;Verified&lt;/strong&gt;&quot; 标记，感觉酷酷的，就给自己也弄了一个。
过程和遇到的一些小坑做下记录。
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/496e0b74dec3155eb2d7a071d66f60c5/d7abb/commit-with-verified-badge.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 20.88607594936709%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAqklEQVQY03XPXUsDMRCF4f3/f06wglivtqhJ2mSj0t1kkkle260FPw+cu+HhzHB3/4gxDu8D3nus89iwcIgzIoK2Ru/Qe//Wa46p8jYXkihVG8P29oZx3GHsnp2JPL1YrN0zxVdSymjT31gHqYVchEV0RaV8gvn5AWfsZWGI69EVWfPXui/gMSnhXVhypdTGMI8bDs4Rpsg0RZLki/MD+e/l86vnlqroaeEHlUY24/F21VQAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Commit with &amp;quot;Verified&amp;quot; badge&quot;
        title=&quot;Commit with &amp;quot;Verified&amp;quot; badge&quot;
        src=&quot;/static/496e0b74dec3155eb2d7a071d66f60c5/f058b/commit-with-verified-badge.png&quot;
        srcset=&quot;/static/496e0b74dec3155eb2d7a071d66f60c5/c26ae/commit-with-verified-badge.png 158w,
/static/496e0b74dec3155eb2d7a071d66f60c5/6bdcf/commit-with-verified-badge.png 315w,
/static/496e0b74dec3155eb2d7a071d66f60c5/f058b/commit-with-verified-badge.png 630w,
/static/496e0b74dec3155eb2d7a071d66f60c5/40601/commit-with-verified-badge.png 945w,
/static/496e0b74dec3155eb2d7a071d66f60c5/d7abb/commit-with-verified-badge.png 959w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;首先点开 Verified 徽章，弹出了一个说明。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 434px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b11f1a2ac00cbcddf3878ec8c5ba8efa/ade6e/verified-badge-tooltip.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.16455696202532%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB4UlEQVQoz3WSaW7bMBCFfX+gR2hP0AO0v1t0ddI4m7PUsRVbdmNttkRK3MSvIGUXLooO8DAccObxzXBGrfH8Hz1SO4R2SN3HuFGWNsTKUrc2+u6Q12rLSFo4oj05y0BoYbPbc50umOdbjPNM1y2VMGx3iptEsikVxjmy2lC3mlEolAdFQjmazkbIqCrEQ2LdaaQalLXKIjodEdQG5UFlaxwj13uO6EJRSOoMi2TFMt2QPK95Xq5Zpb9Ilimzp4R5sqLai5jbtCrWKuPQgZBoPqI3nlfnb3h9/ZafD0+MJxMmN1OuplPOJhO+js/5dnbBj8sblumasm55fhFAT7bX1FIz8t5zhNGOd08feD//yO39HZ8vx3y5uuDT5Xdmm4TFJuVxtqDc1XTaIlqF1pree/q+xzn3N2HXqfBYxMu2IMt3bPOKLK+wDoz12DAaHeZokJ3CGDv0eOD4QxjMWkteZBRFhhQ1UjQI0VDXe8qyoKxKtnlBpzShxDoXa05tFOVGyT5ezlcFj0nG7WzLPK24W+Q8JDn30QfSfWztqCq2fGg3tnzKrrWJg62loWo0O6Epaz2cG00twqrYqNAaE2e4ziX4ftjD008JNrxk6QP6f71zhrZTdGr4jICwLqFOW4exjt/pz04Uc8hVaAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;&amp;quot;Verified&amp;quot; badge tooltip&quot;
        title=&quot;&amp;quot;Verified&amp;quot; badge tooltip&quot;
        src=&quot;/static/b11f1a2ac00cbcddf3878ec8c5ba8efa/ade6e/verified-badge-tooltip.png&quot;
        srcset=&quot;/static/b11f1a2ac00cbcddf3878ec8c5ba8efa/c26ae/verified-badge-tooltip.png 158w,
/static/b11f1a2ac00cbcddf3878ec8c5ba8efa/6bdcf/verified-badge-tooltip.png 315w,
/static/b11f1a2ac00cbcddf3878ec8c5ba8efa/ade6e/verified-badge-tooltip.png 434w&quot;
        sizes=&quot;(max-width: 434px) 100vw, 434px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;这里说这个提交使用 GPG key 进行了签名，下面给出了文档链接，就跟着文档一步步做吧。
这里把&lt;a href=&quot;https://help.github.com/articles/signing-commits-with-gpg/&quot;&gt;文档链接&lt;/a&gt;贴出来，
或者可以看&lt;a href=&quot;https://gist.github.com/LauLaman/51f054e9656a707d6df441e9fe1e14bc&quot;&gt;别人总结的精简版&lt;/a&gt;，
另外记下自己的流程。&lt;/p&gt;
&lt;h3&gt;步骤&lt;/h3&gt;
&lt;p&gt;1.因为我还没有在 GitHub 上保存过 GPG key，所以第一步要在自己电脑上生成一个 GPG key。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ gpg --full-generate-key&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;如果你的 Mac 上没有自带 gpg 命令，你可能需要先 &lt;code&gt;brew install gnupg pinentry-mac&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;在 Ubuntu 16.04, gpg 1.4.20 中是&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ gpg --gen-key&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;参考&lt;a href=&quot;https://askubuntu.com/questions/1019793/how-to-solve-gpg-invalid-option-full-generate-key&quot;&gt;这个帖子&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;然后会有一些交互式的选项，选择的值依次是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RSA and RSA (default)&lt;/li&gt;
&lt;li&gt;4096&lt;/li&gt;
&lt;li&gt;0 (default)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;选好后，下一步输入自己的身份信息，主要是 GitHub ID 和邮箱。有可能还需要输入一个备注，我备注了个 &quot;GitHub GPG key&quot;。&lt;/p&gt;
&lt;p&gt;回车后会要求设置一个密码来保护这个 GPG key。
设置密码后这个 GPG key 就生成好了。&lt;/p&gt;
&lt;p&gt;2.将完整的 GPG key 保存到 GitHub。&lt;/p&gt;
&lt;p&gt;这需要在电脑上打印出刚刚生成的 GPG key 完整信息。&lt;/p&gt;
&lt;p&gt;首先显示自己所有的 GPG key，&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ gpg --list-secret-keys --keyid-format LONG
/Users/hubot/.gnupg/secring.gpg
------------------------------------
sec   4096R/3AA5C34371567BD2 &lt;span class=&quot;token number&quot;&gt;2016&lt;/span&gt;-03-10 &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;expires: &lt;span class=&quot;token number&quot;&gt;2017&lt;/span&gt;-03-10&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
uid                          Hubot
ssb   4096R/42B317FD4BA89E7A &lt;span class=&quot;token number&quot;&gt;2016&lt;/span&gt;-03-10&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;其中 &lt;code&gt;sec&lt;/code&gt; 部分的 &lt;strong&gt;3AA5C34371567BD2&lt;/strong&gt; 就是我们刚刚生成的 GPG key 的 ID，我们用它来显示完整的 GPG key 内容，&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ gpg &lt;span class=&quot;token parameter variable&quot;&gt;--armor&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--export&lt;/span&gt; 3AA5C34371567BD2
&lt;span class=&quot;token comment&quot;&gt;# 下面会显示完整的 GPG key&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;将包括开头的 &lt;code&gt;-----BEGIN PGP PUBLIC KEY BLOCK-----&lt;/code&gt; 和末尾的 &lt;code&gt;-----END PGP PUBLIC KEY BLOCK-----&lt;/code&gt; 在内的完整 GPG key 复制下来。&lt;/p&gt;
&lt;p&gt;访问 &lt;code&gt;GitHub -&gt; Settings -&gt; SSH and GPG keys -&gt; New GPG key&lt;/code&gt; (&lt;a href=&quot;https://github.com/settings/gpg/new&quot;&gt;快速链接&lt;/a&gt;)，将复制好的 GPG key 粘贴到输入框并保存。&lt;/p&gt;
&lt;p&gt;3.为本地的 Git 启用 GPG key 签名&lt;/p&gt;
&lt;p&gt;首先设置自己的 GPG key，&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ &lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; config &lt;span class=&quot;token parameter variable&quot;&gt;--global&lt;/span&gt; user.signingkey 3AA5C34371567BD2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;在提交时使用 &lt;code&gt;-S&lt;/code&gt; flag 来告诉 Git 对本次 commit 进行签名，&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ &lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; commit &lt;span class=&quot;token parameter variable&quot;&gt;-S&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;awereghtyjui&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;此时会弹出输入密码的界面，输入密码正确即签名成功。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如果在你的 Mac 上提示&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;error: gpg failed to sign the data
fatal: failed to &lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt; commit object&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;那么你需要将下面一行加入你的命令行配置文件，比如 &lt;code&gt;~/.zshrc&lt;/code&gt; 或者 &lt;code&gt;~/.bash_profile&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;GPG_TTY&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tty&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;签名成功后推送到 GitHub，就会有 Verified 标记啦。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cebb0f3881db495bc3a17f931aeaa06c/72aae/with-and-without-gpg-sign.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 31.0126582278481%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA8ElEQVQY032PyU7EQAwF8/8/yAExkGUOk6WzTW9udxdKBg4wCEslS89S6bna7w6zbJhlZdstWiAX0Fz+RVSJKSGaCZLPnXKhenuveXl9p772dLeJafPMNmClnLhfnFmC1QvGelanzHdh94qLmaprGi6XD279QD+MWOcJEjmmnG3LE0cek+BiwEVls4KPCUlKde06uu6KMTPjONH3A5MxzPOCSPoSlx8c42Ngczs2KGY/5Il4CNumpq4bhnE6pdu2c7cWESHn/CT75ripKj4qLjxkSZWqa1uapmUYJpZlJYSIC/7PZk9NC7jweDlERVLmE2aO0rMIgAA9AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;With vs without GPG sign&quot;
        title=&quot;With vs without GPG sign&quot;
        src=&quot;/static/cebb0f3881db495bc3a17f931aeaa06c/f058b/with-and-without-gpg-sign.png&quot;
        srcset=&quot;/static/cebb0f3881db495bc3a17f931aeaa06c/c26ae/with-and-without-gpg-sign.png 158w,
/static/cebb0f3881db495bc3a17f931aeaa06c/6bdcf/with-and-without-gpg-sign.png 315w,
/static/cebb0f3881db495bc3a17f931aeaa06c/f058b/with-and-without-gpg-sign.png 630w,
/static/cebb0f3881db495bc3a17f931aeaa06c/40601/with-and-without-gpg-sign.png 945w,
/static/cebb0f3881db495bc3a17f931aeaa06c/72aae/with-and-without-gpg-sign.png 964w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;4.(可选)对每次提交都进行签名&lt;/p&gt;
&lt;p&gt;执行如下命令，以后提交时不用加 &lt;code&gt;-S&lt;/code&gt; 标记，Git 也会对提交进行签名了。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ &lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; config &lt;span class=&quot;token parameter variable&quot;&gt;--global&lt;/span&gt; commit.gpgsign &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;耶。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[npm + Travis CI踩坑记录]]></title><description><![CDATA[自从 Vue-APlayer@1.1 开始，因为比较经常更新 npm 上的版本，于是配了 Travis 的自动构建和 npm 发布。
本来一切都很顺畅，直到最近给 RSUITE 配 CI 时出了各种各样的问题，才发现原来有这么多坑。于是记录下。 Node 版本选择 Node…]]></description><link>https://doma.land/blog/npm-Travis-CI踩坑记录/</link><guid isPermaLink="false">https://doma.land/blog/npm-Travis-CI踩坑记录/</guid><pubDate>Fri, 11 May 2018 15:27:28 GMT</pubDate><content:encoded>&lt;p&gt;自从 &lt;code&gt;Vue-APlayer@1.1&lt;/code&gt; 开始，因为比较经常更新 npm 上的版本，于是配了 Travis 的自动构建和 npm 发布。
本来一切都很顺畅，直到最近给 &lt;code&gt;RSUITE&lt;/code&gt; 配 CI 时出了各种各样的问题，才发现原来有这么多坑。于是记录下。&lt;/p&gt;
&lt;h2&gt;Node 版本选择&lt;/h2&gt;
&lt;p&gt;Node 版本并不能随便乱选，一般最好不要选不是 lts 的版本，比如 ７。&lt;/p&gt;
&lt;p&gt;RSUITE 最初用的就是 7，结果在第一次自动发布之后有用户提出 issue 说报了奇怪的错，经过检查发现 npm 上的包只有源码，没有构建后的文件。
因为我没有写 &lt;code&gt;before_deploy&lt;/code&gt; 而是在 prepublishOnly 钩子里跑的构建命令 npm run build，所以有可能是钩子没有执行。检查了 job logs 发现 node 版本是 7 而 npm 的版本是 3.10，而 prepublishOnly 是 npm@4 开始才有的钩子，所以没有调。&lt;/p&gt;
&lt;p&gt;为什么 Node 6 自带 npm@5，而 Node 7 竟然只有 npm@3 呢？这个在最新的 npm@6 中有所回答。如果你在非 lts 版本下执行 npm install -g npm，npm@6 会提醒你 npm 并不支持非 lts 版本。也就是说非 lts 版本的 Node 并不同捆最新的 npm。&lt;/p&gt;
&lt;p&gt;把 Node 版本改成 8 之后，再看 job logs，npm 版本就是 5 了，构建和发布也正常了。&lt;/p&gt;</content:encoded></item></channel></rss>