MoreRSS

site iconGuoqing Zou修改

前端工程师,腾讯、MoeGo,业余维护技术社区,关注CS学习氛围和开源文化。技能涵盖Web领域,包括JavaScript、TypeScript、PHP等技术栈。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

Guoqing Zou的 RSS 预览

2024

2025-02-16 14:05:00

转眼一年又过去,又到了一个回顾自我的时间,过年假期各种事情都暂放一段落时,感触也是最深。

总的来看,这一年做了不少尝试与突破,不过主线上没大的变化,总的来说处于一个稳中求进的状态,也算收获不少。

和往年一样,试着分几个主题,整理一下一年来的尝试与心得吧。

自我

24年的关键字大概是“收敛”,希望自身的有限能量能进一步聚焦于当下行动上。和 @Muyun 聊起这个话题,他提到了“找到自己的时区”,或许也可以说,怎么样让自己的“时区”变得明确和清晰一些,在与外界的互动中避免被过多地带着跑。

比较重要的是自身价值体系方面的收敛,在去年已有一些初步的想法,今年更侧重于执行层面。面对一些事情,我需要有更明确和坚定的选择和决策,而非刻意为了外界认同,去看轻自己、迎合对方而导致内耗。相较于内耗,取而代之的是坚定自我立场、与他人的观念的互相博弈与平衡。这也要求我自身的价值体系需要足够的健壮,包括不限于对待知识、对待人与人的关系、对待金钱、对待理想与现实、长期主义与短期回报的平衡等等… 在面对不同价值观念的冲击之时,可以让自己能站住脚,即使不认同,也能允许它的存在,求同存异,博其所长去完善自己。

在价值体系有所形成,随着人与人的“磁场”作用,进一步会影响着自身的朋友圈子的更迭。邓巴数理论告诉我们,人的社交圈的上限大概 150 人左右,更亲密的朋友家人可能 20 人不到,也意味着我们无形中会有一个筛选过程所产生的“磁场”所在。随着自身价值体系的变化,这一“磁场”也会发生作用,带动这 150 的圈子一起变化。这里需要的也是,将自身的朋友圈子收敛到 150 内,不必太刻意去扩张,也不必去刻意迎合观念不 match 的人,更多交给缘分,我也相信一点是,缘分和“磁场”会让合适的人相遇。

另一点在做的是知识体系方面(尤其是技术)的收敛,我在这方面的积累更多来自于 Blog 和笔记工具,24 年主要是做了 Blog 的重建,试图通过 Blog 和笔记工具等,抓住一些属于我自己的深耕领域。在知识体系的完整性方面,人相比于 AI 而言并没有任何优势;但 AI 相比于人而言,它最大的缺点也是它没有自己的倾向,它的能力更多来自于已有资料的整合。而人的特点在于,每个人的成长经历、社交圈子、知识积累等等都是独一无二的,因此对于个人来说,面对 AI 的普及,保持一个收敛、专注的知识体系就变得更加有必要。

这里也有一点点“内核修炼”的意味,关于个人的价值观、信念、认知、性格、内在的动力等等方面的选择与积累。用巴菲特的“厚雪长坡”的话来说,重要的事情也是找到自己的“核”,再去找到厚厚的雪、长长的坡,这样才好让自己的人生雪球越滚越大,而非反复横跳,损耗自己原本就不多的能量。

当然,“内核修炼” 是个非常长期的话题,不止于 2024,今后的日子我还会再继续思考和完善这方面,适当地付出合适行动。

工作

Dev

这一年在工作上有了更多的锻炼,属于是往“工程师”方向更进了一步。23年是从一个相对发散的 P 人思维,逐渐收敛到可以抓住重点、集中有所产出的状态;24年在项目推进方面,感觉方法模型上正在逐渐走向成熟,基本和我之前写的 Research & Development Engineer 模式比较贴近,可以比较好地把整个流程能转起来。一整年主要推进了一些模块的业务需求改造,穿插一些技术层面的优化,属于一个稳健平滑增长的状态,结果上也还可以。

整体节奏相对偏忙碌,但比较好的一点是,在 leader 支持下有一定的 buffer,会有更多主动发挥的空间。下半年新的研发老大的加入,基于 Jira 跑起了一个更完善的敏捷开发和工作量衡量的机制,在思维模型上和我之前基于 Logseq 搭的 任务管理体系 可以比较无缝地链接在一起,但会多一点类似 Sprint 冲刺、Backlog 池子的概念,以及辅助排期用的大致工时的估算方法等。

在这新的体系下,不是直接写代码的方案评估、技术调研、文档梳理沉淀等“杂活”也可以作为工作量被认可,加入排期节奏,非预期的加班情况也随之减少许多,风险可以提前暴露和调整,产出上相对更透明,总体还是挺爽的。

技术上,24 年的我在侧重发力在重交互逻辑的 GUI App 的开发,有意识地去对 React 及其生态有一个抽离的观察,并尝试从中去总结相关的思维模型。从目标而言更侧重于 “用户故事” 的代码落地,技术实现上,主要是一个基本的思维模式(浅谈 GUI 应用开发),在 React 体系落地的经验(关于 React App 业务逻辑的组织),进一步还会继续关注 CSS 背后的盒子、布局模型等 (这里待一个总结文章)

下一步的想法大概也是,希望这方面的思维模型可以足够稳定沉淀下来。一方面能给身边同事朋友带来更多有价值的参考;另一方面我希望我再去做相关的方案设计和实现时,可以有更多直觉化的思考,不费太多额外心力,能有余力做更多的事情。

Culture

一个直觉感受是,这一年的 MoeGo 扩张得非常快,从 Grooming 扩展到 Boarding、Daycare 等更多 pet care 业务以后,团队也从原本三四十人扩大到了上百人的规模。对于一个公司的企业文化而言,也面临着比较大的挑战。关于组织文化建设的侧重点,是侧重于一个互相关心的有温度的“大家庭”,还是侧重于没有感情的效率向的纯纯“赚钱机器”的模式?能感受到的是,这个极速扩大后的组织,在潜移默化之间,正发生着从前者向后者的转变。

进一步会让我思考一个话题,关于文化与商业的互相利用的关系。商业可以为文化所利用,带来更强的驱动和激励力量,创造更适宜的环境去接近使命与愿景;文化也可以为商业所利用,合适的文化导向可以促进效率的提升,产生更大的金钱收入。比较遗憾的是,后者往往会更容易占上风,毕竟对于商业公司而言,第一顺位的事情还是“高效赚钱”,对于一个“大家庭文化”而言,一旦赚钱的速度慢下来,加之经济下行、外部环境带来的压力,作为老板可以说很难有足够的耐心去等待文化内核的灵魂生长,而倾向于更高效率低成本的“赚钱机器文化”。

因此对于一个参与商业活动的个体而言,即使这个环境标榜着 WLB 等标签,对于自身的商业价值的把握仍然是个核心关注点。经历过裁员的 背刺,在这方面我会更加敏感,当自身的价值输出少了,意味着是一个比较危险的信号。也许也是现实的一个残酷之处,当然,面对这样的矛盾,更希望的是一切可以发生得平缓一些,在利益世界的博弈与取舍之间,可以多一些温度、关怀和安全感(某种意义上或许也是 HR 的职责所在)。

个人而言,尤其是在深圳这一原子化程度相当严重的城市生活,更缺少的是文化层面的归属感,而非纯效率导向的赚钱。一方面赚钱更多只是个人生活的一部分,而非生活的全部,需要一些边界感;另一方面,文化层面的建设可以带来除金钱外更多维度的价值的流动,会是更长期健康能带来复利的状态,同时也能反作用于金钱的增长,而非一次性榨取完就跑路、韭菜割完一茬又长一茬的竭泽而渔。

Action

接下来从行动上来看,更多是思维上的转变,从纯过程任务向式的 “打工”,走向一个结果导向的 “价值交换” 思维。

比如说相比于 996 加班 “看起来很忙” 的 “牛马模式”,更重要的是在目标驱动下,合理的项目管理、风险预判和控制能力。在工作中更重要的也是,聚焦于任务的核心价值,注重行动的有效性,直戳要害,在合适的状态下,更好地拿到结果,让付出真正有所回报。

在团队合作中,注重一个价值输出的链条 / 网络,先能把合作上下游串起来,产生正向的反馈去支撑自己的存在,争取到一定的自由空间。当这样的信任与口碑累积得足够多,我们才有机会以此为力量,去推动和影响身边的环境,往自己想要的状态更进一步。一句话总结或许可以称之为,“用更大的价值去影响价值观”。

技术

Web App 开发

和 23 年一样,在技术学习方面的积累主要也是发生在工作之内,精力更多集中在以 React 技术栈为中心的 Web App 的开发上,前文已有聊到。接下来的方向估计也是继续抱紧 Web 的大腿,研究一波复杂交互逻辑的高质量实现,进一步引申出相关的思维模型。前端技术日新月异,实在让人疲劳,需要的是抓住更加本质和核心的部分。

当前大家对于前端的理解,更多会集中在 React / Vue 等 UI 库生态下架起来的各种 Web 应用的层面,而对于 UI 本身的侧重则相对较少,毕竟大部分时候,只要 flex 一把梭糊上去,能看能用就够了。但当你想要跳出 Web 的世界,到了构建 Native App 的层面,关于布局、UI 细节等,如何做到高质量还原,一下就多了许多细节需要关注,很难通过三言两语能完整表达。

进一步去探索会发现,这些仍然还是 HTML5 的知识体系所能涵盖的内容,就如当年 Flash 主导的 Rich Internet Application 的概念的延伸,它们并未离开 HTML / CSS / JavaScript 三剑客的范畴。对于 UI 的实现,CSS 虽带着许多糟粕,语法容易互相耦合影响导致难以维护,但其体系的背后想要实现的各种 UI 模式,在 CSS 基本都有比较好的覆盖,因此从这些 UI 模式出发去学习,仍然会有很大的价值。这里更难的事情在于,不同的 UI 需求如何融合在一套体系下实现,即使写了多年前端的我,也只能抓住一些日常比较重度使用的部分去入手。

日常 UI 开发,最核心的事情在于“布局”,其次才是各种样式效果的实现。带着对于“核心”的探寻的目标,返璞归真,我开始去尝试啃一些 CSS 的 spec 文档,关于其中各种 module 所关注的布局模式,把一些仅在直觉层面用上的东西,化作更具体的概念深入于心。

具体来说大概是,从 Visual Formatting Model 为核心,以 Box Model 为基础,关注一个盒子内外的布局模式,引申出 normal flow (block / inline)、flex、grid、table 等布局模式和相关的布局上下文;再进一步延伸出有机会跳出原布局模式的 positioned layout;以及 stacking contexts,overflow 和 scroll;还有复杂的 box sizing 的定义和计算、align 对齐的规则、字体渲染、坐标系统、continuous media 和 paged media 的考虑等等;再往深入去看可能会关注 Flutter 相关的技术栈,看它和 Web、RN 等对比之下的异同和取舍。

上面聊到很多对于单个页面的布局实现,如何架构起更复杂的多页交互尤其是移动端的交互,也是个值得思考的话题。桌面的弹窗交互比较直观,暂且不提。相对不那么直观的 Mobile App 相对要复杂许多,前段时间发现 Ionic Framework 在这方面有做不少工作,比如说路由、页面栈、Tabs、切换的动画、常用的全局组件等等,也许也可以借助这个框架去架构起自身在 Mobile App 方面 UI 交互的思维模型。

基于此,再结合 React 及其生态对于整个 App 的组件划分、UI 渲染、状态管理的思路,在 Web App 乃至于整个 GUI App 方向的开发,应该可以将这个领域完整的思维模型建立起来。无论工作内外,抑或是想做一些自己的 App,都能有一个比较好的支撑,这也是我尝试去总结一系列文章的一个核心出发点。

AI 的影响

自 22 年底 ChatGPT 等 AIGC 产品问世至今,AI 的发展速度有些超乎想象,Deepseek 的爆火,更是让我领略了高质量大语言模型的一番魅力。本身我不喜欢去追逐热点(尤其是类似 X子位、XX 之心、X智源 等顶刊媒体,看多了让人焦虑),但也不知不觉开始依赖起了 GitHub Copilot 写代码,偶尔也会找 ChatGPT 帮助我做一些英文翻译,或者一些脚本生成的杂活,不知不觉它们也成为了我工作生活的一部分。

