老T博客

2026-01-09 00:50

父亲阴寿

父亲去世15年,又到他的生日。

尤记得父亲生前最后一个生日,恰好在元旦假期。

那时我正在上大学,对农历日期缺乏敏感性,直到晚上我妈打电话过来,才发现自己把父亲生日给遗忘了。

由于当时父亲已确诊重疾数月,接到母亲电话那一刻,我心里真是特别慌。似乎是头一次感觉到自己没有尽到一个做儿子的义务,连父亲生日日期这么简单的事情都没记住。虽然那次父亲在电话里说“我不会怪你”,但毫无疑问,父亲那个生日,成了我毕生的遗憾。

父亲去世一个多月后,当我再也没有办法对他讲出那句“祝您生日快乐”时,我只能在博客中留下一篇文章 『一个毕生不能忘记的日子』 用作记录。

不过,也许是学生年代的桀骜不驯,现在回头看看当年写的那个文章,多少透露出一点无知和傲慢,甚至夹杂着一些自私的念头在里边。究其原因,我想最主要是当年“生日”这个事情,在我记忆中,确实没有得到过重视,只觉得是多吃一个鸡蛋或新买一件衣服的事情,唯一有点印象的大概是我10岁生日时在照相馆拍下了自己年少时唯一一张照片。包括我父亲在此前的历次生日,似乎也没有被我重视过。大抵跟农村习俗有些关系,重视特定的生日(比如,满周岁、10岁、30岁、60岁、80岁这类),对其他“散生”则没太多讲究。

等到我自己结婚后,也当上了父亲,才逐渐开始对“生日”有了一些自己的想法。

大概一年半前,我母亲就开始谋划说要帮我父亲办一场60岁的阴寿仪式。主要是考虑我父亲是中年去世,而逢一甲子60周岁在传统习俗中有着特殊的重大意义。而之所以提前“一年半”就在筹备,跟我们当地习惯在“进岁”办酒席有关,一般也就是在满59岁“进60岁”时办酒席,但也有“满60岁”才办的,后来跟本地主事和尚商议后,决定放在“满60”时来办。

阴寿仪式在我们家乡是普遍习俗,一般就是 60岁 80岁 100岁 120岁这几种选择,主要按逝者去世时的年龄来选,正常情况只需要办一次即可。

仪式过程与土葬过程中的仪式有相通之处,但时间短得多,只需要一个白天的时间即可。按照我个人理解,大概分为以下几个过程:祷告天地、祭拜司命府君(灶神)、祭拜玄武大帝(水神)、祭拜五方神仙、祭拜本宗祖先、超度经文、祭拜逝者、礼敬地仙等,每个环节半小时到一小时不等,大约有一半的仪式中,我需要跟随主事和尚跪拜。

这部分可以参考我此前写过的两篇文章:

  1. 我所认识的中国式土葬
  2. 湖南农村家庭的神位摆放究竟有什么讲究

由于这种阴寿仪式整体还是偏向于积极氛围,与土葬过程中的肃穆感有很大区别,过程中大家都相对比较放松。并且,这种仪式与绝大多数“吃席”类活动最大区别是“不需要给份子钱”,所有经费都由主家支出,因此,客观上也不会邀请太多人参加,来的都是自家近亲,整体过程都非常顺利。

在为父亲办这次阴寿仪式前,我一直很苦恼一个问题。我父亲生前几乎没有留下过照片,当年用作“遗像”的照片,还是他在年轻时办证时留下的一张一寸大小的证件照放大得来的。好在现在有了 Ai 技术,我翻拍了父亲身份证上的低清晰度照片以及此前那张年轻时拍的证件照,用多个 Ai 分别测试通过两张照片的相貌特征合成出高清晰度照片,然后拿给我妈和多位亲戚挑选,最终成功挑选出一张大家都非常认可的、接近满分的照片,也算是填补了这些年来大家共同的遗憾。

2026-01-07 17:21

和大娘话家常

这两天在老家碰到很多亲戚,大多聊聊家常,但在跟大娘(母亲姐姐)聊天时,她倒是说了些对我来说算是新鲜的事。

外公的爷爷

