MoreRSS

site iconEltrac | 極客死亡計劃修改

常讨论心理学和社会观察相关内容,不敢说很懂哲学;也会写笔记和生产力工具相关内容;还是学生。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

Eltrac | 極客死亡計劃的 RSS 预览

被无限放大的动物性

2026-02-08 00:46:54

在写下这行字的前一天晚上,我有了一段还算惊险刺激的经历。这件事情过去之后,我这个一直有些厌男的男人,终于发现了自己确实有着刻板印象里的男人的特征(或者说弱点)——无法避免被自己的下半身牵着走。别误会,我什么出格的事情都没干,但我的确有了些非常真实的体会。

事情是这样的,寒假我没有找到专业相关的实习,便回到了之前待过的教培机构工作,担任助教。学生基本是大学生,所以都还算能聊得来。前一天刚好结课,于是就有了一些出游的安排。还需要事先说明的是,我是一个会把感知和判断分开的人,也就是说,我感受到了某种东西并不代表我一定会基于这个感知做出决定,比如我觉得花很香,并不代表我一定会去摘这朵花。每天和我相处的基本上都是只比我小一两岁的学生(还有不少和我同岁甚至比我大的),虽然鱼龙混杂,教室里充满了臭直男的味道(字面意义上的臭,汗臭和不知道来源的味道),但偶尔也能见到对我有吸引力的男性。出于职业道德,以及不想惹麻烦上身,我从没有做出过任何行动,而且一两年内也就只遇见过一两个吧。

过去一个月,有在隔壁班见到一个矮矮的男生,经常能看到他在前台和客服姐姐聊天。两位客服姐姐跟我也算老朋友了,比较聊得来,所以上班摸鱼的时候都会跑到前台去跟她们聊天。因此,我经常能和那个男生打上照面。我觉得他的长相和行为都很可爱,尤其是笑起来的时候,所以见到了会忍不住多看几眼,仅此而已,毕竟也没有更多的交集了。

结课的那天下午,他所在的班级比我早结课,可他一直待前台没有走。到了下午,学生走了之后,还有员工的年前茶话会,一直持续到六点,然后就是聚餐。一直到我们出发去聚餐的时候,他都还没有离开,还问他的老师能不能加入我们,最后他也跟着来了。要补充的是,在开会之前,一位客服姐姐突然找到我,带着聊八卦的眼神跟我说那个男生的事情,大致意思是另一位客服姐姐认为他可能是双性恋,而他没有反驳,表示不确定、有些迷茫;然后他们顺水推舟,说可以把我介绍给他,而他对此的回应是:「他肯定不会喜欢我的。」最后,客服姐姐让我自己努力,我当时并不觉得会发生什么事情,直到聚餐快结束的时候。吃过饭之后,他先和朋友离开了,走之前我和他对视,然后他走过来问能不能加我的微信。嗯…… 我当然是很矜持地扫了他递过来的二维码。

聚餐结束后,我和两位客服姐姐以及一位年轻老师去酒吧玩,特地选了比较安静的小酒馆。之前说好要来的还有几位比较熟悉的学生,毕竟年龄差距不大,能玩到一起,但令人气愤的是,在等了将近一个小时之后,有四个学生突然改口说不来了。最后到场的的只有前面提到的那个男生。嗯…… 另外三个人当然在暗中用尽手段让他和我坐在一起了。

每次去酒吧的时候,我都会开玩笑说「我要做全场唯一一个清醒的人」,因为我患有的疾病不允许我喝酒。我点了一杯无酒精的莫吉托,另外三人点的是度数不低的饮品,坐我旁边的男生一边看菜单一边问什么酒度数高,最后还是点了啤酒,大概是碍于价钱的缘故。他一直在酒桌上说自己想要喝醉,因为之前从来没有喝醉过,然后另外三人似乎接上了他们之前就聊过的话题,我没太听明白。大概是说,她们认为他买醉的原因是受了情伤,因为他的一个好朋友在最近变得很冷漠,她们认为他实际上对那个朋友有近似恋爱的情感,所以会觉得对方的女朋友配不上他、会因为对方突然和自己交流变少而念念不忘。他当然没有承认,反复说自己只是因为之前没有喝醉过,想要试试而已。这部分话题我实在不太兴趣,剩下的就略去了。

这个男生很快就把一杯啤酒喝完了,在点第二杯之前,他还尝了另外三个人的饮料,我的没有被动过,因为没有酒精。后来玩游戏和聊天的时候,他显然已经酒意上头了。他还在纠结要点什么的时候,凑过来问我点的什么,知道我点的无酒精饮料之后他把头靠在我的肩膀上,抬着头瞪大眼睛问我为什么不喝酒。后面他借着酒劲儿,好几次往我身上靠,我记得有很长一段时间,我们两个的腿都是贴着的。如果要说我一点反应都没有,那肯定是骗人的。不过,这件事对我这个从来没有过恋爱经历的人(不算单恋的话),属实让人不知所措。

大概是因为觉得他太可爱,所以他性格里那些幼稚的部分没有被我注意到。他的一些行为的确很像小孩子,在事后想起来才觉得出格,比如他趁人不注意偷偷喝别人的饮料(据我朋友后来所说,那杯酒有四分之三都是被他喝掉的),为了引起注意往别人杯子里丢瓜子壳。后来,我们一致认为他真的有些神智不清了,想要让他打车回家。可是一而再再而三地都劝不走,他一直表示自己没醉(每个喝醉的人都会说的话),并且在气氛冷下来的时候叫其他人一起继续聊天。我们知道他第二天还有安排,八点就要到场,他在得知已经过了凌晨的时候,有些犹豫,但很快又恢复笑脸,劝朋友们继续玩,说他早上起得来。这种不愉快的气愤僵持了大概半个小时,才终于拖着他出了酒馆。

嗯,接下来就是惊心动魄的部分了。

出门没多久,他就拉着我的衣袖,带着我往前走。我不知道读者们能不能想象,一个比你矮一个头、有些娇小的、长相可爱还有点小帅的男孩子,轻轻拉住你,和你靠得很近的那种感觉。他有时还摇摇晃晃地,抬起头盯着我问我问题。我不知道怎么描述这种感觉,用诚实又带点粗俗的口气来讲就是:不行了我真的要硬了。 I’m only human!

不知不觉地,他就把我拉到了和朋友们间隔十几米的位置。他用软软的声音问我接下来要去哪,我说我要回家,他又问我我家住哪,很快,他又低下头,说他没有地方去。这里要补充一个背景信息:来这个机构学习的学生有很多都就读于距离较远的高校,通勤时间在四十分钟以上,所以不能住宿舍,一般会在附近的公寓短租。他的意思是,他租的地方到期了,想问今晚能不能住我家。他真的醉了,说完这句话之后就停下来,闭着眼睛说了两遍「就一晚、就一晚」。要说我真的没有一点把他带回家的冲动,那也是骗人的,但我非常清楚自己不应该这么做——我们正式认识才第一天,而且他现在意识不清醒。

我无助地回头,看见朋友们还在后面聊天(后来他们告诉我,是在商量要怎么把我救出来)。我正好手上还拿了东西,于是战术性地装作很忙,把东西收进包里,也顺便把挎包移到身体前面,压一压不应该起来的东西。听到我的呼喊过后朋友们加快脚步走了过来,我告诉她们他没地方去,她们一开始都不敢相信。据他所说,他一开始和一个同学合租,后来和前文提到的那个好朋友一起住,但现在那里的住处到期了,那个好朋友走了,他的行李放在机构那边,现在确实没有地方去。

是的,一个喝醉的大学生没有去处,现在另外四个清醒的人要帮他想办法。一个客服姐姐帮他出主意,联系了一开始和他合租的那个同学,还找了另一个靠谱的住在同一所公寓的学姐接应他;另一个客服姐姐确认他没有神智不清填错打车的目的地;同行的那位老师靠在我肩膀上1,一方面她也有些醉了,站不稳,另一方面她想护着我一下。很令人安心的组合,很快就把他安全送上车,也从接应的人那里得知他安全到达了。车到达之前,男生站在路边啪啪掉眼泪,大概原因是感觉我们想要赶他走。我是看不了人哭的,无论怎么样都会因为同理心想要照顾对方的情绪(除非对方真的疯狂试探我的原则底线,让我觉得他不值得我的善良)。他当时拿起我手上的一盒桌游,一边打量,一边带着哭腔问我之后还能不能一起玩,还问以后能不能去我家玩。还好朋友们帮我打掩护,不然我可能已经约时间了……

后来我们四个人决定走回家,好好聊聊刚才的经历,时间不早了,也好相互护送。其实今天的局进行到一半的时候,一个朋友就向我眼神暗示,告诉我「这个不行!」很显然,当时的我很上头,完全没有听进去。全场唯一一个没喝酒的人竟然比全场喝得第二醉的人还不清醒。 可其实就算事情进行到了这个地步,我也没有觉得刚才他做的事情是绝对不能接受的。在我看来他似乎只是害怕一个人,所以从今天下午结课开始就没有离开,晚上的酒局也想一直玩、不愿意散场,最后打车的时候也很不想一个人走。无论背后的原因是失恋了所以不愿意回到暗恋对象住过的地方,还是真的如他所说只是好朋友心情不好很早就走了,这种在极端情绪下不能忍受一个人待着的心情,我是能够理解的。我知道自己的同理心似乎有些过头了,大概是很容易被人精神控制的类型。此外,除了同理心还有另一个不可忽视的因素,就不必再提了。

回家路上我去上了个厕所,习惯性地掏出手机看消息,发现十几分钟前那个男生给我连续打了两个视频电话。我的手机一直是免打扰模式,所以没有接到。朋友很关心我现在的精神状态,建议我明天晚些时候再回,今晚不要再想和他有关的事情。

我向朋友们承认,他大概是激起了我的某种照顾欲,自然地,我受到了猛烈的批评,这种心态确实是不能有的。一天之后再回想起来,他的确有点像一个情绪黑洞,而且有好多事情都自己憋着不愿意说出口,很多像小孩子一样的行为实际上是为了引起别人的注意,很想和大家一起玩,但又不太能考虑其他人的感受。或许,在一开始,他吸引我的是那种像小孩子一样不会顾虑太多的简单和纯真吧,不过这很不成熟,而这种不成熟也给其他人带来了麻烦。

