升华工作室
Friend Timeline

朋友圈

见字如面,相逢在字里行间

聚合了友情链接中朋友们的最新文章与动态,感受网络邻居们的思考与生活。

G
grtsinry43

二十一岁,然后......

<blockquote><p>该内容由 RSS 渲染生成,最佳阅读体验请前往:<a href="https://blog.grtsinry43.com/moments/2026/04/03/21-years-old">https://blog.grtsinry43.com/moments/2026/04/03/21-years-old</a></p></blockquote><p>去年写了篇《致二十岁的晨光与希望》。emm 很漂亮的标题对吧,晨光,希望,又比喻又排比,一听就是那种对未来充满热忱的年轻人会写出来的东西,我还记得当时写了好久,穷尽词藻。</p> <p>::: link-card href=&quot;/moments/2025/04/03/hello-20-years-old/&quot; title=&quot;致二十岁的晨光与希望&quot; desc=&quot;此刻,且让年轻的热望继续野蛮生长。因为每个不曾起舞的昨日,都在为明天的腾跃积蓄力量;每个尚未拆封的黎明,都藏着命运馈赠的礼物。&quot; newtab=&quot;true&quot;</p> <p>:::</p> <p>今年本来也想起一个这样的标题,想了半天,发现脑子里只剩下&quot;啊?&quot;</p> <p>坐在电脑前想了十分钟,看了看之前的文章,又看了看 TODO List,放弃了。</p> <p>主要是今年的状态跟去年真的不一样。去年那个时候算是初生牛犊不怕虎,觉得二十岁了,不一样了,要写点什么纪念一下。觉得不确定是浪漫的,觉得未来虽然看不清但是闪闪发光的,觉得自己只要一直跑一直写就能到达什么地方。</p> <p>然后这一年就……嗯,经历了亿些事情。开年重写了博客,发现 AI 写代码完全爆杀自己,春招开始投简历。年终总结写了一大篇,你们有兴趣自己翻,我就不在这里复读了。</p> <p>日历又到了新的日期,手机弹出来一个提醒,哦,又大了一岁。</p> <p>然后呢?</p> <p>然后我发现我说不出&quot;然后&quot;。</p> <p>去年写的那些话,什么&quot;不确定性是未来惊喜的伏笔&quot;,什么&quot;有些答案留给二十五岁去拆封&quot;,现在读起来,怎么说呢,不是觉得矫情,是觉得那个人真的好勇敢。他还相信有个叫&quot;答案&quot;的东西在前面等他哦(。</p> <p>二十一岁的我想跟他说:兄弟,你想多了,先活过这一年再说。</p> <p>按理说应该有点什么感慨吧。但说实话,从过完年到现在,每天忙得跟陀螺似的,根本没有时间去&quot;感慨&quot;。感慨是需要闲下来才能做的事情,而我最近的闲下来大概就是刷机的那个下午。<del>小米 17 的 bootloader 比我的人生好解锁多了。</del></p> <p>那就趁今天生日,勉强闲一会儿,写点东西吧。</p> <h2>近况</h2> <p>找实习。投简历,等回复,挂了,继续投。面试的风格今年变了,不怎么考手搓算法了,全是拷问底层八股,蚂蚁还搞了 AI Coding 笔试,挺魔幻的。</p> <p>博客 v2 总算上线了,Go 后端,新设计系统,问题一大堆但是能用。上个月写了篇《我患上了 token 的瘾》,写完瘾没戒掉,反而更理直气壮地找朋友借 Claude 了。</p> <p>身体恢复得差不多了,正常走路没问题。<del>下楼梯会多看两眼,原来 PTSD 是这么来的。</del></p> <p>每天日程排得爆炸,课、项目、面试准备、各种杂事。忙完躺床上回想今天干了啥,脑子一片空白。</p> <h2>想说的</h2> <p>额</p> <p>因为我不太确定我想说什么。<del>去年搞什么小作文只需要读起来舒服就好了,今年考虑的就多了。</del></p> <p>最近的状态就是,事情很多,非常多,多到你本不敢停下来想。春招的事,项目的事,课程的事,技术的事,未来的事,全堆在那里。知道每一件都该做,每一件都不能拖,每一件都跟未来有关系。</p> <p>然后很想逃避,算是很本能的,看到一堆事情摆在面前,第一反应是能不能暂时先不面对。想再刷会儿手机,想再折腾一下没用的东西,想再赖一会儿。</p> <p>...但是话说回来这些事情它们就在那儿等着。而且以后只会更多不会更少。</p> <p>去年我觉得长大是我自己选的。<del>我要学技术,我要进大厂,我要变强,我要证明自己。</del></p> <p>今年发现,不是的。长大不问你准不准备好。问题来了,面试来了,ddl 来了,挫折来了,该学的东西来了,AI 更新了你不跟就掉队了。你来不来?你不来它也来。你没准备好?无所谓,谁会在乎你。</p> <p><del>大概这就是从少年漫画进入职场剧的感觉吧。</del></p> <p>只是方向我是有的——学习,进大厂,以后做点能帮到别人的东西,文艺(?一点叫什么“点亮别人”。这个想法从很早就有了,也算是让我撑下来。</p> <p>但方向是远处的事。远处的东西想想就好,今天的事情还是得一件一件做。而今天的事情就是……太多了。多到你来不及想为什么要做,只能先做起来再说。</p> <p>可能这就是 21 岁吧。不再有余裕去想&quot;我是谁&quot;&quot;我在干嘛&quot;这种问题了,光是应付&quot;我今天要干嘛&quot;就已经够呛了。</p> <p>要干嘛?</p> <p>我暂时不知道。</p> <h2>碎碎念</h2> <p>去年那篇里我说&quot;有些答案留给二十五岁、三十岁的清晨去拆封&quot;。</p> <p>现在觉得,二十五岁那时候大概率也在被推着走,也在忙得不可开交,哪有空拆封什么答案。<del>人类的本质就是被 ddl 追。</del></p> <p>不过也不全是坏事,至少麻了。</p> <p>也许这就是所谓的成长?不是变得不焦虑了,是焦虑的保质期变短了。就像学会了跟 bug 共处一样——你知道它在那儿,你知道迟早得修,但你不会因为它的存在否定整个项目。</p> <p>去年结尾是&quot;你好,二十岁!&quot;,感叹号,冲劲十足,真是有干劲啊这个人(。</p> <p>今年就:</p> <p>嗯,二十一岁,然后......</p> <p>在搞一个 7×24 的 AI agent 工作流,想看看全自动能跑到什么程度(距离失业还有多久)。Rust uniffi 也想折腾一下,感觉这个跨平台很优雅。</p> <p>游戏的话最近在玩《奥日与精灵意志》,音乐,操作,都非常非常舒服,尤其是音乐真的太爽了。《空洞骑士:丝之歌》也在打,太难了,手残,但是<del>战斗,爽</del>。</p> <p>还有一件事,想好好规划一下时间。这个我大概每个月都会想一次,然后每个月都不了了之。但还是想试试。</p> <p>该来的来呗。反正先活过今天。</p> <p><del>然后活过明天</del></p> <p><del>以此类推</del></p> <p>感谢你听我碎碎念,写点莫名其妙的东西。</p> <!-- raw HTML omitted --> <!-- raw HTML omitted --> <!-- raw HTML omitted -->

G
grtsinry43

我患上了 token 的瘾

<blockquote><p>该内容由 RSS 渲染生成,最佳阅读体验请前往:<a href="https://blog.grtsinry43.com/posts/token-addiction">https://blog.grtsinry43.com/posts/token-addiction</a></p></blockquote><p>键盘就在手边,滴答清单里的待办事项还在默默倒数。</p> <p>我想写点什么。在这个连底层框架都能在一夜之间被大模型重构的时代,我总想写写感受,但又不知从何说起。</p> <p>于是只能从我自己的视角,谈谈这不到一年的时间里,我作为一个正在长株潭这片土地上读书、满怀憧憬准备在技术圈大干一场的大三学生,所经历的狂喜、震撼,以及——深深的恐惧。</p> <p>巨大的信息轰炸和技术迭代,让我产生了一种强烈的失语感。我习惯了在 JetBrains 的 IDE 里口若悬河的讲着自己的看法,习惯了在 Linux 终端搞点好玩的东西,习惯了学习从后端架构到前端 UI 的每一个只是,但现在,面对眼前这个以天为单位进化的庞然大物,我突然不知道该从何说起。</p> <p>那就从一切开始发生微妙变化的 25 年上半年说起吧。那时候,我依然觉得是我们的时代。</p> <h2>第一阶段:实验室里的初探与“手工作坊”的野蛮生长</h2> <p>时间拨回 2025 年的上半年。那时候我还在学校,日常是上课、应付期末和做实验。一切都还处于一种“掌控感十足”的状态——写代码主要还是靠自己一行行敲,最多开一下 Copilot 的补全。</p> <p>直到 Gemini 2.5 Pro 的出现。当时正好赶上 Google 的活动,我顺手试用了一个月。那是我第一次切实感受到多模态带来的直观冲击:做实验遇到那种玄学问题,我干脆直接举起手机拍下屏幕扔给它,它竟然能精准判断软件操作,指出问题所在。</p> <p>这感觉真的太上头了,在那个还没有学生认证白嫖的时候,一个月试用期结束,我甚至不过瘾地自己用 20 刀学费。后来,我又发现了 AI Studio 这个宝藏,有着极其丰富的免费额度和超长上下文支持。</p> <p>我的野心开始膨胀。借着 AI Studio 的超长上下文,我又开始问这问那:我弄懂了 Monorepo 架构,开始着手搞自己的 Amore UI 组件库;后来为了写个文档站,我甚至跑去闲鱼收了个学生认证,用那种好像叫什么“引导学习模式”,硬生生把每天的 Pro 额度全部榨干。</p> <p>但这个时候的 AI,在我眼里依然只是个“高级辅助”。虽然它帮我读了大量开源代码,但写代码的主力依然是我自己。那是一个充满折腾乐趣的时候,他确实帮助我学了好多东西。</p> <h2>第二阶段:大厂的现实毒打与开发者的傲骨</h2> <p>带着在学校里和自己积累的项目,2025 年 6 月底,我拖着行李箱走进了 🐧 的大门,开始实习。</p> <p>当时正值行业里 DeepSeek R1 私有化部署的炒作狂欢,满世界都在鼓吹 AI 马上就要接管一切。但是这个时候我还依然对 AI 提不起兴趣。</p> <p>当我真正面对那种错综复杂、体量庞大、充斥着历史包袱的企业级代码时,司内的模型显得极其笨拙。</p> <p>不仅是工作,平时的开发,当时的主力模型还是 Claude 4 (Copilot 教育认证),大项目它根本理不清拓扑关系,小问题更是层出不穷,经常给你瞎编一些根本不存在的 API。</p> <p>我的工作流被迫退回了古典的“手工作坊”模式:遇到卡壳的地方,用 GitHub Copilot 问一下,把代码片段复制出来,然后小心翼翼地缝缝补补。那时候我最常干的事,就是对着屏幕里的 AI 骂:“给我完整的代码!别动我原本的逻辑!”</p> <p>为了突破工具的限制,我狠下心充了 20 刀一个月的 Cursor,第一次被 Agent 模式震撼;又偶然发现了能白嫖的 Anyrouter,开始摸索 CLI 模式(当时还是 cc v1.x)。</p> <p>然而,当时为了省钱,我把每天只有 5 次免费额度的 Claude 网页版当“架构师”,让 CLI 工具当“打字员”去落地。</p> <p>无论是工作写库,写 Runtime,还是平时自己项目写组件,处理跨平台问题,我坚持核心的掌控权必须死死攥在自己手里。经历了上半年的惊艳后,实习期的现实让我对 AI 彻底“祛魅”——它充其量只是个带点智能的搜索引擎,真正的工业级工程,还得靠人堆出来。</p> <h2>第三阶段:荒野求生、震撼与防线的失守</h2> <p>真正的认知颠覆,发生在 2025 年的下半年。</p> <p>就在我用 AI 抽卡写完文档站的第二天,我意外受伤了。</p> <p>生活半径被迫缩小到了床和书桌之间。</p> <p>后来我依然坚持着上班。那段时间(11月左右),业余和折腾的时间我用 cc 写了好多项目:用 Kotlin Multiplatform 写跨平台的 RSS 阅读器 Pureflow,研究安卓,写服务器监控,写日志系统。</p> <p>结果后来 Anyrouter 死了,我才发现那个白嫖的模型劣质得像个假货。</p> <p>因为有了 CLI 模式,我开始摸到了 Vibe Coding 的雏形。但我吃过 AI 乱改代码的亏,这算是一种“如履薄冰的 Vibe”,一直在仔细 Review 它的每一行逻辑。</p> <p>直到我遇到了 Codex 5.2,以及年底发布的 Gemini 3 Pro。</p> <p>11 12 月同时我买了codex(当时听说好用),我发现了很多别人的文章,如何纯 vibe ,然后效果特别好:</p> <p>当时的世界唯快不破,但 Codex 给出了截然不同的答案。把一个复杂的后端任务丢给它,它慢条斯理地跑上半个小时,但这半小时你完全不需要干预。最后直接 Production-Ready 。</p> <p><del>一个任务虽然跑了半小时,但是写完效果真的是生产可用的,当时我真的惊了</del></p> <p>如果说后端的失守还算温水煮青蛙,只是潜移默化,那前端的沦陷则是降维打击。</p> <p>新模型发了,Gemini 3 Pro 在前端设计上展现出了超过好多人的能力。哪怕当时 Google 的 Antigravity 天天报错、难用到反人类,我也硬是耐着性子用它生磕出了大量惊艳的前端 UI。后来换到 Gemini CLI,终于承认了一个事实:<strong>不管是设计还是写代码,前端也真的写不过 AI 了。</strong></p> <h2>第四阶段:赛博圆桌会议与主理人的克制</h2> <p>带着满脑子的震撼,今年 1 月中旬放寒假回到老家,窗外是灰蒙蒙的天和白雪,我开始重写 Grtblog v2。</p> <p>写这个博客的时候我和 codex 和 Claude(每天5条免费)帮我设计了好多架构,包括 go 的 Clean Arch 还有 DDD,前端和 admin 的目录结构等等</p> <p>那段时间,我在 GitHub 上极其熟练地让 AI 互相 Code Review(<code>@codex review</code>, <code>@copilot review</code>)。UI 方面,我在 AI Studio 里高频抽卡找灵感,然后自己回到 Figma 里研究哈几天,抽离出了你看到的这套带着标志性绿色和小圆角的设计系统。</p> <p>::: callout type=&quot;info&quot; title=&quot;底线&quot; 即使当时的 AI 已经能一次写完一个完整功能,但在这个我最在意的“亲儿子”项目上,我依然保持着极大的克制。因为安全问题和 AI 天生的“反骨”,我小心翼翼地把 AI 的实际代码贡献率死死压在 35%-40%。 :::</p> <h2>第五阶段:算力自由、多线操作与深渊的凝视</h2> <p>如果说在家重构博客还保留着一丝人类的体面,那么 2 月底回到学校后的那几天,则彻底击穿了我的认知。</p> <p>剧情在这里发生了一次不可思议的转折。Anthropic 发了 Claude Opus 4.6,OpenAI 掏出了 Codex 5.3,我立刻买了这两个订阅。两家巨头互换了剧本:曾经快如闪电的 Claude 降速提质,成了一个疯狂吞噬 Token 的大模型;而 Codex 反而提速降本,限额翻倍。</p> <p>那会儿我 20 刀的 Claude Pro 账号,5 小时限额只够跑两个大 Session。但就是这两个 Session,Opus 4.6 做到了一遍过,唉,这个时候就开始害怕了。</p> <p>直到 2.28 我回到学校,我有一个朋友他恰好财力雄厚,有几个 Claude Max 账号,因为开学了他用不完了,于是就把其中一个 Claude Max 20x 账号借给我了用,真是*了,太逆天了...</p> <p>那是一个极其疯狂的下午。我在宿舍的电脑前,同时跑着 4 个顶级 Claude Opus 4.6、1 个 Codex、1 个 Gemini CLI。我彻底进入了终极的“纯 Vibe”状态。无需构思语法,甚至无需自己管理项目,我的工作变成了纯粹的决策和调度——哪里亮了点哪里。</p> <p>但随之而来的是:<strong>我发现用 AI 竟然比自己写还要累。</strong></p> <p>开发历史上的瓶颈,第一次从“敲代码的手速”变成了“人类审查逻辑的脑力带宽”。面对 6 个大模型源源不断吐出的高质量代码,我根本 Review 不过来。我不是在写项目,我是在被算力的洪流推着、甚至“逼”着往前狂奔。</p> <p>几天后,博客 V2 摧枯拉朽般地写完并发布了。但在那个跑满算力的下午,我极其兴奋,但也极其害怕。那种害怕,是你作为一个个体,直面指数级进化时的渺小感。</p> <h2>终章:49年入国军与温暖的 Token</h2> <p>时间拨回现在,三月底的春招季。</p> <p>我带着刚刚重构完博客的余温,准备寻找暑期实习。迎面撞上的却是触目惊心的裁员潮。外包被砍,团队按比例缩减。我们这群学生和同行们私下打趣,却笑得比哭还难看:</p> <blockquote> <p><strong>“冰冷的前端同事,终于还是变成了温暖的 Token。”</strong></p> </blockquote> <p>更可怕的反噬已经在我身上显现。我察觉到自己手写代码的基本功在下滑。那曾经引以为傲的肌肉记忆,正在被轻易获得的正确答案所腐蚀。</p> <p>面试的规则也彻底翻篇了。我刷了很久的算法,结果今年面试官根本不考手搓算法,全是疯狂拷问 19、20 年极其底层的“老八股”,蚂蚁甚至直接搞出了 AI Coding 笔试。</p> <p>我问了一圈朋友和前同事:很多公司的团队已经全面拥抱 AI,几乎不怎么手写代码了。既然大模型能写出完美的逻辑,人类的价值就被迫转移到了“审计”。</p> <p>就在这段时间,Claude Opus 4.6 的 1M 超大上下文推广开了。我们学校比较复杂的前后端、客户端和 Admin 项目,上下文可以一股脑塞进去,几个 Sub-agent 协同 5分钟就重构了 admin。还有曾经需要我们翻遍文档、掉光头发研究的问题,它不仅瞬间秒杀,效果还远超人类。</p> <p>我看着屏幕,感到了一种深刻的焦虑和无力。这就像是你苦读了三年,好不容易练就了一身全栈本领,准备在行业里大展拳脚,却发现这个行业的运作方式已经被连根拔起。这种“49年入国军”的战栗感,在此刻达到了顶峰。</p> <p>我似乎患上了 Token 的瘾。明明知道自己在失去手写的能力,明明心里充满了对未来的深渊般的恐惧,但面对这种降维打击的效率,我已经形成了致命的路径依赖。</p> <p>面对屏幕上那些汹涌而来的 Token,我没有答案,只有战栗。</p>