时常思考的一个问题是,AI 是否会取代程序员的工作,尤其是 Cursor 等代码生成工具的流行,日常的编码只需要和语言模型对话就可以完成,甚至不用太多人工的干预,就能产出比较高质量的代码。这无疑会带来一些生存焦虑,不过仔细思考下来其实还好,人依然有其独特的作用所在。

首先是明确 AI 究竟在做什么,结合它引入了大量的互联网、学术论文信息训练的过程,它更像是一个更高级的知识库实现。就如人的学习一般,通过自身的积累,不断批判性地输入、思考、优化自身的认知。用 DIKW 模型的理论来说,学习是在完成一个 Data -> Information -> Knowledge -> Wisdom 的转化过程。在计算机的世界,属于是大大加速了这一个过程的节奏,因为数据足够多,批判和检测之下走向 Knowledge 阶段的知识体系也越来越完整,到了一定程度,它所生成的信息的可靠性也会更增加。

显然在这里 AI 更像是一个高级搜索引擎,虽然生成有用信息的概率更高,但要真正用起来,还是离不开人的层面的牵引和背书,以及一个最终“拍板”负责的过程。如果直接用 AI 完全未接触过的陌生领域,使用者因为没有对应的知识基础,很容易淹没在信息的海洋中,并不好判断哪一些可信,哪些不可信。换句话说,即使是训练得比较完善的 AI,仍然无法很好地解决一个信任和托付的问题,它只能基于已有信息去做更多的推理和发散,而现实需要的是更多实战经验的积累,以及专注深入某个领域的状态。

由此,面对 AI 带来的信息爆炸,信息素养仍会是一个非常关键的能力。对于个人而言,更重要的是抓住信息的源头,基于第一性原理出发,基于最真实的信息和最基础的信任,去推导、判断和决策,而非轻信 AI 现编的结论,以免被误导。

又回到刚刚聊到的,关于 AI 乃至于未来的 AGI 是否会取代程序员乃至于其他知识工作者的话题,我想还是会有一部分。这里相当于将一部分低阶的知识型工作替代掉,原本属于它们的蛋糕,也切了比较大的部分给了大模型基础设施,虽然说总体的蛋糕可能会更大,但个体差距上会更加悬殊,这是一个略有些负面的影响。

进一步去看,AI 难以撼动的是个人深入某一个领域后,带来的直觉智慧。毕竟它更像是一个通用的模型,缺少个体视角的感知。因此,对于知识工作者而言,把手上的事情做到“精通”,有足够深厚的积累,外加一些信任与人脉的资源,AI 的存在更多会是一个“如虎添翼”的状态。

0xFFFF

近一年在坛子和社群的运营上更加佛系,我更多还是在忙自己的工作、学习自己的东西,然后适当地分享一些心得,更多时候是和朋友们去回答一些后辈的问题。中间有尝试增加一些不同的圈子的不同观念,事实上是失败的,反而会影响原生的氛围。因此来看,社群的人群定位其实更加重要,相比于啥都想要结果啥都没,更重要的是保持一个小而专注的圈子,适当做一些筛选。

其中更密切的是和 @Bintou 老师的图灵班的联系,尤其是 @sh 同学的大力支持,许多新的内容和分享、视频等等,主要也是他在支撑。这一年由于买了车的关系,出行动力大大增加,也得以多回了几次华师,找老师和师弟师妹们喝喝茶。体会比较深刻的也是文化的建设的重要性,年底 SCNU 图书馆搞了一个关于《黑客与画家》的分享,聊到了计算机文化的话题,老师对于核心教材的坚持,某个视角有点异类的存在,但时间也证明了,在计算机的行业,确实需要这样的坚持和视野,不至于局限于现有的框架之中而难以跳出。

24 年在内容上也有一些突破,我去年未完成的 CS 入门专题 终于完篇,在 X 上有比较好的反响。感动的是热心坛友 @johnbanq 将他读博数年对深度学习的 工程实践科研方法 的总结放于论坛首发,还有 @Tover 的密码学笔记等等,无形中也拔高了整个社群的技术深度之天花板。

在社区的经营上,困境依旧还在,和朋友也有聊起来。一方面是我个人的精力更多关注于自己的生活,在技术社区的分享欲有些下降;另外大家工作忙碌以后,对于一些比较不带回报预期的事情,做起来的机会成本也会逐渐增加,比如说有啥活动,如果只是单纯消磨时间的话,是很难有动力出来玩的。

最核心一点大概是就像去年说的,当下的我距离学生状态已经越来越远,很多事情已经习以为常,很难再从一个入门者的视角去尝试做些什么。年底我尝试回华师给图灵班师弟师妹去分享知识管理相关的内容,和老师聊下来,听闻一个班竟然能有一半的人挂科,一下有些震惊,毕竟当下的学习资源相较于我们过去,已经非常丰沛了。我想这更多是缺少原生动力导致的问题,更深层次也是“文化”层面的影响阻碍了行动。

对于社区未来的发展而言,一方面在文化上需要有更清晰理性的定位和愿景,另一方面需要投入实际资源去尝试推进起来。在文化的建设上,关注点可以不只放在学生,更多聚焦于身边的同行朋友,以及在各个方向有一定积累的人之上,创造一些交流的契机;在投入资源上,可以设计一个简单的商业模式,譬如对活动的收费,对于支持者的分成等,让大家能有更清晰的回报预期,提升主动性和积极性。行动上则从小的方面做起,形成一个反馈循环转起来,慢慢地让其越转越大。

更长远的视角来看,继续将社区的发展,和我个人的技术成长联系在一起,大概是更好的状态。短期内也在尝试去模仿孟岩的 有知有行,再结合内容和论坛形成一个 App,在技术上对我来说可以是一个比较好的练手项目(前后端都有涉及),这也是我当前正在尝试的事情。

生活

买车

考了驾照以后,隐隐约约感觉可能需要买辆车,尤其是这几年假期回到韶关,同学朋友约我去市区吃饭,从家里出来特别不方便。每年这个时刻对于买车最为蠢蠢欲动。毕竟还是大额支出,一直以来我本着一个观望的心态,不过真正看下来,下决定的过程来的要比我想象中要快许多。

选车的历程差不多 1 个月,热情师弟带我试驾各种新能源,从蔚来到特斯拉到极氪问界小鹏比亚迪等。对于油车我倒是没有什么执念,更喜欢电车给我的一种简洁省心的感觉。并且我没有任何想去卷参数的想法,感觉这样的比较没有一个尽头,尤其像我日常几乎大部分时间都在和技术打交道的生活状态,再比来比去,似乎整个世界都走向了参数攀比的单调之间,这是我最难受的一点。也如一个开 Model Y 的哥们和我说的,其实车最重要的是自己喜欢,参数其实都还好。于是乎,选择的参考也变成,平时主要一个人开,只要基本的动力、安全等配置做到位,其他只要舒服方便就可以。

最早规划个人财务时候,我给自己留了一个长续航版 Model Y 的预算,然后就在这个范围去看和比较。挑到最后,决赛圈只剩 Model Y 与蔚来 ET5T(当时小米 su7 还未发布)。但对于 Model Y 而言,感觉它内饰和车机功能差了点意思。并且特斯拉的行事风格有些过于激进,蔚来会更加自然有设计感一点,内心会更倾向于后者。某一天销售老哥说 24 款 ET5T 快出来,23 款有比较大的优惠。恰逢我的 25 岁生日,就当是给自己一个人生 25 周年礼物吧,还怪有纪念意义的... 于是就这么下了订,而后是提车、上牌、车位等一系列琐碎,就不赘述了。

ET5TET5T

ET5T 的体验上可以说比较丝滑,属于比较能感受到驾驶乐趣的车子,换电站加成下也没有续航和电池方面的焦虑,车机的地图、网易云等,都在同一个账号联动,和手机电脑等,可以比较无缝地结合。原以为只会周末出去玩时候开开,但实际开车频率有些超乎想象。刚提车从龙岗开回宝安 30km 没啥压力,第一周便开始了宝安大道和深南大道的来回通勤;周末上高速冲了广州,回来时和一宝马狭路相逢喜提 First Blood,第一次保险修车(这车确实太宽了);几个月来也跑了不少地方,深圳大鹏、龙华、龙岗、广州、佛山、中山、还有韶关的各个县城。各种路况都有尝试,开车技术也得到了比较大的锻炼,慢慢地也算是融入了生活的一部分。

开车这件事也给我带来一些潜移默化的影响。一方面是目标感会变强许多,过往由于我的感知力较强,很容易掉进一些无关紧要细节中,开车这件事需要一直专注于当下和目的地、排却杂念,对于我来说是一件非常解压的事情,潜移默化会影响到日常的行事;另一方面是对于距离的敏感度变得急剧降低,不像公共交通对于人、天气等会带着许多的不确定性,仿佛只要上车就到达了目的地。深中通道的开通,南山到中山的耗时与龙华龙岗几乎一致,但没有太多深圳的拥堵,总的来说还是很舒畅。这甚至让我开始考虑起未来在中山定居的可能性,综合经济、生活节奏、舒适度、地理位置等,对于我而言可能是更合适的选择。

买车这件事还带着另一层的意义,作为农村出来的孩子,家里原本无车,对于车子本身会带着一些滤镜所在,某个视角下,这里也完成了一个祛魅的过程,当下的我已可以比较平和地将其视作一个普通的代步工具。另外对于落后地区而言,汽车尤其是 BBA 会带着一种“奢侈品”属性,作为一个“面子”的象征,通俗来说带着一种“装逼”的成分。家人生活在一个缺少边界感、攀比心重的环境,不得不与一些眼界狭隘的邻居相处,无由头的欺负和吵架也时常发生。由此适当的作一些超出其认知水平的消费,可以作为“降维打击”的存在,让父母辈可以少一些被蛐蛐,多一些肯定和自信。当然这里更重要还是我自己喜欢,这种攀比更多时候在于价格数字,挺无聊的。

抛开“装逼”的意义不谈,进一步而言,对于车子的选择,背后带着很多品牌、文化方面的思考。比如特斯拉的科技感与激进风格,小米、小鹏主打的年轻亲民性价比,极氪的性能潮流向,理想的家用实用属性,BYD 和华为系所代表的体制友好风格,蔚来的设计导向、主打恰到好处的自洽和谐等等。这样的品牌差异,不仅仅影响造车,也会对于人群的定位方面带来一定的影响。在此蔚来做了许多发力,比如说构建车主社群、各种活动、NIO House、NIO Life 周边的产品等等,遇到的车友也都挺真诚友好,绝大部分都有着相当的认知和同理心,对于个人圈子的对外发散而言,是个不错的联结点。

某种意义上这也影响着我对于品牌和社群文化的理解,关于自我的进一步发展,还有技术社区的运营等等,会带来许多的参考。当然这里的核心还是在于一个定位、价值观念的问题,当我自己在这方面做得足够的稳定,在茫茫人海可以带来更多更有意思的相遇,以及故事的发生。

健身

健身属于一个老生常谈的话题,自 20 年毕业也将近三年多,体脂和体重一直居高不下,逐渐比较深刻意识到,作为一个日常对着电脑、鲜有做饭、外卖度日的打工人,若只依赖于日常的活动的能量消耗,是很难控制自己不发胖。需要加入一点主动健身的成分,在做健身教练的邻居哥哥的鼓励下,我也了开始这方面的尝试,回顾下来大致经历过三个阶段。

首先是得有一个运动的节奏,5月劳动节假期回来开始,晚上下班回到家就去附近宝体田径场跑圈,从一个小走一段就气喘吁吁的状态,逐渐变得自在许多。大致坚持了一个多月,感觉习惯是能练起来了。

接下来是尝试利用午休时间去公司楼下健身房去练一练,最早是跑步机爬坡为主的有氧运动。两三个月过去,对于减重的效果不是太大,不过比较好的是精神状态会改善很多,练完会有一种充满能量的感觉。

进一步自然是想要有更好的效果,这时候就走入了减肥的正道:提高自身的基础代谢,以及适当控制能量摄入。具体的执行而言,则是引入力量训练的成分,增增肌肉。恰好好同事推荐了一个名为“练练健身”的 App,相比于“训记”而言没那么硬核,感觉适合我的起步阶段。

