<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>叁月的博客</title><description>把日常写成可以翻阅的样子</description><link>https://sanyue0608.site/</link><language>zh_CN</language><item><title>建站踩坑记：从本地跑通到 GitHub Pages 上线</title><link>https://sanyue0608.site/posts/deploy-to-github-pages/</link><guid isPermaLink="true">https://sanyue0608.site/posts/deploy-to-github-pages/</guid><description>一个不懂前端的人把 Astro 博客从零搭到上线，中间踩过的五个坑，以及最后为什么放弃了 EdgeOne 改用 GitHub Pages。</description><pubDate>Thu, 12 Nov 2026 09:30:00 GMT</pubDate><content:encoded>&lt;p&gt;这个博客从「想搞一个」到真正能打开，花了两天。技术上的难点其实不多，难的是&lt;strong&gt;每一步都有一个看起来像玄学的报错&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;把过程记下来，一是给以后的我省事，二是如果你也想搭一个，能少走我走过的弯路。&lt;/p&gt;
&lt;h2&gt;一、选型：为什么是 Astro&lt;/h2&gt;
&lt;p&gt;一开始的需求很朴素：能写文章、打开快、别让我维护服务器。&lt;/p&gt;
&lt;p&gt;筛到最后剩下两个方向：WordPress 那种「动态站点」，和 Astro 这种「静态站点」。&lt;/p&gt;
&lt;p&gt;最后选静态，理由很实际：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;文章是 Markdown 文件，存在自己电脑上，不锁在某个平台的数据库里&lt;/li&gt;
&lt;li&gt;没有数据库，就没有「数据库挂了」「被拖库了」这些事&lt;/li&gt;
&lt;li&gt;构建出来的是一堆 &lt;code&gt;.html&lt;/code&gt;，扔到任何静态托管上都能跑，随时可以搬家&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;主题选了开源的 &lt;strong&gt;Shirone&lt;/strong&gt;——Material 3 风格、中文支持好、配置项都有中文注释。对新手来说，注释比文档管用。&lt;/p&gt;
&lt;h2&gt;二、五个坑&lt;/h2&gt;
&lt;h3&gt;坑 1：主题说要 Node 26，但 EdgeOne 最高只给 22&lt;/h3&gt;
&lt;p&gt;主题的 &lt;code&gt;package.json&lt;/code&gt; 写着 &lt;code&gt;engines.node: &quot;&amp;gt;=26.13.0&quot;&lt;/code&gt;。我老老实实装了 Node 26，本地跑得很好。&lt;/p&gt;
&lt;p&gt;结果一上 EdgeOne 就炸：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Node.js v22.x is not supported by Astro!
Please upgrade Node.js to a supported version: &quot;&amp;gt;=22.23.4&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里有个反直觉的地方：&lt;strong&gt;主题声明的要求，往往比它依赖的框架严格得多&lt;/strong&gt;。我把依赖一个个翻出来看：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;包&lt;/th&gt;
&lt;th&gt;实际要求&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;astro@7.4.5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;=22.23.4&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;vite@8&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;=22.12&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;svelte@5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;=18&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;没有任何一个依赖真的需要 26。&lt;/strong&gt; 于是把 &lt;code&gt;engines.node&lt;/code&gt; 改成 &lt;code&gt;&amp;gt;=24.0.0&lt;/code&gt;，本地和线上统一到 Node 24，装依赖、构建、开发服务器全部复测通过。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;教训：看到 &lt;code&gt;engines&lt;/code&gt; 报错，先去看依赖的真实要求，别直接信主题写的数字。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;而且 EdgeOne 预装的最高版本是 &lt;strong&gt;22.11.0&lt;/strong&gt;，比 Astro 要的 &lt;code&gt;22.23.4&lt;/code&gt; 还差一点点——就差这一点点，整个构建起不来。这种&quot;差 0.12 个版本&quot;的坑最恶心，因为报错信息看起来像是网络问题或者配置问题。&lt;/p&gt;
&lt;h3&gt;坑 2：字体是构建时从 CDN 下载的（最坑的一个）&lt;/h3&gt;
&lt;p&gt;本地构建偶尔失败：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[CannotFetchFontFile] An error occurred while fetching the font file from
https://cdn.jsdelivr.net/fontsource/fonts/jetbrains-mono@5.3.0/...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重跑一次又好了。我一开始以为是网络抖动。&lt;/p&gt;
&lt;p&gt;后来清空构建缓存再跑，&lt;strong&gt;必现失败&lt;/strong&gt;。才意识到：前面那些&quot;成功&quot;都是在吃缓存，真实的构建流程一直在依赖外网。&lt;/p&gt;
&lt;p&gt;原因：主题的字体配置用的是 &lt;code&gt;source: &quot;fontsource&quot;&lt;/code&gt;，Astro 在构建时要&lt;strong&gt;实时从 jsDelivr 下载字体文件&lt;/strong&gt;来计算排版度量（行高、基线这些）。&lt;/p&gt;
&lt;p&gt;这意味着：&lt;strong&gt;任何一台全新克隆、没有缓存的构建机（CI、云平台）第一次构建都会挂在这里&lt;/strong&gt;，而且报错信息完全指向错误的方向。&lt;/p&gt;
&lt;p&gt;修法很干脆：把字体文件复制进项目，改成 &lt;code&gt;source: &quot;local&quot;&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 之前：构建时要联网下载
source: &quot;fontsource&quot;,