大娘说她非常不理解一个事情,为什么我外公的父母、爷爷奶奶,都会葬在一个叫“大塘冲”的地方,这个地方并不在外公家祖上传统居住地点附近,甚至都隔了几个村的距离。我简单回忆了一下《曾国藩家书》中的一些记录,提到,曾国藩母亲江氏去世后,原本葬在七斗冲,但后来迁到了两屏山,主要就是看中两屏山风水好。然后大娘就回忆起来,说我外公的爷爷,当年就在曾国藩家当管家,名字叫“曾纪鸿”。我当即拿手机查了下,说曾纪鸿是曾国藩的二儿子,这名字应该不会相同吧?然后大娘说,不是“纪”而是“志”,因为我们本地土话,纪和志的读音一样,然后才考证出来,外公的爷爷名叫“曾志鸿”按家中字辈名是“曾纪鹍”,生于同治二年,确实跟曾国藩儿子曾纪鸿、曾纪泽是同一个辈分的同宗兄弟,光绪年间帮着曾国藩家族后代打理老宅。

村委会换届

大娘提到她外孙女想回老家竞选村干部的事。因为大娘比我妈大接近20岁,她所说的外孙女,实际跟我小时候是同班同学,是大娘家我二表姐的大女儿。她一说起这事就愤愤不平,说现在乡镇府搞换届不够“民主”,附近几个村新上任的村书记,都是谁谁谁家女儿,谁谁谁家女儿,一连说了四五个,似乎都是30出头的“女村书记”。这个事确实也让我感觉很新奇,因为我家所在的村,之前也有一位“女村书记”,而我堂弟前阵子也有过想法,说回村里竞选一下看看,但最后选出来的结果是,原来“女村书记”的女儿当选了新的“村书记”。最主要原因是,现在村书记待遇都还不错,在农村有四千多块钱工资,比在外边打工强的多,而且比乡里超市、加油站这些地方上班也要高出一倍多工资。

交通事故处理

大娘回想起大爷(丈夫)因为交通事故去世后,肇事方赔偿的事。提到肇事方赔偿后,当时村书记曾让她拿3000块钱给派出所当“感谢费”,但是大娘找了在检察院工作的亲戚咨询,结果那位亲戚提出,没必要给钱去“感谢”,这些赔偿款,都是应该得的,而且派出所也只是公事公办,并没有“超出权限”的情况。她说自己想了很久后才想通,可能是村书记自己想跟派出所搞好关系才出了个这么“点子”,连续慨叹说“还是有人好办事”,如果不是有个亲戚在检察院懂这些,还真可能被坑了。

孙子不想结婚

大娘现在全家最头痛的就是唯一的孙子,现在 30 岁出头了,但一点结婚的想法都没有。我这个外甥,也算是自己看着长大的,从小其实也还比较上道,不像大娘家周边有些家庭的小孩一样,有的就学坏了。只是这外甥,读书确实差劲点,初中毕业后没多久就一个人跑云南谋生,后来独自一个人在那边开汽修店,也不喜欢我表姐过去,总觉得如果母亲在身边不自由,最近十来年,都是一个人自顾自的在工作和生活。每年过年回来时,家里都帮他相亲好多次,但总感觉还是”长不大“的样子,压根就没兴趣谈婚论嫁。当年大娘因为只生了3个女儿,所以这个孙子是”招郎入赘“生的,但前些年,大爷去世后,招进来的”儿子“前年也因病去世了,现在全家就这么一个男丁。大娘想着自己都快80岁了,也不知道还能等孙子多少年,一说起来就潸然泪下,我也不知道该怎么安慰好。

2025-12-19 23:35

佛山最偏远的村

今天和朋友一起去了一趟佛山最远的村:坪岭村。

IMG_20251219_230757.jpg
IMG_20251219_230757.jpg

这个村是今年10月才从肇庆划归佛山,主要原因是该村属于广州新机场征地范围内,因广州新机场选址在佛山肇庆交界处,为了便于管理,就把与佛山相邻的 9 个村都给了佛山。

由于这个村最为偏远,出到最近的有中间分隔线的县道都有10公里,去镇上要15公里,走路的话怕是要三个小时。交通不便,地里条件闭塞,村里基本上只剩下零星一些老人小孩。

村委会就几间平房,应该也可以算是佛山最简陋的村委会了。

想想也是命好,本来这个地方基本也就是无数空心村的缩影,日渐凋零,用不了多少年可能就彻底荒废。结果,谁曾想,突然就“撞大运”可能会飞黄腾达起来。

2025-12-11 09:43