然后我就按着 App 内的“高效减脂”的规划,开始了一个类似于“三分化”的训练计划,背 / 二头 + 胸 / 三头 / 肩 + 腿 / 腹 的循环,大致练了两个多月。年底再看报告,效果还是不错的,过年回到家,家人亲戚不经意间也说我瘦了,在多个不同角度都能收到相似的反馈,说明确实有练到而非纯客套,暗自高兴一下哈哈。

就减肥速度来看,相较于硬核减肥选手而言会差一些,不过在我没有刻意控制饮食的情况下,算是符合预期。所以下一阶段大概也是,更加系统地去了解健身方面的知识,更合理的方式去控制体重。

这一过程也给我带来一些关于学习新事物的体悟,当时写了一条朋友圈:

不仅仅是健身,在很多方面的学习都有着类似规律,从业余玩玩体会其中乐趣,到融入日常生活,再到产生正向反馈,然后才是引入专业的支持实现更系统地学习和精通。我想这也是如何实现“发自内心的主动学习”的一个心法。

书影音游

这一年的阅读量(准确说是读书量)在进一步减少 (小红书反而刷得多了),更多是网上冲浪,还有在行动中折腾。回顾过去的记录只看完三本书,项飙、吴琦的访谈《把自己作为方法》、路遥的《人生》、以及 D.Q.麦克伦尼的《简单的逻辑学》,偏向于人生的迷茫与选择的话题,以及工作思维工具向。

日常听歌方面,由于开车的关系,更倾向于节奏感强的 BGM,因此加入了很多林俊杰、陶喆、周杰伦的成分,以及一些学生时代听的 K-pop(BigBang、T-ara 之类),伍佰和许巍相对少一些。某段时间在重度循环卢广仲的《我爱你》,初听咋还是小男生对于爱情的迷茫,再听才发现是比赛前找不到灵感的绞尽脑汁,配上某种广普特色的台湾腔,无厘头之间带着一种随性自由的感觉哈哈... 现在更多在听逃跑计划,关于理想与现实的落差和接纳,冥冥中会有一些共鸣所在。

当然这一年会有更多现场的成分,先后冲了缺省、侧田、周杰伦、许嵩的现场,感受不同的音乐人的魅力。相较于周杰伦现场的合家欢点歌模式,感觉许嵩的演唱会的编排上,会带着更多自己的成长历程与自身做事理念的传递。他也在试着鼓励大家,在忙碌的生活之间,能给自己多争取一些空间,多一些思考和探索欲,不必刻意迎合什么;相信每个人都值得爱与被爱,永远打开自己的怀抱去迎接。

大部分时间都在工作、研究、练车和出去玩,游戏基本打不动,王国之泪继续吃灰。电影只看了《周处除三害》,对于片中角色邪教头头林碌和的各种片段还是印象深刻,以及《新造的人》确实洗脑。我想对于信仰而言,它更多应该是负责解决科学和理性之外的归属和意义感的问题,选择相信之前,还是需要经历一番批判和检验的过程。

感情

24年在感情没有啥特别进展,虽然我自己在现实经济条件、恋爱观、可能的期待等方面,都想得都 比较清楚 了,但这样似乎反而更难再开始一段恋爱。从中也因为各种机缘巧合,认识了一些想做些尝试、条件合适的异性朋友,但没啥进一步的发展。

仔细观察下来,可能我自己本身也不是很有安全感的人,对于亲密关系带着比较强的防御,以至于只要嗅到一些不对路,就会火速撤退。毕竟过往很多内耗过的事物,也是自己一步步探索才有些眉目,而后对内耗更加敏感,这不免也会为自己的恋爱对象附加不少的完美主义期待。不过也还好,清醒的单身状态,至少比内耗的恋爱关系要好一点,我是这么想的。

关于这个困境的破局,可能需要的也是,破除掉一些完美主义想象,多一些现实的实践和体验,去作一些不求结果的尝试。当然这里也希望对方也是类似的想法,如果都带着很强的结果的预期,那可能会有新的内耗产生,咱就又火速撤退了。

进一步去观察,这里的不安全感,有很大部分是因为,个人对于未来生活的期待尚未稳定,导致难以去做出一些超越当下的承诺。这一点估计也是需要我自身在内心的方向、以及现实的规划等方面能有所收敛,才能有更强的决心和坚定。谈恋爱更多时候也在于,希望可以遇到一个在生命之舟上一起掌舵的那个人,如果方向不一致,承诺带来的可能更多的是内耗,进一步就是撤退了。

未来

关于我对未来有何期待,可以往哪个方向去收敛,是一个值得思考的话题。从具体到当下的生活而言,自从习惯开车,体感的生活半径有所扩大后,会有许多相较于过去不一样的思考。

更大的话题在于定居和生活节奏的考虑,从小城市出来漂泊的人,精神上逐渐靠近城市的生活,多少会带着一线城市买房定居的期待。但对于未有家底支持的小镇做题选手而言,除非跳出打工天花板,收入再加一个数量级,不然经济层面其实不太能接受;如果考虑租房定居的方案,房子租金也差不多可以覆盖小城市的房贷月供了。对于深圳的居住体验而言,周末的深圳能玩能逛的地方哪哪都是人,看着龙华密密麻麻的住宅小区,还有壹方天地附近堵的水泄不通的车流,多少会有些怀疑,是否有必要在这里卷生卷死。

从理性地角度来看,互联网与科技行业适合在珠三角大湾区发展,但不一定要钉死在深圳,周边的二线城市可能会是更好的选择。一方面房价相对较低,可以整一个落稳脚,有合适的生活节奏,余下即使要考虑别的计划,财务上也不会有太大压力;另一方面,大湾区交通的便利性也就意味着,我们可以考虑工作日待在深圳(简单租个低成本通勤便利的落脚“宿舍”),休息日或假期再回家爽一爽,整个电竞房、做个徒步钓鱼佬,或者周边探探店吃吃、公园露营等等,都还是不错的选择。

最后

不知不觉写了许多,这一年的思考,会带着更多的现实和行动的考虑,最近在听逃跑计划的《Take Me Away》,也会有类似的感受。面对不那么理想的现实,下意识地想要逃离,但逃避其实让人走向虚无,并不会因此感觉变好。能做的似乎也只有,带着些许无奈,去接受当下的不完美、接受把手弄脏的事实,然后下场去把现实情况向理想的方向去推进一些,不拘泥于能把结果推动到很完美程度,有一点进步就足够。

也如这几年的我在尝试做的事,但也不希望把自己搞得超级英雄一般,以为自己无所不能,把所有不如意都揽在自己身上。在新一年的行动上,更向往的是有所收敛,在合适自己的 scope 内一步步脚踏实地的真正自由状态。借助理性的工具,去多做尝试,真正构建起自己能触及的、想要的环境和文化。

在行动之余,也别把自己搞得太累,适当出去走走玩玩,同样也是有必要的事情,也许会策划一些相对远途的旅行,多一些通感和体悟吧。

同理心:跨过理工科与人文社科艺术的桥梁

2024-11-01 19:30:20

近期在继续尝试整理一些在我过去的成长经历中给我带来启发的事物,某种意义上也是一种自我观察的状态。当然这个视角比较私人,更适合放在博客上,估计未来的博文也会是类似的方向。关于同理心与理工科、人文社科的关系,是其中的一个比较重要的认知基础。

过去的我属于是纯纯理工男,对于技术之外的事情不甚关心。也许是跨越多个圈层的成长经历使然,外加高敏感容易共情的性格,让我无法回避很多问题,也因此陷入很长时间的撕裂与内耗状态。

内耗的同时,伴随的是数年的阅读与探索,内心人文与理工的隔阂逐渐打通,回头看有种一步步走向柳暗花明的感觉。前几天刷到小红书网友的帖子,想起这个话题,顺带评论提了一嘴,再看也许可以继续展开整理一下。

一切的开始是 Roger Antonsen 老师的演讲 《数学是理解万物之源》,老师聊到,理解的本质在于转换角度的能力,以及其中蕴含的数学思维和想象空间。

数学的关键数学的关键

在这个视角下,面对复杂问题,我们可以通过数学语言,用隐喻和类比的手段,转化为直觉易于理解的视角。而隐喻和类比,背后也来源于人类共有的同理心与共情能力。从这个角度来看,数学乃至于理工科与人文社科艺术之间,其实并没有太多的隔阂。

数学与共情数学与共情

进一步的打通,来自于 19 年开始看一些不太理工的书籍,偶然在学校图书馆翻到的《禅与摩托车维修艺术》。作者借摩托车旅行的思考,穿插了许多关于科学与艺术、古典与浪漫的分裂的讨论。

在其中也尝试用“良质”的概念去重组那些被割裂的东西,以及现实中可能只可意会不可言传的现象,以求内心的平静自洽,乃至于探讨到"良质"与东方道家哲学等关系,挺有意思。

知乎上有一篇写的很不错的书评,描述了书中的比喻,类似于楼上 Roger 老师说到的的隐喻与共情的视角:

另外作者的比喻非常精巧,思想的鬼魂、分析的刀子、心灵的高原地带、理性教会、溶液结晶、公牛两角的两难问题、知识火车和良质铁轨。贴切巧妙,读起来非常美。

随着阅读的进行,心境也是随着这本书的推进而走向平和,行动也不自觉在贴近“良质”与其带来的“心流”状态,这样的状态仿佛冥冥中会带来一个向好的指引,帮助自己把一些不确定的事情做好。

当然,这里给我的启发主要还是在理工科领域,再接下来的彻底开窍,来自于朋友推荐的费孝通的《乡土中国》,在开头构建概念的视角给我很大的启发,费老说“概念在这个意义上,是我们认识事物的工具”。这本书也是尽可能少的引入抽象概念,用最平实的语言,把中国最基层社会的方方面面勾勒起来。

概念与认识事物的工具概念与认识事物的工具

这也让我一瞬间对各种看似高大上的理工科概念体系突然祛魅,对把关注点放在其背后想要表达的东西,有了更大的信心。当脑海在里这两大块有了更多的打通,各种事物也就这么联系起来,成为一个共情力驱动的互相依赖的知识网络(知识网络的搭建则是 这一篇 的话题)。

至此我可以确信的一点是,理工科的每个知识点背后或多或少都带着一些历史因素,带着背后的权衡与妥协,并非与生俱来。不是他懂这个就一定很厉害,你不懂就是 loser 还是怎么样。

进一步说,在数学和理工科学习方面的许多障碍,除了那些必须依赖刻意练习产生肌肉记忆才能构建起来的直觉,很多时候只是因为表达过程的欠缺、概念不清晰明确,导致溯源与理解困难。绝大部分情况其实并不需要太纠结与恐惧,干就对了。

本文草稿 2024-11-01 写于小红书,2025-01-31 搬运回 blog

关于 React App 业务逻辑的组织

2024-10-26 18:35:15

上篇文章 聊到了 GUI 应用开发的一些参考原则,接下来打算结合我这几年的 React App 开发经验,总结一些在 React 生态下,一些比较好的实践经验和方法论。

一切的原则在于用尽可能少的代码及其潜在的沟通成本去实现业务,让各个模块向高内聚低耦合状态靠近;在维护者的精力分配上,可以更多聚焦于业务逻辑以及对计算机本身的思考,减少不必要的沟通摩擦。

相较于 UI 实现,本文更侧重于业务逻辑的组织上,主要讨论几个点:

  1. 渲染逻辑的拆分
  2. 代码文件的组织
  3. 业务 Flow 的表达
  4. 业务状态的划分策略

渲染逻辑的拆分

在 React App 的开发中,随着组件要渲染的 UI 元素的增多,写代码时很容易不自觉会延续过往写 class 组件的习惯,写一些 render function 拆分渲染逻辑。这种做法看似简洁,实则会因各个拆分后的 render function 依赖的业务逻辑相互交叉,导致组件愈发复杂,越来越不好维护。

一个好的做法是,优先考虑拆子 UI 组件,即使子组件的逻辑非常简单。这么做看似文件和组件数量会变多,但后续扩展无需再动上层的组件。业务快速迭代的背景下,从可维护性的角度来说,采用灵活度更高的方案,要比过早优化好一些。业务逻辑日渐复杂,拆分子组件给运行过程带来的性能损耗,其实远远小于把业务逻辑混杂在一起的维护成本,总的来说还是划算的。