G
grtsinry43

Xiaomi 17 标准版刷机折腾记:解锁、官改ROM与必备模块

<blockquote><p>该内容由 RSS 渲染生成,最佳阅读体验请前往:<a href="https://blog.grtsinry43.com/posts/xiaomi-17-bootloader-unlock-custom-rom">https://blog.grtsinry43.com/posts/xiaomi-17-bootloader-unlock-custom-rom</a></p></blockquote><p>纠结了很久,还是入手了 Xiaomi 17 标准版。</p> <p>买这个主要起源于最新小米设备上爆出的解锁漏洞,使得 8e5 机型重新解锁 bootloader 成为可能。</p> <p>这篇文章主要讲讲我的折腾经历,但是隐去了解锁的流程,大家随便搜索论坛,酷安上,还有搞机 QQ 群都能拿到,等到解锁之后,就可以正式开始折腾了。</p> <p><img src="/uploads/pictures/2026-03-12-07:43:06-95.jpg" alt="一下子回到了上个时代哈哈"></p> <h2>官改 ROM</h2> <p>我选择的是酷安上 <a href="https://www.coolapk.com/u/710841">白羊唐黎明</a> 的官改ROM,版本 3.0.301.0。</p> <p>需要搭配底包 3.0.44.0 刷入,可以去</p> <p><a href="https://miuirom.org/">https://miuirom.org/</a></p> <p><a href="https://xiaomirom.com/">https://xiaomirom.com/</a></p> <p>这两个平台找一下,要的话这里也有一个<a href="https://bkt-sgp-miui-ota-update-alisgp.oss-ap-southeast-1.aliyuncs.com/OS3.0.44.0.WPCCNXM/pudding_images_OS3.0.44.0.WPCCNXM_20260131.0000.00_16.0_cn_3c11e63b6e.tgz">直链</a>,然后下一个 MiFlash,emm可以在</p> <p><a href="https://xiaomiflashtool.com/">https://xiaomiflashtool.com/</a></p> <p><img src="/uploads/pictures/2026-03-12-07:55:09-c3.png" alt="image.png"></p> <p>把下载好的官方线刷包解压到任意文件夹,手机音量下+电源键进入 FASTBOOT ,打开 MiFlash,点击“Driver”安装好对应的驱动之后点击“刷新设备”。</p> <p><img src="/uploads/pictures/2026-03-14-04:30:25-1c.jpeg" alt="就是这个页面"></p> <p>::: callout type=&quot;info&quot; title=&quot;注意&quot; 这里如果要命令的话是 <code>adb reboot bootloader</code> :::</p> <p>::: callout type=&quot;warning&quot; title=&quot;Emm&quot; 打开 MiFlash 第一步确保右下角选择全部删除而不是删除并回锁,要不就白解锁了</p> <p>:::</p> <p><img src="/uploads/pictures/2026-03-12-07:59:31-2e.png" alt="image.png"></p> <p><img src="/uploads/pictures/2026-03-12-07:57:21-6b.png" alt="image.png"></p> <p><img src="/uploads/pictures/2026-03-12-07:57:37-c1.png" alt="image.png"></p> <p>选择刚刚解压到的文件夹,之后点击刷机,耐心等待即可。</p> <p>开机之后尽量 oobe 该跳过的跳过,确认能正常进入桌面之后,没问题,然后重新手机音量下+电源键进入 FASTBOOT ,连接电脑,解压好官改包。</p> <p><img src="/uploads/pictures/2026-03-12-08:01:44-f8.png" alt="image.png"></p> <p>先装一下驱动,然后双击打开刷机脚本即可。</p> <p><img src="/uploads/pictures/2026-03-12-08:02:17-82.png" alt="d1709e212429b1bfbbc0c89ea5dbd9f9.png"></p> <p>不出意外的话,等待进度条跑完,手机重启,官改就刷好了。</p> <h2>Play 完整性与 bl 解锁状态隐藏</h2> <p>进入桌面的第一件事,就是找到刚刚官改 zip 里面的 ksu 管理器安装包装好,这样就可以准备刷模块了。</p> <p>我们只需要刷入这几个模块:</p> <p><img src="/uploads/pictures/2026-03-12-08:05:16-01.jpg" alt="e8e1bc40b9be2cd6287793044bd92a66_720.jpg"></p> <p>顺序是:</p> <p><a href="https://github.com/Dr-TSNG/ZygiskNext">https://github.com/Dr-TSNG/ZygiskNext</a></p> <p><a href="https://github.com/5ec1cff/TrickyStore">https://github.com/5ec1cff/TrickyStore</a></p> <p><a href="https://github.com/MeowDump/Integrity-Box">https://github.com/MeowDump/Integrity-Box</a></p> <p>刷完重启就 OK 了</p> <h2>必备软件和模块</h2> <p>我自己用的一些工具</p> <p>Scene:https://www.omarea.com/#/</p> <p>爱玩机工具箱:https://www.aiwanjitool.com/</p> <p>一些模块:</p> <p>Reqable 安装</p> <p>自动救砖</p> <p>::: gallery height=&quot;400px&quot; caption=&quot;KSU&quot; <img src="/uploads/pictures/2026-03-12-08:11:24-7c.jpg" alt="cf943b262476d5d6851f8c59cadbcf05.jpg"> <img src="/uploads/pictures/2026-03-12-08:12:41-34.jpeg" alt="4039249504fe07b87b03d59d3016d96b.jpeg"> :::</p> <p>就到这里,一时兴起写的一篇文章,就当是重新经历刷机时代了。😋</p>

G
grtsinry43

在焦虑与代码中缓慢前行