或许会有不怀好意参透人性的读者猜测我有没有在当天晚上睡觉之前做一些奇怪的事情,我只想说这个问题的答案是不言自明的。我在清醒的时候永远无法想象自己为什么在那种时候变得像动物一样渴望性爱。不过,这就是人嘛,人性也有动物性的部分。人可以靠近神性产生智慧,也可以临时降级为动物被简单的生理冲动支配,只是还好理智的人在大多数时候有自制力。

唔,越写越像个臭男人了,还是在合适的地方赶快结束为妙。

第二天早上七点四十,他给我发来消息:「学长,没有迟到」——因为昨天想让他早点回家的原因之一就是他早上八点有安排。我注意到,在我们四个朋友的小群里,有一个朋友说他在早上十点半的时候收到了类似的消息,也就是说,我是第一个收到消息的。我不知道如何解读。后来他发消息为昨晚的行为道歉,接下来的一天里都很太平。

希望在这样的小插曲之后,这个冬天能安安稳稳地过去吧。


  1. 写到这里突然想问问读者中的男同,你们的异性朋友也会这样毫无顾忌地靠在自己身上吗?或者女性读者能不能接受和熟悉的男同朋友有这样亲密的举动。印象中有好几个和我关系不错的女性都有这样的动作,不过基本上都是喝了酒之后。我倒是不介意,只是好奇这是不是普遍现象。 ↩︎

所有人都在交智商税

2026-02-05 11:15:46

对经济学很感兴趣那一阵子,我读过一本叫《How to Teach Economics to Your Dog》(如何教你的狗经济学)的书,半途而废了,没有读完,不过有一句话我印象很深刻:没有人能独自制成一台烤面包机。

仔细想想,烤面包机的技术含量实际上不高,它要做的只是把面包加热而已,但要制作出能够加热的电器,对包括机械工程师在内的所有人来说,都不是可能独自完成的事情。独自完成意味着你要独自研究烤面包机的工作原理,自己制作零件,甚至自己熔炼钢铁、开凿铁矿,在此之前还要想办法搞到一把用来挖矿的工具,现实世界里可不能用两根木棍和三块圆石做镐子。除非你把「自己动手制作一台烤面包机」当作自己的人生目标并为之奋斗,那么你是没有办法在闲暇时间里仅靠自己做出一台小家电的。

回到标题,我所说的「智商税」,实际上是指更广泛的因能力、智力、精力和注意力不足,而不得不通过花钱将一部分工作外包出去的做法。这对现代社会的运转来说是必要的,而且是有益的,每个人都可以提供用自身的能力、智力、精力和注意力创造的东西,换取金钱,再使用金钱兑换其他人的产物。人们能够通过发挥自己的比较优势创造出更多的价值。所谓的「用金钱购买别人的时间,以此让自己省心」,在我看来,并不算消费主义。消费主义是指认为不断购买商品和为服务支付是有益于经济的意识形态1,我在本文中不会从社会和经济层面讨论消费,我也没有那个能力,我想谈的是个体在进行消费决策时,应该如何平衡「花精力」和「花钱」这两件事。

一般意义的智商税

一些商品因为价格虚高、宣传噱头和实际效用不佳等性质,导致人们认为「购买这件商品的人都是傻子」,所以把这些消费者花出去的钱比作「智商税」,就像是在给低智商、没有判别能力的人征税一样。事到如今,这个词由于太传神,被广泛传播,也导致了滥用,成为了标签。我要说的是,那些动不动就称某件商品是智商税的人,和他们批评的人,也就是真的交了智商税的人,实际上没有什么两样。2

交智商税的人多半是因为自己没有判别能力,听信了宣传话术,继而做出了不理性的消费决策。然而,我观察到的大部分使用「智商税」这个词的人,并不会阐述某件商品是智商税的理由。我经常听到这样的对话:

“我买了一包洗衣凝珠。”

“那不是智商税吗?”

“啊,可是我觉得很好用啊?”

“不如用洗衣液。”

批评者可能有道理,但并没有办法证实,因为对方并没有给出理由,没有逻辑就没有逻辑漏洞。交智商税的消费者,实际上是听信了一种「说法」,也就是来自商家的说法,这种说法是具有迷惑性的糖衣炮弹;而批评者,许多时候,是听信了另一种说法,这种说法不来自商家,而是来自网络上的意见领袖、测评博主和身边的其他人。他们只是换了个队伍站而已。

从我有限的经验来看,能给出实在理由的人,也就是对商品的性质、种类、设计和原理都更加了解的人,并不会使用「智商税」这个词,或者,不会只使用「智商税」一锤定音。举个例子,我不推荐大部分人购买编程课程,因为程序员是最爱在互联网上分享的一群人,任何编程语言和构建软件的技术,几乎都有公开的文档和热心人士制作的教程,这些教程不仅是免费的,而且质量更好。与此同时,我理解有人会花上万的钱购买编程课程,原因可能有三个:一、权衡后选择将搜索和整理学习资料的工作外包出去,直接学习已经归纳好、更容易吸收的课程;二、受到十几年来的教育影响,认为学习必须通过上课来完成,不了解有其他途径;三、信仰知识付费,愿意支持知识工作者,通过支付金钱的方式鼓励更高质量的内容创作。除了第二个可以被视作低智商的某种表现以外,另外两种消费决策都很难说得上是交了智商税。3

简单来说,交智商税的人听信宣传话术,批评其他人交智商税的人听信评测博主。如果没有自己的思考,两者没有本质区别。

那么,要如何做出「自己的思考」呢?

花费精力还是花费金钱?

首先要明确的是,一些智商税是不得不交的,而另一些是可以通过支付精力来获得的。所以,在进行消费决策时,问自己的第一个问题就是:我愿意为这个东西耗费精力吗?第二个问题是:如果我不愿意,消费能够帮助我把精力用在别的更重要的地方吗?

举个例子,前阵子有一家咖啡器具品牌推出了新的咖啡称,要价三百多。这个称的设计的确很好,在读数精准的基础上,配备了显示屏,能够直观地切换模式、查看冲煮曲线,以便更好地控制变量(总觉得手冲咖啡越来越像实验室制取咖啡因……)。在相关的内容下,我看到有人自己改装了一台称,把线缆接入开发板,连接显示屏,也实现了冲煮曲线的绘制。

尽管我已经被邪恶的消费主义文化改造得认为「三百多块钱买个称好像也没有很贵」,但看到有人愿意付出时间精力自己动手,还是觉得佩服。私以为,在商品经济建立起来以前,人们在大多数情况下都是需要自己动手的,而自己动手并不只是为了满足生活所需,还提供了某种满足感,就是生活的一部分。然而,现在却有越来越多的人把自己的生活外包出去,不去体会自己动手的乐趣了。我的想法是,对于自己并不在意、不感兴趣的物件,以消费的形式外包是很好的,比如我就没心思研究一台冰箱的工作原理,然后自己造一台冰箱。当然,有的人可能会感兴趣,但他多半会对造冰箱的某些部分不感兴趣,比如如何制造冰箱的外壳、如何制造感应灯、如何在冰箱太久没关门的时候发出警报……

我每过一两个月,就会想换新的手机壳(也有过裸奔的想法,但最终还是狠不下心来)。我的新想法是,与其在购物网站上浪费大把的时间找自己喜欢的手机壳,不如买一个透明的壳子自己设计图案,毕竟自己已经会做手帐了,而且我也真的很感兴趣。简而言之,我选择了把一部分原料的制造外包了出去,交了这一部分的「税」,把设计交给了自己,在这一部分上投入时间和精力。

我想,每个人的生活中一定有某些部分是愿意交给自己完成的,而不需要通过消费外包出去。做出了花费精力还是花费金钱的选择后,就可以继续论述了。如果你选择了花费金钱,那要如何确保自己交的不是「智商税」呢?

如何筛选商品相关的信息?

你可能会以为我要开始大谈「现在有了 AI 我们所有人都可以低成本、快速地了解到商品的基本信息并做出理性客观的判断了,现在买东西比以前更容易!」之类的话了——醒醒吧!OpenAI 已经在给 ChatGPT 添加广告了!4而且,有关商品的很多信息都被挡在平台上,完整的评论多半要登录才能查看,大语言模型是不会有这一部分数据的访问权限的,它只能收集到一些新闻报道、开放的评测网址和诸如 Reddit 这种平台上的讨论,来自中文互联网的内容更是少之又少。

更不用说,判断和决策这件事情本来就应该交给人类。我之所以坚持不乱用 AI 这个词,而使用更精确的 LLM(Large Language Models)和对应的中文名「大语言模型」,一方面是因为 AI 是一个非常广泛的概念,另一方面是因为,大语言模型做的事情真的只是「处理自然语言」而已——因为不具备产生意识的结构,所以无法在意识的层面处理想法,只能在语言的层面处理文字,把东西拼凑在一起。让这样的东西帮人做决策,是很可怕的。此外,你知道吗?由于日益增长的算力需求,电力需求也大幅增长,导致天然气发电量增大,温室气体排放也随之上升。如果美国目前计划的所有数据中心全部建成,二氧化碳排放量将来到所有能源年排放量的两倍。5更不用提给服务器降温消耗的水资源了!不要觉得人工智能的成本很低,也不要再什么事情都求助 AI 助手了!

回到文章的话题上来。显然,不同类型的商品有不同的考量。对于机械,无论是家电还是电子产品,需要关注「参数」,这是毋庸置疑的。这个思路也可以推广到其他商品上,比如,一个杯子有器型、材质、涂层和容量等性质,这些信息也可以抽象为「参数」。所以,要筛选与商品有关的信息,就是要先明确应当关注哪些参数,然后在充满话术和噱头的商品详情页找到这些真正重要的信息。

任何空气净化器都会声称他们的净化能力很强,但了解之后就会知道这与滤网有关。一般来说,要达到好的净化效果,应该使用 HEPA 滤网(High Efficiency Particulate Arrestment,高效空气颗粒物拦截),这是一种技术规范。欧洲标准化委员会有为其指定等级标准,一般来说要购买 H136 等级及以上的空气净化器,才会有比较好的效果。此外,还有颗粒物 CCM,简单来说,这个参数的等级越高,表示滤网能够净化的颗粒物总重量越多,寿命也就越长,不容易出现频繁更换耗材的情况。相关的参数还有很多,要逐个了解,比较好的方式是搜索有经验的人写下的文章,另一个方式是查看相关产品的参数列表,把常见的名词记录下来,再一个一个地查询。