一年级小朋友的遗憾

前天晚上儿子应该是感染了甲流,高烧,到晚上四点半的时候第二次服用0.1g布洛芬,才终于止住。昨天一天都是有气没力的在家里自己一个人玩。但他总挂念着今天学校里面要举办的运动会,因为最近这两个月他一直在努力学习跳绳,想超过姐姐的记录。

昨天晚上大概整晚都是低烧状态,我本来想着他今天应该没有太大问题,但早上还是没有起得来,未能跟姐姐一起去上学。等到接近九点钟我才送他出门。因为9:30学校要运动会要开始了。

在送他去学校路上,他先是呕吐,把我车后排地垫吐的满地都是。然后我只好在路边找了一个小超市买了一大包纸把这个先解决了。

在快到学校的时候他说很想要尿尿,也是刚停好车就让他在旁边的草丛里先解决一下。没想到一撒尿,就变成同步蹿稀,把裤子拉的全是黄色。

这下没办法,只能先回家。在回家路上他又呕吐了一遍,这次更加彻底。好的他自己拿了个塑料袋接住了。

最后,只能让他跟奶奶继续在家玩了。

2025-11-26 00:27

围观富人区

周末带小孩去她同学家玩,顺便跟着同学父母去看了两套本地富人区的房子。

小孩
小孩

这个朋友家本来住的房子都不错,180方的别墅,有4层,地下是游泳池、娱乐室,一楼客厅、餐厅和厨房。不过他们嫌只有4个卧室太少,而且有一个卧室在顶楼太晒,而老人卧室则在底层太潮,今年还被洪水淹过(Orz)。于是又在旁边买了一套 150 方的联排别墅给老人住,说趁着现在物业不管违建的事,赶紧搞起来,加上违建面积,最后大概也有180方。

但他们在给老人买房时,又看到同小区有独栋别墅现在售价也便宜。想把目前住的这栋卖掉,换成大别墅。

独栋别墅
独栋别墅

我也跟着过去看了一下他们意向购买的那栋房。好家伙,550方建筑面积,原来的业主装修了10个卧室9个洗手间3个会客厅两个厨房以及棋牌室、电影院、桌球室等。外边还有大的游泳池和儿童娱乐设施。

院内情况
院内情况

只能说有钱就是任性。而且这个片区总体非常安静,实际入住的人数也不多,主要是本地之前已经有好几个非常成熟的大型别墅盘,已经形成了集聚效应。而且这个位置稍微偏点。大概离城中心有15公里。

IMG_20251125_235800.jpg
IMG_20251125_235800.jpg

总的来说,我目前阶段对这种别墅倒没什么需求,虽然单价跟市中心的商品楼相比也没贵多少,但生活不便,又很费精力。最主要还是囊中羞涩,买一套吧,不够用,买两套吧又买不起,只能等以后再说了。像现在这样两套商品房上下楼住着其实也挺好,起码有电梯,也不用担心老人家摔着。

2025-11-25 23:53

清远古龙峡一日游

昨日参加工会组织活动,到清远一日游。

这种活动,由于成本限制,客观上也不可能报太大期望。上午就是在清新县城的大公园逛了一圈,走了一万多步。下午去到古龙峡景区,继续走了一万多步。

古龙峡
古龙峡

这古龙峡景区,总体上跟很多乡村休闲旅游景点类似,比如这个景区拿着各种恐龙模型在山路两旁“吓唬人”,比如千篇一律的“玻璃桥”,比如人造瀑布等。

玻璃桥
玻璃桥

比较有特色的是这个古龙峡有两个机动项目还是挺有趣。一个是悬崖飞车,另一个是峡道飞车(也叫QQ飞车)。前者类似一般景点的“过山车”但特色是真是在山里边玩。后者是跟QQ飞车联名搞的,个人驾驶无动力“小车”从山上开下来,确实别有一番风味,车道长度也比较合适。

QQ飞车项目
QQ飞车项目

另外,在进入QQ飞车项目时,需要两次搭乘“魔毯”电梯上去。由于是第一次体验这种模式,感觉还是挺新奇。

魔毯电梯
魔毯电梯

总的来说,由于是在工作日去的,门票价格倒也不贵。但在前台看到这两个“飞车”项目都是标价 288元,如果在周末或节假日去,可能就有点不划算了。毕竟288这价格,都够动车从广州到桂林一个来回了。