// 之后：文件就在仓库里
source: &quot;local&quot;,
variants: [{ file: &quot;src/assets/fonts/outfit-latin-400.woff2&quot;, weight: 400 }],
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;代价是仓库多了 122KB 的字体文件，收益是&lt;strong&gt;构建全程零外网请求&lt;/strong&gt;，离线也能构建。这笔账很划算。&lt;/p&gt;
&lt;h3&gt;坑 3：EdgeOne 绑定域名要绑银行卡&lt;/h3&gt;
&lt;p&gt;Node 版本调好之后 EdgeOne 终于能构建了。但到绑定自定义域名这一步，要求填银行卡。&lt;/p&gt;
&lt;p&gt;我能理解平台要防滥用，但一个个人博客，我确实不想为此绑卡。于是换方案。&lt;/p&gt;
&lt;h3&gt;坑 4：git rebase 卡在中间，仓库变成 detached HEAD&lt;/h3&gt;
&lt;p&gt;改提交作者的时候跑了个 &lt;code&gt;git rebase&lt;/code&gt;，它停在了交互模式没走完：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interactive rebase in progress; onto 934f0fb
No commands remaining.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这时候的仓库处于&quot;半完成&quot;状态，之后的提交不会挂在任何分支上。修法是补一句 &lt;code&gt;git rebase --continue&lt;/code&gt; 收尾，然后 &lt;code&gt;git push --force-with-lease&lt;/code&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;--force-with-lease&lt;/code&gt; 比 &lt;code&gt;--force&lt;/code&gt; 安全：远程有你不知道的新提交时它会拒绝，而不是直接覆盖。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;坑 5：Jekyll 会把 &lt;code&gt;_astro/&lt;/code&gt; 目录吃掉&lt;/h3&gt;
&lt;p&gt;GitHub Pages 默认会用 Jekyll 处理文件，而 Jekyll &lt;strong&gt;忽略所有下划线开头的目录&lt;/strong&gt;。Astro 的产物里正好有 &lt;code&gt;_astro/&lt;/code&gt;，里面是全部样式和脚本。&lt;/p&gt;
&lt;p&gt;不处理的话：页面能打开，但&lt;strong&gt;没有任何样式，白板一张&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;解法是放一个空的 &lt;code&gt;.nojekyll&lt;/code&gt; 文件在产物根目录。（用 GitHub Actions 部署的话其实不会走 Jekyll，但加上没有坏处。）&lt;/p&gt;
&lt;h2&gt;三、最后的架构&lt;/h2&gt;
&lt;p&gt;折腾完，落成这样：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;flowchart LR
    A[&quot;本地写 Markdown&amp;lt;br/&amp;gt;src/content/posts&quot;] --&amp;gt; B[&quot;git push&quot;]
    B --&amp;gt; C[&quot;GitHub Actions&amp;lt;br/&amp;gt;pnpm build&quot;]
    C --&amp;gt; D[&quot;dist/ 静态产物&quot;]
    D --&amp;gt; E[&quot;GitHub Pages&quot;]
    F[&quot;DNSPod&amp;lt;br/&amp;gt;A 记录 + CNAME&quot;] --&amp;gt; E
    E --&amp;gt; G[&quot;sanyue0608.site&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;具体是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;代码&lt;/strong&gt;放 GitHub 仓库（公开，因为免费账号的 Pages 不支持私有仓库）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;构建&lt;/strong&gt;交给 GitHub Actions，推一次 &lt;code&gt;main&lt;/code&gt; 就自动 &lt;code&gt;pnpm install &amp;amp;&amp;amp; pnpm build&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;托管&lt;/strong&gt;用 GitHub Pages，产物是 &lt;code&gt;dist/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;域名&lt;/strong&gt;在 DNSPod 加四条 A 记录指向 GitHub 的 IP，&lt;code&gt;www&lt;/code&gt; 走 CNAME&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;整套东西的成本是：域名几十块一年，其余全免费。&lt;/p&gt;
&lt;p&gt;日常更新就三步：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 写文章
pnpm new-post 文章名
# 本地看效果
pnpm dev
# 推上去，2-3 分钟后线上更新
git add . &amp;amp;&amp;amp; git commit -m &quot;post: 新文章&quot; &amp;amp;&amp;amp; git push
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;四、给同样想搭博客的人&lt;/h2&gt;
&lt;p&gt;如果你完全不懂前端，也想搞一个：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;先想清楚&quot;为什么&quot;&lt;/strong&gt;。技术选型会变，理由不会。我是想要一个&quot;不会被算法决定顺序、也不会被平台随时关掉&quot;的地方。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;静态站点是新手最省心的选择&lt;/strong&gt;。没有服务器要维护，没有数据库会挂，搬家成本几乎为零。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;报错先看第一行&lt;/strong&gt;。这次两个大坑——Node 版本、字体 CDN——真正的错误都在日志最上面，而平台日志默认展示的往往是最后几行&quot;失败&quot;信息。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&quot;重跑一次就好了&quot;是危险信号&lt;/strong&gt;。它通常意味着有缓存掩盖了真实问题，等换到干净环境就会必现。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;别怕推翻方案&lt;/strong&gt;。EdgeOne 那条路我折腾了大半天，发现要绑卡就立刻换 GitHub Pages，两小时搞定。沉没成本不该影响决策。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;最后&lt;/h2&gt;
&lt;p&gt;这个站现在还很空：一篇文章、一条动态、一个系列。&lt;/p&gt;
&lt;p&gt;但至少它是活的——我能往里写东西，写完推一下，它就在那儿了。&lt;/p&gt;
&lt;p&gt;下一篇大概会写「怎么把首页改成我想要的样子」，毕竟现在用的还是主题自带的图。&lt;/p&gt;
</content:encoded></item><item><title>你好，世界</title><link>https://sanyue0608.site/posts/hello-world/</link><guid isPermaLink="true">https://sanyue0608.site/posts/hello-world/</guid><description>这里的第一篇文章。记录一下这个小站是怎么来的，以及它以后会长成什么样。</description><pubDate>Wed, 11 Nov 2026 12:30:00 GMT</pubDate><content:encoded>&lt;p&gt;这是这个博客的第一篇文章。&lt;/p&gt;
&lt;p&gt;建这个站花了点时间，但真正难的部分其实不是技术——是想清楚「我为什么要有一个自己的地方」。答案很简单：有些话说完了会散，有些想法不写下来就没了。我想要一个不会被算法决定顺序、也不会被平台随时关掉的角落。&lt;/p&gt;
&lt;h2&gt;这里会有什么&lt;/h2&gt;
&lt;p&gt;大概三类：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;技术笔记&lt;/strong&gt;：踩过的坑、读完文档之后的整理。写给别人看之前，先写给自己。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;生活碎片&lt;/strong&gt;：某天的天气、某个忽然的想法，短一点的那种。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;长期的东西&lt;/strong&gt;：一个会慢慢变长的系列，而不是发完就沉下去的单篇。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;关于这个站&lt;/h2&gt;
&lt;p&gt;它用 &lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt; 构建，主题是开源的 &lt;a href=&quot;https://github.com/LyraVoid/Shirone&quot;&gt;Shirone&lt;/a&gt;。选它是因为两件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;静态站点&lt;/strong&gt;——页面在你打开之前就已经生成好了，所以很快，也不需要数据库和服务器维护。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;写作体验好&lt;/strong&gt;——Markdown 直接写，公式、代码、图表都能用，不用为了排版跟编辑器打架。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;:::note
下面这段是给未来的自己看的备忘：本站的文章源文件都在 &lt;code&gt;src/content/posts/&lt;/code&gt;，
写一篇新文章只需要 &lt;code&gt;pnpm new-post 文件名&lt;/code&gt;，然后编辑生成的 &lt;code&gt;.md&lt;/code&gt; 文件。
:::&lt;/p&gt;
&lt;h2&gt;一个小例子&lt;/h2&gt;
&lt;p&gt;比如代码块，写起来是这样：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// post.frontmatter 里填的那些字段，决定了文章出现在哪里、以什么顺序
export const greeting = (name: string) =&amp;gt; `你好，${name}`;

greeting(&quot;世界&quot;); // &quot;你好，世界&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;数学公式也没问题：&lt;/p&gt;
&lt;p&gt;$$
\sum_{n=1}^{\infty} \frac{1}{n^2} = \frac{\pi^2}{6}
$$&lt;/p&gt;
&lt;h2&gt;最后&lt;/h2&gt;
&lt;p&gt;如果你刚好路过这里，欢迎随便看看。这里没有更新计划，也没有 KPI——想到了就写，写完了就放在这儿。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;把日常写成可以翻阅的样子。&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item></channel></rss>