此外,适当地在社交媒体或商品评论区搜索评价也有帮助,比如我就在得知玻璃吸管可能会在日常使用中产生微小的破裂,是人眼很难发现的,玻璃渣可能会随着水一起进入身体,划伤食道。这是我通过搜索材质的性质和了解各种参数都无法得知的。之所是说要「适当」,是因为这些信息很杂,而且充斥着广告和水分,寻找有用的信息就像在辣子鸡里翻开辣椒找鸡肉一样。

在我看来最好的方式是寻找身边的真实用户,听他们的反馈和建议。我之前有一段时间想买一台用来洗袜子和内裤的小洗衣机,但我的朋友告诉我那个东西很难用。不过我没有听劝,购买之后才发现是真的难用:需要手动加水,中途漂洗的时候还要再加一次水;容量太小,一次也洗不了太多东西;还有红光杀菌,完全是噱头。后来我把它退掉了,购买了隔菌洗衣袋和除菌的洗衣液,这样就可以把内裤和袜子放在洗衣机里清洗。如果我当时听劝,或许早就想到这个方法了。

此外,一定要避免任何广告,广告非常擅长创造需求,诱导人购买他们根本不需要的东西。真正理性的消费决策应该是在了解自身需求,并且确定需要购买一个产品来满足这个需求,再对产品本身进行研究和评估之后做出的。是的,这很麻烦,但不费点精力怎么能守好自己的钱包呢?

在一个理想的世界里,商家会把产品参数放在最显眼的位置,商品详情页不会有人和误导人的话术,各种技术指标都会公开,消费者可以清晰地了解到自己是在为了什么而付费,为什么商品要定这个价。可惜我们并不生活在这样的世界里,所以需要自己把这些信息筛选出来,再做判断。

如何处理与品牌的关系?

在《 制造消费者 》这本书里,作者提出了一个有意思的观点:

现代人与市场的关系让我们对物品感到越来越陌生,也因此觉得它们越来越「神秘」,并产生一种类似宗教信仰的崇拜。如今人们不再关心商店里售卖的清洁剂是不是白醋和小苏打的混合物,只关心商品的品牌,用品牌和品牌对商品的塑造判断清洁能力的高低。

之所以要自己筛选信息,就是要重新建立对物品的关怀,而不是相信甚至崇拜品牌。 判断一块清洁海绵能不能把盘子刷干净,看的不是海绵的品牌,而是海绵的材质和设计,更重要地,要看自己能不能亲手用这块海绵把盘子刷干净。与其买看起来很可靠的清洁品牌的除水垢剂,不如直接买一斤一水柠檬酸粉末,只要五块钱左右,能用很久,而且除水垢效果真的很棒。

简而言之,某个产品好用,并不是因为品牌施展了魔法,而是因为它本身的性质使得它很好用,后者才是最应该关心的。

诚然,品牌无处不在,一些品牌的确把品质控制得很好,值得信赖,他们的产品也值得反复购买。不过,我想要提出的观点是:人们应该自己建立与品牌的信任。如果只是因为看到其他人在使用这个品牌,或者听信了社交媒体和视频平台上的宣传,就对某个品牌产生好感,是不恰当的。

可以把品牌理解为一个黑箱,毕竟他们真的不公开透明。与其把这个黑箱贴上好用或难用的标签,不如自己做黑箱测试,试试看好不好用,有没有更好的选择。更好的方法是做白盒测试,把黑箱拆开,了解和验证产品的参数和规格。完成测试并得出结论之后,在持续的与品牌「合作」的过程中评估这个品牌是否值得信赖。比如,我比较信任的咖啡烘焙商是 Collin Plus 和绝对小孩,这种信任是在反复从他们那里购买商品并使用后建立起来的。对于品牌的信任不像对人,需要随时评估和做判断,如果发现原来值得信任的品牌突然变得不可信,也要相信自己的判断,寻找新的「合作关系」。比如,嗯…… 我现在就不太信任 Apple 了。

减少对品牌的依赖也是重要的。不过,就像使用开源自由软件而非 SaaS 一样,更自由、更开放也不用依赖于某个中心,这也会带来一些麻烦,使用开源软件就是会比包装好的商业软件更难,这是自由必须支付的代价。购买商品也是如此,无论如何你都要支付代价,要么放弃思考、打开钱包,大喊「Shut up and take my money!」,交上你的智商税;要么做个吝啬但敏锐的人,选择支付精力和时间,换取自由、更少的花费和完全属于自己的生活。

这两种都是不错的,全看自己的选择。至于我,我选择后者。


  1. 参见: Consumerism - Wikipedia  ↩︎

  2. 这里可以联系到《 警察警察与警察警察警察 》这篇文章中的观点:人们相互讨厌,可能只是因为他们是同类人。 ↩︎

  3. 还有一个原因:一些人觉得自己花了钱就一定能学好,以此来获得心理安慰,到真的没学好的时候,还能把责任转嫁给课程制作者,指责课程设计不合理,继而避免质疑自己的学习能力。这是心理学的话题了。 ↩︎

  4. 来源: Ars Technica  ↩︎

  5. 来源: The Guardian  ↩︎

  6. 滤净率达 99.95% 以上 ↩︎

稻草人周刊 Vol.66

2026-02-02 09:28:22

⋈︎ · 连接

维生素 D 与心脏健康

📃

一项新研究发现,原本有过心脏病发作的人群中,开始服用维生素 D 补剂并维持在最佳剂量内的,心脏病发作率降低了 52%。维生素 D 其实和心血管疾病没有直接关联,本文解释了维生素 D 与心脏健康的关联。简而言之,维生素 D 被人提吸收后,会调节上百种作用效果不同的基因,能够促进钙的吸收、调节炎症反应、调节血压——可以这样想,维生素 D 减少了其他疾病突然引发心脏病的概率。

维生素 D 的主要获取途径有三种:

  1. 阳光:暴露在紫外线下 10~30 分钟就可以满足许多人的需求,不过这和肤色、种族以及居住地相关。1
  2. 饮食:含脂肪的鱼类、蛋黄、鱼肝油和一些强化食物2,不过,对大部分人来说,食补是远远不够的。
  3. 补剂:维生素 D3 补剂一般更好。

我大概从一两个月前就开始服用维生素 D3 补剂,因为我发现自己根本没有办法每天晒 30 分钟太阳,无法从阳光获得足够的维生素 D。维生素 D3 补剂其实比较便宜,几十块钱买一瓶能吃几个月。关于购买补剂,这里分享一些个人经验供参考。3

  • 如果在国内购买补剂,请分清楚「食品」「保健食品」和「药品」。标注了「国药准字号」的是药品;标注了蓝色的「保健食品」的是保健食品;以上都没有的就是食品。一般来说,要购买的是保健食品,不过很多便宜的 OTC 药,例如小白瓶的维 C、复合维 B 等,成分和保健食品差不多,而且价格便宜非常多,也可以购买。
  • 宁少毋滥。一些营养元素不能缺乏,也不能超出每日最大耐受量,服用补剂前建议查清楚每天建议服用的剂量,根据性别、年龄、体重等因素会有不同。药品和保健食品上一般会写用量,我一般取一个中间值或者最小值。
  • 别看品牌、广告和唬人的认证,只看物质、含量和药品形式。比如,维生素 D3 一般建议每天补充 1000~4000 IU4,那就要在商品详情里看清楚每日服用量有没有在这个范围内。再比如,口服维生素 D 一定是要比所谓的「阳光喷雾」更有效率的,隔壁防晒喷雾还经常被吐槽大部分物质都逸散到空气里了,怎么忍心对更贵、更有价值的补剂这样做?

补剂不是智商税,自己动脑子找靠谱的资料研究,并在选购时忽略宣传话术找到有用信息自己做判断,才是交智商税。

搞笑的为什么比搞学问的更高尚?

📻

学者与喜剧演员的相似之处在于,这两类人都不太可能是为了名利而走上这条职业道路的,并且他们都需要一定的天赋。不同之处在于,搞笑有着一个不可违抗的自然标准,那就是「使人发笑」——即便一个人性格卑劣,但他要是真的让你忍不住笑出了声,那他就是搞笑的。然而,搞学问却有不少人造标准,比如某某学者发了多少篇论文、书是哪个出版社出版的,如今的学者似乎已经不在乎学者自己的观点了,更多地关注「别人怎么评价他的观点」。

允许我忽略内容,谈谈这期播客的结构。树老师在一开始先建立了「共识」,也就是「学者和喜剧演员可以在高尚的尺度上进行比较」的前提。我认为这是在进行智识交流时必要的步骤。之前有人说我文章写得太长,我认为我是把大部分的字数都用在了交代背景和建立共识上。当然,读者可以不同意我建立的「共识」,这样就没必要继续读下去了。

⁂ · 星群

HTTP Cats

这个网站收集了各种猫猫图片,用来表示 HTTP 状态码,可以很方便地使用 https://http.cat/[status_code] 来引用某个状态码对应的图片。没有太多实用价值,但是猫猫可爱。

看起来很冷淡的胖猫,眼神涣散

200 状态码的猫猫图片

访问: http.cat

Good Enough

一家小公司,是 Letterbird 背后的团队,有不少和 Letterbird 类似的小巧的互联网产品,可以访问他们的 Lab 页面。他们的理念和行事风格我都很喜欢。他们以前甚至有一个连接到互联网的打印机,用户可以直接用这个打印机给他们发送内容。真想在这样的团队里工作。

访问: Good Enough

↯ · 当下

数字花园

新网站 Jar 在本周一上线了,名字和 Java 没有任何关系。法语里的花园是 Jardin,前三个字母连在一起就是英语里罐子(Jar)的意思。对我来说,比起不断生长的数字花园,什么东西都往里面塞的罐子,大概更符合我的需求。这里面会存放极客死亡计划的废稿,一些不知道写在什么其他地方的笔记,未来还可能存放计划、路线图之类的东西。