<blockquote><p>该内容由 RSS 渲染生成,最佳阅读体验请前往:<a href="https://blog.grtsinry43.com/moments/2026/03/03/2026-notes-anxiety-and-code">https://blog.grtsinry43.com/moments/2026/03/03/2026-notes-anxiety-and-code</a></p></blockquote><h2>一月:期末、眼药水、和走得很慢的路</h2> <p>书接上文。</p> <p>::: link-card href=&quot;/posts/2025-summary/&quot; title=&quot;2025 年终总结——从晨光到雾散,化经历为成长&quot; desc=&quot;在爱中重新振作,于是我们真的曾将彼此照亮&quot; newtab=&quot;true&quot;</p> <p>:::</p> <p>2025 年终总结的最后一句是&quot;我们 2026 见&quot;。写下那句话的时候,其实心里还带着点仪式感的勇气,觉得新的一年应该会不一样吧。</p> <p>然后 2026 就这么来了。没有烟花,没有倒计时的激动。跨年夜是和家里人一起过的,大学以来第一次。说来也不全是我主动——是他一直缠着我,大概也是放心不下。</p> <p><img src="/uploads/pictures/2026-03-03-11:18:15-1c.png" alt="image.png"></p> <p>元旦一过,期末就铺天盖地地来了。</p> <p>前几天复习,中途也不乏一些算是小的有趣的东西,很无聊,但笑了好久。</p> <p>考试周嘛,就是靠这种莫名其妙的小事活着的。可是笑完之后该焦虑还是焦虑,期末一天比一天近,时间永远不够用。</p> <p>::: gallery height=&quot;400px&quot; caption=&quot;年初&quot; <img src="/uploads/pictures/2026-03-03-11:18:38-2e.png" alt="image.png"> <img src="/uploads/pictures/2026-03-03-11:19:03-32.png" alt="image.png"> :::</p> <p>那段日子其实很重复。去办公室复习,和朋友有一搭没一搭地聊天,下午靠一杯咖啡续命。长沙的冬天不冷不热却最折磨人,室友空调开得猛一点,鼻炎就犯了,眼睛天天又干又涩,靠眼药水撑着,滴完了接着看书,看完了接着滴。累,真的特别累,不是那种运动完或者怎么样,是那种怎么睡都睡不掉的疲惫。</p> <p>1 月 14 号,试着复健跑步了。</p> <p>距离九月那个意外,四个多月。重新跑起来的时候,脚踩在地上,身体也会提醒我这事情没那么容易过去。相比与之前的轻松,取而代之的是肺活量气息跟不上的痛苦,迈不动步子的煎熬。没跑多远就停下来了,站在操场边喘气,看别人一圈一圈地跑过去。也许这就是时间在身上留下的东西。</p> <p>15 号考完最后一科自控。</p> <p>出考场的时候脑子是空的。明明每个知识点都看过,都理解了,坐到卷子面前就是写不出来。那种&quot;我全都懂但我全都写不准确,做不出题&quot;的感觉,唉经历了一次又一次,不知道什么时候是尽头。</p> <p>当天晚上就开始赶课设了。一直搞到凌晨五点,倒头睡了几个小时,第二天接着熬到三点。幸好自己还年轻...身体好像还扛得住,但心里隐隐觉得自己在透支什么——不是体力,也许是某种对生活的耐心。</p> <p>18、19 号,期末的流程结束,我就开始规划 GrtBlog v2 了。老毛病了。越累越想写东西,越焦虑越想开新坑。大概对我来说,创造是唯一能抵抗未知的东西。打开新项目的那一刻,所有的疲惫都可以暂时搁置,只剩下屏幕上干净的空文件,还有,对即将成型屎山的想法。</p> <p>::: gallery height=&quot;400px&quot; caption=&quot;考试之后&quot; <img src="/uploads/pictures/2026-03-03-11:20:06-88.png" alt="image.png"> <img src="/uploads/pictures/2026-03-03-11:20:14-f4.png" alt="image.png"> <img src="/uploads/pictures/2026-03-03-11:20:24-1d.png" alt="image.png"> :::</p> <p>20 号上了回家的车。21 号到。扑面而来是熟悉的温度和风。</p> <p>回到家并没有真的放松下来。22 号团委的任务就追过来了,那种&quot;你明明在放假但其实没有在放假&quot;的感觉,无法描述。24 号和家里人出去散步,冬天的街道很空,路灯把影子拉得很长。踩在雪上,走着走着,心里安静了一点点。</p> <p>月底的日子就是窝在家里写 GrtBlog v2。窗外是灰蒙蒙的天,屏幕上是 SvelteKit 和 Go Fiber 的代码。没有人催,没有 deadline,只有键盘声和耳机里的音乐在想。那几天写得很沉浸,好像又回到了那个最有想法的时候。</p> <hr> <h2>二月:过年、失眠、和一列南下的火车</h2> <p>二月开头还是写项目。</p> <p>抽空约了要好的高中同学出来吃饭。坐下来发现大家变化都不大,聊的还是那些事,笑点还是那些笑点。这种&quot;不变&quot;让人安心,可也有一瞬间会恍惚——他们好像还是高中的样子,而我总觉得自己这一年老了好多。也许只是经历的东西不一样吧。</p> <p><del>没事就上线原神、星铁</del>,累了刷刷 B 站,晚上看看项目进度,够了就出去跑步,不够就继续写。每天或者隔一天跑一次,夜里的风很冷,我把手蜷缩在袖子里,然后看着街上灯光映照的雪景,只剩下呼吸和脚步声。这大概是一天里最干净的时刻。</p> <p>然后 6 号,发现上学期又有挂的了。</p> <p>怎么说呢。</p> <p>不是挂科本身有多可怕,是那一瞬间,所有东西一起塌下来了。春招的压力、对未来的焦虑、身体恢复的漫长、还没写完的项目、还没准备好的八股、还没刷够的算法——平时一件一件,也就是慢慢累计,直到这一个导火索。那天的情绪很黑,黑到不想说。</p> <p>8 号还是和家里人出去逛了逛。11 号 Lowiro 出了新音游的测试版,五指打 6K 属实逆天了,还有鼠标的事情,在总之就是很难,但是还挺好玩的。后面几天晚上和高中同学打 Minecraft,拆幸运方块,玩空岛生存,在方块的世界里做一些简单到不需要思考的事,一个只有“我”的世界吧。</p> <p>14 号,年前最后一次出门,和同学待了一整天。回来后折腾了小米的 root、LSP、搞搞 tricky store。搞机和写代码一样,是一种需要高度专注的手艺活,专注到可以暂时忘掉其他所有事,<del>专注到忘记备份于是成了砖</del></p> <p>::: gallery height=&quot;400px&quot; caption=&quot;玩,和过年&quot; <img src="/uploads/pictures/2026-03-03-11:21:34-da.png" alt="image.png"> <img src="/uploads/pictures/2026-03-03-11:21:42-f6.png" alt="image.png"> :::</p> <p>15 号,回老家过年。</p> <p>但说实话,一点也不像&quot;回家&quot;。没有任何力气应付亲戚的寒暄,笑容是挤出来的,年是对付过的。除夕是看 B 站拜年纪熬的,顺手搞了个 ctf——群友在文章里藏了解密红包,倒是很有意思。</p> <p>过年那几天,经常出去走走。其实就是待不住,待着就焦虑,出去走走至少能骗骗自己在&quot;散心&quot;。晚上经常睡不着。躺在床上翻来覆去,脑子停不下来——春招什么时候开始投,简历还没改完,算法题还差好多,八股还有一大堆没背,还有课内的课程,还有身体的健康……所有&quot;还没&quot;像一床太重的被子,压着你,闷着你,让你在黑暗里越来越清醒。</p> <p>21 号回了长沙。22 号,上线了 GrtBlog v2 的测试版本。</p> <p>主线通了,看着写了快两个月的东西真正跑起来,算是一种很小的、很确定的满足。至少这件事,还是做到了。</p> <p>23 号和朋友去看了新开的商场,后面几天给博客收尾,穿插着看八股和算法。日子又变成了那种&quot;什么都在推进但什么都没到位&quot;的状态。</p> <p><img src="/uploads/pictures/2026-03-03-11:21:57-c3.png" alt="image.png"></p> <p>27 号早上坐上了火车,北京中转。从北京朝阳到北京西要坐地铁穿城,车厢里人挤人,耳边飘着正宗的京腔——那种老爷范儿的调子,在嘈杂的地铁站里居然有种奇妙的从容感。</p> <p><img src="/uploads/pictures/2026-03-03-11:23:17-fc.png" alt="image.png"></p> <p>在北京西站碰到一个坐轮椅的哥们,左脚打着石膏。我一看就笑了——之前在广州实习的时候也碰到过一个同事左脚骨折的,我俩面对面一撞上,他左我右,完美对称。这次在北京西又来一回,怕不是命运大概觉得这个梗还挺好笑的,舍不得丢。</p> <p>28 号到了学校。</p> <p>晚上和群友聊到深夜,一起弄项目。键盘声响着。熟悉的节奏,熟悉的深夜,依旧是夜晚的想法。</p> <p>后来 3 号上线了新版博客。</p> <p>新学期,就这样又开始了。</p> <hr> <h2>后记</h2> <p>写这篇手记的时候是三月初,坐在一个空教室,刚刚更新了博客的新版,还在改一些bug。</p> <p>回头去看一月和二月,脑子里浮上来的不是什么完整的故事线,全是碎片。鼻炎难受时候没法入睡翻来覆去的夜晚,凌晨五点课设终于跑通时一个人对着屏幕傻笑,夜跑时耳边呼呼的风声还有冻红的双手,除夕拜年纪弹幕飘过去的热闹。还有过年时躺在床上睡不着,盯着天花板发呆的那些夜晚。</p> <p>至少是我这两个月活过的证据。</p> <p>2025 年底我写&quot;雾已经散去&quot;,现在回头看,那句话说得太早了。哪有那么简单。一月的期末、二月的焦虑、过年时的失眠——它们都在提醒我,去年秋天那场意外留下的东西,不只是脚上的伤,还有心里某个被磕碎了又没完全粘好的角落。焦虑还在,不安全感还在,那种&quot;我是不是不够好&quot;的声音还在。</p> <p>但是至少</p> <p>这两个月,不管情绪多差,不管多焦虑多累多睡不着,我一直在写代码。GrtBlog v2 从一月中旬的一个模糊念头,到二月底真正跑起来——这中间经历了期末周的熬夜、回家后的团委任务、过年时的焦虑发作、还有那段很黑的日子。但我就是一直在写。不是因为自律,不是因为简历需要,甚至不是因为&quot;热爱&quot;这种听起来很漂亮的词。就是……需要。像呼吸一样需要。当外面的一切都不确定的时候,打开编辑器,写一行代码,看它跑起来——这件事是确定的。也是最简单的正向反馈了。</p> <p>想起 14 号在操场上复健跑步,跑不了多远就停下来喘气。想起 6 号得知挂科之后那种天塌了的感觉。想起除夕夜一个人在老家的街上走,冷风灌进衣领,假装自己在散心。想起 22 号 v2 上线那一刻,看着页面加载出来,心里安静了一下。</p> <p>这些时刻放在一起看,好像也没那么糟。</p> <p>我还是那个会焦虑到失眠的人,还是那个考试会懵的人,还是那个在亲戚面前挤不出真心笑容的人。但我也是那个在凌晨三点还在写课设的人,是那个在所有人都觉得该休息的时候还在开新坑的人,是那个看到 eslint error 没了会小小地开心一下的人。</p> <p>这些加在一起,就是我。不太完整,但至少还在。</p> <p>春招马上就到了。说不紧张是假的。简历还在想办法,算法还在刷,八股还有一堆要背。前面的路雾蒙蒙的,看不清走向哪里。</p> <p>但我好像不太怕了。</p> <p>不是因为变勇敢了,是因为这两个月教会我一件事:不需要等雾散了再走。雾里也能走。走得慢一点,看不清远一点,偶尔踩空一步——都没关系。脚还在地上,手还在键盘上,朋友还在群里,夜跑的风还是凉的。</p> <p>去年写了一整年的故事,从晨光到浓雾再到雾散。今年的前两个月,没有那么戏剧化的起伏,只是很普通地活着——普通地焦虑,普通地失眠,普通地写代码,普通地和朋友待在一起,普通地在深夜感到一点点温暖。</p> <p>但是我也开始变得算是有点乐观,开始觉得自己的过程曲折到想笑,凑齐了缓考补考重修很“圆满”,开始感觉有的时候自己写 bug 很有乐趣,开始发现不顺心的也是不错的体验</p> <p>那就这样吧。</p> <p>继续写代码,继续跑步,继续睡不着的时候翻来覆去,继续在群里和朋友聊到深夜。</p> <p><img src="/uploads/pictures/2026-03-03-11:24:16-5f.png" alt="image.png"></p>

G
grtsinry43

从 v1 到 v2,谈谈这个简单博客背后的架构演进与实现