拆子 UI 组件的一个蛋疼点在于,我们要建新的 jsx / tsx 文件、写一堆重复的组件定义代码,手打会比较烦躁。这些烦恼可以借助于 IDE 来改善,VSCode 自带的 snippetsMove to new file 的能力,可以说为此节约了不少的精力和时间。

那么,render function 写法是否就真的一无是处呢?其实不然,在一些涉及到条件渲染、皮肤定制等等的场景,render function 相比子组件会更加灵活,它可以自由地把渲染逻辑插入到父组件的合适地方。甚至你可以考虑把 render function 及其依赖的上下文状态,封装到一个 hook,再用 render function 将其加入到父组件的渲染流程,如例子附上的 FooBarCounter 的 hook 和 render 函数。这样的做法也非常适合需要在历史包袱很重的组件渲染新的 UI 元素、但不想大改旧组件逻辑的场景。

// hook
const useFooBarCounter = () => {
  const [count, setCount] = useState(0);
  return {
    reset: () => setCount(0),
    renderFooBarCounter: () => <div>{count}</div>
  };
};

// app
const App = () => {
  const {
    reset: resetCounter,
    renderFooBarCounter,
  } = useFooBarCounter();
  
  return (
    <div>
    	<button onClick={() => resetCounter()}>Reset</button>
      <div>balabalabala</div>
      {renderFooBarCounter()}
    </div>
  );
};

抛开那些纷繁复杂骚操作,复杂组件渲染逻辑拆分的核心目的,大概在于把控模块间的边界感,避免给交织的业务 Flow 在代码的表达上发生太多相互交叉的情况。

代码文件的组织

众所周知,React 官方并未规定 App 在代码文件的组织策略,开发者写 React App 通常八仙过海各显神通,当业务增长到一定的规模,找代码这件事情的心智负担也随即增大,容易让项目代码走向混乱。这时候就需要一个稳定统一的策略,降低心智负担成本。

大原则是避免过度设计,从组件业务逻辑开发自简单向复杂演进的视角,我们可以划分出几个阶段,每个阶段针对性引入一些的概念和工具去辅助处理。

按业务复杂度大概可以拆成三个阶段,分别起名 “简单组件”“小型组件”“大型组件”

  1. 简单组件:纯 UI 逻辑,非常简单,一个文件就可以写完
  2. 小型组件:由数个模块(组件 / hooks / utils 等)构成,考虑引入目录来划分
  3. 大型组件:由数个 小型组件 构成,按模块类型拆分目录,并引入 modules 的概念来管理子组件

对于 小型组件 来说,我们可以直接用 组件名.模块类型.ts/x 的模式来组织文件名,借助编辑器默认按文件名排序的能力去归类组件本身、样式文件、工具函数 / hooks、子组件 等,整体来看大概是这样的格局:

  • TestComponent
    • TestSub.tsx
    • TestSub.styles.ts
    • TestComponent.tsx
    • TestComponent.styles.ts
    • TestComponent.xxxxx.ts/x
    • TestComponent.styles.css

演进到 大型组件 的复杂度,可以进一步引入目录辅助拆分,然后把对外的根组件相关的模块放在根目录:

  • TestComponent
    • components
    • hooks
    • modules
    • contexts
    • store
    • TestComponent.tsx
    • TestComponent.styles.ts
    • TestComponent.xxxxx.ts/x
    • TestComponent.styles.css

React App 的概念模型,是一棵由组件和子组件以递归的模式构造出来的树。我们对三个阶段的组件的文件组织策略都有了定义,我们可以递归地把它们应用在整个 React App 的组件树设计上,以此实现文件系统的目录树和 React App 的组件树的大致对应。带着一定的思维惯性,读/找代码时的认知负担也随之大大降低。

针对 大型组件,我的一个特殊做法是拆分出 modulescomponents 的概念。主要考虑点在于子组件的可复用性会有强弱之分,有的子组件仅仅是逻辑的拆分,仍然和父组件有着千丝万缕联系,适合放在 modules 里,有的与父组件关联较弱,有机会被其他组件复用,近似于纯 UI 组件,就适合放在 components 下。

对于复用性强的组件,由于业务需求的不确定性,在开发过程我们其实没法预测到它最后会发展到一个什么样的通用程度,有时候会做一些不必要的过度设计,更好的做法是允许它有一个能不断“成长”、“升级”的预期。在开发一个组件时,若发现一个组件可以被通用化,但暂时没有别的组件引用,可以先把它放在当前的 components;随着业务发展,部分子组件可能需要被其他模块引用,这个时候我们可以进一步把它挪到更上层的 components。这样子开发者对组件当下所能作用的 scope 也会一目了然,让文件系统能在潜移默化之间给维护者传递更多关于项目的背景信息。

需要指出的是,这里分三阶段走并非严格策略,核心目的仍在于降低找代码 / 读代码的负担。对于文件/目录的组织,可以抓大放小,在个人直觉可以接受的范围内,是可以允许一定的杂乱存在的。

业务 Flow 的表达

当一个软件承载的业务 Flow 变得多而复杂,代码的维护负担也随之越来越重。相信开发过复杂 React 应用的朋友,应该体会过代码迭代过程中要梳理各种飞来飞去的状态变化、订阅、事件的痛苦。一个比较迫切的需求也在于,如何能在代码里简洁清晰地表达出业务 Flow 的逻辑实现,并保留未来基于此做自动化测试的可能性。

状态机的封装

抽象角度来看,实现一段业务逻辑,实质上是在实现一个状态机,伴随着一系列的 action,驱动 state 的初始化、state 的转移和 state 的结束。大方向上,我们需要一个载体去承载这样的 stateaction

在 React 的世界,我们可以用 hook 去承载这样概念的落地,一个 hook 可以定义 state,也可以定义修改 state 的各种 action。并且,借助于 JS 函数闭包的特性,我们可以通过 hook 导出自带 state 上下文的 callback 函数,实现对外暴露 action 的作用。借助于这样的能力,我们可以显式地把一条 Flow 从开始到结束的所有动作,都集中在一个 hook 里面。

继续以 useFooBarCounter 为例,我们可以围绕着 count 的 state,分别暴露出 resetaddsub 这几个 action,业务可以不用关心内部的变化,直接调用就可以驱动 count 的 state 的更新。

// hook
const useFooBarCounter = () => {
  const [count, setCount] = useState(0);
  return {
    count,
    reset: () => setCount(0),
    add: () => setCount(count => count + 1),
    sub: () => setCount(count => count + 1),
  };
};

Event based -> 命令式

对于一些相对较长的 Flow,尽可能用命令式的写法去组织代码,可以让业务逻辑的表达更加直观清晰。但有时候涉及到类似 Modal 组件 visible,用户交互 confirm 后、再继续往下走的流程,代码上就很容易出现前面说到的业务逻辑跳来跳去的情况。

一个可以参考的做法是,把这种类似 Modal 交互的 Event based 逻辑转换为命令式的写法。代码层面可以用 Promise 构造函数 + Ref 的方式来实现,外层 Flow 直接 await 这个 Promise,当用户在 Modal 确认后,通过 resolve / reject 结束 Modal 的 Flow,再把流程交还给上层 Flow 继续往下走。

如以下例子代码,这里忽略了一些定义、传值的细节。在实际的操作上,你还可以把这类逻辑封装成一个 hook 实现通用化。

const TestComponent = () => {
  const [modalVisible, setModalVisible] = useState<boolean>(false);
  const resolveRef = useRef(null);
  const rejectRef = useRef(null);

  const startModalFlow = () => new Promise((resolve, reject) => {
    resolveRef.current = resolve;
    rejectRef.current = reject;
    setModalVisible(true);
  });
  
  const startMainFlow = async () => {
    // before modal flow logic...
    await startModalFlow();
    // follow up logic...
  };

  return (
    <TestModal
      visible={modalVisible}
      onConfirm={() => {
        resolveRef.current?.();
        setModalVisible(false);
      }}
      onCancel={() => {
        rejectRef.current?.();
        setModalVisible(false);
      }}
      onClose={() => {
        rejectRef.current?.();
        setModalVisible(false);
      }}
  );
};

当我们能把业务流程用命令式的写法串起来以后,即使涉及到流程结束、报错的情况,我们也可以很轻松地兜住,正常结束我们可以用 return,异常结束可以直接 throw 一个定义好的 Exception / Error,只需要简单地用 try / catch 等语法就能实现错误的统一处理。并且,这样的做法也可以促使开发者多多思考业务的错误降级、兜底逻辑,对用户使用过程可能遇到的各种场景都有预期去做兜底,获得更加丝滑的体验。

Flow 的分阶段组织

对于一些要跨越多个不同交互界面的 Flow(比较常见是页面跳转),并不好直接在一个函数就完全处理完,这时候可以把这个 Flow 拆分为多阶段的 Flow 分步处理。

当其中一个 Flow 结束,我们可以在其中安插一些启动新的 Flow 的逻辑。或者也可以通过全局渲染状态的大水漫灌,在需要的模块订阅这个状态的更新,实现下一阶段 Flow 的启动。这属于 React App 开发的常规操作,就不多赘述了。

业务状态的划分策略

状态管理是个老生常谈的话题,之所以老生常谈,也是大家对于应用大小状态的划分不清晰导致,有的 useState 一把梭,有的将其存在外部 store,然后 atomaction / selector 满天飞,阅读代码负担也特别重。相较于徘徊于各种状态库实现的眼花缭乱,我更倾向于把概念搞清晰,具体的实现,根据项目情况因地制宜即可。

针对状态的划分,可以按复杂程度分两种情况去考虑,再通过 hook 对业务暴露 API。

对于逻辑简单的业务,不需要怎么去管理状态,直接放在顶层组件的 state 即可,再通过 Context 来传递状态的方法。一个比较好的做法是直接封装一个业务逻辑组件,类似上面的 useFooBarCounter 那样定义好 stateaction,然后通过 Context Provider 嵌入组件树,再对应暴露一些 hook 给下层 UI 组件去触发业务逻辑组件内的 action

对于逻辑复杂的业务,状态通常是存在外部(或者最顶层组件)的 store 上,也由于 storeselector / action 等能力比较底层,直接暴露出去,很容易发生引用的互相交叉,降低可维护性。

因此我们应该尽可能减少这类底层 API 在上层的直接使用,比较好的做法也是,按照对应业务模块划分 module,module 之间通过 hook 来给业务组件,或其他 module 提供对其业务生命周期方法和状态的引用。

关于对业务组件的 hook API 代码的设计,思路上也类似于楼上说的 useFooBarCounter,我们可以封装一些诸如 fetchXXXinitXXXhandleXXX 的方式,供上层业务去调用。业务依赖 hook 提供的业务接口概念,而非依赖于 store 的底层实现,以此可以减少因为不同业务模块与 store 逻辑互相交叉增大复杂度的情况。

小结

以上简单展开了一些 React App 业务逻辑组织的经验和策略,总的来说,核心都围绕着 “如何减小业务 Flow 之间的交叉” 而进行,实现它的方式有很多,也许你有更好的方案。

从中可以发现,React Hooks 的抽象所提供的业务逻辑拆分能力,可以说是降低复杂 React 应用上手门槛、提升可维护性的一个有力工具,如何用好它,背后有着不少的学问。

值得注意的是,即使是在当下业界强调函数式、响应式编程去实现 UI 的背景,就业务流程的组织而言,命令式的写法仍然十分有用且实用。

相关讨论:关于 React App 业务逻辑的组织 - 0xFFFF

浅谈 GUI 应用开发

2024-08-10 17:30:01

自接触编程起,我一直挺向往 GUI 应用开发的方向,妄想有机会可以做出像千千静听那样能给人一种“哇塞”感觉的软件。自初中开始接触 Adobe Flash / Air 到 HTML5 摸到一些皮毛,再到大学时写 JavaScript 和 React 逐渐上道,满打满算经历了一轮技术浪潮的冲刷,外加几年的实习、工作经历一路走来,也让我有了不少的思考和感悟。

关于 Web 与 GUI 应用开发方面的想法,过去我在 Blog 写下了部分,比如 Web App 发展史单页应用的交互模式R&D 工程师的角色定位 等等。当前我自 Web 开发切入工作也近五年,自觉在 GUI 应用开发方面有了一定的认识,差不多可以写一篇东西展开讲讲,也借此理一理思路,让这件事变得更清晰简单一些。