设计上,Jar 秉承 mother-fucking website 的理念,几乎没有编写太多的 CSS,并且也是静态网站,用的也是 Hugo。值得一提的是,Jar 的域名是 jar.geedea.pro,而没有被放在 eltr.ac 域下,你可以把它理解为极客死亡计划的「后花园」。

不建议订阅这个网站,但可以偶尔来看看,因为这里面什么都有。

评论系统已被移除

详见《 极客死亡计划书 IV 》。从现在起,请使用联邦宇宙、Webmention 或者电子邮件与我交流。另外,博客的点赞功能也被我偷偷移除了。


  1. 文中没有提,但我记得,玻璃窗会削弱大部分的紫外线,必须到室外晒太阳才有效果。 ↩︎

  2. Fortified Food,指代添加了额外营养物质的食物。 ↩︎

  3. 不构成医疗建议,如果有因缺乏某种营养物质引发的疾病,请咨询医师。 ↩︎

  4. IU:International Unit,国际单位。 ↩︎

极客死亡计划书 IV

2026-02-01 23:53:07

书接上回 ,先前的一期《极客死亡计划书》最后留了个有关评论系统的引子。如你所见,目前我已经完成了评论系统的变更:我把它删掉了。当然,我提供了其他交流的途径,你仍然可以与我和其他读者,像在正常的评论系统中一样讨论文章内容。今天的文章将会解释评论系统变更的原因、新的与读者互动的方式、我对独立博客的评论的想法,以及读者还可以了解到如何使用新的「交互」系统。

本博客评论系统的历史变更

从 Typecho 换到静态博客之后,给博客添加评论系统就变得很棘手。本质上,评论是「用户生成内容」(User-generated content),涉及到用户向服务器发送 HTTP 请求,服务器响应请求后将评论数据写入数据库并返回状态信息,其他用户访问页面时,服务器会从数据库读取评论数据。显然,对于静态网站而言,服务器和数据库都是不存在的,所以就需要外接一个评论后端。

记得早年间我还使用过 Gitalk ,一个基于 GitHub Issues 的评论系统。后来 GitHub Discussion 推出,更符合「讨论」这个逻辑,于是 Giscus 也就诞生了,几天前,我还在使用 Giscus 作为博客的评论系统。这两个评论系统都依托于 GitHub API,需要 GitHub 账号才能使用。用户也不真正拥有自己的数据,所有的讨论实际上都发生在 GitHub 这一个平台上。上一期极客死亡计划书我也表达了对 Giscus 的批评——依赖于 <iframe>,使得数据交换、优化加载速度和自定义都变得很难。不过,毋庸置疑地,Giscus 非常易用,不需要自己维护后端,使用 GitHub 登录也避免了绝大多数的垃圾邮件。

在此之前,我也有使用过 Valine 和 Waline,它们都依托数据库,可以很轻易地在 LeanCloud 上部署(不过就在最近,LeanCloud 已经停止服务了),也可以自托管。我也有听闻,并且作为访客使用过其他人网站上的 Twikoo、Artalk 和 Remark42。这些评论系统的设计各有特色,但共同之处是你需要自己搭建并维护后端和数据库,这不难,但必然会增加网站架构的复杂性。

动态博客加上评论系统是顺带的事情,但对于静态博客来说,为静态网页加上动态的内容,还要单独维护一个后端,就显得有些不符合静态网站这种架构的哲学了——既然你都选择维护额外的后端和服务器了,那你为什么不直接用动态博客呢?反正都要有后端的,一个轻量的 CMS 并不会占用太多资源。

本来我是为了摆脱 Giscus,才开始思考要不要自托管评论系统这件事的,但我很快意识到我并不想增加网站的复杂性。如果使用传统的填写称呼、电子邮箱和网址直接评论的方式,就会有身份被冒充的风险(获得某人的邮箱地址简直是轻而易举),而且垃圾信息也很难避免,需要做额外的措施。如果使用必须登录才能评论的方式,的确能解决上述问题,但我希望自己的个人信息被上传到尽可能少的地方,而且如果要变更邮箱地址,四处更改账号信息也是吃力的活儿,我的上网原则之一是「如非必要,绝不注册」,我当然不希望我的读者必须额外注册才能评论,维护一个用户认证系统也会让维护后端变得更复杂。

显然,这已经告别了绝大部分的评论系统了。

为了认证用户且不必维护新的认证系统,那么最简单的方式当然是使用一个现有的认证系统。我指的并不是 Google 登录、Apple 登录和社交媒体账号登录这种方案,因为实际上我仍然需要将用户的登录信息储存在自己的数据库里。最好是使用去中心化的身份系统,比如 OpenPGP 密钥对。

Email and OpenPGP

诚然,OpenPGP 也受到批评,它有时会 失效 。不过,在本文中,我们假定它是有效的,因为我还没有开始了解和研究它为什么有缺陷,而且,OpenPGP 也有替代品,你可以把本文中提及的 OpenPGP 理解为「OpenPGP 和与之类似的加密标准」。

在上一期《极客死亡计划书》中,我提到,电子邮件的发信一般是可信的。如果你收到了来自 [email protected] 的邮件,那么发信人基本可以确定就是 Eltrac。尽管也有伪造发信地址的技术手段存在1,但 SPF、DKIM 和 DMARC 等反制手段也很成熟了。在发信协议之上,也可以使用 OpenPGP 加密或者签名邮件2,使用公钥验证再次验证发信人的身份。

所以,如果能开发一种基于电子邮件的评论系统就太好了,大多数用户都有电子邮箱账号,而且电子邮箱是去中心化的,大家可以使用自己熟悉的邮件服务进行评论。我的设想是,评论者向一个电子邮箱地址发送邮件,比如 [email protected],邮件主题是页面标识(标题、URL 或者别的什么 ID),而邮件正文是评论内容。邮件服务器在这个邮箱收到特定格式的邮件后,如果没有问题,就把它当作有效的评论处理,存入数据库或者通过 CI/CD 工作流直接写入博客源代码。如果用户有提供签名,验证身份之后还可以像 Git commit 那样,在前端显示 Verified 或者一把小锁,表示评论者的身份是可靠的。技术上来讲,这完全可行。用户交互层面也不会很麻烦,因为可以使用 mailto: 链接,帮用户自动填写邮箱地址和邮件主体,用户只需要在自己的邮件客户端里撰写评论正文。

不过,我仍然需要一个用来接收和处理邮件的后端,把邮件内容写入博客显然也会增加网站架构的复杂度。通过发送邮件在网站上发送评论,仔细想想,其实非常反直觉,用户需要在不同的界面之间跳转,而且邮件发送和服务器处理会造成时延,用户不能立刻得到反馈。

这个方案最终还是没有被我采用,尽管头脑风暴的过程很爽。兴许写下来之后,会有人觉得有用吧。

Let’s ActivityPub

谈到去中心化,那就不得不谈联邦宇宙了。尽管我并不了解 ActivityPub 规范和设计思想,但我大概了解联邦宇宙的工作方式。简单来说,所有人都可以待在自己选择的服务器上,并且与其他服务器通信,构成「互联网」。联邦宇宙并没有提供密码学意义上的身份认证,但至少伪造身份并不容易,尤其是对于那些拥有自己的域名,并且使用这个域名自建联邦宇宙实例的人来说。

比起一对一的电子邮件,开放的联邦宇宙更适合「公开讨论」。将联邦宇宙用作评论系统可以一举两得地解决身份验证和评论数据管理的难题,毕竟这两部分的复杂度都已经被成熟的联邦宇宙软件解决了。其实这和 Giscus 的逻辑差不多,如果使用 Giscus,讨论实际上在 GitHub 上发生,用户也使用 GitHub 账号登录,Giscus 所做的只是把 GitHub 的讨论和万维网页面映射关联起来。区别在于,使用联邦宇宙作为评论系统的话,尽管讨论也在博客之外的地方发生,但联邦宇宙是开放的,不受 GitHub 这样的中心控制。

如果为了评论而专门开发一个实现了 ActivityPub 的软件,显然有些没必要。符合直觉的做法是,文章发布后,将页面 URL 发布在联邦宇宙账号上,关注者看到之后就能直接对帖子进行互动(点赞、表情回应和评论)。这个帖子(ActivityPub 对象)就作为网站页面在联邦宇宙上的「载体」,对这个帖子的回应就被视为对原文的回应。

听起来只需要让访客到联邦宇宙上讨论文章内容就好了,不过要怎么把联邦宇宙上的讨论内容展示在网站上呢?一是可以在前端通过 API 动态抓取,二是可以用 CI 工作流每隔一段时间抓取一次数据,如果有更新就提交到 Git 仓库,Hugo 直接渲染保存在源码里的数据。那一开始要如何把联邦宇宙帖子和网站页面关联起来呢?一是可以每隔一段时间扫描指定账号下的帖文,检查是否有指定网站的 URL,如果有,就把这个帖子和帖子引用的链接关联起来;二是可以在网站源代码(比如 Markdown 文件的 Frontmatter)中指定帖文链接,直接请求这个帖文的数据。

听起来还是要从头开始搓一个工作流呢,增加复杂性看来是不可避免了,除非有另一个现成的解决方案?

Now, Webmention

我在 第 60 期周刊 中提过,博客实现了 Webmention 的接收;又在 第 63 期周刊 中描述了通过自动化工作流从 webmention.io 拉取 Webmention 数据并保存在 Git 仓库,让 Hugo 直接渲染本地数据文件的做法。简而言之,博客现在已经能够很好地接收并展示 Webmention 了,并且不会牺牲 Hugo 作为静态博客的速度优势和简单性。

Brid.gy 很好地实现了前文提及的「定时扫描联邦宇宙账号和网站页面并建立映射」的做法,简单来说,只要用联邦宇宙账号登录 Bridgy,它就会抓取与你有关的互动,比如有谁点赞了你的某篇帖子、有谁回复了你的帖子等等。与这些互动直接关联的帖子,如果包含一个 URL,Bridgy 就会尝试向这个网址发送 Webmention。

Bridgy 的用户页面,显示了一系列抓取到的联邦宇宙互动,其中的一些找到了对应的 URL 并且向网站发送了 Webmention

由于博客已经能够接收 Webmention,我也就不需要再想办法把联邦宇宙上的互动信息显示在博客上了,只需要渲染 Webmention 就好。