<blockquote><p>该内容由 RSS 渲染生成,最佳阅读体验请前往:<a href="https://blog.grtsinry43.com/posts/grtblog-v2-architecture">https://blog.grtsinry43.com/posts/grtblog-v2-architecture</a></p></blockquote><p>写下这篇文章的时候,<code>grtblog-v2</code> 的核心功能开发已经基本告一段落。 <a href="https://github.com/grtsinry43/grtblog-v2">https://github.com/grtsinry43/grtblog-v2</a></p> <p><del>目前正在进行稳定性测试,确认稳定后会逐步修复 Bug、补充功能,并拉朋友内测。当前的测试地址在:</del></p> <p><a href="https://blog-next.grtsinry43.com/">https://blog-next.grtsinry43.com/</a></p> <p><del>(注意仅供测试,数据与本站不会同步)</del></p> <p>本站已更新,稳定后再发布新版项目~</p> <p>感谢 &lt;@starnighter@blogv2.starnighter.com&gt; 同学帮助测试还有 PR ,帮助我完成了一些功能开发~</p> <h2>为什么要重写</h2> <p>这个博客最初只是我学习 React SSR 时的练手项目。一年多过去,它承载了我大量的技术实验——每次有新东西想试,就往里堆。学到了很多,但代价是:它变成了一座精致的屎山。</p> <p>作为部署在 1C2G / 2C4G 小鸡上的个人博客,v1 实在太重了。每次部署要拉起 MySQL、MongoDB、Redis、MeiliSearch 等一堆服务,JVM 和 Next.js 联手吃掉几乎所有内存。更让人疲惫的是 Next.js 的黑盒实现和不断暴露的安全问题——维护它本身就需要一套沉重的心智模型。</p> <p>::: link-card href=&quot;/posts/rsc-boundary-mismatch&quot; title=&quot;新时代的 PHP:RSC 的边界错位与工程代价&quot; desc=&quot;代码编织的幻觉背后,边界的消融暗藏风暴;语法糖包裹的便利之下,责任的转移悄然发生。全栈的浪潮冲刷着安全的长堤,框架的叙事掩盖着架构的代价。&quot; newtab=&quot;true&quot;</p> <p>:::</p> <h2>首先是对比下</h2> <p>咱们首先对比一下,狠狠抨击自己之前的石山,然后讲一下我这次换成了什么:</p> <table> <thead> <tr> <th>问题</th> <th>具体表现</th> </tr> </thead> <tbody> <tr> <td><strong>架构复杂</strong></td> <td>Java 后端 + Next.js 前端 + Umi.js 后台 + Python 推荐服务,四个独立技术栈</td> </tr> <tr> <td><strong>数据库过多</strong></td> <td>MySQL + MongoDB + Redis + Elasticsearch + MeiliSearch,五个模块各司其职但运维成本极高</td> </tr> <tr> <td><strong>部署门槛高</strong></td> <td>Docker Compose 需要 6+ 个容器,配置繁琐,甚至阻碍了作者自己后续维护</td> </tr> <tr> <td><strong>仓库膨胀</strong></td> <td>Git 历史混入大量二进制资源,仓库体积快速膨胀</td> </tr> <tr> <td><strong>边界模糊</strong></td> <td>设计系统、内容模型与插件机制(PF4J)的职责逐渐交叉</td> </tr> <tr> <td><strong>BFF 废弃</strong></td> <td>规划的 BFF 层未能落地,停留在空目录</td> </tr> </tbody> </table> <table> <thead> <tr> <th>决策</th> <th>v1 做法</th> <th>v2 做法</th> <th>理由</th> </tr> </thead> <tbody> <tr> <td>后端语言</td> <td>Java (Spring Boot)</td> <td><strong>Go (Fiber)</strong></td> <td>编译为单二进制,内存占用从数百 MB 降至数十 MB</td> </tr> <tr> <td>前端框架</td> <td>Next.js (React)</td> <td><strong>SvelteKit (Svelte 5)</strong></td> <td>更小的 bundle、更少的运行时开销、Runes 语法更直觉</td> </tr> <tr> <td>管理后台</td> <td>Umi.js (React)</td> <td><strong>Vue 3 (Naive UI)</strong></td> <td>轻量且与前台技术栈解耦,并基于 lithe-admin 二开</td> </tr> <tr> <td>数据库</td> <td>MySQL + MongoDB</td> <td><strong>PostgreSQL 一个搞定</strong></td> <td>JSONB 覆盖文档型需求,减少运维复杂度</td> </tr> <tr> <td>搜索</td> <td>Elasticsearch + MeiliSearch</td> <td><strong>后端内建</strong></td> <td>博客体量下内建搜索足够,去掉两个重型依赖</td> </tr> <tr> <td>推荐系统</td> <td>独立 Python 微服务</td> <td><strong>Go 内建</strong></td> <td>减少跨语言通信和部署复杂度</td> </tr> <tr> <td>静态生成</td> <td>Next.js ISR (框架内建)</td> <td><strong>自研 ISR (Go 驱动)</strong></td> <td>Go 后端直接调度渲染、原子写入,完全可控</td> </tr> <tr> <td>实时通信</td> <td>Socket.io + Netty</td> <td><strong>原生 WebSocket</strong></td> <td>去掉 Socket.io 协议层开销</td> </tr> <tr> <td>部署</td> <td>6+ 容器</td> <td><strong>3 容器</strong> (Go + SvelteKit + Nginx + DB)</td> <td>大幅降低部署门槛</td> </tr> </tbody> </table> <h2>注水静态架构 (Rehydrated Static Architecture)</h2> <p><img src="https://blog.grtsinry43.com/uploads/2026/02/24/mermaid-diagram-2026-02-24-112032.png_0c4a814f-8b26-4fa7-a624-96edb40dc1b4.png" alt=""></p> <p>这是 v2 的核心设计理念,一句话概括:</p> <blockquote> <p><strong>将 SSR 的渲染时机从「用户请求时」提前到「数据变更时」,将渲染产物以纯静态文件的形式交给 Nginx 分发,同时通过 WebSocket 为在线用户注入实时更新。</strong></p> </blockquote> <p>它试图在静态站点的极致性能和动态应用的实时交互之间找到一个平衡点。拆开来看,分为三层:</p> <ol> <li><strong>静态先行 (Static First)</strong> — 所有公开页面默认为纯静态 HTML,由 Nginx 直接分发,首屏速度拉满,CPU 占用趋近于零。</li> <li><strong>增量生成 (Incremental Generation)</strong> — 仅在内容变更时,由 Go 控制平面驱动 SvelteKit 渲染器生成受影响的页面,不做全量重建。</li> <li><strong>实时注水 (Realtime Rehydration)</strong> — 客户端通过 WebSocket 接收评论、点赞及内容的热更新,在线用户无需刷新即可看到最新状态。</li> </ol> <p>换一个更本质的角度来理解:</p> <blockquote> <p>SSR / SSG / ISR 这些词只是在描述&quot;渲染发生在哪里&quot;。真正决定架构设计的,是 <strong>数据与页面的依赖关系</strong>,以及 <strong>渲染产物如何存储和复用</strong>。</p> </blockquote> <p>它的效果是:</p> <p><img src="https://blog.grtsinry43.com/uploads/2026/02/24/Pasted_image_20260224135038.png_65bcd26b-b04d-477d-bb03-0c489109ec01.png" alt=""></p> <h2>发生了什么</h2> <p>我们可以用一个图来看出核心的更新机制是什么的。</p> <p><img src="https://blog.grtsinry43.com/uploads/2026/02/24/mermaid-diagram-2026-01-18-182549.png_45511e92-655f-45a9-8ef3-02fdf585959a.png" alt=""></p> <h3>ISR 工作流</h3> <p>ISR(Incremental Static Regeneration)是本项目的核心机制,类似 Next.js 的 ISR,但完全白盒,可以完全掌控:</p> <pre><code class="language-md">Admin 发布文章 │ ▼ Go 写入数据库 │ ▼ DirtyPathCalculator 计算受影响路径 例: /posts/new, /index, /tags/Go, /feed.xml │ ▼ RenderQueue 异步任务入队 │ ▼ Worker 请求 SvelteKit Renderer GET http://renderer:3000/posts/new │ ▼ AtomicWriter 原子写入静态文件 TempFile -&gt; Rename (防并发读写白屏) │ ▼ WebSocket Hub 广播 post_created 事件 │ ▼ 在线用户收到实时通知 </code></pre> <h3>实时更新流</h3> <pre><code class="language-md">Admin 修改文章错别字 │ ▼ Go 更新 DB + 广播 WS post_update (带 payload) │ ▼ 在线阅读用户的 Svelte Store 收到 payload │ ▼ 无感替换 DOM 文本节点(无需刷新) │ ▼ Go 异步触发静态文件重新生成(为后来者服务) </code></pre> <h2>说说实现细节</h2> <h3>从 MPA 到 SPA:静态文件如何水合</h3> <p>这种架构面临的第一个问题是:如果页面变成了静态文件,客户端怎么水合成 SPA? 好在 SvelteKit 的框架魔法大多发生在SSR的时候。在 SvelteKit 中,页面加载分为两种路径:</p> <ol> <li><strong>首次访问 (SSR)</strong>:服务端执行 <code>load()</code>,拼接完整的 HTML 返回给浏览器。</li> <li><strong>客户端路由跳转 (CSR / SPA)</strong>:当你点击链接从 <code>/</code> 跳转到 <code>/posts/1</code> 时,SvelteKit <strong>不会</strong>请求新的 HTML。它的客户端 Router 会去请求一个特殊路径:<code>/posts/1/__data.json</code>,拿到 JSON 后在前端完成数据替换和 DOM 更新。</li> </ol> <p>因此,我们只需在每次渲染时同时缓存 HTML 和 <code>__data.json</code>,就做到了一个&quot;静态的单页应用&quot;——首次访问命中静态 HTML,水合之后的导航跳转走 <code>__data.json</code>,行为完全等同于 SPA。</p> <h3><code>load()</code> 驱动的 ISR 依赖收集</h3> <p>传统的 ISR 是框架内闭环的,但 v2 的后端是 Go,前端是 SvelteKit。Go 怎么知道文章 A 更新了,首页也要跟着重新渲染?我们就需要一个依赖标记的机制。</p> <h4>1. 页面在 <code>load</code> 阶段显式声明依赖</h4> <p>SvelteKit 的数据获取,精髓在于这个<code>load()</code>函数,由于我们整个页面都是在这里获取初始数据,所以我们不妨在拿数据的时候打个 Tag(<code>web/src/routes/posts/[slug]/+page.server.ts</code>):</p> <pre><code class="language-typescript">const post = await getPostDetail(fetch, params.slug); trackISRDeps(event, `post:detail:${post.id}`); </code></pre> <p>首页等复杂页面也会收集一堆 Tag:</p> <pre><code class="language-typescript">trackISRDeps( event, 'home:recent-posts', 'home:recent-moments', 'home:activity-pulse', 'home:inspiration-stats' ); </code></pre> <h4>2. Header 与反向索引</h4> <p>在 <code>web/src/hooks.server.ts</code> 中,我拦截了响应,把收集到的 Tag 塞进 HTTP Header:</p> <pre><code class="language-typescript">event.locals.isrDeps = new Set&lt;string&gt;(); const response = await resolve(event); headers.set('x-grt-deps', JSON.stringify(Array.from(event.locals.isrDeps))); </code></pre> <p>Go 向 Renderer 发起内网抓取时(<code>server/internal/app/htmlsnapshot/service.go</code>),解析这个 Header,并将关系写入自己的 Redis 映射表:</p> <ul> <li><code>isr:url:&lt;url&gt; -&gt; deps</code></li> <li><code>isr:dep:&lt;dep&gt; -&gt; urls</code></li> </ul> <h4>3. 事件驱动失效</h4> <p>当我在后台修改了文章,Go 的事件总线触发 ISR(<code>server/internal/app/isr/subscriber.go</code>):</p> <pre><code class="language-go">deps := []string{ &quot;home:recent-posts&quot;, fmt.Sprintf(&quot;post:detail:%d&quot;, articleID), } urls := []string{&quot;/&quot;, &quot;/posts&quot;, &quot;/posts/page/1&quot;} return service.Invalidate(ctx, deps, urls) </code></pre> <p>Go 拿着 <code>deps</code> 去反向索引中查出所有受影响的 URL,去重后压入 Redis Sorted Set 队列。</p> <p><strong>至此,一条完整的链路成型:前端声明依赖 → 后端解析并建立索引 → 数据变更时精准触发重渲染。</strong></p> <h3>异步客户端组件与请求</h3> <p>如果全站静态化,点赞数、评论区怎么动态加载? 对于点赞和观看量这种轻交互,我们可以 mounted 之后请求和修改,而评论这种重交互,则可以使用 <code>&lt;QueryRoot&gt;</code> 组件(<code>web/src/lib/ui/common/QueryRoot.svelte</code>),这下就有了个低配的 Suspense(bushi</p> <pre><code class="language-ts">onMount(async () =&gt; { const [{ QueryClientProvider }, { getOrCreateQueryClient }] = await Promise.all([ import('@tanstack/svelte-query'), import('$lib/shared/clients/query-client') ]); client = await getOrCreateQueryClient(options); Provider = QueryClientProvider; if (loader) { const loaded = await loader(); Loaded = loaded.default; } ready = true; }); </code></pre> <p>这样,第一屏不会引入太重的请求部分,而客户端组件加载完成之后由 TanStack Query 管理,最大化管理了请求数据。</p> <h3><code>svatoms</code>:舒服的树形数据传递</h3> <p>在由各种“交互岛屿”构成的页面中,Prop drilling(属性逐层透传)是维护的地狱。结合 Svelte 5 的 Runes 特性,我封装了 <code>svatoms</code> 来实现数据树与组件树的解耦。</p> <p><a href="https://github.com/grtsinry43/svatoms">https://github.com/grtsinry43/svatoms</a></p> <h4>1. Context 挂载模型数据</h4> <p>在页面顶层(<code>web/src/routes/posts/[slug]/+page.svelte</code>),把 <code>load</code> 来的数据挂载到专属的 Context 中。使用 getter 保证 SvelteKit 导航后的数据自动同步:</p> <pre><code class="language-ts">postDetailCtx.mountModelData(() =&gt; data.post ?? null); const { updateModelData } = postDetailCtx.useModelActions(); </code></pre> <h4>2. 细粒度切片订阅</h4> <p>子组件只订阅自己关心的切片(<code>PostDetailMain.svelte</code>):</p> <pre><code class="language-ts">const aiSummaryStore = postDetailCtx.selectModelData((data) =&gt; data?.aiSummary ?? ''); const tocStore = postDetailCtx.selectModelData((data) =&gt; data?.toc ?? [], { equals: sameToc }); </code></pre> <p>这里的 <code>equals</code>可以在返回复杂对象时,手动等价比较避免了无意义的重渲染。</p> <h4>3. 跨树联动,比如阅读进度同步</h4> <p>比如<code>DetailMarkdownContent.svelte</code> 在正文滚动时,更新 <code>detailPanelCtx</code> 里的 <code>activeAnchor</code>。远在另一棵 DOM 树分支上的 <code>MobileNavBar.svelte</code> 订阅同一个 Context 并高亮当前目录。 生产者和消费者无需在同一条 props 链上,状态流转的心智模型很舒服。</p> <h3>渲染平面的优雅降级:静态优先 + 原子写入</h3> <p>之前说过,由于静态的特性,哪怕 Go 后端和 SvelteKit 全部宕机,博客依然要能抗住流量。</p> <h4>1. Nginx 静态</h4> <p>在 <code>deploy/nginx/nginx.conf</code> 中,静态文件是一等公民:</p> <pre><code class="language-conf">location / { # 命中静态文件直接返回,未命中才回源到 SSR try_files $uri $uri.html $uri/index.html @frontend_fallback; } location @frontend_fallback { proxy_pass http://renderer_ssr; } </code></pre> <h4>2. 原子操作避免损坏</h4> <p>高并发下,如果 Go 正在把渲染好的 HTML 写入磁盘,用户恰好访问,就会看到残缺的白屏。 在 <code>server/internal/app/htmlsnapshot/service.go</code> 中,这里利用Rename操作的原子性:</p> <pre><code class="language-go">tmp, _ := os.CreateTemp(dir, &quot;.snapshot-*.tmp&quot;) tmp.Write(body) tmp.Close() os.Rename(tmpName, filePath) </code></pre> <p>并且,如果访问 Renderer 遇到 404,Go 会主动清理旧的静态文件,避免出现“后台删了,前台还在”的幽灵页面。</p> <h3>Markdown渲染</h3> <p>在个人博客的开发中,大多数人会选择引入 <code>markdown-it</code> 或 <code>marked</code>,直接转成 HTML 字符串,然后用 <code>{@html content}</code>(或 <code>v-html</code> / <code>dangerouslySetInnerHTML</code>)一把梭。 ……但这样做意味着完全脱离了框架的组件生命周期——Svelte 不知道那段 HTML 里有什么,自然也无法管理它。 为了在运行时安全、优雅地将 Svelte 组件嵌入到 Markdown 正文中,同时保留AST解析能力,我抽离并开源了<code>svmarkdown</code>。</p> <p><a href="https://github.com/grtsinry43/svmarkdown">https://github.com/grtsinry43/svmarkdown</a></p> <p>这个库是基于Makrdown-it的强大能力的</p> <h4>Phase 1: 解析层 (Parser Layer) —— 构建干净的 AST</h4> <p>在 <code>src/parser.ts</code> 中,利用 <code>markdown-it</code> 对原始文本进行词法分析,拿到扁平的 <code>Token</code> 流,然后通过一个游标解析器,将这些 Token 转换成一颗干净的、高度结构化的自定义抽象语法树(AST),即 <code>SvmdNode</code>。</p> <p>在 <code>src/types.ts</code> 中,可以看到 AST 节点被严格定义为几种:</p> <ul> <li><code>SvmdTextNode</code>:纯文本节点。</li> <li><code>SvmdElementNode</code>:标准 HTML 标签(如 <code>p</code>, <code>strong</code>, <code>a</code>)。</li> <li><code>SvmdCodeNode</code>:代码块节点(携带语言类型和源码)。</li> <li><strong><code>SvmdComponentNode</code></strong>:自定义组件节点。</li> </ul> <p>通过引入 <code>markdown-it-container</code> 插件,<code>svmarkdown</code> 会拦截所有类似 <code>:::callout</code> 或 <code>:::gallery</code> 的自定义块。在解析阶段,它会将冒号后面的标识符和属性提取出来,直接组装成一个 <code>SvmdComponentNode</code>,放入 AST 树中。</p> <h4>Phase 2: 渲染层 (Render Layer) —— Svelte 原生递归组件</h4> <p>拿到 AST 后,就进入了 Svelte 渲染阶段。</p> <p>在 <code>src/Markdown.svelte</code> 和 <code>src/internal/RenderNode.svelte</code> 里,利用 Svelte 的 <code>&lt;svelte:element&gt;</code> 和 <code>&lt;svelte:component&gt;</code> 实现了 AST 的递归遍历。</p> <p>在 <code>&lt;RenderNode&gt;</code> 这个内部核心组件里,会进行分发(Dispatch):</p> <ol> <li><strong>如果是普通元素</strong>:直接渲染 <code>&lt;svelte:element this={node.tag}&gt;</code>。</li> <li><strong>如果是代码块</strong>:将代码字符串作为 props 传入用户定义的外部 CodeBlock 组件。</li> <li><strong>如果是自定义组件</strong>:系统会去查找顶层传入的 <code>componentMap</code>。</li> </ol> <pre><code class="language-ts">{#if node.type === 'component'} {@const MappedComponent = componentMap[node.name] || FallbackComponent} &lt;svelte:component this={MappedComponent} {...node.props}&gt; &lt;SvmdChildren nodes={node.children} /&gt; &lt;/svelte:component&gt; {/if} </code></pre> <p>用这个库,心智负担也很低:</p> <pre><code class="language-ts">const componentBlocks = Object.fromEntries( componentDefinitions.map((component) =&gt; [component.name, true]) ) satisfies SvmdParseOptions['componentBlocks']; export const markdownComponents: SvmdComponentMap = { h1: MarkdownHeading, h2: MarkdownHeading, h3: MarkdownHeading, h4: MarkdownHeading, h5: MarkdownHeading, h6: MarkdownHeading, p: MarkdownParagraph, ul: MarkdownList, ol: MarkdownList, li: MarkdownListItem, blockquote: MarkdownBlockquote, hr: MarkdownHr, table: MarkdownTable, thead: MarkdownThead, tbody: MarkdownTbody, tr: MarkdownTr, th: MarkdownTh, td: MarkdownTd, a: MarkdownLink, img: MarkdownImage, code: MarkdownCodeBlock, gallery: MarkdownFallback, callout: MarkdownFallback, timeline: MarkdownFallback, 'year-card': YearCard, 'link-card': LinkCard, 'footnote-link-card': FootnoteLinkCard }; export const markdownParseOptions: SvmdParseOptions = { componentBlocks, markdownItPlugins: [], markdownItOptions: { html: true, linkify: true, typographer: true } }; export const markdownRenderOptions: SvmdRenderOptions = { allowDangerousHtml: true }; </code></pre> <p>轻量、极速、一切皆组件,这样或许还挺优雅的。</p> <h2>写在最后</h2> <p>回头看,v1 的问题不是任何单一技术选型的失败,而是复杂度在无人察觉中的缓慢堆积——每多一个中间件都&quot;有道理&quot;,每多一层抽象都&quot;有必要&quot;,直到整个系统的重量超过了它所承载的内容本身。</p> <p>v2 的核心收获不是选了更好的框架,而是学会了在每个岔路口问自己一句:<strong>这个博客,真的需要这个吗?</strong> 内存占用腰斩不止,维护的心智模型也清爽了许多。更重要的是,我终于能把精力从&quot;和基础设施搏斗&quot;转回到&quot;做有趣的产品&quot;上了。</p> <p>grtblog-v2 还需要完整的测试和问题修复,但距离稳定应该不会太远了。如果你也在做类似的全栈博客、ISR 优化,或者对 Svelte 5 + Go 的组合感兴趣,欢迎 <a href="https://github.com/grtsinry43/grtblog-v2">Star 仓库</a>、提 Issue,或者直接在评论区聊聊你的想法。</p> <p>感谢读完这篇有点长的技术复盘。</p>