在聊 GUI 之前需要明确的是,软件这一事物是怎么与外界互动的,在现实中承担着一个什么样的角色。在我看来,计算机世界与现实世界有机结合,才有助于我们找到软件应有的迭代方向。一个软件的使命,在于在计算机世界开辟一个特有的环境,承接住现实的业务流程;在特定的设计下,借助计算机的力量、把一些现实难以完成的流程变得更加顺畅,把适合机械化的部分交给机器,创造性的部分留给人工。

至于哪一部分留给机器,哪些交还给人工,更多属于产品经理、设计师层面思考的话题,这里会更侧重于工程师视角的实现层面。我想可以从 状态管理生命周期交互模式屏幕适配,以及 研发流程 等几方面去展开讨论,主打一个抛砖引玉。

状态管理

状态管理可以说是一个 App 实现的核心部分,某种意义上来说,计算机本身就是一种状态机,一个 App 内部也是,只是相比于机器而言 scope 更小一些。计算机从 01 两种基础状态开始,慢慢发展出许多复杂的数据结构和概念,让人眼花缭乱。人的精力是有限的,很多细节其实无暇顾及,如何在有限精力的局限下,让分散各处的状态变化变得可控,应该是状态管理关注的核心目标。

面对眼花缭乱的 App 状态,需要的是抓住最关键的部分。在 React / Vue 等 UI 库流行的当下,这个关键部分其实已经非常符合直觉,UI = f(state) 的深入人心,意味着我们只需关注那些会引起 UI 变化的 state 部分,其余交给 UI 库,CSS 样式(Web)本身去处理,或自己写写各种样式 Hack 逻辑。

当然,这里的 state 的组成可以很复杂,但整体来说,我想可以拆分为“核心状态”,以及支撑核心状态的“附加状态”两部分。举个例子,在做 B/S 相关的业务时,UI 变化很多时候来自后台数据的更新,用的最多的大概是 loading, success, error 这几种 case。对于这种简单的情况,一般会用 isLoadinghasError 等方式组合实现。更清晰的是将其定义为 Enum,比如说 LoadingStatus,把所有可能性一一列出来。

enum LoadingStatus {
  INIT,
  LOADING,
  SUCCESS,
  ERROR,
}

interface PageState {
  status: LoadingStatus;
  pageData: {};
  errorMessage: '';
}

然后找个地方存一下核心状态和附加状态,再处理状态转移需要的 UI、数据层面的同步逻辑,再在合适的地方驱动状态的流转,一个基本的 GUI 应用就这么成型了。

const pageState: PageState = {
  status: LoadingStatus.INIT,
  pageData: {},
  errorMessage: '',
};

function setPageState(params: {
  nextStatus: LoadingStatus,
  payload: Partial<Pick<PageState, 'pageData' | 'errorMessage'>>,
}) {
  pageState = {
    status: params.nextStatus,
    ...params.payload,
  };
  // 可以是 React / Vue 以及各种 balabala...
  syncWithUI(pageState);
}

对于传统的网页前端而言,以上 LoadingStatus 的关键状态,已能覆盖绝大多数的业务场景,工作量更多集中在 UI 样式的还原,页面结构的规划,与后端开发的联动等方面。当我们的目光上升到 App 的层面,需要关注的状态自然不会这么简单,应对复杂的 App 状态,对应的技术方案自然也会复杂很多,相对应的也是前端百花齐放的状态管理方案。

在 Web 生态的视角,最直接的状态管理,大概还是 “组件化” 拆分代码,对于大型的复杂 App ,采取一种“分而治之”的策略,将状态分散在各个子组件,组件内局部状态自成一套。某种意义上有种“大水漫灌”的感觉,通过上层组件层层向下传值的方式去同步状态的变化,依次确保子组件能同步最新的状态。然后再通过类似 useEffect 的手段,识别状态的变化,在做一些对应的副作用动作。

在这样的场景下,各类状态管理库,定位上更多是一种针对不同场景定制的“代码拆分”的艺术,以支撑 App 的迭代、测试等各种需要。譬如 React 自带的 useState / useReducer + Context,以及类似 reduxzustandjotai 等等第三方外置状态管理、共享方案。

生命周期

值得思考的一点是,是否所有的业务场景都适合这样的“大水漫灌”模型?在 React 开发中常见一个现象是 useEffect 的滥用,导致 App 代码复杂后难以 debug 的问题。

想象一个软件拥有自己的灵魂,伴随它的一整个生命周期,大概分为三个阶段:

  1. 启动阶段:加载代码、状态初始化
  2. 工作阶段:与操作系统、网络、用户、其他软件等打交道
  3. 退出阶段:清理状态,释放资源,然后被回收

从 React App 角度来说,它也有它自己的生命周期(想必各位面试八股文选手早已耳熟能详),核心在于组件渲染 mountupdateunmount 的过程,以及各种用户事件的处理函数。和外界的交互,主要通过 updateuseEffect 和用户输入、后台接口等来打交道。基于一个个小组件拼装形成成大组件,进而构成了整个 App。

很明显,一个 App 所涉及的交互流程,就不仅仅单纯像命令行 CLI 程序那样只有一根筋的 input / output / error,在生命周期中的每一个动作,大都会牵扯到一条新的流程,有它自己的 inputoutput 以及 error。随着 App 的复杂,流程互相交织,流程所对应的代码逻辑足够简洁清晰,就应该成为代码设计的一个大原则。

概括而言,一条流程有几个生命周期:

  1. 流程的启动,考虑流程的触发源头
  2. 流程的运转,各种不同的逻辑判断,启动新的流程等
  3. 流程的终止,包括正常结束,以及异常终止的情况

在流程启动的视角,它的触发来源并不总是很清晰。比如说启动与退出,它们发生的通常比较自然,Web App 的启动,发生在 HTML 代码加载与渲染之间,退出则伴随着很多意外情况(浏览器标签关闭、系统死机、断电等)。对于 React 的组件来说,许多流程发源于 useEffect 订阅的 props 等状态变化,属于一种 “大水漫灌” 的背后隐含的事件订阅的模型。当然很多时候类似的流程变化,我们没有太大必要“精细化浇灌”、单独去定义订阅和同步的逻辑,交给 “大水漫灌” 是更合适的选择。

进入流程运转的过程,需要明确的一点是,涉及到核心业务逻辑的 case,有必要避免在流程中间过于依赖“大水漫灌”的逻辑。一条业务流程,遇到这种汇集了多种不同流程的步骤,遇到问题的 debug 难度也会直线上升。就像是人走在十字路口的迷茫,缺少背景信息的参考,你不知道下一步会往哪走,只能误打误撞一个个试错。遇到组件相互通信的场景,有时候单独定义新的 event bus 去做 “精细化浇灌”,要比走全局 UI 渲染的“大水漫灌”逻辑更简单直接和可靠。不过独立的 event bus 不能太多,“精细化浇灌” 维护难度会对应增大,通过合理的代码拆分,借助 useEffect ,也可以实现类似的精简版 event bus 模型。

流程终止的场景常常容易被忽略,尤其是在当下大多数团队只满足于“能跑就行”的背景下。如果真心想要把软件做得足够完善,需要更多地从用户视角去思考异常处理。一条流程中途遇到异常导致的中断,这个中断会导致什么影响?需要用户做出什么样的行动,研发视角又怎么监测到这个问题,背后是一整套错误 catch、上升的策略。用户能做的行动通常是 “重试” -> “采取备选方案” -> “寻求研发支持” 的过程;研发的视角则需要做好错误的分级、上报和告警机制,在用户意识到问题前能提前发现并修复,在重要流程适当设计一些降级策略,避免阻塞用户的使用。

到这里,我们对流程有了一个相对清晰的定义。随着软件复杂度的提高,代码迭代可能会影响某些流程而未被发现,进而产生新的 bug,需要有对应的机制去保障流程不会随着软件更新而中断,也是软件测试核心关注的点。在研发的视角,也可以针对一些比较重要的流程,写写单元测试,减少这里反复验证的人力损耗,覆盖单纯从黑盒视角的功能测试难以覆盖的极端 case。

交互模式

上一节聊到了流程的概念,这里关注的是依托浏览器的 Web App 与平台原生 App 运行环境的差异,重点在于 Web App 与 Mobile App 交互模式的差别,以及可能被忽视的细节。

两者一个大的差异点在于 Web App 的工作模式更像是一个单 Screen 的“画布”,而 Mobile App 是多个 Screen 堆叠起来的 Screen Stack。因为 Web 本来是文档渲染起家,在代码环境提供了更多预设能力的画布模型,也就是 DOM,我们可以通过 React / Vue 等 UI 库方便做 state 与 DOM 的相互映射。浏览器同时提供了 History API,开放了 History Stack 以及地址栏给外部操作。浏览器通过 History 跳转到别的页面,相当于整个页面直接刷新,原来的 UI 元素都会重新渲染甚至销毁。并且 History Stack 的每一层都可以保存一份 state,因此如果想要把交互做的精细完善,还需要考虑清楚这个 Web App 应如何管理和应对不同的 History Stack,是 push,还是 replace,以及用户如果直接从地址栏进入页面,又应该如何处理。

Mobile App 的交互和 Web 有些不同,它会涉及到不同页面同时层叠的 UI(微信小程序也是类似交互),因此需要考虑 Screen Stack 的维护,以及多 Screen 并存时可能的互相通信。在一些特殊场景,还需要考虑类似 Web 直接输入地址打开某页面的模式,当用户通过 Deep link / Universal Link 打开 App,打开用户想打开的目标页面,或给出合理反馈。

浏览器与 Native App 还有一个差异点在于窗口之外的 UI 交互,浏览器在处理拖拽、浮层、上下文菜单等能力会有缺失,开发成本考虑,大部分时候会自行在 DOM 上模拟实现;要实现 Native App 的效果,还需要操作系统的参与,这个细节就会涉及更多的 Native API 的调用,就得单独考虑了。

屏幕适配

另一我想说的点是 UI 在不同屏幕、窗口尺寸的适配,原则上应避免简单粗暴的按窗口宽度缩放。在一个网页开发者的视角,思考模式通常更倾向于直接缩放页面元素,用 rem 和浏览器宽度动态控制,或者用 vw 等单位处理(一个典型例子是微信小程序的 rpx 单位),这样的操作,在预期之外的屏幕尺寸上可能会面临一些奇怪表现,比如说在平板上大而难看的字体;或者缩放后字体小于浏览器最小 12px 的限制进而影响布局(更新:Chrome 118 后,默认最小字号放开了),或者因为字体大小舍入原因导致字体发虚等问题。

比较好的做法是统一用 px 单位,并借助 flex 弹性布局的灵活性和 Media Query,老实做好设计层面的适配,在不同的屏幕尺寸有所兜底。缩放和屏幕适配问题,则交给 浏览器 / 宿主平台 处理,现在的浏览器缩放也是基于 px 的,不会再动字体大小。如果需要考虑文本的单独缩放,需要的是研发与设计配合,专门针对其去做适配和处理,引导用户使用产品提供的方案,而非仅凭猜测行动,让用户去动那些充满不确定性的字体大小设置。一个参考:R.I.P. REM, Viva CSS Reference Pixel! | Mind The Shift,关于用 px 还是 rem,也是一个 Web 领域的争议话题。

还有一个比较容易忽视的细节是关于 UI 中作为容器的盒子的尺寸稳定性,尤其是对滚动区域的把控上,Web 开发者常直接依赖添加 overflow: autodiv 盒子,在内容超过最大高度时,浏览器会自动生成滚动条,而这个默认滚动条会挤占被滚动区域的宽度,某些情况下会发生抖动影响用户体验。比较好的做法是显式地指定一个 ScrollView 的角色,在最外层就定义好盒子的宽高与滚动状态,让元素可以相对于它来布局,保持外层容器的稳定性。针对被滚动的区域,可以单独包装一层,定好宽高,避免其被滚动条影响。这么一波操作下来,即使要再兼容适配 safe area、沉浸式刘海等特殊场景,也会非常方便。

研发流程

明确了 GUI 开发涉及到的一些生命周期和交互模式,对应研发流程的关注点也需要做一些调整,在我去年的 总结 有提到,这里类似于 “用户故事地图” 的思维模式。前文说到的“流程”,其实和这里产品经理视角的“用户故事”,设计视角的 “User Flow”,还有 QA 视角的“功能用例”可以说是指向了一个东西,让我们姑且就把它称为 “Flow” 吧。