这种方案有缺陷,比如 Webmention 的同步有延迟,并且只能展示与帖文直接关联的互动,所以只能展示一级评论(评论的评论,也就是子级评论,由于是对回复帖文的互动,并不是直接关联的,所以不会被抓取)。不过,我认为这很符合 Webmention 的逻辑,即「有人在万维网的另一个地方提及了这个页面」,也就是说,这并不是评论系统的替代,而是告知读者「在另一个地方有人讨论了这篇文章」,如果读者感兴趣,就可以通过链接前去查看。

“所以,怎么点赞和评论你的博客?”

如果你现在移动到页面最下方,就能看到这样一个条:一个写着「+」的按钮,右边是一些人的头像,最右边显示的是喜欢了这篇文章的人的数量。点击按钮,就能看到一个浮窗,里面有两个表单。

第一个是联邦宇宙表单,你可以在这里填入你的实例域名,比如 c7.io mastodon.social 或者你自己的域名,点击「打开帖子按钮」,浏览器就会打开 https://<实例域名>/authorize_interaction?uri=<这篇文章对应的帖子链接>,效果就是在你的实例上打开这篇文章对应的帖子。不过,这个功能我自己测试的时候有点问题,还请更了解 ActivityPub 的读者给我一些反馈。你也可以选择留空,这样就会直接打开源地址。

值得一提的是,很多页面上都不会显示联邦宇宙表单,原因是页面发布的时候我还没有开始使用联邦宇宙,所以没有关联的帖子(当然你仍然可以在联邦宇宙上 @ 我并附加文章链接,来与我交流),另一种可能是你来得太早,我还没有发布对应的帖子。

第二个是 Webmention 表单,如果你撰写了一篇文章,并且在文章中提及了某篇文章(指页面中包含指向这篇文章的链接),就可以在这篇文章下方的 Webmention 表单里输入你的文章的 URL,点击发送即可向我发送 Webmention。你会被跳转到 webmention.io,如果成功就只会显示一串 JSON 数据,我之后可能会优化一下交互反馈。如果你使用的是 WordPress 或者别的什么实现了 Webmention 发送的博客软件,这个过程就是自动的,你无需手动提交 Webmention,具体能否自动发送还请读者自行研究。

简单来说,如果你要点赞或讨论某篇文章,请点击「打开帖子」并在联邦宇宙上对帖子进行点赞或讨论;如果你在你的网站上提及了我的文章,就可以向我发送 Webmention。

什么是联邦宇宙?我可以注册吗?

联邦宇宙是 Mastodon、Pleroma、Misskey 等实现了 ActivityPub 协议的开源自由软件组成的社交网络,你可以理解为去中心化的 Twitter(也有实现了 ActivityPub 的 YouTube 和 Instagram 替代)。你可以在任何一个联邦宇宙服务器上注册账号,或者自己搭建服务器,在任意一个服务器上登录都可以与任何一个联邦宇宙中的账号互动3

我自己的实例( Eucalyptus )是不开放注册的单人实例,以下是公共实例,你可以选择一个加入,然后就可以和包括我在内的整个联邦宇宙互动了。

你也可以在 FediDB 上查看较完整的服务器列表。如果你对联邦宇宙一无所知,可以访问 这个网站 ,这里有直观的介绍。

有了一个联邦宇宙账号,你就可以点赞和评论我的帖子了。按照上述方式在你的实例上打开我的帖子,就像 Twitter 一样操作吧!当然,有了联邦宇宙账号之后,你能做的事情非常多,你可以去关注更多的人,也很容易在上面认识新朋友。

什么是 Webmention?

Webmention 和 ActivityPub 一样,都是 W3C 标准。我相信你一定有在微信群聊和社交媒体等平台上 @ 某人的经历,我们把 @ 称作「提及」,@eltrac 就是「提及 Eltrac」。显然,「提及」是局限于平台的,你只能提及和你在同一个社交媒体平台上的人。Webmention 直译为「万维网提及」,也就是发生在网站之间的「提及」。假设 A 在自己的网站上发布了一篇文章,B 觉得这篇文章写得不错,也有感而发写了一篇文章,并且在这篇文章里「提及」了 A 的文章。正常来说,A 并不会像在微信群聊里被「提及」了一样收到通知,但 Webmention 补足了这一点——如果你在自己的网站上提及了某人,而对方开放了接收 Webmention 的端口,你就可以向他发送 Webmention 通知;对方不仅能收到通知,还能把提及的内容展示在自己的网站上。

如果你在联邦宇宙账号上和我互动,你的点赞和评论就会通过 Webmention 的形式同步到我的网站上。你也可以不在联邦宇宙上与我互动,而是通过撰写文章页面的方式提及我的某篇文章。如果你使用 WordPress 等博客程序,你的网站大概会自动发送 Webmention;如果你不能自动发送 Webmention,你就需要手动在页面下方的表单里提交你的回应文章。

或者,你也可以用上古神器 curl 发送,毕竟 Webmention 实际上就是一种特殊的 HTTP 请求。

curl -i
 -d "source=<你的文章URL> &target=<目标URL>"
 https://webmention.io/www.geedea.pro/webmention

如果你想要自己的博客能够接收 Webmention,你可以参考 Taxodium 的 这篇博客文章4

邮件联系可以吗?

当然!非常欢迎!我很喜欢和人用邮件这种方式交流。

之前有一些读者询问能否添加我的微信好友,我毫不犹豫地拒绝了。第一是我讨厌微信,我只在不得不使用的场景下使用这个软件,比如和现实中的朋友交流、工作和付款等等;第二是我希望尽可能保持匿名,我的互联网生活和现实生活是分开的,我的微信好友位一般只会留给现实生活中的朋友。之前我还会使用 Telegram 和网友交流,现在也弃用了。5

如果你偏好一对一的沟通,我最推荐的方式是通过邮件联系。不必注重格式,只要写清楚主题,交代清楚来意就好,就算是随便打个招呼我也会觉得很开心。相反,如果是给我发微信,就算是给我发钱我也会觉得压力山大;如果是打电话,那我会在做好接电话的心理建设之前大声尖叫。

这是我的邮箱地址:[email protected]。你也可以在文末的互动浮窗里点击「📧 也欢迎邮件交流!」这行字,这是一个 mailto: 链接,点击后会打开你的默认邮件客户端,并且会自动帮你填好邮件主题(Re: <文章标题>)。

论独立博客的评论

去年二月份我写过一篇《 论独立博客的评论 》。这篇文章下也有不少人分享了他们的看法。这些评论原本是保存在 GitHub Discussion 上的,你现在可以到页面底部点击最下方的一行字,展开旧的评论数据,我都有保留。翻到这篇文章的时候我也有思考,我这么放弃了传统的评论区,是不是也放弃了这样有趣的交流呢?

我想我必须试试才知道。那篇文章中有提到 Owen 把 Telegram 群组代替评论系统当作一场实验,那么我在今年年初开始用联邦宇宙和 Webmention 代替评论系统,也算是一场不错的实验。我也很期待,我在这篇文章关联的联邦宇宙帖子上,以及发送到我网站上的 Webmention 里,也能看到同样有趣的交流和回应。

最近有读到一些博主对评论的思考,尤其是早就禁用了评论区的博主,再加上我最近也收到了我不太喜欢的评论。我又开始思考,评论区真的是最适合我的交流形式吗?我回忆起最近让我感到最愉悦的交流,也包括那些让我受到启发的留言,几乎全都来自联邦宇宙和电子邮件(那些给我发邮件的读者,谢谢你们!)。我也有想过要不要写一篇文章,谈谈我希望收到什么样的评论,不希望收到什么样的评论6,但仔细想想,我讨厌的是不能完全尊重我和我的观点的评论,如果评论者都不尊重我的观点了,他会同意我有关「我希望收到什么样的评论」的观点吗?这显然不会起作用。

此外,我还经常发布大多数人并不在乎的内容,比如法国文学的书评、塔罗牌的牌意解析,未来大概还会有…… Lisp 的学习感受?而这样的内容往往只能收到很少的评论,但它们又是我非常感兴趣的话题。原来的评论区会让我感到压力,而现在,即使一篇文章没有收到太多的回应,我也不会那么在意了,因为评论区都被我砍掉了。联邦宇宙互动和 Webmention 较少,是可以理解的,毕竟使用这些技术的人本来就比较少。抱着这样的心态,我大概会更有勇气写自己感兴趣的文字,虽然我大概还是会阴暗地在 GoatCounter 仪表盘查看某篇文章的访问量

至于我在写作之外,能和读者以及其他的创作者通过新的交流方式碰撞出什么火花,那就在 2026 年一起好好见证吧!


  1. 参见: Email Spoofing - Wikipedia  ↩︎

  2. 一般来说,不建议发送加密的邮件,因为不符合标准。有关为什么不应该加密邮件内容的讨论,可以参考 Migadu 的说法 。况且,有了 SSL/TLS 的保护,邮件在发送过程中往往是安全的。 ↩︎

  3. 除非有管理员封禁了某个实例的情况,但一般来说不会。 ↩︎

  4. 是的,我会给他发 Webmention 的。 ↩︎

  5. 如果你还是更喜欢即时通信工具,你可以在 Matrix 上找到我,这是我的账号:@eltrac:matrix.org ↩︎

  6. 比如,那些显然没有读完我的文章,也对我没有太多了解,就凭借零碎的信息开始想象我的人物形象,给我贴标签或者对我进行说教的。哪怕没有恶意,也有爹味溢出屏幕的。这就是我不希望收到的那一类。 ↩︎

帕尔马修道院

2026-01-29 14:49:53

读完最大的感受是,现在的读者和观众看了《艾米丽在巴黎》这样一部剧之后都会觉得剧情太狗血,十多年前的《绝望的主妇》也有人做出相似的评价,动不动就要给编剧寄刀子,真该来读读这部小说。

主要情节

先容我快速地叙述一遍故事主线和结局。按照惯例,我相信好的小说不会因为被透露剧情就影响阅读体验,所以我会毫无保留地写作。故事发生在意大利,拿破仑还在征战的时期,主角叫作法布里斯·台尔·唐戈,是一名贵族。小说主要讲述他与几名异性的情感纠葛,也涉及不少宫廷政治(这部分对我来说很无聊)。法布里斯刚长大,就极度崇拜拿破仑(《 红与黑 》里的于连也是),于是干了件蠢事,跑到法国去投靠拿破仑的军队,想要去打仗。这样的蠢事在之后的故事中还会出现很多次。