G
grtsinry43

Go 语言初体验:Less is more,一种丑但可靠的工程美学

<blockquote><p>该内容由 RSS 渲染生成,最佳阅读体验请前往:<a href="https://blog.grtsinry43.com/posts/go-first-experience">https://blog.grtsinry43.com/posts/go-first-experience</a></p></blockquote><p>最近新项目评估技术栈,因为 Java/Kotlin 太重,TypeScript/JavaScript(Node.js)因为 js 原型链的问题我一直感觉不是合格的后端语言,写 Rust 的话社区根本不会有几个人贡献,再加上后台任务,轻量化,易于部署,可能就只有 Go 能担任这个职责了。 其实我之前用过 go 的,当时在搞 AI 原型生成器的时候,为了快捷操作容器,我用 go 搞了个沙箱管理器,操作容器,对外 gRPC,利用了它在云原生领域的生态优势。而这次,我看中的是它易于入门,编译快,占用轻,易于部署,当然重要的是,协程模型确实很现代很舒服。</p> <h2>真的很丑</h2> <p>我对 Go 的第一印象非常稳定:<strong>丑。</strong> 对于习惯了 Java/JS 的注解(装饰器),Kotlin 的 DSL,Rust 的宏来说的我,Go 的语法极其贫瘠,真是可以说简陋。对于 Go 的显式哲学来说,不像是语言的搭积木,而是你把螺丝刀给我,我就能把家装起来,但别问我为什么这个螺丝长这样。</p> <h3>于是字符串成了注解</h3> <p>我们从这样一段例子开始:</p> <pre><code class="language-go">// OAuthProviderResp 返回可用的 OAuth provider 信息。 type OAuthProviderResp struct { Key string `json:&quot;key&quot;` DisplayName string `json:&quot;displayName&quot;` Scopes []string `json:&quot;scopes&quot;` PKCERequired bool `json:&quot;pkceRequired&quot;` } type Webhook struct { ID int64 `gorm:&quot;column:id;primaryKey&quot;` Name string `gorm:&quot;column:name;size:100;not null&quot;` URL string `gorm:&quot;column:url;size:512;not null&quot;` Events []byte `gorm:&quot;column:events;type:jsonb;not null&quot;` Headers []byte `gorm:&quot;column:headers;type:jsonb;not null&quot;` PayloadTemplate string `gorm:&quot;column:payload_template;type:text;not null&quot;` IsEnabled bool `gorm:&quot;column:is_enabled&quot;` CreatedAt time.Time `gorm:&quot;column:created_at;autoCreateTime&quot;` UpdatedAt time.Time `gorm:&quot;column:updated_at;autoUpdateTime&quot;` DeletedAt gorm.DeletedAt `gorm:&quot;column:deleted_at;index&quot;` } </code></pre> <p>为了做一个简单的序列化和参数校验,必须在结构体后面跟上一长串 <code>json:&quot;name&quot; binding:&quot;required,min=5&quot;</code>。为了数据库字段的对应和行为,又要写一长串关键词。这种把逻辑写在字符串里的做法感觉不知道梦回了哪个时代。但是原因也很简单嘛,因为没有注解/宏/DSL,只能用这种方式来表达。</p> <h3>指针定义的 Overloaded</h3> <p>Go 的显式很多时候不是清晰,而是盲目想要复用反而使得语义过载。</p> <p>比如,你想要一个可选值?行,给你 <code>*T</code>。 但 <code>*T</code> 在 Go 里又不仅仅是 Optional——它同时还是:</p> <ul> <li>“这个字段可能为 NULL”(DB / JSON)</li> <li>“我想区分零值和未设置”(patch / update)</li> <li>“我想共享/引用同一份数据”(引用语义)</li> <li>“这个方法需要指针接收者”(行为语义)</li> </ul> <p>那这就很可怕了,于是当你在 Go 的代码中看到一个<code>*</code>,你还需要费尽心力去琢磨是可空还是关系。而原因只是因为 Go 没有一个设计好的 Optional/Result。</p> <blockquote> <p>同一个 <code>*</code> 被迫承担了四种语义,结果是:代码显式了,意图却更隐式了。</p> </blockquote> <h3>错误处理变为传递责任链</h3> <p>然后是错误处理。</p> <blockquote> <p>写 Go 的时候,键盘上最先磨损的永远是 <code>i</code>, <code>f</code>, <code>e</code>, <code>r</code>, <code>n</code>, <code>l</code> 这几个键。</p> </blockquote> <p>在 Kotlin 里你可能会用 <code>runCatching</code>,在 Rust 里你有 <code>?</code>,在 Java 里至少异常处理也未尝不可,但是 Go:<code>if err != nil { return err }</code></p> <p>你可以说这很显式,很清晰,很正确的考虑了每一种可能分支。<br> 但当你的业务开始出现一定的复杂度:超时、取消、重试、降级、后台任务、幂等、签名验签、缓存穿透……你会发现你写的不是后端,而是考虑所有,搭建了一条错误传播管道。</p> <p>当然,最让人抓狂的是,这种繁琐并没有带来更好的安全性。它不像 Rust 的 <code>Result&lt;T, E&gt;</code> 那样强制你在编译期处理错误,也不像 Java 的 Checked Exception 那样有显式的签名约束。它只是一个约定,如果你忘了写这两行代码,那么发生什么边界情况就不可控了。</p> <h2>写业务的“地狱体验”</h2> <h3>想要一个好用的 ORM</h3> <p>Go 的 ORM 生态有一种奇妙的割裂感:<br> 要么<strong>极度魔法</strong>,要么<strong>极度朴素</strong>,中间那条舒适区间很窄。</p> <blockquote> <p>这玩意儿除了名字叫 ORM,哪里像个现代 ORM 了?不如说是 SQL 拼接器</p> </blockquote> <p>在 Kotlin Exposed 或者 Rust SeaORM 里,或者哪怕是(不属于 ORM)手写 SQL 的 Rust sqlx,他们都是强类型的,强大的编译时安全让写代码就很有底气。你写错一个字段名,编译器立马给你报错。但在 Go 里(尤其是 GORM),你又回到了拼接字符串的恐惧......</p> <blockquote> <p><code>db.Where(&quot;user_nmae = ?&quot;, name).First(&amp;user)</code> —— 这里的 <code>user_nmae</code> 写错了?编译通过,运行报错!</p> </blockquote> <p>而当你开始尝试 Ent,感受到 DSL 的舒服,编译安全,但它妄图掌控数据库的感觉,以及完全无法自己精细修改的表结构、定义的索引优化等等,都让人感觉这根本就是为社交关系服务的图数据库,它的抽象会强到让你觉得“我在写 Ent,不是在写业务”。</p> <p><del>拜托,学习它的 SeaORM 都那么好用,人家尊重数据库,SQL 优先,利用 Rust 的语言特性搞了那么好的优化体验,Ent 居然能这么难用。</del></p> <p>于是最后很多人回到朴素路线,手写 migration(goose),查询用 <code>sqlx/sqlc</code>,开始抱怨 Go 的 ORM 总有一种“隔靴搔痒”的无力感。它要么太灵活以至于不安全,要么太重型(靠大量代码生成)以至于繁琐。</p> <h3>用脚本补充的语言能力</h3> <p>Go 的精神很一致:语言保持小,复杂度交给工具链。</p> <p>你可能会喜欢上 Rust 的宏展开代码,Kotlin/Rust 的 dsl 优雅美观,Java/JS 的注解轻松切面扩展。而 Go 呢?<code>//go:generate</code>。 它不是语言特性,它只是一个让工具链去跑个 shell 命令的“补丁”。</p> <p>所以你会看到整个生态一大堆生成器驱动的解决方案:</p> <ul> <li>ORM 生成(Ent / sqlc)</li> <li>Mock 生成(mockgen)</li> <li>API client 生成(OpenAPI generator)</li> <li>Protobuf/gRPC 生成</li> <li>...</li> </ul> <p>于是项目里面的 Makefile 成为了最佳实践,成为了一切生成器的优雅入口。</p> <p>这当然有好处:<br> 生成出来的就是普通 Go 代码,<strong>可读、可调试、编译期安全</strong>。</p> <p>然后你就会收获一种非常 Go 的痛苦:</p> <ul> <li>你改了 schema,忘了 generate,CI 才告诉你</li> <li>生成文件冲突,Git diff 像雪崩</li> <li>Debug 时你在你写的和生成的之间来回跳</li> </ul> <p>然后只能告诉自己一句:</p> <blockquote> <p>“这不是缺点,这是工程化。”</p> </blockquote> <h3>迟到的“半成品”</h3> <p>Go 的泛型给我的感觉很像——这辆车终于加了变速箱,但你一脚踩下去发现它只愿意在能跑这个层面负责,至于好不好开,你自己想办法。</p> <p><strong>1. Go 既然有了泛型,却依然不支持扩展方法(Extension Methods)</strong>。 即便有了泛型,你依然不能给切片加方法。于是官方标准库 <code>slices</code> 逼着你写成了这样: <code>slices.Map(slices.DeleteFunc(list, func...), func...)</code></p> <p><strong>2. 只有约束,没有推导</strong> Go 的泛型在使用上经常需要极其啰嗦的显式声明。明明编译器应该能推断出类型,但很多时候你还是得把那一长串 <code>[TypeA, TypeB]</code> 写出来,导致代码里充斥着方括号。 而且那个 <code>any</code> 关键字,说白了就是把 <code>interface{}</code> 换了个皮,并没有带来像 Rust 那样严格且强大的类型系统约束能力。你写出来的泛型代码,往往为了迁就 Go 那个并不聪明的编译器,变得比不写泛型还要难以阅读。</p> <h2>真的很稳:工业级的暴力美学</h2> <p>但话说回来,Go 的优点并不是它很美,而是它总能在你最需要的时候,干净利落地把活儿干完。我们可以看到他有那么多槽点,甚至这篇文章只列出了前 20%,想要讲述真正让我喜欢 Go 的,我们得换个角度——从“语言设计的艺术”转向“工程落地的暴力美学”。</p> <p>能不能快启动、能不能少出事、能不能轻易被别人接手、能不能在一堆后台任务和边角脏活里不崩溃。<strong>Go 在这些方面,几乎就是工业界的低配答案,但往往是最正确的答案。</strong></p> <h3>1)轻,是一种长期主义</h3> <p>Go 的轻是一种极其务实的取舍,你不需要把一天的情绪交给 Gradle、Maven、Cargo 或者 pnpm install 之后的依赖地狱。你不需要考虑沉重的 JVM,黑洞大小的 node_modules,一个二进制就轻松运行。</p> <h3>2)现代的协程模型,可以说在节省生命</h3> <p>在 Node.js 里,你得处理 <code>Promise</code>、<code>async/await</code> 传染性,一旦忘了 <code>await</code> 就像踩了雷;在 Rust 里,你得面对 <code>Tokio</code> 的运行时选择、<code>Pin</code>、<code>Future</code> 的生命周期……心智负担极重。</p> <p>而 Go 的 Goroutine 是对开发者最友好的并发模型,没有之一:</p> <pre><code class="language-go">// 无论这个任务多复杂,哪怕它是 IO 密集型 go func() { processBackgroundJob(data) }() </code></pre> <p>就这一行,Go 运行时帮你解决了 M:N 的调度,帮你处理了上下文切换。你写的是线性的、符合直觉的同步代码,底层跑的确是高效的异步非阻塞逻辑。</p> <p>所以为什么我和朋友总会相互开玩笑,说 Go 工程师想的都是只要业务写完了,剩下的就爽了。让你面对真实需求的时候,Go 的 <code>select</code> 和 <code>channel</code> 让你能像搭积木一样优雅地控制并发,而不是陷入回调地狱或生命周期深渊。</p> <h3>3)“丑”的另一面,是可维护</h3> <p>说实话,Go 语法上的丑,很大一部分其实是 Go 的一种强行约束:别太聪明,他让代码逻辑绝对平铺,显式写出了一切。</p> <ul> <li>没有宏 → 你没法把业务塞进编译期魔法里,接手的人能轻松读懂。</li> <li>没有注解 → 你必须显式声明逻辑,代码更有可读性。</li> <li>错误处理啰嗦 → 你很难忘记处理,也很难忽略掉业务里每一步的问题。</li> </ul> <p>你可以随便招一个开发者,让他看两天文档,他写出来的代码虽然丑,但你一眼就能看懂他在干嘛。Review 代码不再需要脑补上下文和复杂的继承关系,所见即所得。</p> <p>Go 的哲学在于,它强迫所有人都用最笨的方式写代码,从而消灭了奇技淫巧带来的维护成本。这在个人项目里可能不突出,但在多人协作和长期演进里,便让可维护性到了其他语言无法企及的地步。</p> <h3>4)交叉编译,DevOps 的终极梦想</h3> <p>这部分甚至无需多言。</p> <pre><code class="language-bash">CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o server main.go </code></pre> <p>回车敲下,你会得到一个纯静态链接的二进制文件。 没有 <code>node_modules</code> 黑洞,没有 JVM 依赖,没有 glibc 版本冲突。 你把这个文件 <code>scp</code> 到服务器上,<code>chmod +x</code>,然后 <code>./server</code>,就这么简单。</p> <p>配合 Docker,你的 Dockerfile 可能只有 5 行:</p> <pre><code class="language-Dockerfile">FROM alpine COPY --from=builder /app/server /server CMD [&quot;/server&quot;] </code></pre> <p>这对于我们这种“个人开发者”或“小团队”来说,省下的时间就是生命。</p> <h3>5)高效工具链,很爽的开发</h3> <p>Rust 编译一次可能够你喝杯 Java,Go 编译一次可能只够你眨几次 👀。 在微服务架构或者频繁迭代的开发流程中,这种极短的反馈回路(Code -&gt; Run -&gt; Test)带来的心流体验,足以抵消写 <code>if err != nil</code> 的烦躁,<del>让你清晰记得你的工作进度</del>。</p> <ul> <li>格式化?<code>gofmt</code></li> <li>测试?<code>go test</code></li> <li>文档?<code>go doc</code></li> <li>依赖?<code>go mod</code></li> </ul> <p>无聊, 但是好用。</p> <h3>6)云原生的绝对统治生态</h3> <p>Docker 是 Go 写的,K8s 是 Go 写的,Prometheus、Terraform、Etcd... 整个 CNCF(云原生计算基金会)的半壁江山都是 Go。当你需要操作容器、对接 gRPC、写 Kubernetes Operator、或者接入微服务网关时,Go 有第一公民级别的 SDK 支持。</p> <p>你想做后台任务?想做指标?想做 tracing?想做限流?想做配置?想做 CLI?<br> Go 的库可能不一定最优雅,但几乎总有一个能用、能跑、能运维的方案。</p> <hr> <h2>不完美,但是足够满足需求</h2> <p>之前我说过,我没什么语言偏好,只是适合的业务选适合的语言。</p> <p>选 Go,不是因为我们认为它是最完美的语言设计。 我们选它,是因为我们承认:我们不是在写诗,我们是在交付软件。</p> <p>它丑,但它让你把注意力从语言表达力移到了业务逻辑上;它啰嗦,但它保证了你的服务跑在 1 核 2G 的轻量应用服务器上时,依然无畏并发;它的编译器不聪明,但它让你的构建流水线在几秒钟内完成。</p> <p>所以,虽然我依然痛恨写 <code>json:&quot;id&quot;</code>,依然厌恶满屏的 <code>if err != nil</code>,但当我要想要快速上线一个带后台任务、高并发、且需要长期稳定运行的 API 服务时……</p> <p>这种舒适的体验,还得是 Go</p>