2025-11-21 09:03

给HUGO添加文章搜索功能

之前用 Hugo-theme-stack 主题,自带了搜索功能,我以为这种功能是 Hugo 默认就有,后来才知道,是外挂。

在更换 Bear cub 主题后,之前也没觉得这功能有多重要,但实际使用中发现,有时候记不起来一些事情,还得借助搜索才行。于是研究了下怎么外挂搜索。其实也很简单,只需四步。

content 目录下新建 search.md 页面

如果不需要放置在导航栏,内容填入默认信息即可,需要进导航栏的,效仿其他 md 文件设置。

---
title: "搜索"
date: 2025-11-20T00:00:00+08:00
type: "search"
layout: "search"
---
在此搜索本博客文章。

layouts 目录下新建 /search/single.html 模板

模板内容可以参照其他模板修改,大致内容如下。

{{ define "main" }}
<content>
  <h1>{{ .Title }}</h1>
  <div class="search-box">
    <input
      id="search-input"
      class="search-input"
      type="text"
      placeholder="输入关键词搜索…"
      autocomplete="off"
    />
  </div>

  <ul id="results" class="search-results">
    <li style="color:#666">请输入关键词搜索</li>
  </ul>

</content>

<script>
// ========== 工具函数 ==========
function escapeHtml(str) {
  if (!str) return "";
  return str
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;");
}

// ========== 精确搜索 ==========
function exactMatch(haystack, needle) {
  if (!haystack || !needle) return false;
  return haystack.toLowerCase().includes(needle.toLowerCase());
}

// ========== 渲染结果 ==========
function renderResults(list) {
  const resultsEl = document.getElementById("results");

  if (!list || list.length === 0) {
    resultsEl.innerHTML = '<li style="color:#666">未找到结果。</li>';
    return;
  }

  const itemsHtml = list.map(item => {
    const title = escapeHtml(item.title || "(无标题)");

    // 外链或本地链接
    const url = escapeHtml(item.link || item.url || "#");
    const isExternal = !!item.link;

    const linkAttrs = isExternal
      ? ' target="_blank" rel="noopener noreferrer"'
      : "";

    // 摘要:取 summary 或 content 开头一段
    const summaryRaw =
      item.summary ||
      (item.content ? item.content.slice(0, 200) + "…" : "");

    const summary = escapeHtml(summaryRaw);

    return `
      <li>
        <div class="sr-title-col">
          <a class="sr-title" href="${url}"${linkAttrs}>${title}</a>
        </div>

        <div class="sr-snippet-col">
          <div class="sr-snippet">${summary}</div>
        </div>
      </li>
    `;
  }).join("");

  resultsEl.innerHTML = itemsHtml;
}

// ========== 加载 index.json 数据 ==========
async function loadIndex() {
  try {
    const res = await fetch("/index.json");
    return await res.json();
  } catch (err) {
    console.error("加载 index.json 失败:", err);
    return [];
  }
}

// ========== 主逻辑 ==========
(async function () {
  const data = await loadIndex();
  const input = document.getElementById("search-input");

  input.addEventListener("input", () => {
    const q = input.value.trim();

    if (!q) {
      renderResults([]);
      return;
    }

    // 精确搜索:title / content / tags 均可匹配
    const result = data.filter(item =>
      exactMatch(item.title, q) ||
      exactMatch(item.content, q) ||
      (item.tags || []).some(t => exactMatch(t, q))
    );

    renderResults(result);
  });
})();
</script>
{{ end }}

在 hugo 配置文件中添加 json 输出

比如我的hugo配置文件是 toml 格式,内容如下。

[outputs]
  home = ["HTML", "RSS", "JSON"]  # 添加 JSON 输出

[outputFormats.JSON]
  baseName = "index"
  mediaType = "application/json"

layouts 中新建 index.json 模板

主要就是将整个博客文档内容输出到 index.json 文件,搜索时直接在该文件内搜索,可依照自己需求进行修改,比如只搜索标题、摘要、标签等信息。