作为一位侯爵的儿子,法布里斯的行径让本不看好他的父亲和与他父亲是同类人的哥哥感到非常愤怒。法布里斯的姑姑,彼埃特拉内拉伯爵夫人,很喜欢法布里斯。法布里斯由于没有护照、身份可疑,又遇上了一些军队里的纷争,惨兮兮地在好心人的帮助下逃到巴黎,在那里收到了母亲和姑姑的信,得知他的哥哥向米兰的警察告发了自己(大概是…… 叛国罪?),他要是回家就会被抓起来。他姑姑伯爵夫人借助她和帕尔马警卫大臣莫斯卡伯爵的关系,将法布里斯送到了安全的国外,此时,狗血的剧情就开始了。

法布里斯是个很清秀又很有热情的年轻人,他姑姑(丈夫死了)感到自己对法布里斯有种禁忌的爱,而法布里斯走后,帮助法布里斯脱险的莫斯卡伯爵由于和她频繁交往,再加上她实在美丽,爱上了他。莫斯卡伯爵提出,彼埃特拉内拉伯爵夫人和他搬到帕尔马,与宫廷里的一位老公爵形婚,这位桑塞维利约公爵人很好,他们无须经常见面也不用做什么事情,她就能有一栋很好的宅邸和遗产,这位老公爵很快就死了。从现在开始,彼埃特拉内拉伯爵夫人成为了桑塞维利约公爵夫人,并且在此期间继续和莫斯卡伯爵交往,而伯爵让法布里斯去进修神学,三年后好为他谋得一个大主教的职位(他这样出生高贵的人,当上大主教是很容易的)。此时,复杂的宫廷政治也开始了。

时间直接跳跃到四年后,法布里斯被莫斯卡伯爵接回来,此时人们已经忘记了他之前的冲动,公爵夫人也对他的改变感到震惊(有种…… 看到年轻的情人长大成人的感觉,更爱了呢)。亲王不喜欢法布里斯,因为不喜欢聪明人的气质,所以写匿名信给莫斯卡伯爵提及公爵夫人对法布里斯的爱情,这里伯爵忌妒得疯狂的心理活动非常值得品味,我会在后文展开谈到。法布里斯暂时离开了一会儿帕尔马,回到他父亲的城堡,去找布拉奈斯神父(他的精神父亲,某种程度上也是他真正的父亲)。他受到了很好的招待,神父也通过占星术预言他将会遭受一段牢狱之灾。

法布里斯意识到了自己的姑姑公爵夫人对自己的感情,而他自己也有着相似的爱,但是他并不想提出来,毁了他与他生命中最重要的女人的关系,于是,他的解决方案是…… 去追求另一个女人。他追求的是一个戏班子的女朋友,玛丽埃塔,戏班子吉莱蒂也就是他的轻敌,与法布里斯在边境决斗,法布里斯杀死了吉莱蒂。贵族杀死平民本不是件大罪,但由于他和莫斯卡伯爵的关系,而宫廷里有不少小人都想要莫斯卡伯爵下台(伯爵是亲王的心腹),其中一人就是审判官拉西,所以这桩小小罪行就被闹大了,法布里斯不得不在国外避风头。帕尔马这边,公爵夫人和伯爵一直在帮法布里斯想办法,兰德里亚尼大主教(法布里斯是代理大主教)也站在他那边。不过,宫廷政治还是太复杂了。在国外,法布里斯又遇到了玛丽埃塔,意识对对她实际上没有真情实感的之后,法布里斯冒险回到了帕尔马,去追求一位女歌唱家,浮斯塔,又和情敌纠缠打斗。由于交际圈的重叠,他在某人的客厅里同时见到了浮斯塔和他姑姑公爵夫人,这使得公爵夫人很生气。此时,上卷完。

伯爵政治上的敌人拉维尔西夫人写假信诱骗法布里斯回国,法布里斯被捕,关进了帕尔马的一间要塞,还是在高塔上(上卷的预言在此应验)。法布里斯被关进要塞的第一天,见到了要塞司令的女儿克莱莉亚,他们之前也有过一面之缘,当时公爵夫人帮助过她。两人就这样在要塞门口看对眼了,法布里斯爱上了她,克莱莉亚也觉得法布里斯有颗高尚的心,关在塔顶的法布里斯想办法在窗户上钻了个洞,每天和克莱莉亚眉目传情,还做了一套字母交流。从现在开始,克莱莉亚的磨难开始了,她想要帮助法布里斯,却不想让父亲为难。之后,公爵夫人带人协助法布里斯越狱,为了把绳子带进要塞,他们迷晕了要塞司令。克莱莉亚见自己的父亲因为法布里斯差点被毒死,感到悔恨,发誓再也不见他。她决定遵从父亲的意愿,嫁给一位很有钱的侯爵。当然,这段时间他们也有过各种拉扯,总之克莱莉亚一直摇摆不定,为道德观念和情感所折磨,而且,她知道法布里斯是个多情的人,她不相信他们俩的感情会有任何不同。公爵夫人和莫斯卡伯爵这边,因为救不出法布里斯,让伯爵这个警卫大臣很为难,他俩闹矛盾分手了,好几章的政治把戏过后,伯爵终于有了进展,公爵夫人收到伯爵的信过后,感觉自己简直要爱上他,还说要嫁给他。

这里省去各种不必提及的人和细节,公爵夫人最后成功帮助法布里斯越狱,并且极力推动克莱莉亚和那个侯爵的婚姻,想着这样能让法布里斯死心。后来,帕尔马亲王死了,他的儿子上任。公爵夫人发觉新亲王对她有爱慕,便卖弄风骚、耍了些把戏靠近亲王,想要借此帮法布里斯脱罪。法布里斯这边,甚至想要劫婚,不过他做了更疯狂的事——当公爵夫人和伯爵正在大力推进重新审判他这件事时,他跑回了要塞,让要塞司令给他关起来,这样就能再次见到克莱莉亚了…… 然后他就又在那个窗洞上跟克莱莉亚打招呼,让克莱莉亚痛苦不已。这是蠢还是浪漫呢? 法布里斯上一次在要塞的时候,就有人想给他下毒,这次也是。克莱莉亚听到消息之后,害怕法布里斯被毒死,带着面包冲进了牢房。这时,法布里斯倒在地上,其实还没有吃饭,但他知道,要是克莱莉亚以为他快死了,她就会流露真情——然后他们就在牢房里做爱了

由于担心法布里斯被毒死,公爵夫人也陷入了疯狂当中,去找年轻的亲王帮忙。谈判多次后不得结果,公爵夫人答应了亲王的请求:如果你救出法布里斯,我就做你的女人…… 然后法布里斯就被救出来了,伯爵政治上的敌人也下台了,法布里斯当上了副大主教,客厅里挤满了人。可是法布里斯开心不起来,因为克莱莉亚已经成了侯爵夫人。公爵夫人没有履行对亲王的诺言,并且因为亲王要求这样一件损害她名誉的事情,和莫斯卡伯爵一起离开了帕尔马,她恨透了这个监禁他侄子的国家。离开后,公爵夫人成了莫斯卡伯爵夫人。亲王为了报复她,又把伯爵的敌人雇了回来,结果只是把他的宫廷搞得一团乱。

现在只有法布里斯还留在帕尔马,意外地受到亲王的喜爱,作为副大主教的声誉甚至比大主教更好。由于陷在爱情的痛苦中,法布里斯足不出户,吃不下饭,竟被人认为是高尚的举动,更受人崇拜了。之后,法布里斯准备开始讲道,并且在靠近克莱莉亚的教堂里讲道,想引起她的注意。终于,克莱莉亚被法布里斯的才干折服,但是她不能违背她向圣母承诺的「永远不见他」的愿心——与是他们就在晚上不点灯的时候偷情(我真的没招了)。三年过去,法布里斯和克莱莉亚有了儿子,当然还是由侯爵抚养,偷情一直没有被发现。法布里斯没有莫斯卡伯爵和姑姑的陪伴,也不能和克莱莉亚在白天见面,孤身一人,所以向克莱莉亚要求他的儿子——然后他们让儿子装病,在伯爵出差的时候伪装了儿子的死,然后把儿子接了出去。接出来不久后,儿子就真的病死了,克莱莉亚以为这是对她的惩罚,不久后也随儿子去世了。法布里斯悲痛万分,辞去了主教职务,到帕尔马的一间修道院去生活,每天也去找姑姑社交。搬到修道院的一年后,法布里斯去世了,莫斯卡侯爵夫人也在心爱的侄子死后不久去世了。

故事就这样结束了。

公爵夫人

桑塞维利约公爵夫人,也就是一开始的彼埃特拉内拉伯爵夫人,到最后的莫斯卡伯爵夫人,法布里斯的姑姑,是本作中塑造得最饱满的角色,比法布里斯的人物形象还要饱满。公爵夫人是很有魅力的女人,帕尔马亲王和后来继位的年轻亲王也为她倾心,甚至让莫斯卡伯爵费心帮助他的情敌脱困,只为了和她交往。公爵夫人其实是很讨人喜欢的角色,她对仆人很好,对真心帮助她的人都毫不吝啬地给予回报,所有跟随她的仆人都十分忠诚。由于法布里斯受到不公正的判决,她曾向帕尔马老亲王扬言要离开他的国家,她收拾行囊时,所有的仆人都忍不住落泪。公爵夫人也很会社交,尤其会举办宴会,给帕尔马宫廷增添了不少色彩。亲王不愿意她离开帕尔马,除了对她有爱慕,也因为她过人的社交能力。

公爵夫人思维缜密,尽管做了许多不道德的事情,是非观念也很明确。比如,她从来没有向法布里斯真正地表达过感情,到了故事的后期,她也的确对法布里斯没有爱情了,真心实意地爱上了伯爵,与他一同生活。在我看来,公爵夫人是会为自己相信的东西奋斗的人,为了捍卫自己的立场、尊严和爱人,她会笃定地做出任何决定。这类重情感的角色也有致命的缺点,那就是容易被情感裹挟和驱动。法布里斯被再次关进要塞,很有可能被毒死的时候,她发疯地跟亲王请求,还做出了她不能兑现的承诺。当然,歇斯底里也是情感真挚的体现。