G
grtsinry43

职规赛,一次特别的体验

<blockquote><p>该内容由 RSS 渲染生成,最佳阅读体验请前往:<a href="https://blog.grtsinry43.com/moments/2025/12/26/a-unique-career-planning-experience">https://blog.grtsinry43.com/moments/2025/12/26/a-unique-career-planning-experience</a></p></blockquote><p>聊聊年底的最后一点事情。</p> <p><del>本来以为年终总结最后一篇了,结果没忍住又来一篇,自控实在看的头疼,码码字缓解一下,果然码字可以改善心情。</del></p> <p>12 月 24 日,我参加了学校的职业规划大赛。这是我大学里唯一的一场正式比赛,或许也是最后一场。最终成绩是三等奖,排名倒数第二。但这个结果,从报名那天起,我就没太放在心上。</p> <p>学长推荐我参赛时,其实想法是:“要是能进省赛,暑期实习请假就好请了。”我一笑,图个新鲜,也图个仪式感,便报名了。赢了是惊喜,没进也无憾,就当给学生时代添一段特别的记忆。</p> <p>准备的过程,出乎意料地认真,但是其实完全没有经验,一点点摸索。我第一次静下心,把这些年的经历摊开在桌上:简历、项目、实习、零散的想法,试图塞进“职业规划”的框架里——职业缘起、探索历程、人岗匹配、未来路径……走完这一套流程,才发现自己原来很少真正思考过长远的事,更少将个人选择放在更大的时代背景下去审视。</p> <p>取出夏天投实习的那份简历,还带着浓浓的技术味:代码细节、debug 记录、优化数据、偏执的钻研。为了比赛,我大刀阔斧地删改——保留了核心,却稀释了锋芒,加入了更结构化、更温和的表达,让它至少能被评委老师读懂。可我终究没把自己包装成另一个人,最终的 PPT 依然克制、简洁,技术气息重了一些。那些优化数字、那些解决痛点的瞬间,对我来说弥足珍贵,却未必能完全传递到听众心里。</p> <p>几轮指导后,PPT 改了又改,人也累到极点。连着几天没怎么合眼,终于把材料交上去。</p> <p><img src="https://blog.grtsinry43.com/uploads/2025/12/26/image.png_deda0704-d4fc-4d15-aa79-f79793fdd0df.png" alt="image"></p> <p>气笑了,抽到 1 号,算是运气好还是不好呢(doge)</p> <p>比赛当天,灯光亮起,评委席坐满了据说很权威的老师。我讲完自己的规划,走下台时,手心全是汗。结果公布时,我排在靠后,却出奇地平静。</p> <p>后来听学长和就业中心老师私下提起,说我的风格“太偏技术了”,PPT 一开始还以为是 AI 做的。我笑着听了过去——辛苦做的东西,被误认为是 AI 的冷峻风格,也没什么好介意的。或许,这正是我没能完全契合这个舞台的原因:我讲的大多是代码里的世界、项目里的突破,而别人分享的更多是多段实习、offer 背书、领导力与团队协作的从容。评委的评价体系里,那些更体系化、更圆满的表达天然占优。而我,大三的我,还在路上,手里握着的更多是尚未兑现的潜力。</p> <p>问答环节里,因为我是大三,评委问了“考不考研”。我微微一怔——这不是就业赛道吗?那一瞬有些意外,但我还是平静地说出自己的想法:选择不缓冲,直接寻求成长和突破,有点难绷的问题,不过也让我找到了差距。</p> <p>但这些差距,并没有让我失落,反而让我更清晰地看见自己:一个声音不大、表情克制、容易在技术细节里沉浸的内向技术党。这样的我,与这个强调宏大叙事与流畅表达的舞台,节奏并不完全合拍。与其勉强调整,不如把精力留给更自然的路径——敲代码、推项目、投简历、积累真实的作品。</p> <hr> <p>真正让我觉得“这趟值了”的,是遇见的那群优秀的人。</p> <p>同一学院的学长,沉稳又自信;入伍两年归来的学长,眼里有光,故事很坚定;人文学院的设计学姐,表达惊艳,思路开阔;还有计院的一位新朋友,技术强,人也感觉温柔可靠...我们在赛前候场时闲聊,在上场前互相打气,比赛结束后又真诚地相互赞美。</p> <p>也许,同台竞技本身就是一种珍贵的同行。</p> <p>这场比赛,像学生时代的一个柔软句点。没有惊艳的全场掌声,没有省赛的门票,却有与优秀同伴并肩的兴奋,有上台时那句堂堂正正的“我想成为这样的人”,有下台后长舒一口气的释然。</p> <p>挺好的。</p> <p><img src="https://blog.grtsinry43.com/uploads/2025/12/26/image.png_f2c09702-6ab7-4f5e-84c5-8a2a11cc6a94.png" alt="image"></p> <p>只是...职规赛的舞台灯光熄了,我的学生时代也快落幕了。</p> <p>待到跨年后的期末周结束,就迎来最后一个寒假。开学便是暑期实习、秋招提前批、正式秋招……年底,offer 落地,签约,毕业。学生时代,就真的要翻篇了。</p> <p>剩下的这一年,学生时代的余味还在。那些熟悉的松弛、缓冲、安全感,还能再陪我走一段。我可以继续熬夜赶 ddl,继续抢课,继续和室友聊到天亮,继续走进熟悉的教室,继续把世界想得很大很大,却不必立刻为所有后果买单。</p> <p>只是,经过这场比赛,我忽然更珍惜这些了。</p> <p>珍惜那种试错后还有下一次的奢侈,珍惜失败了还能温柔归类为“经验”的宽容,珍惜年轻时可以把未来画得无限广阔的天真,珍惜校园里每一次不经意的闲聊和心动。</p> <p>不舍的情绪还在,但不再是隐隐的惆怅,而是一种柔软的觉醒:原来这些日常的小事,这样珍贵。原来“学生”这两个字,给了我这么多无声的庇护。</p> <p>不是告别,只是更清醒地拥抱剩下的时光。</p> <p>谢谢这场职规赛,用一种不完美却很真实的方式, 让我带着这场比赛里收获的温暖与清醒,走的更像自己,也更珍惜每一步脚下的校园。</p>