[
{{- $pages := where .Site.RegularPages "Type" "not in" (slice "page" "something-you-want-to-exclude") -}}
{{- $first := true -}}
{{- range $i, $p := $pages -}}
  {{- if not $first }},{{ end -}}
  {
    "title": {{ $p.Title | jsonify }},
    "url": {{ $p.RelPermalink | absURL | jsonify }},
    "date": {{ $p.Date.Format "2006-01-02" | jsonify }},
    "summary": {{ with $p.Params.description }}{{ . | jsonify }}{{ else }}{{ $p.Summary | plainify | jsonify }}{{ end }},
    "content": {{ $p.Plain | chomp | jsonify }},
    "tags": {{ $p.Params.tags | jsonify }},
    "categories": {{ $p.Params.categories | jsonify }}
  }
  {{- $first = false -}}
{{- end -}}
]

其他 CSS 配置

如果需要自定义搜索页面的 CSS,可以直接在主题 CSS 或自定义 custom.css 文件中添加,或者写在前边 /search/single.html 模板中也可以。

/* ====== 搜索框区域布局 ====== */
.search-box {
  max-width: 720px;
  margin: 24px 0 32px;
}

.search-input {
  width: 100%;
  padding: 10px 14px;
  font-size: 16px;

  border: 1px solid var(--border);
  border-radius: 8px;

  background: var(--entry);
  color: var(--primary);

  outline: none;
  transition: border-color .15s ease, box-shadow .15s ease;
}

.search-box {
  max-width: 720px;
  margin: 24px 0 32px;
}

.search-input {
  width: 100%;
  padding: 10px 14px;
  font-size: 16px;

  border: 1px solid rgba(150, 150, 150, 0.35);
  border-radius: 8px;

  background: var(--entry);
  color: var(--primary);
  outline: none;

  transition: border-color .15s ease, box-shadow .15s ease;
}

.search-input:focus {
  border-color: var(--text-highlight);
  box-shadow: 0 0 0 2px rgba(100, 108, 255, 0.20);
}

.search-results {
  list-style: none;
  padding: 0;
  margin: 0;
}

.search-results li {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  padding: 12px 0;
  border-bottom: 1px solid rgba(0,0,0,0.04);
}

.search-results .sr-title-col {
  flex: 0 0 40%;
  min-width: 180px;
  max-width: 420px;
}

.search-results .sr-title {
  font-size: 1.02rem;
  line-height: 1.3;
  text-decoration: none;
  color: var(--primary);
}

.search-results .sr-title[target="_blank"]::after {
  content: " ↪";
  font-weight: 400;
}

.search-results .sr-snippet-col {
  flex: 1 1 60%;
}

.search-results .sr-snippet {
  color: var(--secondary);
  font-size: 0.95rem;
  line-height: 1.5;

  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
}

@media (max-width: 500px) {
  .search-results li {
    flex-direction: column;
    gap: 8px;
  }

  .search-results .sr-title-col {
    max-width: none;
  }
}
2025-11-21 08:36

广电手机卡最近每天都会遇到QOS限速

每天早上测速时基本都是满速500Mbps,但一到下午,就直接限速到1Mbps。不知道是不是因为我长期挂广电卡当wifi热点的缘故。但一查流量情况,这个月才用了15GB,套餐内还剩200GB没用,也是醉了。

Image

Image
Image

2025-11-19 22:23

真是巧得很

刚才想在博客上搜个东西,因为新主题不带搜索功能,所以点开旧版查看。没想到意外看到字数统计 1088888。

这么吉祥的数字,必须纪念一下。

Screenshot_20251119_222120.jpg
Screenshot_20251119_222120.jpg

2025-11-13 12:13

稀奇古怪的墙

我这个 vmiss 香港小鸡,大概十几天前突然三网断联,只能套 Cloudflare 使用,但刚才随手一刷,居然发现又回来了。断的莫名其妙,回来的也稀奇古怪。

Screenshot_20251113_121106.jpg
Screenshot_20251113_121106.jpg

2025-11-08 21:31

CS游戏时长破3000小时

不知不觉,CS游戏时长达到3000小时。这个游戏是我2012年9月份内测时候购买的,主要的游戏时长都是刚工作那几年。生小孩后,连续好几年没玩,去年开始,闲来无事就做做每周任务,也拿到了去年和今年的纪念币。

Image
Image

昨天偶尔发现,卡片下方有一个评分展示,但需要完成十场比赛胜利才行。于是花了10来个小时,取得10胜4负战绩,终于是把这任务给刷完。过程中,很心酸,反应慢、枪法差,最终只定级6600分。

Image
Image

2025-11-08 00:29

Git 下载速度死活只有20K