开发的核心关注点恰好也是在这个 Flow 上面。首先得评估技术方案,明确改动的目标状态,当下状态,改动策略等;在开发任务的前期,铺垫好基本的依赖之后,尽可能早些走通这个 Flow,提前暴露出潜在较大的风险;接下来才是如何完善这个 Flow,让用户操作走的更顺体验更好。

这里大概可以形成一个 Roadmap:

  1. 评估技术方案,改动影响
  2. 开发走通 MVP,形成一个草稿状态的 Demo
  3. 产品、设计介入,拉不同角色联调,完善,产品化这个 Demo
  4. 产品、设计、QA 介入,进一步覆盖 Edge Case 和打磨细节,提升软件在各种复杂环境的鲁棒性和兼容性

从 Flow 这个视角来看,所有的参与方的目标是非常一致的,可以尽可能减少沟通上的拉扯和损耗,开发贯穿于其始终,起到一个承上启下的作用,因此对于研发工程师而言,这种对 Flow 的目标感就非常重要,毕竟很难有一个角色可以完整关注到从想法到实现的流程,乃至于每个设计细节的落地。当然,这里的 Roadmap 会更侧重于需求开发阶段,至于其他阶段如何行动,在我之前写的关于 R&D Engineer 的文也有 聊到,关于一名研发工程师在软件开发运维工作中的一个角色定位。

以上更多是个目标,从执行角度来看,要想达成更高的交付质量,总结起来大概有三个值得注意的点:

  1. 放下完美主义,抓大放小,在有限的资源下尽量做到全局最优。开发功能的最初,更大的目标在于核心 Flow 的走通,就不需要过早去关注一些比较边界的细节,细节可以放到需求的后期,或者是上线后再做一些 Follow up 优化版本;
  2. 需重视测试建设,由于 Flow 可预见的会越来越多,可能涉及到流程互相交织的情况,仅凭人脑经验很难完整覆盖,交付质量的把关就变得困难起来;对于一些比较追求精确性的模块,可以适当写写单元测试;
  3. 保持强的同理心,在核心逻辑得以保障,有机会做些优先级相对低的优化的情况,多思考用户是怎么用自己正在开发的软件,发现一些可能影响体验的问题时,对应做处理。同时也通过一些比较细致的情感化的扩展,给软件注入多一些灵魂,给用户带来多一点品牌理念的传达与归属感觉。

小结

写到这里,对于 Web 向 GUI 开发的工作,大概就形成了一个比较完善的思维模型。当然这里也伴随着一点视野的局限,更多是在 Web 前端的视角所展开的,Mobile Native App 和 Desktop Native App 我暂时没有太多研究,也许未来有机会研究到那里的时候,会有些新的感悟,到时候再继续写写和聊聊吧。

相关讨论:浅谈 GUI 应用开发 - 0xFFFF

个人网站的再次重建

2024-07-25 23:00:04

自从我 22 年中迁移博客 后,事情发展算是比较符合预期,过往旺盛的表达欲也有所集中,从知识管理、人生体系、个人的方向和规划等视角,随着文章的梳理,也经历了一轮重塑。

但在技术层面,我对于这套方案仍有些不满,只是因为工作生活的精力分配原因一直没有投入时间去改。一方面是 Notion 的表达力的有限,以及生成的站点 CSS 样式的结构略复杂,不好定制;另一方面是其 RSS 订阅的逻辑会消耗大量的 Vercel 的云函数运行时长,频频超限;比较不能忍的是有的 Page 莫名其妙无法访问,但我不知道背后发生了什么,只能复制粘贴重新搞,这也让我对以 Notion 作为博客后端 CMS 的方案信心大减。

这件事情的推进,伴随着许多巧合和水到渠成:某天偶然意外拿到了 zgq.me 域名;设计师好友 Yvon 在思考自己新的职业规划,尝试做了自己的 作品集站点;去年和 @johnbanq 说起要搞个人网站,最近终于上线;外加去年在写 0xFFFF 计算机入门专题 的时候也比较重度地使用了 Markdown 文件和静态网站生成器,重建个人网站的的动机愈发旺盛。在酝酿了好多个周末以后,终于是落地了一个版本。

由此,也在这里简单梳理一下重建个人网站这事儿的考虑和选择,以供参考。

目标

大的目标还是聚焦,在 个人知识管理体系 中,将所有对外的输出,归集到 zgq.me 域名下,比如较长的文字、个人状态、项目介绍等等,引用的源头都来自于此(Single source of truth 原则)。与此同时,将内容页面都统一在一套代码仓库,解除第三方依赖,确保数据的完整与安全。

在技术栈的角度,我想把个人的关注点集中在 Web App 开发,积累尽可能聚焦在这一领域。所以这里也继续用已有的前端技术去实现,继承 Nobelium 的衣钵,把内容来源换成 Markdown 文件。

数据导出

这里好像没什么可复用的经验,主要是把 Notion 中存放的文章一股脑导出,然后再针对性地做些处理。

Notion 导出 Database,它会将数据打包到一个 zip 文件中,其中有一个 csv 存 Database 的各个字段,以及一系列 Markdown 的 md 文件,以及 md 文件对应引用的图片资源等。

这里一个核心是,以文章的 slug 作为文章的 key,然后以此建立一个目录,存放其引用的各项资源(主要是图片)。md 文件内容参考 Hexo / Jekyll 中名为 Front Matter 的做法,头部将元数据(发布时间、标签等)以 yaml 的格式输出至 md 文件的头部,紧接着才是文章正文。

目录结构上,以年为单位划分(毕竟一年产出也没有几篇),整理出来大概类似以下的结构:

内容目录的结构内容目录的结构

核心

最开始考虑 Hexo,其生态有些复杂,懒人有点不想琢磨,遂先战术放弃。实质上我只是想要一个方便的 Markdown 转 HTML 能力,以及生成网站其他必要部分的框架,并不想在别的方面纠结太多,所以先用成熟的方案跑起来即可。所以这里网站构建上还是会继续用 Next.js,它的 SSG 方案基本可以满足我这里的需求。当然也保留使用其他方案(比如 Waku)的空间,目前看 Next.js 耦合太多 Vercel Only 的东西,并不必要。

然后需要关注的是 Markdown -> HTML DOM 的解析方案,这里就直接用 @mdx-js/mdx 梭哈,综合下来还涉及到一些扩展组件,如 Front Matter、数学公式、表格等 Markdown 扩展语法的支持,基本上抄抄默认配置就可以。

接下来是网站的 CSS 样式方案,原则上基础 CSS 样式需要尽可能薄。这里直接用 Tailwind CSS 来做,日常用到的 CSS 样式基本都可以被它 cover 住,并且它背后是一套完整的 PostCSS 工具链支撑,基于它做样式方案代码可以更加简洁,尽可能减少全局的重复样式定义。基础文章排版则直接引用 @tailwindcss/typography 的样式。

RSS Feed 与 sitemap 的方案参考 Nobelium 的,用 feednext-sitemap 整一个,也挺简单方便的。

图片处理

图片是直接从博文 Markdown 中引用的,但由于 Next.js 的设定,无法直接访问到 public 目录外的静态资源,显然不是太方便。查了一堆资料,暂时没找到更好的解法,只能单独 copy 到 static 目录,然后用 _next/static 来访问到对应的文件。

config.plugins.push(
  new CopyPlugin({
    patterns: [{
      from: './content/',
      to: './static/assets/',
      filter: async (resourcePath) => {
        if (resourcePath.includes('.md')) {
          return false;
        }
        return true;
      }
    }],
  })
);

另一个问题是在我把网站部署 Vercel 之后,发现每次打开页面都会请求一次图片资源,虽然是 304 Not Modified,但其实也很影响体验。检查请求发现 cache 的 max-age 竟然是 0...

找了很久没有找到 Next.js 和 Vercel 可以怎么调整文件缓存,我还是在 CDN 上配置好了,在 Next 这边做一组 URL 替换,以及禁用默认在 Vercel 烧钱的图片自动压缩服务。另外尽可能在后端读到各个图片的 size,避免页面初始化加载过程发生 Layout Shift(类似我之前在 Flarum 上的 做法)。

整体处理代码如下:

const components: ReturnType<UseMdxComponents> = {
  img: (props) => {
    const { src, alt } = props;
    const realSrc = `${CDN_URL || ''}/_next/static/assets/${pathBase}/${src}`;

    // next/image support
    const { width, height } = (src && imageSizeMap?.[src]) || {};
    const useNextImage = width && height;
    const imgWithAltElement = useNextImage ? (
      <Image width={width} height={height} className="mx-auto mb-4 max-h-[300px] rounded-l object-contain" src={realSrc} alt={alt || ''} />
    ) : (
      <img className="mx-auto mb-4 max-h-[300px] rounded-l" src={realSrc} alt={alt} />
    );
    const imgWithOutAltElement = useNextImage ? (
      <Image width={width} height={height} className="mx-auto max-h-[300px] rounded-l object-contain" src={realSrc} alt="" />
    ) : (
      <img className="mx-auto max-h-[300px] rounded-l" src={realSrc} alt="" />
    );

    if (alt) {
      return (
        <span className="block">
          {imgWithAltElement}
          <span className="block mb-4 text-center text-xs text-gray-400 px-4">{alt}</span>
        </span>
      );
    }
    return imgWithOutAltElement;
  },
};

评论区

不想再用 cusdis,感觉逻辑有些复杂,并且 Randy Lu 没有很积极的维护它。所以我先把原有的评论随着数据导出一起存了一份,准备写一个新的,尽可能极简,减小维护的负担。

一个评论框业务逻辑本身并不复杂,主要是提交评论的组件,以及后端存储的数据库。

嵌入文章的 UI 评论组件,先糊一个 React 组件自用,后续看情况再扩展到 iframe 嵌入的 widgets。

数据库的考虑,个人博客站点的评论量其实很小,SQLite 已足够支撑。核心在于针对某个页面维护一个 comment 列表,以及 comment 之间可能的父子关系。所以用一个 comments 表,补上核心数据以及一些必要的字段,就足够。

然后需要一个针对博主的简单 CRUD 管理后台,实现评论回复,删除 / 审核,并根据用户回复的动态,实现 Email 通知提醒的能力。

准备是用 Koa + SQLite + Vite React 先做一个,写好再整理分享出来。整体结构类似 weibo-rss;因为只有我一个用户,所以后台的登陆权限校验相关,用 JWT 认证就足够,hash 直接写死;Email 发送直接走 SES。主打一个「老夫写代码就是一把梭」。

Reference

新网站的建设,参考了不少同路人的实现,特此鸣谢:

  1. 博客的内容结构,参考了 Limboy 的 我的博客系统演变之路,还有 板桥工学Sukka's Blog
  2. 关于 content 目录的组织,友情链接的设定,参考了二花的 This Cute World

2023

2024-01-06 20:00:00

又到了新的一年,22年的总结 还未写完,23年不觉也结束了,就如 21 年底回看 19 年 那会 的我,可谓非常真实。现在在写博客方面改变了策略,尽量避免堆积到年底再去整理与回顾,一些相对成型的想法,其实都已拆分成了单独的博文,只求减少年底回首往事时,想法太多,思绪太杂,导致可能的不必要负担。

总的来看,这两年的经历,更多是一个借着业务变动离开“大厂”的契机,在逐渐寻找自我定位的过程,相比于19年那会近似自我革命式的变化,这两年来的转变,算是在预期之内,在方向上没有大的波动。总的而言算在比较平稳的行进状态,只是好像多了一点在社会时钟之下,可能不太属于我这个年龄的从容。

对于这篇总结,我想也是从自我的视角出发,还有这两年来的工作学习生活书影音游等等几个角度,大概写一写那些对我影响较大的经历与思考。

自我

22 年来感觉对自我的认知要清晰不少,伴随着裁员和新工作的洗礼,还有待在深圳原子化生活的孤独与渺小感觉,也意识到许多问题的背后,关键的关键在于自己的主动,而非呆在原地等待环境改变。从决策的视角来看,无意识中选择 “随缘”,本质上也是策略一种,更多是随环境和文化而定的被动选择。当自己身处一个文化多元交织的环境,“随缘”并不一定能带来满意的结果。这就如一个人在大海中的扬帆航行,是选择随风飘荡,还是说去寻找属于自己的方向,向着心中的彼岸迎风而行?