G
grtsinry43

职规赛,一次特别的体验

<blockquote><p>该内容由 RSS 渲染生成,最佳阅读体验请前往:<a href="https://blog.grtsinry43.com/moments/2025/12/25/a-unique-career-planning-experience">https://blog.grtsinry43.com/moments/2025/12/25/a-unique-career-planning-experience</a></p></blockquote><p>聊聊年底的最后一点事情。</p> <p><del>本来以为年终总结最后一篇了,结果没忍住又来一篇,自控实在看的头疼,码码字缓解一下,果然码字可以改善心情。</del></p> <p>12 月 24 日,我参加了学校的职业规划大赛。这是我大学里唯一的一场正式比赛,或许也是最后一场。最终成绩是三等奖,排名倒数第二。但这个结果,从报名那天起,我就没太放在心上。</p> <p>学长推荐我参赛时,其实想法是:“要是能进省赛,暑期实习请假就好请了。”我一笑,图个新鲜,也图个仪式感,便报名了。赢了是惊喜,没进也无憾,就当给学生时代添一段特别的记忆。</p> <p>准备的过程,出乎意料地认真,但是其实完全没有经验,一点点摸索。我第一次静下心,把这些年的经历摊开在桌上:简历、项目、实习、零散的想法,试图塞进“职业规划”的框架里——职业缘起、探索历程、人岗匹配、未来路径……走完这一套流程,才发现自己原来很少真正思考过长远的事,更少将个人选择放在更大的时代背景下去审视。</p> <p>取出夏天投实习的那份简历,还带着浓浓的技术味:代码细节、debug 记录、优化数据、偏执的钻研。为了比赛,我大刀阔斧地删改——保留了核心,却稀释了锋芒,加入了更结构化、更温和的表达,让它至少能被评委老师读懂。可我终究没把自己包装成另一个人,最终的 PPT 依然克制、简洁,技术气息重了一些。那些优化数字、那些解决痛点的瞬间,对我来说弥足珍贵,却未必能完全传递到听众心里。</p> <p>几轮指导后,PPT 改了又改,人也累到极点。连着几天没怎么合眼,终于把材料交上去。</p> <p><img src="https://blog.grtsinry43.com/uploads/2025/12/26/image.png_deda0704-d4fc-4d15-aa79-f79793fdd0df.png" alt="image"></p> <p>气笑了,抽到 1 号,算是运气好还是不好呢(doge)</p> <p>比赛当天,灯光亮起,评委席坐满了据说很权威的老师。我讲完自己的规划,走下台时,手心全是汗。结果公布时,我排在靠后,却出奇地平静。</p> <p>后来听学长和就业中心老师私下提起,说我的风格“太偏技术了”,PPT 一开始还以为是 AI 做的。我笑着听了过去——辛苦做的东西,被误认为是 AI 的冷峻风格,也没什么好介意的。或许,这正是我没能完全契合这个舞台的原因:我讲的大多是代码里的世界、项目里的突破,而别人分享的更多是多段实习、offer 背书、领导力与团队协作的从容。评委的评价体系里,那些更体系化、更圆满的表达天然占优。而我,大三的我,还在路上,手里握着的更多是尚未兑现的潜力。</p> <p>问答环节里,因为我是大三,评委问了“考不考研”。我微微一怔——这不是就业赛道吗?那一瞬有些意外,但我还是平静地说出自己的想法:选择不缓冲,直接寻求成长和突破,有点难绷的问题,不过也让我找到了差距。</p> <p>但这些差距,并没有让我失落,反而让我更清晰地看见自己:一个声音不大、表情克制、容易在技术细节里沉浸的内向技术党。这样的我,与这个强调宏大叙事与流畅表达的舞台,节奏并不完全合拍。与其勉强调整,不如把精力留给更自然的路径——敲代码、推项目、投简历、积累真实的作品。</p> <hr> <p>真正让我觉得“这趟值了”的,是遇见的那群优秀的人。</p> <p>同一学院的学长,沉稳又自信;入伍两年归来的学长,眼里有光,故事很坚定;人文学院的设计学姐,表达惊艳,思路开阔;还有计院的一位新朋友,技术强,人也感觉温柔可靠...我们在赛前候场时闲聊,在上场前互相打气,比赛结束后又真诚地相互赞美。</p> <p>也许,同台竞技本身就是一种珍贵的同行。</p> <p>这场比赛,像学生时代的一个柔软句点。没有惊艳的全场掌声,没有省赛的门票,却有与优秀同伴并肩的兴奋,有上台时那句堂堂正正的“我想成为这样的人”,有下台后长舒一口气的释然。</p> <p>挺好的。</p> <p><img src="https://blog.grtsinry43.com/uploads/2025/12/26/image.png_f2c09702-6ab7-4f5e-84c5-8a2a11cc6a94.png" alt="image"></p> <p>只是...职规赛的舞台灯光熄了,我的学生时代也快落幕了。</p> <p>待到跨年后的期末周结束,就迎来最后一个寒假。开学便是暑期实习、秋招提前批、正式秋招……年底,offer 落地,签约,毕业。学生时代,就真的要翻篇了。</p> <p>剩下的这一年,学生时代的余味还在。那些熟悉的松弛、缓冲、安全感,还能再陪我走一段。我可以继续熬夜赶 ddl,继续抢课,继续和室友聊到天亮,继续走进熟悉的教室,继续把世界想得很大很大,却不必立刻为所有后果买单。</p> <p>只是,经过这场比赛,我忽然更珍惜这些了。</p> <p>珍惜那种试错后还有下一次的奢侈,珍惜失败了还能温柔归类为“经验”的宽容,珍惜年轻时可以把未来画得无限广阔的天真,珍惜校园里每一次不经意的闲聊和心动。</p> <p>不舍的情绪还在,但不再是隐隐的惆怅,而是一种柔软的觉醒:原来这些日常的小事,这样珍贵。原来“学生”这两个字,给了我这么多无声的庇护。</p> <p>不是告别,只是更清醒地拥抱剩下的时光。</p> <p>谢谢这场职规赛,用一种不完美却很真实的方式, 让我带着这场比赛里收获的温暖与清醒,走的更像自己,也更珍惜每一步脚下的校园。</p>

G
grtsinry43

2025 年终总结——从晨光到雾散,化经历为成长