今晚遇到个稀奇古怪的事,以往我从Github拉个仓库到本地,都是50Mbps起步,但也不知道是不是因为最近将博客写作路径调整为 submodule 的原因,当随主仓库克隆到本地时,死活只有200Kbps速度,也就是20多KB/s。一开始我也没当回事,就让它在后台下载,然后打开Steam做本周CS2的任务,结果玩了四局游戏两三个小时过去后,居然一个250MB的仓库还没下载完,只到70%。

然后撤销重新试了多次,发现主仓库下载时都有100Mbps,而一旦拉取submodule,立马降速到200Kbps。网上一查,说可能是submodule协议问题。的确,我这个submodule为了配合一些workflows功能,在设置中使用SSH连接,也就是 git@github.com:user/repo.git 这种链接模式。然后git这边,可能会使用 git:// 协议,通过9418端口进行通信,而这个端口很可能被运营商限速了。

解决办法也比较简单,为git中这个端口添加一个全局代理或者将ssh协议改写为https即可。

# 创建全局协议转换规则
git config --global url."https://github.com/".insteadOf "git://github.com/"
git config --global url."https://github.com/".insteadOf "git@github.com:"

# 强制转换所有子模块
git submodule foreach 'git config url.https://github.com/.insteadOf git@github.com:'
2025-11-05 12:49

微信公众号上谜之私信

去年写了篇关于 小米智能门锁故障修复文章 ,发在前些天搬到微信公众号上,虽然只有300多阅读,但有读者反馈按我说的方法修复成功,还是很欣慰。不过今天收到稀奇古怪的私信,请我上门去维修,这就有点迷了。仔细看了下对方账号基本信息,是江苏南通的,从头像来看,可能是一位年长的女性。我在公众号后台数据发现她应该是通过微信搜一搜功能找到了我这篇文章。

按道理来说,会用微信搜一搜功能,应该也有点基本常识吧,但却出现这种情况,也是个迷。

Screenshot_20251105_124621.jpg
Screenshot_20251105_124621.jpg

2025-11-04 15:03

给博客更换主题 Hugo Bear

我很早之前就一直在用 Hugo theme Stack主题写东西。这款主题挺好的,但我觉得它更适合图片比较多的博客。而我现在的网站上面已经积攒了1700多张照片,在Stack主题的处理下,生成网站时会生成7000到8000个文件!这就导致整个网站的仓库体积特别大,想部署到第三方免费平台(比如Cloudflare Pages)就变得很困难。

现在的问题很明确了:我得换一个更轻便的主场,最好主要面向纯文字的。于是我在Hugo主题网站上找来找去,最后选中了这款叫Bear(熊)的主题。下载下来一看,这主题确实轻量,总共才几十KB,改起来也很方便。它本身是没有任何CSS的,只有最基础的HTML结构。不过我在实际用的时候,发现还是需要加点CSS的,就又对这个主题“魔改”了一番。后来又发现其实官方主题库有多个 Bear 版本,其中 Bear Cub 就带了 CSS,于是又换成了这个。

迁移到这个新主题的过程中,最难搞定的问题是我原来Hugo项目的文件组织结构

我所有的内容,都是放在项目的content目录下面的。但这里边,我还分了两个文件夹:一个叫post,另一个叫posts(就是post加了个s)。之所以要分两个,最主要的原因是:文章数量实在太多了。如果都放在一个文件夹里,平常写东西或者整理的时候,很容易手滑把文件复制到错误的地方。我的策略是:把“已归档”的历史文章放在一个文件夹里(比如posts),然后新写的文章就放到另一个文件夹(比如post)。等post这个文件夹里的文章积累到一定数量了,我再手动把它们挪进posts文件夹去归档。这样用下来感觉就比较清晰可控,不容易出错。

另一个特点是,我原来的做法是:每篇博客文章的Markdown文件和它用到的图片都放在同一个文件夹里。比如写一篇叫《猫》的文章,那对应的路径可能是/content/posts/猫/,里面直接放着index.mdindex.en.md(因为我博客是双语的)和所有这篇用到的图片(比如cat1.jpg)。

这种方式其实我自己觉得是优点

  1. 本地写作超方便:要插图的时候直接粘贴进来就行,图片瞬间就存到文章的文件夹里了。
  2. 非常便于预览和管理:图片就在Markdown文件旁边,用任何支持本地图片的预览器都能直接显示。
  3. 几乎不会丢图片:路径相对简单且固定,文件和图总是捆在一起。

