[{"content":"前言 博客在本地已经搭建完成，也做好了一些优化和个性化配置，万事俱备，只欠东风，胜利就在前方。接下来我们开始着手将其部署到远程托管平台。\n目前公认的稳定且免费的托管方案主要有两个： GitHub Pages 和 CloudFlare Pages,两个都行，但成年人不做选择，当然是两个都要了。\n下面是一份针对部署到这两个平台的保姆级教程， 一步一步跟着做，保你通关。\n二、准备工作 2.1 你需要的东西 项目 说明 GitHub 账号 免费版即可 Cloudflare 账号 免费版即可 本地 Hugo 站点 已经能正常 hugo server 预览 域名（可选） 两个平台都支持自定义域名 三、第一步：创建仓库 A（源码仓库） 3.1 在 GitHub 创建仓库 打开 github.com/new Repository name：blog-source（可以随便取，比如 my-blog） Description：Hugo blog source code Visibility：Private （这里选公开或私有都可以，没有强制要求） Initialize this repository with：勾选 Add a README file 点击 Create repository 3.2 本地 Hugo 站点关联到仓库 A 打开终端，进入你的 Hugo 站点根目录（包含 hugo.toml 和 content/ 的文件夹）：\nbash\r# 初始化 git（如果还没初始化） git init # 添加远程仓库（把 你的用户名 替换成你的 GitHub 用户名） git remote add origin https://github.com/你的用户名/blog-source.git # 添加所有文件 git add . # 提交 git commit -m \u0026#34;Initial commit: Hugo blog source\u0026#34; # 推送到 main 分支 git push -u origin main\r如果提示 error: failed to push some refs，先执行 git pull origin main --rebase 再 push。\n四、第二步：配置 Cloudflare Pages（自动构建） 4.1 授权 Cloudflare 访问你的私有 GitHub 仓库 登录 Cloudflare Dashboard 左侧菜单找到 Workers \u0026amp; Pages（或直接在首页搜索框搜 \u0026ldquo;Pages\u0026rdquo;） 点击 Create a project → 选择 Pages 标签 点击 Connect to Git 首次使用会弹出 GitHub 授权页面，点击 Authorize CloudflarePages 授权后，在仓库列表中找到你的私有仓库 blog-source，点击 Begin setup 4.2 配置构建设置 在 Set up builds and deployments 页面填写：\n配置项 填写内容 Project name blog-source（会自动填充，可以改，只能小写字母、数字、连字符） Production branch main Framework preset 下拉选择 Hugo Build command hugo --minify Build output directory public 如果下拉框里没有 Hugo，选择 None，然后手动填写上面两项。\n4.3 设置环境变量（重要） 向下滚动到 Environment variables (advanced)，点击 Add variable：\n变量名 值 说明 HUGO_VERSION 0.146.7 填写你本地 hugo version 看到的版本号，确保一致 HUGO_ENV production 告诉 Hugo 这是生产环境 ⚠️ 务必设置 HUGO_VERSION，否则 Cloudflare 可能用很旧的 Hugo 版本构建，导致失败。\n点击 Save and Deploy。\n4.4 等待首次构建 Cloudflare 会自动拉取仓库 A 的代码并构建。构建完成后会显示：\nYour site was deployed 和一个 *.pages.dev 的预览链接 点击链接即可查看 4.5 （可选）绑定自定义域名 进入 Cloudflare Pages 项目 → Custom domains 点击 Set up a custom domain 输入你的域名（如 blog.example.com） 按提示添加 DNS 记录（如果在 Cloudflare 托管域名，通常自动完成） 五、第三步：创建仓库 B（GitHub Pages 目标仓库） 5.1 创建公开仓库 github.com/new Repository name：你的用户名.github.io 例如你的 GitHub 用户名是 zhangsan，就填 zhangsan.github.io ⚠️ 必须严格符合这个格式，才能使用 https://zhangsan.github.io 这个默认域名 如果你不想用这个域名，可以用任意名称（如 blog-public），但访问地址会变成 https://zhangsan.github.io/blog-public/ Visibility：Public ⚠️ GitHub Pages 免费版必须公开 Initialize this repository with：勾选 Add a README file 点击 Create repository 5.2 开启 GitHub Pages 进入仓库 B → 顶部菜单 Settings 左侧菜单找到 Pages（在 Code and automation 下面） Source 部分，选择 Deploy from a branch Branch：选择 main，文件夹选择 /(root) 点击 Save 此时页面会提示：Your site is ready to be published at https://你的用户名.github.io/\n六、第四步：配置 GitHub Actions（仓库 A → 仓库 B） 这是核心步骤。我们需要在仓库 A 中创建一个工作流，每次推送时自动构建 Hugo，然后把 public/ 文件夹推送到仓库 B。\n6.1 创建 Personal Access Token (PAT) 因为要从私有仓库 A 推送到仓库 B，需要一个有权限的 Token：\n点击 GitHub 右上角头像 → Settings 左侧最下方 Developer settings → Personal access tokens → Tokens (classic) 点击 Generate new token (classic) Note：Deploy to GitHub Pages repo Expiration：选择 No expiration（或 90 天，到期后重新生成） Select scopes：勾选以下权限： ✅ repo（完整仓库控制） ✅ workflow（如果需要更新 Actions） 点击页面底部 Generate token ⚠️ 立即复制这个 token（页面关闭后无法再次查看） 6.2 将 Token 存入仓库 A 的 Secrets 进入你的私有仓库 A（blog-source） 顶部菜单 Settings → 左侧 Secrets and variables → Actions 点击 New repository secret Name：GH_PAGES_TOKEN Secret：粘贴刚才复制的 PAT 点击 Add secret 6.3 创建工作流文件 在本地 Hugo 站点根目录，创建文件夹和文件：\nbash\rmkdir -p .github/workflows\r然后创建文件 .github/workflows/deploy.yml，内容如下：\nyaml\rname: Deploy to GitHub Pages on: push: branches: [main] workflow_dispatch: # 允许手动触发 jobs: build-and-deploy: runs-on: ubuntu-latest steps: # 1. 检出源码 - name: Checkout source uses: actions/checkout@v4 with: submodules: recursive # 如果用了 git submodule 管理主题，必须加 fetch-depth: 0 # 2. 安装 Hugo Extended - name: Setup Hugo uses: peaceiris/actions-hugo@v3 with: hugo-version: \u0026#39;latest\u0026#39; extended: true # 如果主题用了 SCSS/Sass，必须开 extended # 3. 构建站点 - name: Build run: hugo --minify # 4. 推送到仓库 B（GitHub Pages） - name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pages@v4 with: personal_token: ${{ secrets.GH_PAGES_TOKEN }} external_repository: 你的用户名/你的用户名.github.io # 修改这里！ publish_dir: ./public publish_branch: main commit_message: \u0026#34;Deploy from blog-source: ${{ github.event.head_commit.message }}\u0026#34; cname: \u0026#34;\u0026#34; # 如果有自定义域名，填在这里，如 blog.example.com\r6.4 修改配置中的占位符 把上面 YAML 中的这两个地方改成你的：\nexternal_repository: 你的用户名/你的用户名.github.io\n例如：external_repository: zhangsan/zhangsan.github.io 如果用了自定义域名，把 cname: \u0026quot;\u0026quot; 改成你的域名，如 cname: blog.example.com\n6.5 提交并推送 bash\rgit add .github/workflows/deploy.yml git commit -m \u0026#34;Add GitHub Actions for dual deployment\u0026#34; git push origin main\r推送后，进入仓库 A → Actions 标签，应该能看到工作流正在运行。等待它变绿（✅）。\n6.6 验证 GitHub Pages 进入仓库 B，确认 main 分支有了新提交（来自 Actions 机器人） 进入仓库 B → Settings → Pages 看到绿色提示：Your site is live at https://你的用户名.github.io/ 点击访问 七、第五步：开关控制（停用/启用某个平台） 7.1 停用 Cloudflare Pages 方法 A（推荐）：暂停自动构建\n登录 Cloudflare Dashboard 进入你的 Pages 项目 顶部菜单 Settings → Builds \u0026amp; deployments 找到 Pause deployments 按钮，点击暂停 需要恢复时，点击 Resume deployments 方法 B：取消 GitHub 关联\nPages 项目 → Settings → Git 点击 Disconnect 断开与仓库 A 的关联 恢复时重新 Connect 7.2 停用 GitHub Pages 进入仓库 B → Settings → Pages Source 下拉框选择 None 点击 Save 站点会显示 404，表示已停用 恢复时重新选择 main 分支 7.3 更优雅的开关：用 GitHub Actions 条件控制 如果你不想登录后台，也可以在仓库 A 的 Actions 中加开关变量：\n仓库 A → Settings → Secrets and variables → Actions → Variables 新建变量： Name: ENABLE_GITHUB_PAGES Value: true（启用）或 false（停用） 修改工作流文件，在 Deploy 步骤加 if 条件： yaml\r- name: Deploy to GitHub Pages if: ${{ vars.ENABLE_GITHUB_PAGES == \u0026#39;true\u0026#39; }} uses: peaceiris/actions-gh-pages@v4 with: ...\r这样你只需要改一个变量值就能控制是否推送到仓库 B，连代码都不用改。\n八、第六步：日常写作流程 配置完成后，你每次写博客只需要做：\nbash\r# 1. 本地写新文章 hugo new content posts/新文章.md # 2. 编辑内容 # 用你喜欢的编辑器打开 content/posts/新文章.md # 3. 本地预览 hugo server -D # 4. 满意后提交 git add . git commit -m \u0026#34;Add new post: 文章标题\u0026#34; # 5. 推送（一键触发双平台部署） git push origin main\r推送后：\nCloudflare Pages：自动检测推送，约 30 秒~2 分钟完成构建 GitHub Pages：Actions 自动运行，约 1~3 分钟完成构建并推送 九、注意事项与优化 9.1 构建版本一致性 Cloudflare 和 GitHub Actions 可能使用不同版本的 Hugo。建议：\nCloudflare：已在环境变量中设置了 HUGO_VERSION GitHub Actions：在 peaceiris/actions-hugo@v3 中，hugo-version: 'latest' 会自动拉最新版。如果你需要固定版本，改成具体版本号如 hugo-version: '0.146.7' 9.2 关于 baseURL Hugo 构建时 baseURL 只能有一个。两个平台的域名不同，解决方案：\n方案 A（推荐）：使用相对路径\n修改 hugo.toml：\ntoml\rbaseURL = \u0026#39;/\u0026#39;\r这样无论部署到哪个域名，链接都能正常工作。\n方案 B：分别构建两次\n在 Actions 中构建两次，分别指定不同 baseURL。但这会显著增加构建时间，不推荐。\n9.3 主题作为子模块 如果你用 git submodule 管理主题（如 PaperMod），确保：\n本地添加子模块：\nbash\rgit submodule add --depth=1 https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod\r.github/workflows/deploy.yml 中已包含 submodules: recursive\nCloudflare Pages 构建时也会自动拉取子模块（默认行为）\n9.4 构建失败排查 如果 Actions 失败，点击失败的运行记录，查看日志：\n常见错误 原因 解决 hugo: command not found Actions 没装 Hugo 检查 peaceiris/actions-hugo 步骤 permission denied Token 权限不足 重新生成 PAT，勾选 repo remote: Repository not found 仓库名写错 检查 external_repository Cloudflare 构建失败 Hugo 版本不兼容 检查 HUGO_VERSION 环境变量 9.5 安全性提醒 PAT (Personal Access Token) 拥有你 GitHub 账户的仓库写入权限，不要泄露 如果 Token 泄露，立即去 GitHub Settings → Developer settings → Tokens 中 Delete 并重新生成 建议给 Token 设置过期时间（如 90 天），定期轮换 十、总结 你的方案完全可行，而且设计得很合理：\n优点 说明 源码始终私有 仓库 A 是私有的，核心资产受保护 Cloudflare 零配置 直接 Git 集成，无需 Actions，简单可靠 GitHub Pages 可控 通过 Actions 推送，开关方便 双平台冗余 一个挂了另一个还能访问 唯一的\u0026quot;缺陷\u0026quot;是推送到仓库 A 时，Cloudflare 和 Actions 会并行构建，资源有点浪费，但功能完全正常。如果你介意，可以把 Cloudflare 也改成通过 Actions 部署（用 cloudflare/pages-action），但那样配置更复杂，你的当前方案已经是最优解。\n跟着上面的步骤一步一步做，应该能顺利跑通。如果遇到具体报错，把错误日志贴出来我可以继续帮你排查。\n","permalink":"/posts/blog-building/05-hugo-blog-building/","summary":"\u003ch2 id=\"前言\"\u003e前言\u003c/h2\u003e\n\u003cp\u003e博客在本地已经搭建完成，也做好了一些优化和个性化配置，万事俱备，只欠东风，胜利就在前方。接下来我们开始着手将其部署到远程托管平台。\u003c/p\u003e\n\u003cp\u003e目前公认的稳定且免费的托管方案主要有两个： \u003cstrong\u003eGitHub Pages\u003c/strong\u003e 和 \u003cstrong\u003eCloudFlare Pages\u003c/strong\u003e,两个都行，但成年人不做选择，当然是两个都要了。\u003c/p\u003e","title":"从零搭建 Hugo + PaperMod 博客之五： 部署到远程服务端"},{"content":"前言 本站点基础搭建与内容撰写流程已就绪。本篇聚焦「个性化配置与体验优化」，记录笔者在使用过程中的几项偏好设置。\n重要说明：本文所有内容均为可选项，非必须步骤。更多是对个人配置优化的记录，读者可根据实际需求选择参考，或直接跳过本篇。\n一、文章元数据优化：显示修改日期与分类链接 1.1 需求说明 默认 PaperMod 主题的文章元数据仅显示发布日期、阅读时长、字数等。笔者希望：\n当文章有修改时，显示「更新于」日期 在元数据区域显示分类的可点击链接 1.2 实现步骤 在 layouts/partials/ 目录新建 post_meta.html：\ncmd\rmkdir layouts\\partials notepad layouts\\partials\\post_meta.html\r粘贴以下内容：\nhtml\r{{- $scratch := newScratch }} {{- if not .Date.IsZero -}} {{- $scratch.Add \u0026#34;meta\u0026#34; (slice (printf \u0026#34;\u0026lt;span title=\u0026#39;%s\u0026#39;\u0026gt;%s\u0026lt;/span\u0026gt;\u0026#34; (.Date) (.Date | time.Format (default \u0026#34;:date_long\u0026#34; site.Params.DateFormat)))) }} {{- end }} {{- /* 仅在 front matter 手动声明了 lastmod 且与 date 不同时显示 */ -}} {{- if and (not .Lastmod.IsZero) (ne .Lastmod .Date) -}} {{- $scratch.Add \u0026#34;meta\u0026#34; (slice (printf \u0026#34;\u0026lt;span title=\u0026#39;%s\u0026#39;\u0026gt;更新于 %s\u0026lt;/span\u0026gt;\u0026#34; (.Lastmod) (.Lastmod | time.Format (default \u0026#34;:date_long\u0026#34; site.Params.DateFormat)))) }} {{- end }} {{- if (.Param \u0026#34;ShowReadingTime\u0026#34;) -}} {{- $scratch.Add \u0026#34;meta\u0026#34; (slice (printf \u0026#34;\u0026lt;span\u0026gt;%s\u0026lt;/span\u0026gt;\u0026#34; (i18n \u0026#34;read_time\u0026#34; .ReadingTime | default (printf \u0026#34;%d min\u0026#34; .ReadingTime)))) }} {{- end }} {{- if (.Param \u0026#34;ShowWordCount\u0026#34;) -}} {{- $scratch.Add \u0026#34;meta\u0026#34; (slice (printf \u0026#34;\u0026lt;span\u0026gt;%s\u0026lt;/span\u0026gt;\u0026#34; (i18n \u0026#34;words\u0026#34; .WordCount | default (printf \u0026#34;%d words\u0026#34; .WordCount)))) }} {{- end }} {{- /* 分类链接 */ -}} {{- with .GetTerms \u0026#34;categories\u0026#34; }} {{- $categoryLinks := slice }} {{- range . }} {{- $categoryLinks = $categoryLinks | append (printf \u0026#34;\u0026lt;a href=\u0026#39;%s\u0026#39;\u0026gt;%s\u0026lt;/a\u0026gt;\u0026#34; .RelPermalink .LinkTitle) }} {{- end }} {{- $scratch.Add \u0026#34;meta\u0026#34; (slice (printf \u0026#34;\u0026lt;span\u0026gt;%s\u0026lt;/span\u0026gt;\u0026#34; (delimit $categoryLinks \u0026#34; · \u0026#34;))) }} {{- end }} {{- if not (.Param \u0026#34;hideAuthor\u0026#34;) -}} {{- with (partial \u0026#34;author.html\u0026#34; .) }} {{- $scratch.Add \u0026#34;meta\u0026#34; (slice (printf \u0026#34;\u0026lt;span\u0026gt;%s\u0026lt;/span\u0026gt;\u0026#34; .)) }} {{- end }} {{- end }} {{- with ($scratch.Get \u0026#34;meta\u0026#34;) }} {{- delimit . \u0026#34;\u0026amp;nbsp;·\u0026amp;nbsp;\u0026#34; | safeHTML -}} {{- end -}}\r1.3 启用自定义元数据 编辑 hugo.toml，在 [params] 下添加：\ntoml\r[params] # ... 其他配置 customPostMeta = true\r注意：PaperMod 默认加载 partials/post_meta.html，若主题更新后未生效，可检查 themes/PaperMod/layouts/single.html 中是否引用了该 partial。\n1.4 文章 Front Matter 示例 toml\r+++ title = \u0026#39;示例文章\u0026#39; date = 2026-05-31T12:00:00+08:00 lastmod = 2026-06-01T10:00:00+08:00 categories = [\u0026#39;Tech Notes\u0026#39;] +++\r效果：元数据区域将显示「2026-05-31 · 更新于 2026-06-01 · 5 min · 1200 words · Tech Notes」\n二、文章导航文案自定义：区分「上一篇」与「上一页」 2.1 需求说明 默认 PaperMod 中，文章详情页底部的「上一篇/下一篇」与列表页分页的「上一页/下一页」共用同一套 i18n 文案。笔者希望：\n文章导航：显示「上一篇」「下一篇」 列表分页：保持「上一页」「下一页」 2.2 创建中文语言文件 在 i18n/ 目录新建 zh.yaml：\ncmd\rmkdir i18n notepad i18n\\zh.yaml\r粘贴以下内容（注意 YAML 缩进）：\nyaml\r- id: prev_post translation: \u0026#34;上一篇\u0026#34; - id: next_post translation: \u0026#34;下一篇\u0026#34; - id: prev_page translation: \u0026#34;上一页\u0026#34; - id: next_page translation: \u0026#34;下一页\u0026#34;\r2.3 自定义文章导航模板 在 layouts/partials/ 新建 post_nav_links.html：\ncmd\rnotepad layouts\\partials\\post_nav_links.html\r粘贴以下内容：\nhtml\r{{- $pages := where site.RegularPages \u0026#34;Type\u0026#34; \u0026#34;in\u0026#34; site.Params.mainSections }} {{- if and (gt (len $pages) 1) (in $pages . ) }} \u0026lt;nav class=\u0026#34;paginav\u0026#34;\u0026gt; {{- with $pages.Next . }} \u0026lt;a class=\u0026#34;prev\u0026#34; href=\u0026#34;{{ .Permalink }}\u0026#34;\u0026gt; \u0026lt;span class=\u0026#34;title\u0026#34;\u0026gt;« {{ i18n \u0026#34;prev_post\u0026#34; }}\u0026lt;/span\u0026gt; \u0026lt;span\u0026gt;{{- .Name -}}\u0026lt;/span\u0026gt; \u0026lt;/a\u0026gt; {{- end }} {{- with $pages.Prev . }} \u0026lt;a class=\u0026#34;next\u0026#34; href=\u0026#34;{{ .Permalink }}\u0026#34;\u0026gt; \u0026lt;span class=\u0026#34;title\u0026#34;\u0026gt;{{ i18n \u0026#34;next_post\u0026#34; }} »\u0026lt;/span\u0026gt; \u0026lt;span\u0026gt;{{- .Name -}}\u0026lt;/span\u0026gt; \u0026lt;/a\u0026gt; {{- end }} \u0026lt;/nav\u0026gt; {{- end }}\r2.4 确保模板被引用 检查 themes/PaperMod/layouts/single.html，确认底部导航引用的是 post_nav_links：\nhtml\r{{- if (.Param \u0026#34;ShowPostNavLinks\u0026#34;) }} {{- partial \u0026#34;post_nav_links.html\u0026#34; . }} {{- end }}\r若主题文件中引用的是其他 partial 名称，可将上述代码直接替换原内容，或通过 layouts/single.html 覆盖主题模板。\n三、代码块体验优化：常驻复制按钮 + 语言标签 3.1 需求说明 复制按钮始终可见，无需鼠标悬停（对键盘/屏幕阅读器用户更友好） 在代码块左上角显示语言类型，便于快速识别 3.2 常驻复制按钮：扩展 CSS 在 assets/css/extended/ 新建 blank.css：\ncmd\rmkdir assets\\css\\extended notepad assets\\css\\extended\\blank.css\r粘贴以下内容：\ncss\r/* 让代码块复制按钮始终可见 - 视障用户友好配置 */ /* 为绝对定位创建包含块 */ .post-content .highlight { position: relative; } /* 合并后的按钮常驻样式 */ .post-content .highlight .copy-code { display: block !important; opacity: 1 !important; visibility: visible !important; position: absolute !important; top: 8px !important; right: 8px !important; z-index: 10 !important; } /* 关键优化：为复制按钮留出顶部空间，避免遮挡代码第一行 */ .post-content .highlight pre { padding-top: 2.5rem !important; } /* 复制成功后的反馈文字 */ .copy-code.copied::after { content: \u0026#34; 已复制\u0026#34; !important; }\r说明：!important 用于覆盖主题默认样式；padding-top 确保按钮不遮挡代码内容。\n3.3 显示代码语言标签：自定义渲染模板 在 layouts/_markup/ 新建 render-codeblock.html：\ncmd\rmkdir layouts\\_markup notepad layouts\\_markup\\render-codeblock.html\r粘贴以下内容：\nhtml\r\u0026lt;!-- 让代码块前面显示代码语言类型 --\u0026gt; {{ $lang := .Type | default \u0026#34;text\u0026#34; }} \u0026lt;div class=\u0026#34;highlight\u0026#34; data-lang=\u0026#34;{{ $lang }}\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;code-lang-label\u0026#34;\u0026gt;{{ $lang }}\u0026lt;/div\u0026gt; {{ transform.Highlight .Inner $lang .Options }} \u0026lt;/div\u0026gt;\r3.4 为语言标签添加基础样式（可选） 在 assets/css/extended/blank.css 末尾追加：\ncss\r/* 代码块语言标签样式 */ .highlight { position: relative; } .code-lang-label { position: absolute; top: 4px; left: 12px; font-size: 0.75rem; color: var(--secondary); opacity: 0.8; pointer-events: none; z-index: 5; } /* 确保语言标签不与复制按钮重叠 */ .copy-code { right: 8px !important; left: auto !important; }\r3.5 验证效果 启动 hugo server 后，代码块应显示：\ntext\r[cmd] ← 左上角语言标签 ┌───────────────── │ :: 命令内容 │ echo Hello └───────────────── [📋] ← 右上角常驻复制按钮\r四、图片渲染优化：自动添加宽高属性 4.1 需求说明 图片加载时若未指定宽高，页面会发生布局偏移（Cumulative Layout Shift, CLS），影响阅读体验。通过自定义图片渲染模板，可自动注入宽高属性。\n4.2 创建渲染模板 在 layouts/_markup/ 新建 render-image.html：\ncmd\rnotepad layouts\\_markup\\render-image.html\r粘贴以下内容：\nhtml\r\u0026lt;img src=\u0026#34;{{ .Destination | safeURL }}\u0026#34; {{ with .Title }}alt=\u0026#34;{{ . }}\u0026#34;{{ end }} loading=\u0026#34;lazy\u0026#34; {{ with .Width }}width=\u0026#34;{{ . }}\u0026#34;{{ end }} {{ with .Height }}height=\u0026#34;{{ . }}\u0026#34;{{ end }}\u0026gt;\r4.3 效果说明 loading=\u0026quot;lazy\u0026quot;：图片进入视口时再加载，提升首屏速度 width / height：浏览器预留空间，避免布局抖动 alt：若图片有 Title 属性，自动作为替代文本，提升可访问性 注意：此优化仅对本地图片（/images/xxx.jpg）生效；外链图片需手动添加宽高或使用 CDN 服务。\n五、构建验证与问题排查 5.1 重新构建站点 cmd\r:: 清理缓存并强制重建 hugo --gc --minify --force :: 启动预览（含草稿） hugo server -D -F --disableFastRender\r5.2 常见问题速查 现象 可能原因 解决方案 修改日期未显示 文章未设置 lastmod 或与 date 相同 检查 Front Matter，确保 lastmod 存在且值不同 分类链接不显示 文章未设置 categories 在 Front Matter 添加 categories = ['xxx'] 复制按钮仍隐藏 CSS 被主题更新覆盖 确认 assets/css/extended/blank.css 被加载，或检查 Hugo extended 版本 语言标签位置错乱 代码块嵌套层级特殊 调整 .code-lang-label 的 top/left 值 图片宽高未生效 图片为外链或构建时未处理 本地图片需放在 static/ 或 assets/ 目录 5.3 屏幕阅读器友好性验证 导航至代码块区域，应能直接读到「复制代码」按钮 按钮可通过 Tab 或 B 键聚焦，空格键触发复制 图片的 alt 属性会被屏幕阅读器朗读 小结 本篇记录的优化项汇总：\n元数据增强：显示修改日期 + 分类链接，提升文章信息密度 导航文案区分：文章页用「上一篇」，列表页保持「上一页」，语义更清晰 代码块体验：常驻复制按钮 + 语言标签，兼顾可视与无障碍用户 图片渲染：自动添加宽高，减少布局偏移，提升加载体验 再次强调：以上均为个人偏好配置，非必须步骤。若追求极简，可跳过本篇直接使用默认主题。\n参考资源 Hugo 模板函数：https://gohugo.io/functions/ PaperMod 自定义指南：https://github.com/adityatelange/hugo-PaperMod/wiki/FAQs i18n 多语言配置：https://gohugo.io/content-management/multilingual/ CSS !important 使用建议：https://developer.mozilla.org/zh-CN/docs/Web/CSS/Specificity 优化原则：先可用，再好用。每次只改一处，验证通过后再继续。毕竟，配置是为了服务内容，而非反过来。\n","permalink":"/posts/blog-building/04-hugo-blog-building/","summary":"\u003ch2 id=\"前言\"\u003e前言\u003c/h2\u003e\n\u003cp\u003e本站点基础搭建与内容撰写流程已就绪。本篇聚焦「个性化配置与体验优化」，记录笔者在使用过程中的几项偏好设置。\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e重要说明：本文所有内容均为\u003cstrong\u003e可选项\u003c/strong\u003e，非必须步骤。更多是对个人配置优化的记录，读者可根据实际需求选择参考，或直接跳过本篇。\u003c/p\u003e","title":"从零搭建 Hugo + PaperMod 博客之四： 让文章显示修改日期和分类链接等个性化偏好配置"},{"content":"前言 之前我们创建博客后已经在 hugo.toml 配置文件中开启了文章评论功能\ntoml\r[params] comments = true\r但这只是打开了评论开关，并没有真正实现评论功能，因为 hugo 搭建的是竞态网站，并不提供数据库存储服务，因此需要我们自己实现一套评论系统。\n好消息是目前已经有成熟稳定的方案，我们只需要简单几步和少量代码将其接入到自己的博客即可使用。这里我们介绍的是基于 github Discussion 的 Giscus App。\n一、评论系统：Giscus（完整生产方案） 选择 Giscus 的理由：基于 GitHub Discussions，免费稳定、无广告、无需数据库、支持 Markdown/代码高亮/表情反应，且与 GitHub 账号体系打通（对技术博客读者非常自然）。\n步骤 1：GitHub 端准备 准备仓库：新建（或沿用现有）一个公开 GitHub 仓库，用于存放评论数据。仓库必须是 public，否则 Giscus 无法读取。\n开启 Discussions：进入仓库 → Settings → General → Features → 勾选 Discussions。\n安装 Giscus App：访问 github.com/apps/giscus → 点击 Install → 选择 Only select repositories → 勾选你的仓库 → 保存。遵循最小权限原则，不要授权给所有仓库。\n获取配置参数：访问 giscus.app（有中文界面），填写你的仓库名，选择：\n页面 ↔️ Discussion 映射：pathname（最稳定，换域名不丢评论） Discussion 分类：Announcements（该分类下的讨论不会出现在仓库首页动态，更干净） 主题：先随便选一个，后面 Hugo 端会配置自动切换 语言：zh-CN 页面底部会生成一段 \u0026lt;script\u0026gt;，记录下其中的 data-repo、data-repo-id、data-category、data-category-id 四个值。\n步骤 2：安全加固（防止脚本被盗用） 在 Hugo 站点仓库的根目录（与 hugo.toml 同级，不是 static/ 目录）创建 giscus.json：\njson\r{ \u0026#34;origins\u0026#34;: [\u0026#34;https://example.org\u0026#34;], \u0026#34;defaultCommentOrder\u0026#34;: \u0026#34;newest\u0026#34; }\r这会让 Giscus 只在你自己的域名下加载，防止他人盗用你的评论组件。\n步骤 3：Hugo 配置 在 hugo.toml 的 [params] 块内新增：\ntoml\r[params.giscus] repo = \u0026#34;yourname/yourrepo\u0026#34; # 例：dake/blog-comments repoId = \u0026#34;R_xxxxxxxxxx\u0026#34; # 从 giscus.app 复制 category = \u0026#34;Announcements\u0026#34; categoryId = \u0026#34;DIC_xxxxxxxxxx\u0026#34; # 从 giscus.app 复制 mapping = \u0026#34;pathname\u0026#34; strict = \u0026#34;0\u0026#34; # 0=宽松匹配（推荐），1=严格标题匹配 reactionsEnabled = \u0026#34;1\u0026#34; # 启用表情反应 emitMetadata = \u0026#34;0\u0026#34; # 不发送元数据 inputPosition = \u0026#34;bottom\u0026#34; # 评论框在底部 lightTheme = \u0026#34;light\u0026#34; darkTheme = \u0026#34;dark\u0026#34; lang = \u0026#34;zh-CN\u0026#34;\r注意：repoId 和 categoryId 必须以字符串形式保留（带引号），因为它们是字母数字混合 ID。\n步骤 4：创建评论模板 创建文件 layouts/partials/comments.html，内容如下（完整代码，含 PaperMod 暗黑/亮色主题自动同步）：\nhtml\r{{- if and .Site.Params.giscus .Site.Params.giscus.repo -}} \u0026lt;div class=\u0026#34;comments-wrapper\u0026#34; style=\u0026#34;margin-top: 3rem;\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;comments-header\u0026#34; style=\u0026#34;text-align: center; margin-bottom: 1.5rem;\u0026#34;\u0026gt; \u0026lt;h3 style=\u0026#34;font-size: 1.25em; font-weight: 700; margin-bottom: 0.25rem;\u0026#34;\u0026gt;评论\u0026lt;/h3\u0026gt; \u0026lt;p style=\u0026#34;font-size: 0.9rem; color: var(--secondary);\u0026#34;\u0026gt;使用 GitHub 账号登录后参与讨论\u0026lt;/p\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div id=\u0026#34;giscus-container\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;script\u0026gt; const getStoredTheme = () =\u0026gt; { const pref = localStorage.getItem(\u0026#34;pref-theme\u0026#34;); if (pref === \u0026#34;dark\u0026#34;) return \u0026#34;{{ .Site.Params.giscus.darkTheme | default \u0026#34;dark\u0026#34; }}\u0026#34;; if (pref === \u0026#34;light\u0026#34;) return \u0026#34;{{ .Site.Params.giscus.lightTheme | default \u0026#34;light\u0026#34; }}\u0026#34;; // 用户从未手动切换过，跟随系统偏好 return \u0026#34;preferred_color_scheme\u0026#34;; }; const setGiscusTheme = () =\u0026gt; { const sendMessage = (message) =\u0026gt; { const iframe = document.querySelector(\u0026#39;iframe.giscus-frame\u0026#39;); if (iframe) { iframe.contentWindow.postMessage({ giscus: message }, \u0026#39;https://giscus.app\u0026#39;); } }; sendMessage({ setConfig: { theme: getStoredTheme() } }); }; document.addEventListener(\u0026#34;DOMContentLoaded\u0026#34;, () =\u0026gt; { // 创建 giscus 挂载容器 const giscusDiv = document.createElement(\u0026#34;div\u0026#34;); giscusDiv.className = \u0026#34;giscus\u0026#34;; document.querySelector(\u0026#34;#giscus-container\u0026#34;).appendChild(giscusDiv); // 动态创建 script，确保 theme 初始值正确 const giscusAttributes = { \u0026#34;src\u0026#34;: \u0026#34;https://giscus.app/client.js\u0026#34;, \u0026#34;data-repo\u0026#34;: \u0026#34;{{ .Site.Params.giscus.repo }}\u0026#34;, \u0026#34;data-repo-id\u0026#34;: \u0026#34;{{ .Site.Params.giscus.repoId }}\u0026#34;, \u0026#34;data-category\u0026#34;: \u0026#34;{{ .Site.Params.giscus.category }}\u0026#34;, \u0026#34;data-category-id\u0026#34;: \u0026#34;{{ .Site.Params.giscus.categoryId }}\u0026#34;, \u0026#34;data-mapping\u0026#34;: \u0026#34;{{ .Site.Params.giscus.mapping | default \u0026#34;pathname\u0026#34; }}\u0026#34;, \u0026#34;data-strict\u0026#34;: \u0026#34;{{ .Site.Params.giscus.strict | default \u0026#34;0\u0026#34; }}\u0026#34;, \u0026#34;data-reactions-enabled\u0026#34;: \u0026#34;{{ .Site.Params.giscus.reactionsEnabled | default \u0026#34;1\u0026#34; }}\u0026#34;, \u0026#34;data-emit-metadata\u0026#34;: \u0026#34;{{ .Site.Params.giscus.emitMetadata | default \u0026#34;0\u0026#34; }}\u0026#34;, \u0026#34;data-input-position\u0026#34;: \u0026#34;{{ .Site.Params.giscus.inputPosition | default \u0026#34;bottom\u0026#34; }}\u0026#34;, \u0026#34;data-theme\u0026#34;: getStoredTheme(), \u0026#34;data-lang\u0026#34;: \u0026#34;{{ .Site.Params.giscus.lang | default \u0026#34;zh-CN\u0026#34; }}\u0026#34;, \u0026#34;data-loading\u0026#34;: \u0026#34;lazy\u0026#34;, \u0026#34;crossorigin\u0026#34;: \u0026#34;anonymous\u0026#34;, \u0026#34;async\u0026#34;: \u0026#34;\u0026#34;, }; const giscusScript = document.createElement(\u0026#34;script\u0026#34;); Object.entries(giscusAttributes).forEach(([key, value]) =\u0026gt; { giscusScript.setAttribute(key, value); }); document.querySelector(\u0026#34;#giscus-container\u0026#34;).appendChild(giscusScript); // 监听 PaperMod 主题切换按钮（桌面端 + 移动端浮动按钮） const themeSwitcher = document.querySelector(\u0026#34;#theme-toggle\u0026#34;); if (themeSwitcher) { themeSwitcher.addEventListener(\u0026#34;click\u0026#34;, setGiscusTheme); } const themeFloatSwitcher = document.querySelector(\u0026#34;#theme-toggle-float\u0026#34;); if (themeFloatSwitcher) { themeFloatSwitcher.addEventListener(\u0026#34;click\u0026#34;, setGiscusTheme); } }); \u0026lt;/script\u0026gt; {{- else -}} {{- warnf \u0026#34;comments.html: Giscus 参数未配置完整，跳过渲染\u0026#34; -}} {{- end -}}\r步骤 5：可选 CSS 美化 如果你希望评论区上方的标题更美观，创建 assets/css/extended/comments.css：\ncss\r.comments-wrapper { padding-top: 1rem; border-top: 1px solid var(--border); } .comments-header h3 { color: var(--primary); } .comments-header p { color: var(--secondary); margin: 0; }\rassets/css/extended/ 是 PaperMod 约定自动加载自定义 CSS 的目录，无需额外配置。\n步骤 6：验证 本地重新运行 hugo server -D -F --disableFastRender，打开任意文章页，滚动到底部。 如果看到\u0026quot;使用 GitHub 账号登录后参与讨论\u0026quot;和 Giscus 加载框，说明成功。 点击 PaperMod 顶部的 🌙/☀️ 主题切换按钮，Giscus 评论区应同步切换暗黑/亮色。 ","permalink":"/posts/blog-building/03-hugo-blog-building/","summary":"\u003ch2 id=\"前言\"\u003e前言\u003c/h2\u003e\n\u003cp\u003e之前我们创建博客后已经在 \u003ccode\u003ehugo.toml\u003c/code\u003e 配置文件中开启了文章评论功能\u003c/p\u003e\n\r\n\r\n\r\n\u003cdiv class=\"highlight\" data-lang=\"toml\"\u003e\r\n    \u003cdiv class=\"code-lang-label\"\u003etoml\u003c/div\u003e\r\n    \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-toml\" data-lang=\"toml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003eparams\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003ecomments\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\r\n\u003c/div\u003e\r\n\u003cp\u003e但这只是打开了评论开关，并没有真正实现评论功能，因为 hugo 搭建的是竞态网站，并不提供数据库存储服务，因此需要我们自己实现一套评论系统。\u003c/p\u003e","title":"从零搭建 Hugo + PaperMod 博客之三：给文章添加评论系统"},{"content":"前言 到目前为止，本站点基础环境已就绪。本篇聚焦博客内容生产的核心环节：如何新建文章、理解文章配置、撰写正文并预览效果。\n目标：掌握 hugo new 命令、Front Matter 配置、模板自定义，实现「命令创建 → 编辑内容 → 本地预览」的完整写作流程。\n一、新建文章：命令详解 1.1 基本命令格式 cmd\rhugo new posts/first-post.md\r命令各部分含义：\n部分 含义 说明 hugo new 新建内容命令 Hugo 内置子命令 posts/ 内容子目录 对应 content/posts/，建议文章统一放此目录 first-post.md 文件名 建议使用小写、短横线分隔，便于 URL 生成 执行后，Hugo 会在 content/posts/ 目录下生成 first-post.md 文件。\n提示：若 posts 目录不存在，Hugo 会自动创建。\n1.2 生成路径说明 text\rmyblog/ ├── content/ │ └── posts/ │ └── first-post.md ← 新生成的文章 ├── archetypes/ │ └── default.md ← 文章模板（后文详解） └── hugo.toml\r文章源文件始终位于 content/ 目录下，构建后输出至 public/。\n二、Front Matter：文章元数据配置 2.1 默认生成内容 新建文章后，content/posts/first-post.md 默认内容如下（Hugo 0.161.1 默认使用 TOML 格式）：\ntoml\r+++ date = \u0026#39;2026-06-02T17:53:36+08:00\u0026#39; draft = true title = \u0026#39;First Post\u0026#39; +++\r+++ ... +++ 是 TOML 格式的 Front Matter 分隔符（也可用 --- 表示 YAML） title：文章标题，默认由文件名自动转换（first-post.md → First Post） date：文章创建时间，格式为 RFC3339 draft = true：草稿状态，预览时需加 -D 参数才能显示 2.2 常用配置项说明 在 Front Matter 中可添加以下字段控制文章行为：\ntoml\r+++ title = \u0026#39;文章标题\u0026#39; date = 2026-05-31T12:00:00+08:00 lastmod = 2026-06-01T10:00:00+08:00 # 最后修改时间（可选） draft = false # 是否草稿：true=隐藏，false=发布 series = [\u0026#39;博客搭建\u0026#39;] # 所属系列 categories = [\u0026#39;Tech Notes\u0026#39;] # 分类 tags = [\u0026#39;Hugo\u0026#39;, \u0026#39;PaperMod\u0026#39;] # 标签 description = \u0026#39;文章摘要，用于首页/列表页展示\u0026#39; +++\r配置优先级：文章 Front Matter \u0026gt; 全局 hugo.toml 配置。即文章内设置会覆盖全局默认值。\n2.3 TOML 与 YAML 语法对比 Hugo 支持两种 Front Matter 格式，可根据偏好选择：\n特性 TOML（+++ 分隔） YAML（\u0026mdash; 分隔） 分隔符 +++ ... +++ --- ... --- 字符串引号 必须用单引号 ' 或双引号 \u0026quot; 可省略引号（简单字符串） 数组写法 tags = ['a', 'b'] tags: [a, b] 或 - a 换行 布尔值 true / false true / false 默认格式 Hugo 0.161.1 默认 需手动指定或修改 archetypes 示例：同一配置的 YAML 写法\nyaml\r--- title: \u0026#34;文章标题\u0026#34; date: 2026-05-31T12:00:00+08:00 draft: false series: - 博客搭建 categories: - Tech Notes tags: - Hugo - PaperMod ---\r建议：项目内保持统一格式。若团队协作文档，YAML 可读性略优；若追求解析速度，TOML 略快（差异可忽略）。\n三、修改标题与撰写正文 3.1 修改标题 直接编辑 Front Matter 中的 title 字段：\ntoml\r+++ title = \u0026#39;从零搭建 Hugo + PaperMod 博客之二：新建文章与撰写\u0026#39; +++\r注意：标题中的单引号需转义或改用双引号，如 title = \u0026quot;It's easy\u0026quot;。\n3.2 撰写正文 Front Matter 结束后（即 +++ 或 --- 之后），即可开始撰写 Markdown 正文：\nmarkdown\r+++ title = \u0026#39;示例文章\u0026#39; date = 2026-05-31T12:00:00+08:00 draft = false +++ ## 正文开始 这里是文章内容，支持标准 Markdown 语法： - 列表项 - **加粗**、*斜体*、`行内代码` ### 代码块示例 ```cmd echo Hello Hugo ``` ### 链接与图片 - 链接：[Hugo 官网](https://gohugo.io) - 图片：`![替代文本](/images/photo.jpg)`\r提示：正文中的代码块需用三反引号包裹，并指定语言标识（如 cmd、toml），才能启用高亮与复制功能。\n四、自定义文章模板：archetypes/default.md 4.1 模板作用 archetypes/default.md 是 hugo new 命令的默认模板。修改此文件可统一新文章的 Front Matter 结构，减少重复配置,可根据自己的偏好进行个性化设置。\n4.2 示例配置（适配本系列） 编辑 archetypes/default.md，替换为以下内容：\nmarkdown\r+++ date = \u0026#39;{{ .Date }}\u0026#39; # lastmod = draft = false title = \u0026#39;{{ replace .File.ContentBaseName \u0026#34;-\u0026#34; \u0026#34; \u0026#34; | title }}\u0026#39; series = [] categories = [] tags = [] +++\r字段说明：\n字段 含义 备注 {{ .Date }} 自动填充创建时间 Hugo 模板语法 draft = false 默认非草稿 避免遗漏发布 title 自动由文件名生成 支持后续手动修改 series/categories/tags 预置空数组 方便直接填写，无需手动加 [] 技巧：# lastmod = 以注释形式保留，需要时取消注释并填写即可。\n4.3 创建特定类型的模板（可选） 若需为不同内容类型（如 docs、notes）设置不同模板：\n:: 创建 docs 类型模板:在 archetypes 内创建 docs.md并自定义模板内容。\n:: 使用模板新建文章 hugo new docs/api-reference.md\ntext\r--- ## 五、其他注意事项 ### 5.1 文件命名规范 - 使用小写字母、数字、短横线（`-`） - 避免空格、中文、特殊字符（如 `?` `*` `:`） - 示例：`2026-05-31-hugo-new-command.md` \u0026gt; 原因：文件名会直接影响 URL 路径，不规范命名可能导致链接失效或部署问题。 ### 5.2 分类与标签填写建议 - `categories`：宏观分类，建议 1~2 个，如 `Tech Notes`、`Life` - `tags`：细粒度关键词，可多个，如 `Hugo`、`PaperMod`、`Windows` - `series`：系列文章标识，如 `博客搭建`，便于读者连续阅读 ### 5.3 草稿管理 - 开发阶段：保留 `draft = true`，预览时加 `-D` 参数 - 发布前：改为 `draft = false`，或构建时不加 `-D` ```cmd :: 预览包含草稿的文章 hugo server -D -F :: 生产构建（自动排除草稿） hugo --minify --gc\r5.4 文章描述（description）的作用 SEO 优化：搜索引擎抓取 Description 作为页面描述 在 PaperMod 主题中，description 还有一个前端可见的作用：在单页头部（single.html）或列表页头部（如 tags/_index.md），它会显示为标题下方的灰色副标题 5.5 文章摘要（summary）的作用 首页/列表页显示文章摘要，提升可读性 填写建议：50~120 字，概括核心内容，避免与标题重复 若留空，则默认抓取正文前 70 左右字/词， hugo 会根据实际内容在适当位置截断 还可以在正文任意位置添加 \u0026lt;!--more​--\u0026gt; 分隔符标记，截取文章摘要，这种方式优先级最高。其次是指定 summary，最后 hugo 才会默认抓取正文前一定数量字词 \u0026lt;!--​more--\u0026gt; 必须严格书写，前后可以有空行，但 more 单词前后不能有空格 5.6 文章摘要（summary）与描述(description)的区别 维度 summary description 主要用途 文章列表页的内容预览 SEO、社交媒体、RSS 的元数据描述 生成方式 可自动（正文前70词）或手动定义 必须手动在 front matter 中定义 内容长度 较长，一段话（约70词或更多） 较短，建议 150 字符以内 渲染位置 首页/列表页的文章卡片内 HTML \u0026lt;meta\u0026gt; 标签、页面头部副标题 读者可见性 直接展示在网页上 普通读者看不到（除页面头部可能展示） 六、本地预览：实时查看效果 6.1 启动开发服务器 cmd\r:: 在项目根目录执行 hugo server -D -F --disableFastRender\r参数说明：\n参数 作用 -D 包含 draft = true 的草稿文章 -F 包含 date 为未来的文章 --disableFastRender 禁用快速渲染，确保每次保存都完整重建（避免样式/脚本缓存问题） 6.2 访问与调试 浏览器打开：http://localhost:1313 屏幕阅读器用户：直接按字母 H 导航至文章标题，确认加载成功 修改 .md 文件后保存，浏览器自动刷新（无需手动重启） 提示：若修改 hugo.toml 或主题文件，需重启 hugo server 才能生效。\n七、小结 本篇核心要点：\nhugo new posts/xxx.md 创建文章，文件位于 content/posts/ Front Matter 支持 TOML/YAML，文章配置优先于全局配置 修改 archetypes/default.md 统一新文章模板 正文撰写遵循标准 Markdown，代码块需三反引号 + 语言标识 hugo server -D -F 实时预览，支持草稿与未来日期文章 参考资源 Hugo 内容管理文档：https://gohugo.io/content-management/ Front Matter 说明：https://gohugo.io/content-management/front-matter/ Archetypes 自定义：https://gohugo.io/content-management/archetypes/ TOML 语法指南：https://toml.io/ YAML 语法速查：https://yaml.org/ ","permalink":"/posts/blog-building/02-hugo-blog-building/","summary":"\u003ch2 id=\"前言\"\u003e前言\u003c/h2\u003e\n\u003cp\u003e到目前为止，本站点基础环境已就绪。本篇聚焦博客内容生产的核心环节：如何新建文章、理解文章配置、撰写正文并预览效果。\u003c/p\u003e\n\u003cp\u003e目标：掌握 \u003ccode\u003ehugo new\u003c/code\u003e 命令、Front Matter 配置、模板自定义，实现「命令创建 → 编辑内容 → 本地预览」的完整写作流程。\u003c/p\u003e","title":"从零搭建 Hugo + PaperMod 博客之二： 新建文章与撰写"},{"content":"前言 Hugo 是 Go 语言编写的静态网站生成器，速度快，易用，可配置。作为一款跨平台开源建站系统，当前提供 Windows，Linux，FreeBSD，NetBSD 和 OS X (Darwin) 的 x 64, i 386 和 ARM 架构的二进制预构建包。\nHugo 依赖于 Markdown 文件，元数据字体。用户可以从任意的目录中运行 Hugo，支持共享主机和其他系统。只需要几分之一秒就可以渲染一个经典的中小型网站 ，非常适合博客 ，文档等等网站的生成。\n本文记录在 Windows 环境下使用 Hugo + PaperMod 主题从零搭建静态博客的完整流程。目标：步骤完整、配置可复现、语言简洁。快速完成基础搭建。\n一、环境准备：安装 Hugo 1.1 下载 Hugo 官方发布页：https://github.com/gohugoio/hugo/releases 务必选择 extended 版本（文件名含 extended），否则 SCSS 编译、图片处理等功能不可用 本文当前使用的是：hugo_0.161.1_windows-amd64.zip 1.2 安装与验证 将下载的 压缩包解压到任意位置，最好不含空格与中文字符，然后将hugo.exe 所在路径添加到系统 Path 环境变量（注意是 hugo.exe 所在路径，不是 hugo.exe 本身）。\n或者也可以解压后将 hugo.exe 移至系统路径，例如：\ncmd\rmove hugo.exe C:\\Windows\\ :: 验证安装 hugo version :: 预期输出包含：+extended windows/amd64\r小技巧：若 hugo 命令未识别，请检查环境变量 Path 是否已包含 exe 所在目录。\n二、创建新站点 可选但建议首先安装 git bash，可在 git 官网 下载。\ncmd\r:: 创建站点目录（路径不含中文/空格更稳妥） hugo new site myblog --force cd myblog :: 初始化 Git（推荐，便于主题管理与后续部署） git init\r文件/目录 描述 hugo.toml 全局配置文件 archetypes 存储以Markdown格式的内容模板 content 存储网页内容，如文章等 layouts 存储定义网站结构的HTML文件 themes 存储主题模板文件 data 存储生成网站页面所需的补充数据(JSON、YAML或TOML格式) static 存储不需要任何额外处理的静态资源，如音视频、图片，字体，DNS验证文件等 三、安装 PaperMod 主题 推荐使用 Git submodule 方式安装，便于后续更新：\ncmd\rgit submodule add git@github.com:adityatelange/hugo-PaperMod.git themes/PaperMod :: 或者 git submodule add https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod\r注意：若提示权限错误，请以管理员身份运行 CMD，或检查目录写入权限。\n四、核心配置：hugo.toml 项目根目录的 hugo.toml 是新建站点的配置文件，以下示例配置可以全部替换，也可以按需参考修改。此配置已针对中文博客优化，并启用搜索、目录、代码复制等实用功能：\ntoml\rbaseURL = \u0026#39;https://example.org/\u0026#39; # 部署时替换成你自己的实际域名 title = \u0026#39;你的博客名称\u0026#39; theme = \u0026#34;PaperMod\u0026#34; defaultContentLanguage = \u0026#34;zh\u0026#34; hasCJKLanguage = true enableRobotsTXT = true enableEmoji = true # 输出格式（搜索功能必需） [outputs] home = [\u0026#34;HTML\u0026#34;, \u0026#34;RSS\u0026#34;, \u0026#34;JSON\u0026#34;] page = [\u0026#34;HTML\u0026#34;] # 语言配置（Hugo 0.158+ 标准写法） [languages] [languages.zh] locale = \u0026#34;zh-CN\u0026#34; title = \u0026#34;你的博客名称\u0026#34; weight = 1 # Markdown 与语法高亮配置 [markup] [markup.goldmark] [markup.goldmark.renderer] unsafe = true [markup.highlight] noClasses = false codeFences = true guessSyntax = true lineNos = false lineNumbersInTable = true style = \u0026#34;monokai\u0026#34; [markup.tableOfContents] startLevel = 2 endLevel = 4 ordered = false # 全局参数配置 [params] env = \u0026#34;production\u0026#34; description = \u0026#34;记录技术笔记、生活随笔与日常思考的个人博客\u0026#34; author = \u0026#34;你的名字\u0026#34; hideAuthor = true DateFormat = \u0026#34;2006-01-02\u0026#34; defaultTheme = \u0026#34;auto\u0026#34; disableThemeToggle = false ShowRssButtonInSectionTermList = true # 文章显示选项 ShowReadingTime = true ShowWordCount = true ShowBreadCrumbs = true ShowCodeCopyButtons = true ShowPostNavLinks = true ShowToc = true TocOpen = false # 首页信息 [params.profileMode] enabled = false [params.homeInfoParams] Title = \u0026#34;首页展示信息标题\u0026#34; Content = \u0026#34;博客简介\u0026#34; # 封面图策略 [params.cover] hiddenInList = false hiddenInSingle = true # 搜索优化（Fuse.js） [params.fuseOpts] isCaseSensitive = false shouldSort = true location = 0 distance = 1000 threshold = 0.4 minMatchCharLength = 0 keys = [\u0026#34;title\u0026#34;, \u0026#34;permalink\u0026#34;, \u0026#34;summary\u0026#34;] # 社交图标 [[params.socialIcons]] name = \u0026#34;email\u0026#34; url = \u0026#34;mailto:name@example.com\u0026#34; [[params.socialIcons]] name = \u0026#34;rss\u0026#34; url = \u0026#34;index.xml\u0026#34; # 导航菜单 [[menu.main]] identifier = \u0026#34;home\u0026#34; name = \u0026#34;首页\u0026#34; pageRef = \u0026#34;/\u0026#34; weight = 1 [[menu.main]] identifier = \u0026#34;posts\u0026#34; name = \u0026#34;文章\u0026#34; pageRef = \u0026#34;/posts/\u0026#34; weight = 2 [[menu.main]] identifier = \u0026#34;series\u0026#34; name = \u0026#34;系列\u0026#34; pageRef = \u0026#34;/series/\u0026#34; weight = 3 [[menu.main]] identifier = \u0026#34;archives\u0026#34; name = \u0026#34;归档\u0026#34; pageRef = \u0026#34;/archives/\u0026#34; weight = 4 [[menu.main]] identifier = \u0026#34;categories\u0026#34; name = \u0026#34;分类\u0026#34; pageRef = \u0026#34;/categories/\u0026#34; weight = 5 [[menu.main]] identifier = \u0026#34;search\u0026#34; name = \u0026#34;搜索\u0026#34; pageRef = \u0026#34;/search/\u0026#34; weight = 7 [[menu.main]] identifier = \u0026#34;about\u0026#34; name = \u0026#34;关于\u0026#34; pageRef = \u0026#34;/about/\u0026#34; weight = 50 # 分类系统 [taxonomies] category = \u0026#34;categories\u0026#34; tag = \u0026#34;tags\u0026#34; series = \u0026#34;series\u0026#34;\r说明：\nhasCJKLanguage = true 确保中文字数统计准确 outputs.home 包含 JSON 是启用本地搜索的前提 markup.highlight.noClasses = false 配合 PaperMod 的内置代码高亮样式 菜单 weight 值决定显示顺序，数值越小越靠前 五、创建必要页面 5.1 归档页 cmd\rhugo new archives.md\r编辑 content/archives.md：\nmarkdown\r--- title: \u0026#34;归档\u0026#34; layout: \u0026#34;archives\u0026#34; url: \u0026#34;/archives/\u0026#34; summary: \u0026#34;按时间排序的所有文章\u0026#34; ---\r5.2 搜索页 cmd\rhugo new search.md\r编辑 content/search.md：\nmarkdown\r--- title: \u0026#34;搜索\u0026#34; layout: \u0026#34;search\u0026#34; placeholder: \u0026#34;输入关键词...\u0026#34; ---\r5.3 系列页 cmd\rhugo new series.md\r编辑 content/series.md：\nmarkdown\r--- title: \u0026#34;系列\u0026#34; layout: \u0026#34;taxonomies\u0026#34; url: \u0026#34;/series/\u0026#34; type: \u0026#34;series\u0026#34; ---\r5.4 分类页 cmd\rhugo new categories.md\r编辑 content/categories.md：\nmarkdown\r--- title: \u0026#34;分类\u0026#34; layout: \u0026#34;taxonomies\u0026#34; url: \u0026#34;/categories/\u0026#34; type: \u0026#34;categories\u0026#34; ---\r5.5 关于页 cmd\rhugo new about.md\r编辑 content/about.md，填写个人或博客简介即可。\n6、本地预览与构建 cmd\r:: 启动开发服务器（自动重载，包含草稿和未来日期文章） hugo server -D -F :: 访问：http://localhost:1313 :: 生产构建（压缩 + 清理冗余） hugo --minify --gc :: 输出目录：./public\r提示：-D 包含草稿，-F 包含未来日期文章，开发时方便预览；生产构建请移除这两个参数。\n7、常见问题速查 问题 可能原因 解决方案 hugo: command not found 环境变量未配置 将 hugo.exe 所在目录加入 Path 主题样式未生效 未使用 extended 版本 重新下载含 extended 的 Hugo 搜索框无结果 缺少 JSON 输出 检查 [outputs] 是否含 JSON 中文标签乱码 文件编码非 UTF-8 用 VSCode 保存为 UTF-8 without BOM Git submodule 失败 路径过长或权限不足 启用 git config --system core.longpaths true 或以管理员运行 系列/分类页面空白 未创建对应 layout 页面 确认 content/series.md 等文件包含正确的 layout 和 type 参考资源 PaperMod 官方 Wiki：https://github.com/adityatelange/hugo-PaperMod/wiki Hugo 官方文档：https://gohugo.io/documentation/ TOML 配置语法：https://toml.io/ Fuse.js 搜索配置参考：https://fusejs.io/api/options.html 最后建议：先跑通「最小可用博客」，再逐步添加自定义样式。技术博客的核心是内容，样式是锦上添花。\n","permalink":"/posts/blog-building/01-hugo-blog-building/","summary":"\u003ch2 id=\"前言\"\u003e前言\u003c/h2\u003e\n\u003cp\u003eHugo 是 Go 语言编写的静态网站生成器，速度快，易用，可配置。作为一款跨平台开源建站系统，当前提供 Windows，Linux，FreeBSD，NetBSD 和 OS X (Darwin) 的 x 64, i 386 和 ARM 架构的二进制预构建包。\u003c/p\u003e","title":"从零搭建 Hugo + PaperMod 博客之一：新建站点和本地预览"},{"content":" 以代码为星，以文字为舟；渡滚烫星河，载温柔往事\n关于这个博客 你好，欢迎来到「星河往事」。\n这是一个用 Hugo + PaperMod 搭建的个人静态博客，没有固定的主题，也没有严格的更新计划。这里就像我的一个数字笔记本，记录着我在技术世界里的探索，以及生活中的点滴感悟。\n技术笔记：编程学习过程中的踩坑记录、解决方案、工具分享和项目总结 生活随笔：日常所见所闻、旅行见闻、读书观影感想 心情杂记：偶尔的胡思乱想、深夜的思绪碎片 内容可能会很杂，更新也可能会很随意，但每一篇文字都是我当时最真实的想法和记录。\n关于「星河往事」 为什么叫这个名字？\n在我看来，代码如星，每一行代码都是一颗闪烁的星星，无数的星星汇聚成了浩瀚的技术星河；往事如河，那些逝去的时光、经历的故事、产生的思考，汇聚成了流淌的生命长河。\n我希望在这里，既能记录下技术探索路上的点点星光，也能珍藏起生活中那些温柔的往事片段。用文字把它们一一拾起，在岁月的长河里留下一些属于自己的痕迹。\n关于我 我是一名普通的爱好编程的视障者，热爱编程，也热爱生活。\n为什么写博客 写博客对我来说，主要有三个意义：\n记录成长：把学习和思考的过程记录下来，既是对自己的总结，也能看到自己一步步成长的轨迹 分享知识：如果我的某一篇笔记能够帮助到正在遇到同样问题的人，那将是一件非常有意义的事情 安放思绪：在这个信息爆炸的时代，有一个属于自己的角落，可以安静地写下自己的想法和感受 联系我 如果你对我的文章有任何疑问或想法，欢迎与我交流：\n邮箱: chuanqi.z@foxmail.com 最后 感谢你花时间阅读这篇关于页。希望在这里，你能找到一些有用的信息，或者感受到一丝温暖。\n愿我们都能在自己的星河里，闪闪发光。\n","permalink":"/about/","summary":"\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003e以代码为星，以文字为舟；渡滚烫星河，载温柔往事\u003c/strong\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"关于这个博客\"\u003e关于这个博客\u003c/h2\u003e\n\u003cp\u003e你好，欢迎来到「星河往事」。\u003c/p\u003e\n\u003cp\u003e这是一个用 Hugo + PaperMod 搭建的个人静态博客，没有固定的主题，也没有严格的更新计划。这里就像我的一个数字笔记本，记录着我在技术世界里的探索，以及生活中的点滴感悟。\u003c/p\u003e","title":"关于"},{"content":"测试 CMD 代码块 cmd\recho Hello World hugo version\r测试 TOML 代码块 toml\r[params] test = true\r","permalink":"/posts/code-test/","summary":"\u003ch2 id=\"测试-cmd-代码块\"\u003e测试 CMD 代码块\u003c/h2\u003e\n\r\n\r\n\r\n\u003cdiv class=\"highlight\" data-lang=\"cmd\"\u003e\r\n    \u003cdiv class=\"code-lang-label\"\u003ecmd\u003c/div\u003e\r\n    \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-cmd\" data-lang=\"cmd\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eecho\u003c/span\u003e Hello World\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ehugo version\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\r\n\u003c/div\u003e\r\n\u003ch2 id=\"测试-toml-代码块\"\u003e测试 TOML 代码块\u003c/h2\u003e\n\r\n\r\n\r\n\u003cdiv class=\"highlight\" data-lang=\"toml\"\u003e\r\n    \u003cdiv class=\"code-lang-label\"\u003etoml\u003c/div\u003e\r\n    \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-toml\" data-lang=\"toml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003eparams\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003etest\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\r\n\u003c/div\u003e","title":"代码块测试"},{"content":"测试一下 第一条 第二条 你好 第一段。\n第二段\n","permalink":"/posts/first-post/","summary":"\u003ch3 id=\"测试一下\"\u003e测试一下\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e第一条\u003c/li\u003e\n\u003cli\u003e第二条\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"你好\"\u003e你好\u003c/h2\u003e\n\u003cp\u003e第一段。\u003c/p\u003e\n\u003cp\u003e第二段\u003c/p\u003e","title":"第一篇博客"},{"content":"这是我的第一篇文章 Hello Hugo！\n徐薇 - 第一次爱的人\r测试 ","permalink":"/posts/my-first-post/","summary":"\u003ch2 id=\"这是我的第一篇文章\"\u003e这是我的第一篇文章\u003c/h2\u003e\n\u003cp\u003eHello Hugo！\u003c/p\u003e\n\u003cfigure \u003e\r\n  \u003caudio controls preload=\"metadata\" style=\"width: 100%;\"\u003e\r\n    \u003csource src=\"/audio/xuwei-first-love.mp3\" type=\"audio/mpeg\"\u003e\r\n  \u003c/audio\u003e\r\n  \u003cfigcaption\u003e徐薇 - 第一次爱的人\u003c/figcaption\u003e\r\n\u003c/figure\u003e\r\n\n\u003ch2 id=\"测试\"\u003e测试\u003c/h2\u003e\n\u003caudio controls style=\"width: 100%;\"\u003e\n  \u003csource src=\"/audio/xuwei-first-love.mp3\" type=\"audio/mpeg\" /\u003e\n\u003c/audio\u003e","title":"测试插入音频"}]