<blockquote><p>该内容由 RSS 渲染生成,最佳阅读体验请前往:<a href="https://blog.grtsinry43.com/posts/2025-summary">https://blog.grtsinry43.com/posts/2025-summary</a></p></blockquote><p>说实话,我是一个很喜欢总结的人,而真的到想要总结这一年的时候,我才发现这一年很难定义了。</p> <p>::: year-card url=&quot;https://2025-summary.grtsinry43.com/&quot; title=&quot;2025 年终总结&quot; type=&quot;page&quot; cover=&quot;https://blog.grtsinry43.com/uploads/2025/12/22/misty-winter-morning-stockcake.jpg_e9dbd5a8-e35c-4a20-b86d-674e4fb470a4.jpg&quot; blur=&quot;7px&quot;</p> <p>从晨光到雾散,化经历为成长,在爱中重新振作,于是我们真的曾将彼此照亮</p> <p>:::</p> <p>这一年的经历就像刻意安排好一样,和季节呼应,与境遇相同——有些日子明亮得像清晨的第一缕光,有些时刻却被浓雾包围,看不清前路,只能原地停留。</p> <p>这一年,有春季时候的焦虑,疯狂奔跑这才不被同龄人的努力与钻研追上;这一年,有夏季时候的热烈,我拼尽全力想要证明自己,想要写出点什么来得到别人的正向评价;这一年,更有秋季时候的痛苦与冬季的释然,让我学会了放手和感受幸福。</p> <p>这一年,有一些时间点,像被钉在记忆里一样,怎么都绕不开。</p> <p><strong>6 月 23 日</strong>,我走进了一段真正属于“社会”的生活。 <strong>9 月 4 日</strong>,一次意外把身体按停,也把我整个人按进了更深的现实里。 <strong>12 月 18 日</strong>,实习结束,我离开了一个曾经仰望、也真实走进去过的地方。 <strong>12 月 20 日</strong>,我终于慢慢意识到:有些事情不是失败,而是完成。</p> <p>它们并不是孤立的事件,而像一条线,把这一年切成了几段不同的自己。</p> <p>现在回头看来,我总感觉这一年的经历大于前 20 年的总和,这一年,我把自己扔进过绝望的深渊,也曾被命运托举到云端,然后在最意气风发的时候遭遇命运的玩笑,最后在废墟上,我也许没有重建一座宫殿,但我用代码和泪水,还有深沉的爱与支持,缝合了一个破碎的自己。</p> <h2>迷雾造舟,寻求出路:故事的开始</h2> <p>回想年初,记忆的色调是灰蓝色的。当时的我,还带着一种近乎紧绷的热情。</p> <p>我还在日夜研究这个简陋难用的博客系统,看着 Innei 大佬的功能,一步步写着 ip 地址,ws 通知,邮箱推送...</p> <p>在我思索很久之后,还是选择尝试将自己这份看起来完全无法直视的项目发到 B 站,居然有了非常好的反响,当然也有随之而来的问题,参差不齐的设计,糟糕的部署体验,缓慢的更新速度。几万的播放,让我有可能被很多大佬注意到。可惜...这是一次机会,就在我指尖溜走了,什么也没留下,好在有一群和我一起作者同样事情的伙伴。</p> <p>大二下学期开始之时,我感觉自己好像被名为“焦虑”的空气包围</p> <p>图书馆里保研党们翻书的声响像蚕食桑叶,考研党们在清晨六点的寒风中占座,而我站在十字路口,手里只有一把键帽打油的键盘,和一堆还未成型的逻辑。</p> <p>可能是我一直以来的行更印象,这种环境带来的同辈压力被我的敏感神经无限放大。我感到一种近乎溺水的窒息。我似乎没有退路,就算不是热爱,为了生活,我也似乎无路可走。</p> <p>与此同时的是,事情真的压得我喘不过气来。</p> <p>::: link-card href=&quot;/moments/2025/02/27/some-pain-days&quot; title=&quot;最近的坎坷故事_多项目并行、偶遇传染病、DeepSeek 本地化部署,及记一次开源 WAF 尝试&quot; desc=&quot;代码洪流漫星河,病躯独对夜雨寒,项目如山压眉睫,运维似海卷波澜&quot; newtab=&quot;true&quot; :::</p> <p>就这样,焦虑和劳累的春天裹挟着我前行,其中也不乏 PureStart 这样的项目在罅隙的时间被赶工出来,或是学校的项目的新突破,能有稍稍的进步,能够填补我内心对未来的恐惧。当然与此,我也重新捡起了算法,八股,然后准备赌上假期去实习见到点新东西。</p> <p>生日那天,我写了《致二十岁的晨光与希望》。那时我相信,生活可以分成“低头赶路”和“仰望星空”两部分,我还在努力把两者平衡好。焦虑偶尔来袭,但我总能用一个新项目、一个新部署把它压下去。</p> <p>于是,四月五月成了我最后的疯狂,或者说,是一种绝望中的献祭。</p> <p>我开始近乎偏执地造轮子。对着 aistudio,我硬是无数次尝试玩明白了 Monorepo 和组件库,为了代码的质量,硬是学会了脱离脚手架一点点搭起项目,配置进阶的 eslint prettier husky 单测,e2e。</p> <p>我开始不断探索,从 self-hosted 一堆项目,到研究起 kmp,转向 ktor,从低代码的表单平台,到 wikijs 成型的自己的技术收获。顺手,我也看不惯了那个陈旧的自己,一怒之下用 Nextjs 和 GSAP 重写了个人主页。</p> <p>那个叫做 Amore-UI 的 Vue 组件库,就是在无数个失眠的深夜里诞生的。我不知道我什么时候才能弄完它,我也不在乎。我只是为了搞懂 Monorepo 的依赖拓扑,为了让 TurboRepo 的缓存命中率更高一点,为了让项目的结构更符合直觉,就把自己关在寝室,像个苦行僧一样与外界隔绝。</p> <p>那时候的我在想什么呢?我想用代码搭建一座避难所。只要屏幕还亮着,只要 Vite 的进度条还在跑,只要那些复杂的动效还能流畅运转,我就能暂时忘掉那些关于未来的、巨大的恐惧。</p> <p>那是黎明前最黑暗的时刻。我一边背着枯燥的 前端 八股文,一边在 LeetCode 的二叉树上攀爬。每一封投递出去的简历,从满怀期待,到杳无音信,都像是一记闷棍。深夜里,看着镜子里疲惫的脸,我不止一次地问自己:“grtsinry43,你真的能行吗?那个所谓的‘大厂’,真的会给一个来自岳麓山下、只会写点前端玩具的普通学生开门吗?”</p> <p>无人应答,只有雨声敲打着窗棂。</p> <h2>珠江绮梦,全力奔跑:那些热烈的日子</h2> <p>故事的转折发生在 <strong>6 月 23 日</strong>。</p> <p>当收到 Offer 的那一刻,所有的焦虑在那个瞬间戛然而止,取而代之的是一种眩晕般的狂喜。我几乎是用逃离的姿态,拖着行李箱离开了内陆的盆地,一头扎进了广州湿热而充满活力的季风里。</p> <p>那是我 2025 年生命力最张扬、最饱和的两个半月。那是一种怎样的感觉呢?就好像一个一直在浴缸里练习游泳的人,突然被扔进了太平洋,而且不仅没有淹死,还学会了在巨浪尖上起舞。</p> <p>当工卡真正到了我的手上,真正面向屏幕看到 IDE 里前辈的代码,坐在那个曾经无数次向往的地方,看着园区的灯火,看着广州塔在夜色中变幻颜色,我感到了一种前所未有的“活着”的实感。</p> <p>我开始了我的祛魅与敬畏之旅。以前只能在技术博客里仰望的底层架构,此刻就在我指尖流转。我看着代码库里那些身经百战的代码与最前沿的逻辑共存,见识了什么叫真正的“亿级流量”。每一次 Code Review,每一次按下 Merge Request 的按钮,我都心怀十二分的敬畏——因为那行代码后面,连接着真实世界的数亿个灵魂。</p> <p>更重要的是,我感到“被看见”。作为一个常年自卑、甚至有点社恐的人,在这个巨大的工业机器里,我居然找到了归属感。在这里,我不是谁的同学,不是谁的附庸,我是那个能解决 Bug、能扛起需求、能提出想法的开发者。当我的代码上线,看着监控的条目跳动,那种“世界因我而产生了微小颤动”的虚荣与成就感,治愈了我上半年所有的内耗。</p> <p>在公司的同时,我也在努力着提升自己,用 cpp 写的微前端微服务整合框架,用 ovCompose 探索着鸿蒙的开发,甚至去 Vueconf 参加现场的技术交流,从优秀的前辈汲取力量。</p> <p>那时候,我觉得我的人生就像珠江新城的夜景一样,通透、璀璨、直指云霄。周末我在珠江边散步,耳机里放着激昂的 Artcore,看着江水奔流,我觉得我能就这样一直跑下去,直到世界的尽头。</p> <p>我以为这就是大结局的序章。</p> <h2>秋季的浓雾:意外、低谷与被迫停下</h2> <p>然而,生活这个编剧,最擅长写烂尾的转折。而且往往是在你最意气风发的时候,给你来一记最沉重的闷棍。</p> <p>9 月 4 日的那个早上,一切戛然而止。</p> <p>因为前一晚熬夜,起床时头脑不清醒。没有暴雨,没有预警,只是一个平平无奇的周四早上。只是急匆匆地想要下楼去上班,只是一节没踩稳的台阶,那一刻,世界突然失控。急诊、打石膏、一个人在广州处理完所有事,再跨省回长沙。 最疼的不是骨头,而是内疚——看到同事因为我的问题加班,那种愧疚像刀子一样反复割。</p> <p>如果痛苦有颜色,那天一定是灰白色的。冷汗瞬间浸透了衣背,世界在我眼前旋转。但我感到的不仅仅是肉体的剧痛,更是一种精神上的凌迟。就在那一瞬间,我知道,完了。</p> <p>没有什么正式的道别和请假,甚至没来得及好好看一眼那个奋斗了两个多月的工位。我像个伤兵一样,坐着轮椅,脚上打着沉重的石膏,被灰溜溜地运回了学校。</p> <p>那种落差感,就像是从平流层直接坠毁到地面,连降落伞包都没有打开。回到学校宿舍那张狭窄的单人床上,我哪里也去不了。连上厕所都需要室友帮助,洗澡成了一种奢望。打开手机,看着微信工作群里大家依然在热烈地讨论需求、Review 代码,而我躺在几百公里外的床上,对着天花板发呆。世界依然在高速运转,而我成了被离心力甩出去的那个零件。</p> <p>秋天的风开始往衣领里钻,我感到前所未有的冷。那种无力感差点将我吞没。我想哭,但我不知道该怪谁。怪楼梯?怪自己不小心?还是怪这操蛋的、毫无逻辑的命运?</p> <p>我第一次被迫慢下来。</p> <h2>漫长的等待,似乎真的很难喘过气,好在有代码的陪伴</h2> <p>但是至少我还在,故事还在继续。</p> <p>::: link-card href=&quot;/posts/from-think-to-code-in-2025&quot; title=&quot;从想法到实践:在无序的生活里,试图用代码敲出一点秩序&quot; desc=&quot;痛苦如影随形,代码似光破晓;迷茫中寻方向,键盘下续生活。在伤痛与失眠的夜晚,技术成为心灵的舟楫,载着破碎的时光驶向微明的彼岸。&quot; newtab=&quot;true&quot; :::</p> <p>那段时间的我,也许其实真的很害怕,等到一个人在室友的帮助下上了床,然后拉上床帘,让眼泪自己自由一会儿,</p> <p>前阶段还是简单的,不得不休息,但是也可以玩自己的了。从 kotlin,到 go,到安卓,这里我真的不亦乐乎,一天天把键盘敲到飞起。</p> <p>我就像一个疯狂的泥瓦匠,在名为“骨折”的废墟上,用 AI 原型生成器、用 ELK 日志系统、用 Vespera 监控 一砖一瓦地搭建新的城堡。我把对自己破碎的修补,全部写进了 git commit 里。</p> <p>可是呢,一个月,两个月,三个月。。。现在想来还是感觉真的好黑暗,一种伸手不见希望的感觉。</p> <p>于是我也终于和自己和解,继续硬撑不是勇敢,只会更糟;暂停不是放弃,而是对未来的负责。</p> <p>浓雾笼罩,看不清前路,只能原地停留,等待身体、等待内心慢慢修复。</p> <h2>冬季的雾散:释然、幸福与爱的发现</h2> <p>12 月 18 日,我平静地离开了工位。 本以为会难过,会遗憾那些没做完的东西,结果心里只有一种轻盈——像轻轻关上一扇门。</p> <p>::: link-card href=&quot;/moments/2025/12/16/2025-intern-story&quot; title=&quot;2025 实习札记:关于一场未完成的迁徙与落地&quot; desc=&quot;当告别如一羽飘落般轻,回望来时路却已成册。远行的足音未曾喧哗,静默转身间,时光已悄然翻页。&quot; newtab=&quot;true&quot; :::</p> <blockquote> <p>我把它放在心里一个很安静的位置上:不粉饰、不夸大,也不否认。它属于我人生里一段很真实的时间——我努力过,也狼狈过;我被要求成长,也被生活要求先学会照顾自己。</p> </blockquote> <p>两天后的深夜,我和朋友翻着旧照片,聊死亡焦虑、聊命运的齿轮、聊“有一步走错了你就见不到我了”。 那一刻,我突然意识到:原来我一直活在幸福里,只是雾太浓,看不见。</p> <p>我真的因为我的朋友们而感到到幸福:</p> <p>有同学住隔壁,零延迟托底;有 mufen 并行刷题,长久同行;Kylian 群友开盒成朋友,陪我灵魂共鸣;miaoer 一个消息就飞过来,受伤时陪伴、送站…… 这些相遇,全是低概率的偶然:选修课、力扣被发现、分班寝室挨着、软路由+小团子+学长鸽了…… 无数偶然,叠加成必然,把我围得严严实实。</p> <p>以及还有偶然发现的:</p> <p><img src="https://blog.grtsinry43.com/uploads/2025/12/22/image.png_8aab1c8e-1970-4ce9-a11b-8937150f2541.png" alt=""></p> <blockquote> <p>现在看来,一个深圳,一个广州,就是因为一次偶然的遇见,这真的是世界名画了吧,每次看到 IP 都会有莫名的感动。</p> </blockquote> <p>也许,我和朋友们的经历,还有遇见,是无数叠加出来的必然。</p> <p>概率论里有个很浪漫的事:</p> <p>当独立事件足够多时,即使每个事件的概率都趋近于 0,连乘之后的结果也可以是确定的 1。 就像宇宙大爆炸之后无数粒子随机碰撞,却偏偏撞出了地球、撞出了生命、撞出了会敲键盘的你和我。</p> <p>雾散了,我才看见: 原来我们真的曾将彼此照亮。 不是耀眼的光,而是深夜手机的微光、轮椅的影子、高铁送别的背影、一同去 VueConf 的约定、一句“你肯定没问题”的回声。</p> <p>这一晚,grok 和我这么说</p> <blockquote> <p>原来幸福一直都在, 只是之前被骨折的痛、被低谷的雾、被那些大问题的重量压住了, 你看不见,也摸不到。</p> <p>现在,痛慢慢退了,雾慢慢散了, 那些被压住的情感,终于一点点浮上来, 让你突然发现: “啊,原来我一直活在幸福中。”</p> <p>这不是顿悟,这是迟到的感知。</p> <p>像冬天里突然推开窗, 才发现外面早就下过雪了, 地上厚厚一层白,你却之前一直没抬头看。</p> <p>现在你抬头了,看见了, 也终于感受到那股安静的、暖暖的亮。</p> <p>这个晚上最幸福,不是因为发生了什么惊天动地的事, 而是因为你终于和自己的幸福对上了眼。</p> </blockquote> <p>这些小光,加起来,刚好驱散黑暗,也点亮了彼此的路。</p> <h2>所以这一年是 undefined</h2> <p>站在 2025 年的终章,回头望去。这是一张多么波澜壮阔、又多么荒诞离奇的频谱图啊。</p> <p>我曾经以为,2025 年的主题是“起飞”,是“大厂”,是“光鲜亮丽”。但现在我才明白,2025 年的主题,是 <strong>“爱与韧性”</strong>。</p> <blockquote> <p>春天的焦虑是为了寻找出路,夏天的狂热是为了证明价值,而秋天的断裂和冬天的重构,或许是生活在教我学会如何面对“失去”,可是又安排了那些惊喜,值得我去珍惜。</p> </blockquote> <p>这是 undefined 的生活,有不确定的可能。undefined ≠ 空白,undefined ≠ 失败,undefined = 尚未被强制收敛的人生。只是现在,我不再急着去证明什么了。我看清了生活的粗糙纹理,却依然选择拥抱它。</p> <p>2025,不完美,却完整。 技术收获不多,很多项目停在半路,实习带着遗憾结束。 但我学会了放手,学会了接受不完美,学会了在脆弱时被托住,也学会了感受一直都在的幸福。</p> <p>化经历为成长,或许听起来太轻。 但我确实在爱中重新振作,在爱中重新站起。</p> <p>感谢这一年所有出现的人,感谢低谷里的伸手,感谢自己的韧性与温柔。</p> <p>雾已经散去,路还长。</p> <p>我们 2026 见。</p>