于是我也开始整理自己 写过的文,以及 博客知识管理工具 的重构迁移,试图收拢自己在技术领域的关注点,还有日常工作的方法模式。因为 tianheg二花 了解到 Derek Sivers 的 Now Page 的 思路, 然后也学习了下 搞了个,整理下我自己当前生活工作学习关注的目标。一方面希望自己精力和行动能更专注聚焦一些,避免因兴趣的发散或陷入宏大话题导致的各种浅尝辄止;一方面关心我的朋友们也可以以此了解到最近我在关注的目标,一起交流进步。

另一块比较漫长的思考是关于自我定位与规划方面,写了人生规划与体系搭建,关于自我与各种不确定性的相处心得、人类科学知识体系的依托、以及与现实既成体制的平衡与改变的话题。疫情以来许多事情都不太明朗,我也在关注有什么事情是自身力所能及的,就写了 疫情后的一些思考,关于如何对于中国社会现状建立基础的认知,立足于自身做些力所能及可以抓住的行动。

在这其中,因为社区经营、以及在各种网站、社交 App 观察人类多样性,外加受 MoeGo 创始人 Ethan 所重视的务实向长期主义创业价值观的启发,我也开始关注自己在纷繁复杂的世界中,做选择和决策的一些原则。结合我过往的成长经历,自己在不同的视角,也有了一些相对稳定的参考方向,整理下来,大概有这几个维度:

  • 人生:主要是关于“有限人生”与“无限选择”的矛盾,庄子“人生而有涯,而知也无涯;以有涯随无涯,殆已” 是一直在警醒我的一句话,我也曾为此栽过跟头;在有限的人生里,不得不需要做一些选择,并专注于自身的选择,印象最深的是侯捷老师《左手程序右手诗》中化用红楼梦的话,“弱水三千,只取一瓢,并且安心好好饮这一瓢”。
  • 知识:这方面我受胡适的影响比较多,目标上注重“深度”和“广度”,“为学当如金字塔,要能广大要能高”;心态上则安心“日拱一卒”,“怕什么真理无穷,进一寸有一寸的欢喜”,积少成多,自然“功不唐捐”。
  • 关系:一方面是“无常”与“爱”的关系,近年听的许多歌曲,有许多关于它们的思考与表达;另一方面,对待他人时,保持真诚,但也需尊重彼此的独立性,与外界和他人相处中秉承课题分离原则,保持一定的边界感,给予彼此的尊重与接纳。
  • 工作:“平衡”与“专注” 是我自实习以来在工作上的一个重心,职业上希望是做一名靠谱的工程师,专注于研发的 核心价值;行事上秉承健康可持续的原则,力求达成多方共赢,而非社会达尔文主义盛行当下各种各样零和博弈的内耗。
  • 商业:关注 “信任”与“影响”,在与他人的交往中,注重信誉积累、个人口碑的沉淀,构建完善属于自己的价值体系,金钱作为其中的一个载体存在而非以此为目的;与此同时,还需要适当做一定的影响力建设,让自己的努力可以被看见,类似在技术层面上搞搞 域名与个人主页开源项目 等等。

以上列了很多,更多在于方向与目标,有的我可能未能完全做到,但确实在努力接近。随着我的进一步观察,这些维度未来可能会接着变化,但就目前来看还挺自洽的。我想这也属于我当前的一个“航向”所在,同时它也作为一个“牵引之力”,对抗自然的“无序熵增”,继续组织着身边各式各样的“养料”,去浇灌属于我的“人生之树”吧。

路过南外看到的校训:“像树一样成长”路过南外看到的校训:“像树一样成长”

工作

22年经历着变化,从腾讯出来来到 MoeGo 至今,满打满算待了一年半,总体来说我挺满意。遇到了非常 nice 的同事们,有很多主动推进事情的自由,外加团队重视知识沉淀、信息相互引用链接的状态,注重有效的结果而非流于形式,也伴随着许多的锻炼和成长。从最初的工作节奏变化、慢慢调整自己,到今年逐渐步入正轨,经历过一些迷茫,也逐步开始有些产出。

工作内容上,延续着在腾讯时的业务导向,算是比较深度地在 React 生态之间完成各种各样的 UI 和交互,相对复杂的表单逻辑,antd form 的各种小细节也踩过一轮。技术的版图上集中在 Web 与 React、RN 这块,写代码上,基本稳定在类似 React Hooks / Jotai 的思维模式,以及 CSS 与 Web API 等,在实现业务逻辑方面,基本没有什么特别障碍。

背后伴随着一些对工作的小迷茫,一点迷茫来自于偏理想主义的技术研究动力与现实的商业需求的冲突。对技术有兴趣的朋友,多少有些将个人的技术情怀寄托于工作的冲动,这在工作的层面也容易走偏。老板更在意的是你能为他解决的问题,工作能带来的影响和改变,带着一个回报方面的预期。在一个相对务实的环境,也促使我去思考一个自我角色定位的话题,写了 Research & Development Engineer,关于一名 R&D Engineer 在团队中究竟在产出什么样的价值,自己领的这份薪水又付在了哪里。

另一点迷茫在于,我过往的经验更侧重于 UI 细节的精细化还原,偏重逻辑的应用经验相对较少。年中逮着了一次做 C 端页面的机会,算是独立负责的一个最大的模块,上线后有许多来自客户和同事们的正反馈,还是挺开心的。也感谢 leader 的支持与引导,下半年的工作状态也比较专注,基本是在独立负责一些 B 侧模块的开发支撑,完整地经历了几轮从需求 Kick-off、方案设计、开发测试到上线运维的过程。某个 feature 在年底肝了俩月,基本把手上最复杂的模块完整撸过一遍,对于工作的信心也增强不少。

当下比较棘手的一个问题也是,时长估算和预期管理方面还有些欠缺,常把 buffer 用尽。反思自身,我有点像是一个最近火热的 MBTI 测试中的 “P人” 状态,相比于计划而言,更多是基于自身能量驱动去做事情。并倾向于以一个共情的视角代入事情其中,有时候难以从中抽身。仔细想来,比较需要的也是通过自身的观察,解锁一个全局的视野。对个人的行动力的把握,在 Logseq 的帮助下 有改善很多;在时间的掌控方面,还需要更多完整而独立的项目的磨练。

下半年因为所在公寓的社群活动,意外参加了一场关于《用户故事地图》的读书讨论活动,主要由 ThoughtWorks BeeArt 团队的冰沁老师组织。在这其中经历了一些有意思的碰撞,发现其实我当前的工作也和 “用户故事地图”方法 所关注的思维模型非常相似。

故事的开始故事的开始

研发工程师的职能,在于承载产品需求从想法到最终落地的一整个过程,对于工作中的难以掌控,很多时候也是因为在产品落地的过程中,自身没有相对完善的方法模型,在不同角色的对接间疲于奔命所导致。当业务复杂到一定的规模,各个用户交互流程互相交织时,这里的瓶颈也开始暴露出来,故事地图这一思维模型,属于解决这一瓶颈的一个思路所在。

行动上,初步的想法也是,先从大的角度去捋清自己所负责的业务,维护一个大体的脑图框架,迭代功能时候可以以此作为一个上下文,去和 PM、Design、QA 等角色更好沟通。在项目开发中则以此作为核心目标,形成技术方案,大致界定模块各部分的优先级,把自身有限的行动力尽量花在刀刃上。

这里在我下半年跟进的几个项目之间已有初步实践,大概符合一个从 开发走通 MVP产品 + 设计介入完善 再到 产品 + 设计 + QA 介入进一步打磨细节 的过程。根据同事的反馈,我经手的需求的上线质量还可以,但在提测阶段产品、QA 和走查的设计同学的体验会略有些痛苦;分析背后原因,我想可能是我前期的时间投入相对较多,压缩了后续阶段参与同学的时间,导致其实没有太完善的 MVP 直接走向了提测阶段。时间资源的把控和分配策略,也是我下一步需要改进的点。

进一步的计划也是,先从手上项目实践经验出发,继续去做些探索,未来有心得时再进一步写写,或许可以形成一些新的文章,主题大概是关于如何应对复杂交互 GUI 应用开发的一系列方法集合。

2024.8.10 更新
延续这里的想法,最近写了:浅谈 GUI 应用开发

2024.10.26 更新
想法具体化到 React:关于 React App 业务逻辑的组织

技术学习

这一年花在技术的时间主要都在工作内了,可以肯定的是,社畜模式后,即使下班不晚,在工作以外能专门投入学习的时间其实并不是很多。幸运的是我的职业与兴趣在一个比较 match 的状态,也算是有所成长,日常开发学习到许多奇技淫巧,常在团队中小小分享一下。

在技术上一个比较核心的主题还是,寻找适合我自己的细分课题,在腾讯时候也和 ld 聊起过自身关注点相对较发散的困惑。实习工作到现在也四年有余,也差不多需要选择属于自身专注的子领域了。在现实的工作与理想的技术兴趣相平衡下来,继续搞 Web App 大致也符合我当前的生存状态,也能顾及到知识、技术方面的积累。但 Web 本身概念太大,于个人而言精力有限,关注点需要收拢,目前社区潮流的 Jamstack 也如当年的 LAMP 一般,属于一个关注点收拢后的一个大致方向,感觉可以是一个聚焦的概念。

在这方面更具体的思考,我想我可能可以引用一下我之前写过的帖子博文

Jamstack 这一概念把 Web 在「文档」与「应用」的层面,把技术栈都收拢到了 JavaScript 生态下,覆盖纯 Client Rendering 到 Server-Side Rendering 的场景,再到更精细的 React Server Component 等方案。这也恰好也和我过往在 Flash / React / PHP 的兴趣方向相重叠,就 Web App 开发而言,觉着或许也是一个未来值得专注的发展方向。

基于 Web App 作为锚点,可以再进一步引申出 RN、Flutter 等更贴近操作系统的 App 开发方案,差不多可以符合一个独立承接产品落地的 “Web 全栈工程师” 的角色定位。对我而言,下一步的课题大概也是,围着这样的定位和目标,继续一整个专注学习的过程。

近年来的学习工作经历让我愈发觉得,“技术”并不是生活的全部,视野的打开还是挺重要的,加之我不太喜欢在某种丛林法则之下相互内卷厮杀的生活状态,未来希望能从出海的视角多看一看,无论在国内外,最好都能以一个国际化的视野去面对生活的方方面面。从这个角度出发,尽量习惯英文的语境,是我接下来无法避开的课题,也需抓住已有的机会多关注关注。

0xFFFF

相比于21、22年的各种整活,今年在社区运营上走向佛系,忙于工作和生活,精力投入不是很多。

考虑到 150 定律的魔咒,以及人群价值观的多样,不刻意再做额外宣传,更多立足于身边的小圈子,还有参与人中相对种子的用户;这一年在活动上,主要是和在广深的群友随机约饭,谋求更多的线下交流,上半年回了一趟华师,找老师和图灵班的师弟师妹喝茶;也趁社区建立的重要推手 johnbanq 假期回国时,组织了一些在深圳的朋友们小聚了一下。第一次拉 @Miigon@johnbanq 见面,想来还是非常有纪念意义,背后是一个比较神奇的世界线收束故事,这里先不展开。

小聚、喝茶与聊天小聚、喝茶与聊天

今年在网站架构层面,做了不少的变更,主要是把论坛的代码仓库收拢到 Flarum 0x,并将附件上传等能力的目标都收到对象存储中,减小对服务器本地文件系统的依赖;网站的部署上开始尝试 fly.io,成本与访问速度我都很满意,虽然后来因为备份问题,还是搬回了独立 vps 部署的方案。下半年来花了不少时间去想办法优化网站的访问速度,在 DNS / 对象存储 等方面脱离了对 Cloudflare 的依赖,通过 阿里云香港、fly.io、DNSPod 等服务实现了内外网分流的能力,大大改善了大陆内网打开网站的速度。最终整理得到的建站方案,也收到了这篇文 适合个人网站的云服务组合