结果换主题就撞墙了!因为绝大多数流行的主题,都默认使用“文字和图片分离”的模式——Markdown文件放在content/...下面,而图片统一放在像static/images/这样的目录里。可能是为了仓库管理更清晰或者缓存优化吧?但我就是觉得我原来那种“图片同目录”的方式更顺手方便。所以这次换主题,最大的挑战就在于:怎么让我原有的这套“图片同目录”的文件结构,能完美兼容新主题的需求?

这个适配过程……那自然是反反复复测试了很多次。最后我选择的解决方案是:把我存放博客文章的核心文件夹(也就是content下面那些post/posts目录们)做成一个Git Submodule(子模块)

具体操作是:

  1. 我把存放所有文章内容的文件夹分离出来,形成了一个独立的Git仓库(我们姑且叫它“写作仓库”)。
  2. 然后我在主题仓库(无论是原来的Stack仓库还是新的Bear主题仓库)里,把这个独立的“写作仓库”作为Git Submodule引入。

这么做的好处是:

  • 写作最简化:以后写新的博客文章、添加图片,我只需要在我的这个独立的“写作仓库”里操作就行了。它们天然维持着我习惯的路径结构(图文同目录)。
  • 主题适配灵活:现在,我同时在用两套主题!原来的Semi仓库在用这个内容子模块,新换上的Bear仓库也在用这个内容子模块。这样,我只需要更新一次这个“写作仓库”,然后分别构建Semi和Bear两个主题的仓库,就能同时生成为两个主题适配的网站静态文件了。
  • 便于使用图库:我已经将这个写作的仓库同步到Cloudflare R2存储,如果后续有需要,随时可以将整个图片设置成远程链接的模式,避免很快就用尽Vercel这类免费服务的资源。
  • 为了解决两个主题仓库的自动化构建问题,我还给GitHub Actions那边的配置加了好几个工作流(Workflow),确保当写作仓库更新后,两个主题仓库都能被自动触发构建并部署。

选择现在这种“双主题并存 + 内容子模块”的方案,除了技术适配的考量,还有一个很现实的原因:我现在的阿里云VPS快要到期了。目前博客主要还是托管在VPS上,相对来说比较稳定、速度也快。但长远考虑,我很可能会把整个网站完全托管到VercelCloudflare Pages这类平台上去。要把内容全部迁移过去,网站的体积就必须得尽可能精简优化。这次选择超级轻量的Bear主题,很大程度上也是为未来的这一步部署策略在做准备。

2025-07-18 15:00

如何为静态博客设置一个说说页面

刚看到博友圈微信群中有群友问如何在静态博客添加朋友圈/说说这类功能,我第一反应是可以用一个无头CMS单独管理和发表这个页面,但打开PageCMS试了下,发现其实也很麻烦。于是想到跨站引用。这不,试了下 Github Issue 发现很完美。

使用教程

只需 2 步简单操作,一劳永逸解决静态博客添加朋友圈、说说之类的功能

注意事项

在设置模板过程中,需要留意以下几个问题:

  1. 页面构建缓存。可能导致页面内容可能无法更新。
1    {{ $url := "https://api.github.com/repos/user/moments/issues/1/comments" }}
2    {{ $opts := dict 
3        "headers" (dict "User-Agent" "Hugo Static Site Generator")
4        "cache" 300
5        "cacheKey" (printf "gh-comments-%s" (now.Format "2006-01-02-15:04"))
6    }}
  1. 内容排序。 github issue api 输出数据是最新的内容在后边,需要倒过来。
1    {{ with resources.GetRemote $url $opts }}
2        {{ if and .Content (ne .Content "") }}
3            {{ $comments := .Content | transform.Unmarshal (dict "format" "json") }}
4            {{ $sortedComments := sort $comments "created_at" "desc" }} 
  1. 时间格式。github issue 默认使用 UTC 时间,中国的话,需要在基准上加8个小时。
1<time>  
2     {{ (.created_at | time.AsTime).Add 28800e9 | time.Format "2006-01-02 15:04" }}
3</time>

本页面说明

为减少 Github 图片调用,本页面内容通常只保留最近一个月更新,查看往期内容,可点击下方链接查看。 说说

评论