如果要把公爵夫人比作塔罗牌里的一张牌,毫无疑问是三号牌「女皇」。女皇形象的阔绰、懂得社交礼仪和爱人的特质,都在她身上体现了出来;女皇在逆位的时候,也会展现出类似的歇斯底里。

莫斯卡伯爵

莫斯卡伯爵对待公爵夫人非常专情,我倒是觉得他对法布里斯的态度更值得玩味。首先,就像所有年长者对年轻人的态度一样,他时常教育法布里斯,也能像朋友一样相处。不过,法布里斯很快发现伯爵没有很喜欢他,因为伯爵非常清楚公爵夫人对法布里斯的感情。

在第七章,也就是莫斯卡伯爵受到亲王伪造的信件之后,他一个人在房间里发了疯:

一个凶狠的念头就像痉挛似的支配住了伯爵:“当着她的面攮死他,然后自杀?”

然而,就算没有公爵夫人的直接要求,莫斯卡伯爵也一直在帮助法布里斯脱罪,尤其是在公爵夫人因为伯爵的无能和各种其他政治因素,决定与伯爵分手之后,伯爵也一直在想办法找突破点,当终于有了进展后还第一时间给公爵夫人写信。不过,总体而言,伯爵是一个谨慎且有智慧的人,懂得宫廷里的各种潜规则,也敢于追求自己的幸福。

克莱莉亚

克莱莉亚的人物形象很有意思,表面上是个有教养的女人,很老实,也有宗教信仰,但抑制不住内心强烈的情感。克莱莉亚不容易冲动,因为大部分时候她都在抑制自己的情感,想要做父亲的孝顺女儿,但这种情感太过强烈,她既不愿意见到法布里斯,又深知没有法布里斯她就没办法获得幸福。这种矛盾和纠结从她见到法布里斯的那一刻起就一直纠缠着她,使她的内心不得安宁。

克莱莉亚的内心纠葛其实很现代,她的痛苦源自「既要又要」,既要成全父亲,又要追随爱情。不过,这种情感的另一面是主体性的缺失,克莱莉亚听从父亲的意愿,也离不开另一个男人能给她带来的爱情体验,她也相信天主会给她救赎,但她从来不知道自己想要的是什么。这么看来。「既要又要」并不是一个人要得太多,而是这个人不清楚自己真正想要什么。

最后,克莱莉亚成了侯爵夫人后,用一个自欺欺人的说辞让自己的内心得到了片刻的安宁:虽然我发誓再也不见法布里斯,但只要我们在晚上不点灯见面,就不算违背诺言。这也很现代人。人们很多时候并不是在追求公平正义、道德或者名誉,而是在追求内心的自洽。 之所以说是「片刻的安宁」,是因为在法布里斯提出要让他们的私生子和他一起生活时,她最终还是没有拒绝,这害死了儿子,也让她自己痛苦地死去。

私以为,克莱莉亚是最能在当今的读者心里激起波浪的角色。

法布里斯

在这里,我想分析一下法布里斯和《红与黑》里的于连的异同。

于连是农民出身,但拉丁语背得流利,有学识,受到贵族青睐,维璃叶市长雇他来当家庭教师,他也借此爬上了贵族的交际圈。于连在被判决死刑时,在贵族面前发表了一长串演讲,表明他对这种阶级划分的鄙夷,并声称自己的灵魂是高尚的,是「天生的贵族」;而法布里斯,是世袭的贵族,除了神学书没读过别的书。他们两个都与一位年长的女性有过纠葛(维璃叶市长夫人和公爵夫人),在之后又与一位更年轻的女性陷入爱情(玛蒂尔德小姐和要塞司令的女儿克莱莉亚)。他们两个都有相似的「爱无能」。

不同的是,于连有很高的政治抱负。尽管他和法布里斯实际上都没有真正的信仰,但他们都走上了神学之路。于连进修神学是因为干出一番事业的热情,而他却没办法去投靠拿破仑参军(红与黑中的红代表军装上的红色,黑代表教士服装上的黑色),所以只能在私下保留他的崇拜,在神学道路上另谋出路。法布里斯进修神学,仅仅是因为…… 听话。这是莫斯卡伯爵的主意。可见,于连是一个更有野心的人,而这种野心也使得他失去了爱人的能力,他对地位比他高的女性的爱情,仅仅是他政治野心的投射。法布里斯这边也相似,他追求一位女性,仅仅是因为他觉得「应该这样做」「应该过这种生活」。法布里斯冒险回到帕尔马追求浮斯塔的时候,他的随从跟他说:

“老爷,咱们走吧,”路多维克一再对他说,“您根本没有爱情;我看得出,您冷静和清醒得不得了。”

在上卷的末尾,法布里斯做出了许多荒唐的举动,最后仅仅得出了这样一个结论:我没有一颗爱人的心。

不过,下卷就用了很多笔墨描写法布里斯对克莱莉亚的痴迷。这里要插一嘴,我认为法布里斯对他姑姑公爵夫人实际上是没有爱情的,他只是对这位对他非常重要的女性有着不同于常人的情感而已,由于他不懂得爱人,所以误读了。我想思考的问题是:法布里斯有在下卷学会爱人吗?

如随从路多维克所言,爱情是令人不理智的,而法布里斯的确在下卷变得非常疯狂。我想,他的心的确是终于体会到了爱情,可克莱莉亚作为他第一段真正的爱情体验,并没有让他真正学会爱人,毕竟对方也是一个纠结痛苦、自欺欺人的人物,两个人也没有获得什么好下场。果然,就是要令人掏心挠肺的感情才有看点啊。

如果要从塔罗牌里选一张牌,我觉得法布里斯应该是一号牌「魔术师」,因为他的个人魅力非常强大,总是能够受人喜爱。不只是他姑姑对他的特殊的爱,他被关进要塞的时候,还能受到监狱看守的喜欢,看门狗也会和他玩。最后一章,一个有钱人家的女儿爱上了他这个副大主教(虽然她在故事里作用仅仅是为了让克莱莉亚吃醋),因为他讲道十分有智慧。他当了副大主教之后,也有人翻出他年轻时投靠拿破仑的故事,不过被塑造成了「他曾是一位军官」。写到这里我也想提一嘴,我觉得塔罗牌里的「魔术师」和「教皇」是相似的,但前者更聪明、圆滑、会说话,后者则更像是深耕于神学领域的。

噢,说他像「魔术师」还有一个点,那就是有些不顾他人感受,做事几乎是随心而动的。

最后

想吐槽一下,司汤达的数学好像不太好的样子。译者写的脚注里,有好多都是在纠正「这里实际上是过去了 9 年,而不是 7 年,司汤达算错了」这种问题。

这本书读了一个多月,终于在 2026 年的一月结束之前读完了。今年的读书目标是 40 本,没想到第一个月就只读完了一本。不过本身就是一部有些难啃而且有五百多页长的小说,也能理解。司汤达有在书中简短提及伏尔泰和卢梭,大概可以以此为线索,探索更久以前的法国文学作品。

回见。

如何优雅地部署一个静态网站?

2026-01-28 18:07:29

PaaS 软件用了太久,导致自己对部署网站这件事产生了很多不切实际的想象,总感觉不使用 Vercel、Netlify 和 Cloudflare Pages 就不够现代。实际上,静态网站就是一系列 HTML 文件和必要的静态资源而已,即便是用 Hugo 和 Hexo 等静态网站生成器,得到的也是相同的文件。这些文件如果被用户下载到本地,用浏览器直接打开,也能够正常浏览,毕竟不涉及任何服务端的计算,那为什么还要把事情弄得这么复杂呢?

使用 PaaS

PaaS 的意思是平台即服务(Platform as a Service),可以把平台理解为开发和运行应用的基础设施,PaaS 提供了这些设施,降低了开发者进行运维工作和开发过程中部分工作的复杂度。比如,你编写了一个 Web 应用,通常情况下你需要购置一台服务器,然后配置应用的运行环境、配置 Web 服务器和反向代理、申请 SSL 证书并管理续期、设置防火墙和其他必要的安全措施、维护数据库、维护依赖等等,当应用的代码更新之后,你还需要重新部署。

PaaS 能让开发者做到,直接提交代码库给平台,应用就能跑起来并且运转良好,并且往往具有持续集成和持续部署(CI/CD)的能力,使得新的代码在推送到仓库主分支过后就能自动构建并部署应用。

与之相似的架构是 Serverless,也就是无服务器架构。这两种架构其实都极大地弱化了后端的存在,不过 Serverless 更偏向通过事件触发的云函数,而非随时在线的常规 Web 应用。这里略过不谈。

使用 Vercel、Netlify 和 Cloudflare Workers/Pages 这样的服务构建和部署静态网站是可行的,并且非常简单,一般来说步骤是这样的:

  1. 将你的网站源代码推送到远程 Git 仓库。
  2. 在 PaaS 平台创建一个新的应用,设置 Git 仓库并选择软件架构(如果是 Node.js 等常见技术栈,成熟的 PaaS 平台往往会自动识别)
  3. PaaS 克隆仓库,运行构建脚本,然后将构建产物部署到他们提供的网络上。
  4. 此后每次有新的推送,PaaS 都会重新克隆仓库、构建和部署。

简单来说,你只需要把代码准备好就行,几乎不需要做任何运维操作。

问题在于,部署一个静态网站并不复杂,运行脚本构建好网站之后,将构建产物上传到一个可以分发文件(静态资源)的服务器上就好了。这个服务器甚至不需要有计算能力,可以是对象储存桶。印象中 Vercel 会把应用部署到他们的边缘网络(edge network)上,所谓「边缘」,指的是靠近端系统(end system)也就是用户的网络,网络的边缘部分离用户最近,所以响应速度往往很快。对于静态网站而言,这跟 CDN(内容分发网络)差不多。

所以,如果不用 PaaS,能把这个过程自动化吗?

使用自动化工作流部署

由于我只使用过 GitHub Actions 和 Forgejo Actions,所以这里只谈这两个。Forgejo 的 Actions 几乎是和 GitHub 完全兼容的,语法也相似,所以在下文统称 Actions。