其中也经历了一些小挫折,由于我这里的变更较为激进,备份方案没有做好,在下半年一次日常升级中意外把 MySQL 服务的 volume 数据整丢了,还好之前有手动备份一部分,但一部分数据丢失无法再找回,在 @Miigon 的帮助下,利用搜索引擎的 cache 找回并恢复了绝大部分关键帖子。这也让我充分体悟到了数据安全的重要性,单机部署数据库,多少带着一些风险,备份和 DBA 相关的方案也是其中非常重要的一环。感谢力铭安利他们的 TiDB Serverless,但目前论坛数据规模并不大,还没有到一个亟待拆分 db 的状态,考虑大量 SQL 查询在外网传输的响应时间会增加不少,且 Flarum 本身没有比较完善的 cache 优化机制,所以暂未采用。

今年社区运营的一大重心,在于重修 0xFFFF Wiki 的 入门专题,我希望能把我酝酿已久的关于比较理想的大学学习状态,理想与现实的冲突解决思路等等,再做一次比较系统性的梳理,算是了却我在大学时的一份执念,也希望未来同路人们遗憾可以少一些。同时希望它可以作为社区基本世界观的基础,确立一些基本的原则,更具体的学习方法、资源等,可以基于此再持续延伸。

入门专题的 核心部分,写了两个多月基本成型。还有些具体操作方法相关的手尾,由于下半年更多忙于自己的工作生活,一直找不到感觉继续去写,与原定计划出入比较大。想来更多是我自身的动力不足所致,和群友线下吃饭也有聊到,不知不觉,我在现实中的大学毕业也将近四年了,离学生的生活状态还有本科阶段的迷茫也越来越远。有的在我身边圈子已经理所当然的观念,我很难再从一个抽离的视角去强调。

未来我有我需要关注的生活,入门指南相关的部分,估计也只能先放下。因此就入门指南而言,我的想法也是,先将其完篇,剩下的细分领域方面的内容完善,就留给后辈去做吧。

再看 把博客作为知识管理的一部分 的感想再看 把博客作为知识管理的一部分 的感想

接下来在运营上,除了日常的约饭,估计可能试试搞些 Linux、Web 开发相关的一次性分享活动,看能否激励下后辈在这方面做些事情;也许还可以尝试做一些无主题的讨论,乃至于在播客等自媒体方面有些探索。从一个务虚的方向来看也是,希望在一个持续长期的投入之间,能发展出专注沉淀不浮躁的计算机学习文化,以一个相对较好的氛围,去支撑我自己和身边朋友的持续成长。

书影音游

回看我这一年在读书观影等文艺向的活动,算是一个少而精的状态。感怀于时间精力的有限,也希望心灵多少能有些走在路上,这里作些简单记录,就不太仔细展开了。

今年的读书侧重于个人成长方面,陆续看了比较有感触的几本,刘未鹏《暗时间》、阿德勒《自卑与超越》、梁永安老师的《阅读、游历与爱情》、斯科特派克的《少有人走的路》,还有前文说到的《用户故事地图》,偶尔会再翻翻《黑客与画家》。

电影电视剧方面,今年比过往看得多一些,也有不少的感触和感动。受同事的影响,去了几次线下影院多点,年中时候B站重温了下《音乐之声》;在电影院看了理想主义向的《宇宙探索编辑部》,虽然我不太能 get 到男主的执着,我所期待的理想主义,更多是能落地的那种;中间看过主打唐诗三百首的《长安三万里》,还有漫威 / DC 视觉向的《闪电侠》、《碟中谍7》;年底搬家入了个大电视,在晚饭之余看了《无法成为野兽的我们》,还有《肖申克的救赎》。

日常听歌方面,循环的重心从许巍慢慢转向了 伍佰 & China Blue,除了他演唱会常唱的几首,《白鸽》还有《枫叶》我也挺经常循环,还有闽南语的《世界第一等》。越来越喜欢伍佰身上某种历经沧桑后的一点洒脱与浪漫,和我现在心境好像也有些共鸣,虽然我年龄不到 25 岁,但好像也经历了不少的无奈与成长。

今年在音乐上多了许多现场的成分,5月去看了一场拼盘演出,主办方和场地音响等浓浓的割韭菜气息,但还是被 逃跑计划 & 夏日入侵企画 & 房东的猫 的那种扑面而来的真诚和感染力所治愈。端午朋友来深,一起去看了《歌剧魅影》,后来零星冲了几场音乐会,挺喜欢那种“高雅艺术”震颤心灵的感觉。

吉他练习还在继续,相对较挑战的 F / Bm 和弦、以及常用和弦的转换目前初步熟练,可以刷一些简单的和弦走向了。进一步大概得关注一番乐理相关的知识,不过意识到自己的精力有限,且这里不是我主攻投入的领域,所以就先佛系推进吧。

骑车通勤时会听听播客,偏向于技术和商业、投资理财等方面。不知不觉把《内核恐慌》多年的老节目都听过了一轮,听 Rio 和 吴涛 唠嗑各种计算机的各种技术细节等等,带来很多有趣的脑洞发散,一些属于技术宅世界的陪伴感觉;其中顺藤摸瓜听了蛮多期 Rio 的《疯投圈》,聊到消费与品牌营销相关的话题,解锁了一些新的视角。偶尔听听孟岩整的《知行小酒馆》《无人知晓》,蛮喜欢 E10 让万物穿过自己 聊到的,每个人都有着各自不同的认知与情感的“房子”;在某种抽离的视角去观察人类、反思自己应该是什么样的生活状态,也是一件挺有意思的事情。

今年比较大件事是《塞尔达传说:王国之泪》终于上线,兴冲冲买来卡带,可惜有心思玩的时间不是太多,周末有心情有时间时打一打,或者带带亲戚小朋友,大半年来游戏时长不过 30 多小时;另一个感觉很不错的游戏是《Fitness Boxing 2》,感觉是非常适合我等肥宅的运动起步方式,稍微玩玩就能出一身汗。

日常

总的来说,这一年没怎么出去玩,忙碌于工作与生活为主,伴随着一些变化。

一点变化是今年我终于下定决心开始考驾照,过程比较漫长,大概五六月份开始刷题,7月份考科目一,9月份连续三周早晨七点出门去练车场练科目二,10月份放空了一下,11月连续练了两周的科目三,幸好在12月一次考过终于拿到证,免去了许多来回拉扯、请假调时间的痛苦。总体感受上偏应试教育,不过对开车这件事本身,一波流程走下来,确实恐惧感要小很多。报名时选的 C1,现在来看我日常能接触到的手动挡的车,应该只有教练车吧,在路况复杂情况下连续换挡确实略有些痛苦,理论上 C2 驾照已经足够。

另一点变化是今年多了一丢丢的理财意识,最初我对于金钱是一个完全无所谓的无欲无求态度,随着自我的成长,也慢慢意识到,出来打工,并不是拼命赚钱存着就完事儿,还有许多问题和风险是需要关注的,保持现状反而可能是更坏的选择。因此也开始学习了一下相关的知识,划分了一下大致可能花钱目标,参考 四笔钱 的思路,适当地按回报与流动性的不同预期分了一下,放到银行定期等各类理财产品;并为自己与父母配置了一些保险,在经济理性的层面,适当对冲一下生活中潜在的各类风险,借此也放下了许多的焦虑与不安。

年底公寓到期,搬了个家,从公寓搬到小区房,也终于有了独立的厨房和客厅,外加适合看片打游戏的电视和音响。其中进一步提高了一点预算,与老一辈能省则省的观念有些冲突,在积极的沟通下相互有了许多理解。接下来解锁了日常做饭的模式,主打一个照着小红书教程糊弄熟能吃状态,下班回家开始捣鼓,一份晚上吃,一份顺便可以带饭到公司,还是挺舒适的。

事实上我爸妈在行动上表示真香 🤭,开始寄一些家乡菜来深圳,慢慢我可选的食材从万能番茄、万能生菜、万能鸡蛋、万能胡萝卜、万能猪肉 / 排骨、万能生菜油麦菜,再扩展到西兰花、家乡的腊肠、鱼干、酸菜、鸡肉等等等。周末做做饭煲煲汤,确实非常治愈和放松。未来可能再进化一下,整点减脂餐啥的,保持一个健康的饮食习惯。

感情

偶尔和家人、亲戚聊起恋爱方面话题,感觉就我当前的生活状态来看,单身应该会是一个常态,工作学习的社交圈,在异性接触上也相对有些局限:在深圳的圈子大多家境尚可、难以理解小镇做题一路出来的艰辛与割裂;在家乡的圈子,大都有了比较大的认知差异,总不可能强求对方为了和我同频、继续这样的折腾与破圈吧?因此我这两年开始观察一些偏相亲导向的社交 App、小红书的交友帖等等,在一些问题的引导下,思考我自身的一个定位和方向,还有在深同龄人的生活状态。

有些难过于现代社会的原子化,以及在这背景之下部分年轻人对待恋爱与婚姻,近乎可怕的现实乃至算计。最近看到一段项飙老师与许知远 在《十三邀》的一段对话视频 给我莫大的安慰,他们俩聊到一点,在现代社会中,我们逐渐丧失了在“附近”中构建爱的关系的自信,有的人回归于血缘构建的关系网络,有的人则通过理性计算的手段去实现各种“门当户对”。但基于经济理性的计算,有时候会破坏那种自然的“爱”的感觉。于现代人而言,如何应对这样的自信的消失,又尽量不破坏那种自然的感觉,以此稳步发展、构建出稳定的“爱”的关系,是一个需要大家共同面对的课题。

梁永安老师的《阅读、游历与爱情》在这里也让我有些启发,他说,现代社会更像是一个“游牧民族”、“海洋民族”、“农业民族”的多重属性结合,意味着有更复杂的生活状态互相交织。传统的二十五六岁开始买房成家立业等等压力,更多是传统农业民族的一个思维缩影,但到了深圳等一线城市,其实更像是一个游牧民族乃至于海洋民族的生活状态。这也就意味着这背后有存在很多矛盾冲突,处理这样的矛盾,没有太多前人的路子可以循着走,我们需要从中去探索适合自己的生活方式。

22年在B站看到一期 关于爱情的思考的 Vlog 给我很大震撼,大概聊到了同龄人在感情上的一些困境,以及比较理想自洽的恋爱形态。再结合一些我所观察到的农村传统社会与现代社会的差异点,以及在网络中看到的风格各异的价值观和思维模式,慢慢思考和观察自我,做了个大致的总结,也就是这篇 对恋爱与婚姻的抽象观察

这样对恋爱与婚姻的思考,也和我学习理财的思路类似,从理性的视角,大概对自身的长短板、观念和期待等等,有个兜底的预期,借此放下一些焦虑,在能力圈内腾出一个自由探索的空间,避免被中间穿插的理性现实成分影响太多,以此可以容纳更多的相对感性的“爱”的成分。由此,我对未来会持一个乐观态度,更多就交给缘分好了。

希冀

随着认知的提升,做选择的过程,伴随着许多有益的思考,这其中也带着许多前人智慧的延续,我想这也是读书学习最大的意义所在,由衷地感激所有陪伴、启发与指导过我的朋友、老师、前辈们。

当前的经济大环境处于一个低迷周期,个人对此在策略上也是,不与周期做太多对抗,不强求大富大贵,只求身体平安健康,保持知识与财富的持续积累,过好当下,知足常乐,行稳也致远。

但生活的锤子仍接踵而至,还是希望未来能比较平稳地接住它们,保持住一份对生活的热情。摘录一段这两年来我一直很喜欢也很打动我的话,来自林沛满前辈的《一个技术男的自白》

关于生活,IT男们已经被打上了太多标签:宅、木讷、生活简单。这当然是一种偏见,至少我身边的朋友就不是这样。不过比起国外的工程师群体,我们的业余生活似乎是单调了些。比如与我合作多年的国外同事中,有组乐队的、当冰球教练的、玩帆船的、DIY花园的……有些朋友对此羡慕不已,以为发达国家才玩得起多样化的娱乐,对此我不敢苟同。比如中国学习乐器的人数早就全球第一,在我屈指可数的女同事中,至少有三位在小时候考过钢琴十级。我所住的小区一楼都配有朝南的大院子,园艺条件极佳,只是户户都铺砖硬化了……所以细想起来,经济上并不是主因,只是不够热情罢了。工程师本来就是最擅长DIY的群体,只要行动起来,完全可以让业余生活更加丰富,成为一个更加有趣的人。

新的一年,没有太多额外的期待,希望可以多出去走走看看吧。