Actions 是自动化的工作流,有多种触发条件,最常见的是 push,也就是在有代码推送到远程仓库后触发;也可以限定分支,比如只有代码被合并到主分支之后才触发工作流。工作流可以执行命令,自然也可以执行用于构建静态网站的脚本,比如 npm run buildhugo --minify 等等。

需要说明的是,Actions 往往跑在与其他运行环境隔离的容器里,不能直接在宿主服务器上运行代码,并且工作流被执行完毕之后,往往会恢复现场,运行的结果不会保留在容器里,所以除了执行构建步骤,还要执行部署操作。如果要部署到一个对象储存桶,可以使用 rclone;如果要同步到远程服务器上,可以使用 rsync;如果要部署到 Cloudflare Workers 上,可以使用官方提供的 Wrangler 工具(不过你为什么不直接用 Cloudflare Workers 构建呢?)。

可以把工作流理解为「剧本」,程序员是编剧,把剧本交给 Actions 之后,它就会在规定的场合下按照剧本表演,结束后,会有人收拾舞台,一切都会恢复成原来的样子。如果要把舞台上的表演保留下来,就需要在表演进行时拍照和录像。

同样地,不同的剧目所需要的道具不同,就像不同的软件和脚本运行所需要的依赖项不同,戏班子上场的时候要把道具带上,编剧也要把道具在剧本里写清楚。Actions 一般跑在一个最小的、容器化、只有一个 CPU 核心的 Linux 系统上,以下称作 Runner。Runner 比较轻量,可以在 Docker 里运行,甚至给我一种 WSL(Windows Sub-system Linux)的感觉,上面预装的软件包很少,所以工作流里还要写安装依赖的步骤。

比如,构建一个 Hugo 网站,部署远程服务器上,工作流可能需要这样写:

  1. 安装 Node.js(如果有 Node.js 依赖)
  2. npm install1
  3. npm run build(运行 Node.js 工具,比如构建 UnoCSS)
  4. 安装 Go 语言依赖
  5. 安装 Hugo
  6. hugo --minify(构建网站)
  7. 安装 Rsync
  8. rsync ./public [email protected]:/path/to/site(部署网站到服务器)\

由于在远程服务器上写入文件往往需要认证,最安全的方法是生成一对 SSH 密钥,在 rsync 之前先配置 SSH 私钥,保证目标服务器上有储存对应的公钥,这样才能正常访问目标服务器。此外,还需要在目标服务器上配置目录权限,保证工作流有权限写入。

具体的 workflow.yml 可能是这样的:

name: Build and Deploy Hugo

on:
 push:
 branches:
 - master

jobs:
 deploy:
 runs-on: docker
 steps:
 - uses: actions/checkout@v4
 with:
 submodules: true
 fetch-depth: 0
 - name: setup Go
 uses: actions/setup-go@v5
 with:
 go-version: "1.25.1"
 - name: setup Hugo
 uses: https://github.com/peaceiris/actions-hugo@v3
 with:
 hugo-version: "latest"
 extended: false
 - name: build site
 run: hugo --minify
 - name: Install rsync
 run: |
 apt-get update && apt-get install -y rsync
 - name: Setup SSH
 run: |
 mkdir -p ~/.ssh
 echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
 chmod 600 ~/.ssh/id_rsa
 ssh-keyscan -H ${{ secrets.DEPLOY_HOST }} >> ~/.ssh/known_hosts
 - name: Deploy via rsync
 run: |
 rsync -avz --delete public/ \
 ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:/home/eltrac/homepage/

之后在服务器上配置好 Web 服务器,将通过自动流程部署的文件公开就好了。以下是一个 Caddyfile 示例:

eltr.ac {
 root * /home/eltrac/homepage/
 file_server
}

这个方案的缺点非常明显,我用这个方法搭了一个网站之后就再也不想搭第二个了。

  1. 你需要管理一对 SSH 密钥,并且你允许了外部程序向你的服务器直接写入文件,这有安全风险。
  2. Actions 没法在本地测试,必须推送到远程仓库后,Forgejo/GitHub 才会运行工作流,这使得调试工作流变得非常痛苦。
  3. 每次运行工作流都需要下载并安装依赖项,对于 Hugo 这种构建速度非常快,甚至不会超过一秒的静态网站生成器,大部分的构建时间都花费在「准备运行环境」上了;而且,这个运行环境在跑完一次之后就会被恢复。如果你使用自己管理的 Forgejo Runner,可以在 Runner 里预安装自定义的依赖项,但复杂度不亚于维护一个单独的服务器。
  4. 用 YAML 写工作流十分反人类。

既然如此,我能不能直接在已经安装好运行环境的宿主服务器上直接运行构建脚本呢?构建产物直接在宿主服务器上生成。这样就可以省去与服务器通过 SSH 密钥认证并建立连接的过程,也不需要把大量的时间浪费在准备运行环境上了。

使用 Webhook 触发远程服务器部署

构建和部署静态网站之所以使用自动化工作流,是想要实现持续集成和持续部署,也就是在确认代码更改后就立刻更新生产环境的代码。一般来说,将代码合并到主分支就代表代码可以运行在生产环境了,自动化工作流可以以此为触发点,在有代码推送到主分支后就执行构建和部署步骤。在服务器上直接构建代码而不是用 Git 仓库的自动化工作流部署,最大的不足在于无法通过 git push 自动触发构建,但这个问题并非没有解决方案。

我不清楚 GitHub 的情况,Forgejo 可以在仓库设置里配置 Webhook,即在某些事件发生时向指定的 Webhook 端口发送 HTTP 请求。只要在服务器上开放一个 Webhook 端口,在接收到 HTTP 请求时拉取新代码并重新构建网站,然后配置 Forgejo 仓库在 master 分支收到新的代码推送时向这个 Webhook 端口发送 HTTP 请求,就能够实现 git push 后自动更新网站。宿主服务器不是容器化的最小运行环境,只要配置好了依赖和必要的软件,就不需要像自动化工作流那样,每次构建都需要重新安装依赖,更新可以在几秒内完成,体验上不会比动态网站慢。

具体而言,要先在服务器上配置好必要的工具。我使用 Hugo,也就需要在服务器上安装好 Go、Git 以及 Hugo。我的 VPS 安装的是 Ubuntu 系统,所以这里用 Aptitude 包管理器安装。

sudo apt update
sudo apt install golang-go
sudo apt install git

使用 Aptitude 安装 Hugo 的时候,我发现版本不是最新的,因此遇到了一些问题。所以我打算直接从源代码编译,先克隆仓库,然后编译,将编译后得到的二进制文件移动到 bin,测试能否在终端运行。

git clone https://github.com/gohugoio/hugo.git
cd hugo
go build .
mv hugo /usr/local/bin/
hugo version

其实也可以直接用 Go 安装,但我忘记当时遇到了什么问题,可能是没有把 Go 的 bin 添加到 $PATH,总之没能成功安装。

go install github.com/gohugoio/hugo@latest
hugo version

接下来准备构建网站的脚本。先把网站的源代码克隆到本地,安装好依赖之后,创建一个 .sh 文件

# 这是托管在我自己的 Forgejo 实例上的网站
git clone https://src.eltr.ac/eltrac/jar.git
cd jar
pnpm install # 安装 Node.js 依赖
vim build.sh

这个文件里,按照实际情况编写构建网站的步骤。由于需要被执行,所以要在开头写上 #!/bin/sh

#!/bin/sh

git pull # 先拉取最新的更改
pnpm unocss "**/*.{html,md}" -o assets/uno.css
hugo --minify

运行这个 Shell 脚本,测试能够正常构建网站。如果输出正常,就可以继续配置 Webhook 了。这里使用 adnanh 的 webhook 实现 ,这是一个命令行工具,能够读取 JSON 或 YAML 配置。以下是示例配置文件,用 JSON 编写。

# 先安装 Webhook
sudo apt-get install webhook
// hooks.json
[
 {
 "id": "build-hugo",
 "execute-command": "/home/eltrac/jar/build.sh",
 "command-working-directory": "/home/eltrac/jar"
 }
]

执行 webhook -hooks hooks.json,API 就会运行在 :9000 端口上,我们刚刚配置的 API 路径是 localhost:9000/hooks/build-hugo。要允许公网访问,可以用 Caddy 配置反向代理。这里建议用 systemctl 运行和管理 Webhook 服务,具体步骤此处略去。

hooks.example.com {
 reverse_proxy localhost:9000
}

然后,在 Forgejo 仓库配置 Webhook,当有代码被推送到主分支上时,就向我们刚刚配置好的 API 端口发送 HTTP 请求。运行在服务器上的 Webhook 收到请求后,就会运行我们刚才编写好的 build.sh,网站就会被重新构建。

在 Forgejo 仓库设置找到 Webhook

添加 Webhook

完成后,向 Forgejo 远程仓库推送更新,测试构建脚本有没有被触发。不出问题的话,你就能获得一个几乎能在 git push 一秒后更新的静态网站。

对比三种方案

对于缺少经验的人来说,使用 PaaS 是最简单的,几乎去掉了维护一个网站所需要的所有运维技能,只需要关注写代码。不过,似乎大部分 PaaS 都只支持从 GitHub 和 GitLab 和拉取代码,或者自己手动上传代码,不支持和自托管的 Gitea 或者 Forgejo 实例集成。个人感觉,使用 PaaS 就像是在和恶魔交易,出卖自己的灵魂以换取便捷,代价是会被科技公司锁死在他们的平台上

GitHub Actions 类的自动化工作流,想必是有用武之地的,但我认为并不适合用来部署网站。如果只是运行自动化测试或拉取数据这种操作,把工作流脚本和代码放在一个仓库里大概是合适的。如果某些操作需要大量的依赖项才能运行,那么自动化工作流的效率就会因为需要安装大量依赖而被拉低。

目前实践下来,个人认为最优雅的方案就是在服务器上直接构建,通过 Webhook 触发,这样就省去了部署的步骤。不过,如果没有一台 Web 服务器,也就用不了这个方法了。

印象中还有人会在本地构建,将构建产物使用 rclone 同步到储存桶,或者使用 rsync 同步到服务器上,我没有实践过这种做法,就不评价了。


  1. 其实我更喜欢使用 pnpm,但如果要在自动化工作流里使用,还要额外安装,所以这里就直接用 npm 了。 ↩︎