<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>博客建设 - 她和她的猫</title>
    <link>https://her-cat.com/categories/%E5%8D%9A%E5%AE%A2%E5%BB%BA%E8%AE%BE/</link>
    <description>博客建设的文章列表 - 她和她的猫</description>
    <image>
      <title>她和她的猫</title>
      <url>https://her-cat.com/assets/favorite.jpeg</url>
      <link>https://her-cat.com/assets/favorite.jpeg</link>
    </image>
    <generator>Hugo -- 0.148.1</generator>
    <language>zh</language>
    <lastBuildDate>Sun, 11 Jan 2026 21:48:00 +0800</lastBuildDate>
    <atom:link href="https://her-cat.com/categories/%E5%8D%9A%E5%AE%A2%E5%BB%BA%E8%AE%BE/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>折腾 Hugo PaperMod 主题</title>
      <link>https://her-cat.com/posts/2025/10/08/hugo-paper-mod/</link>
      <pubDate>Wed, 08 Oct 2025 16:51:06 +0800</pubDate>
      <guid>https://her-cat.com/posts/2025/10/08/hugo-paper-mod/</guid>
      <description>从 Eureka 切换到 PaperMod 主题已经快半年时间了，经过一番折腾和优化，基本达到了我想要的效果。这篇文章记录了我对这个主题的各种改造，如果你也在使用 PaperMod，下面的内容可能对你有帮助。</description>
      <content:encoded><![CDATA[<p>从 <a href="https://github.com/wangchucheng/hugo-eureka">Eureka</a> 切换到 <a href="https://github.com/adityatelange/hugo-PaperMod/">PaperMod</a> 主题已经快半年时间了，经过一番折腾和优化，基本达到了我想要的效果。这篇文章记录了我对这个主题的各种改造，如果你也在使用 PaperMod，下面的内容可能对你有帮助。</p>
<p>环境信息：</p>
<ul>
<li>Hugo 版本: v0.147.2+extended</li>
<li>PaperMod 版本: v8.0</li>
</ul>
<h2 id="优化主页个人信息展示">优化主页个人信息展示</h2>
<p>对 PaperMod 的 Home-Info 布局做了优化，增加了头像展示和图标悬浮高亮效果，支持响应式布局。</p>
<p><img alt="Home-Info" decoding="async" height="802" loading="lazy" sizes="(max-width: 768px) 100vw, (max-width: 1024px) 768px, 1024px" src="/posts/2025/10/08/hugo-paper-mod/home-info.png" srcset="/posts/2025/10/08/hugo-paper-mod/home-info_hu_ca6da4c1588377d3.png 384w, /posts/2025/10/08/hugo-paper-mod/home-info_hu_4e0c74c88e4a0dc0.png 768w, /posts/2025/10/08/hugo-paper-mod/home-info_hu_474bed0cc640a2da.png 1024w, /posts/2025/10/08/hugo-paper-mod/home-info_hu_57a1888004a472ba.png 1536w, /posts/2025/10/08/hugo-paper-mod/home-info.png 2206w" style="max-width: 100%; height: auto; aspect-ratio: 2.7506;" width="2206"></p>
<ol>
<li>创建 <code>layouts/partials/home_info.html</code> 文件：</li>
</ol>
<details>
<summary>点击展开完整代码</summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">{{- with site.Params.homeInfoParams }}
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">article</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;first-entry home-info&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;home-info-container home-info-main-container&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;home-info-content-wrapper&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            {{- with site.Params.homeInfoParams }}
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;home-info-avatar home-info-avatar-container&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                {{- if .ImageUrl -}}
</span></span><span class="line"><span class="cl">                {{- $imgSrc := .ImageUrl | absURL }}
</span></span><span class="line"><span class="cl">                {{- $img := resources.Get .ImageUrl }}
</span></span><span class="line"><span class="cl">                {{- if $img }}
</span></span><span class="line"><span class="cl">                {{- $size := printf &#34;%dx%d&#34; (.ImageWidth | default 100) (.ImageHeight | default 100) }}
</span></span><span class="line"><span class="cl">                {{- $img = $img.Resize $size }}
</span></span><span class="line"><span class="cl">                {{- $imgSrc = $img.Permalink }}
</span></span><span class="line"><span class="cl">                {{- end }}
</span></span><span class="line"><span class="cl">                <span class="p">&lt;</span><span class="nt">img</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;home-info-avatar&#34;</span> 
</span></span><span class="line"><span class="cl">                     <span class="na">draggable</span><span class="o">=</span><span class="s">&#34;false&#34;</span> 
</span></span><span class="line"><span class="cl">                     <span class="na">src</span><span class="o">=</span><span class="s">&#34;{{ $imgSrc }}&#34;</span> 
</span></span><span class="line"><span class="cl">                     <span class="na">alt</span><span class="o">=</span><span class="s">&#34;{{ .Title | default &#34;</span><span class="na">profile</span> <span class="na">image</span><span class="err">&#34;</span> <span class="err">}}&#34;</span> 
</span></span><span class="line"><span class="cl">                     <span class="na">height</span><span class="o">=</span><span class="s">&#34;{{ .ImageHeight | default 100 }}&#34;</span> 
</span></span><span class="line"><span class="cl">                     <span class="na">width</span><span class="o">=</span><span class="s">&#34;{{ .ImageWidth | default 100 }}&#34;</span> 
</span></span><span class="line"><span class="cl">                     <span class="na">class</span><span class="o">=</span><span class="s">&#34;home-info-avatar-img&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">                {{- end }}
</span></span><span class="line"><span class="cl">            <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            {{- end }}
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;entry-main home-info-text-content&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;</span><span class="nt">header</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;entry-header&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span>{{ .Title | markdownify }}<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;/</span><span class="nt">header</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;entry-content&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                    {{ .Content | markdownify }}
</span></span><span class="line"><span class="cl">                <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">footer</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;entry-footer&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            {{ partial &#34;social_icons.html&#34; (dict &#34;align&#34; site.Params.homeInfoParams.AlignSocialIconsTo) }}
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">footer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">article</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{{- end -}}
</span></span></code></pre></div></details>
<ol start="2">
<li>在 <code>assets/css/extended/blank.css</code> 文件中添加样式：</li>
</ol>
<details>
<summary>点击展开完整代码</summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="c">/* Home Info Layout Styles */</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">home-info-main-container</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">display</span><span class="p">:</span> <span class="kc">flex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">flex-direction</span><span class="p">:</span> <span class="kc">column</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">gap</span><span class="p">:</span> <span class="mi">24</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">max-width</span><span class="p">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">home-info-content-wrapper</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">display</span><span class="p">:</span> <span class="kc">flex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">align-items</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">gap</span><span class="p">:</span> <span class="mi">32</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">home-info-avatar-container</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">display</span><span class="p">:</span> <span class="kc">flex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">align-items</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">justify-content</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">flex-shrink</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">position</span><span class="p">:</span> <span class="kc">relative</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">home-info-avatar-container</span><span class="p">::</span><span class="nd">after</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">content</span><span class="p">:</span> <span class="s1">&#39;&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">position</span><span class="p">:</span> <span class="kc">absolute</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">right</span><span class="p">:</span> <span class="mi">-16</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">top</span><span class="p">:</span> <span class="mi">50</span><span class="kt">%</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">transform</span><span class="p">:</span> <span class="nb">translateY</span><span class="p">(</span><span class="mi">-50</span><span class="kt">%</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">width</span><span class="p">:</span> <span class="mi">1</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">height</span><span class="p">:</span> <span class="mi">60</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">background-color</span><span class="p">:</span> <span class="mh">#e5e5e5</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">home-info-text-content</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">flex</span><span class="p">:</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">display</span><span class="p">:</span> <span class="kc">flex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">flex-direction</span><span class="p">:</span> <span class="kc">column</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">justify-content</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin-top</span><span class="p">:</span> <span class="mi">8</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">home-info-avatar-img</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">border-radius</span><span class="p">:</span> <span class="mi">50</span><span class="kt">%</span> <span class="cp">!important</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">border</span><span class="p">:</span> <span class="mi">2</span><span class="kt">px</span> <span class="kc">solid</span> <span class="mh">#f0f0f0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">transition</span><span class="p">:</span> <span class="k">transform</span> <span class="mf">0.2</span><span class="kt">s</span> <span class="kc">ease</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">home-info-avatar-img</span><span class="p">:</span><span class="nd">hover</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">transform</span><span class="p">:</span> <span class="nb">scale</span><span class="p">(</span><span class="mf">1.02</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* 响应式设计 */</span>
</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">media</span> <span class="o">(</span><span class="nt">max-width</span><span class="o">:</span> <span class="nt">768px</span><span class="o">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="nc">home-info-content-wrapper</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">flex-direction</span><span class="p">:</span> <span class="kc">column</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">gap</span><span class="p">:</span> <span class="mi">20</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">text-align</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="nc">home-info-text-content</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">margin-top</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c">/* 移动端隐藏分隔线 */</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="nc">home-info-avatar-container</span><span class="p">::</span><span class="nd">after</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">display</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c">/* 移动端社交图标居中 */</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="nc">home-info</span> <span class="p">.</span><span class="nc">entry-footer</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">display</span><span class="p">:</span> <span class="kc">flex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">justify-content</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">align-items</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* 图标悬浮高亮 */</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">social-icons</span> <span class="nt">svg</span><span class="p">:</span><span class="nd">hover</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">transition</span><span class="p">:</span> <span class="mf">0.15</span><span class="kt">s</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">social-icons</span> <span class="nt">a</span><span class="o">[</span><span class="nt">href</span><span class="o">*=</span><span class="s1">&#39;mailto&#39;</span><span class="o">]</span><span class="p">:</span><span class="nd">hover</span> <span class="nt">svg</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="mh">#ea4335</span> <span class="cp">!important</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">social-icons</span> <span class="nt">a</span><span class="o">[</span><span class="nt">href</span><span class="o">*=</span><span class="s1">&#39;github&#39;</span><span class="o">]</span><span class="p">:</span><span class="nd">hover</span> <span class="nt">svg</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="mh">#7c3aed</span> <span class="cp">!important</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">social-icons</span> <span class="nt">a</span><span class="o">[</span><span class="nt">href</span><span class="o">*=</span><span class="s1">&#39;index.xml&#39;</span><span class="o">]</span><span class="p">:</span><span class="nd">hover</span> <span class="nt">svg</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="mh">#ff6600</span> <span class="cp">!important</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></details>
<ol start="3">
<li>在 <code>config.yaml</code> 中配置头像地址（支持本地或远程图片）：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">params</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">homeInfoParams</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">Title</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;她和她的猫&#34;</span><span class="w">
</span></span></span><span class="line hl"><span class="cl"><span class="w">    </span><span class="nt">ImageUrl</span><span class="p">:</span><span class="w"> </span><span class="l">/images/avatar.jpeg</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">Content</span><span class="p">:</span><span class="w"> </span><span class="l">那一天，我被她抱回了家。从此以后，我成了她的猫。</span><span class="w">
</span></span></span></code></pre></div><h2 id="移除主页冗余分页">移除主页冗余分页</h2>
<p>在 PaperMod 主题中，默认会为主页的文章列表生成分页（/，/page/2/&hellip;），这导致主页和文章列表页面（/posts/，/posts/page/2/&hellip;）内容重复，产生了大量冗余页面。</p>
<p>为了解决这个问题，我修改了主页布局，让主页只展示最新的几篇文章，不再生成分页。可以通过「查看更多」按钮跳转到文章列表页面。</p>
<p><figure>
  <img alt="优化前后对比" decoding="async" height="378" loading="lazy" sizes="(max-width: 768px) 100vw, (max-width: 1024px) 768px, 1024px" src="/posts/2025/10/08/hugo-paper-mod/paginator-comparison.png" srcset="/posts/2025/10/08/hugo-paper-mod/paginator-comparison_hu_dad9796f6cf2c39.png 384w, /posts/2025/10/08/hugo-paper-mod/paginator-comparison_hu_974ec68bd5618795.png 768w, /posts/2025/10/08/hugo-paper-mod/paginator-comparison_hu_91454807c7ef5e85.png 1024w" style="max-width: 100%; height: auto; aspect-ratio: 3.5450;" title="Paginator pages 从 62 降到 43，减少了 19 个冗余页面" width="1340">
  <figcaption><p>Paginator pages 从 62 降到 43，减少了 19 个冗余页面</p></figcaption>
</figure></p>
<p>创建 <code>layouts/index.html</code> 文件，覆盖主题的首页模板。</p>
<details>
<summary>点击展开完整代码</summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go-template" data-lang="go-template"><span class="line"><span class="cl"><span class="cp">{{-</span><span class="w"> </span><span class="k">define</span><span class="w"> </span><span class="s">&#34;main&#34;</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nx">site</span><span class="na">.Params.profileMode.enabled</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">partial</span><span class="w"> </span><span class="s">&#34;index_profile.html&#34;</span><span class="w"> </span><span class="na">.</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="cp">}}</span><span class="x"> </span><span class="cm">{{/* if not profileMode */}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="na">.Content</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;div class=&#34;post-content&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="o">(</span><span class="na">.Param</span><span class="w"> </span><span class="s">&#34;disableAnchoredHeadings&#34;</span><span class="o">)</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">partial</span><span class="w"> </span><span class="s">&#34;anchored_headings.html&#34;</span><span class="w"> </span><span class="na">.Content</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="cp">}}{{</span><span class="w"> </span><span class="na">.Content</span><span class="w"> </span><span class="cp">}}{{</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$pages</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">where</span><span class="w"> </span><span class="nx">site</span><span class="na">.RegularPages</span><span class="w"> </span><span class="s">&#34;Type&#34;</span><span class="w"> </span><span class="s">&#34;in&#34;</span><span class="w"> </span><span class="nx">site</span><span class="na">.Params.mainSections</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$pages</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">where</span><span class="w"> </span><span class="nx">$pages</span><span class="w"> </span><span class="s">&#34;Params.hiddenInHomeList&#34;</span><span class="w"> </span><span class="s">&#34;!=&#34;</span><span class="w"> </span><span class="s">&#34;true&#34;</span><span class="w">  </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nx">site</span><span class="na">.Params.homeInfoParams</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">partial</span><span class="w"> </span><span class="s">&#34;home_info.html&#34;</span><span class="w"> </span><span class="na">.</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$displayPages</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">first</span><span class="w"> </span><span class="nx">3</span><span class="w"> </span><span class="nx">$pages</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">$index</span><span class="o">,</span><span class="w"> </span><span class="nx">$page</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">$displayPages</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$class</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="s">&#34;post-entry&#34;</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$user_preferred</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">or</span><span class="w"> </span><span class="nx">site</span><span class="na">.Params.disableSpecial1stPost</span><span class="w"> </span><span class="nx">site</span><span class="na">.Params.homeInfoParams</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="o">(</span><span class="k">and</span><span class="w"> </span><span class="o">(</span><span class="k">eq</span><span class="w"> </span><span class="nx">$index</span><span class="w"> </span><span class="nx">0</span><span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="k">not</span><span class="w"> </span><span class="nx">$user_preferred</span><span class="o">))</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$class</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;first-entry&#34;</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;article class=&#34;</span><span class="cp">{{</span><span class="w"> </span><span class="nx">$class</span><span class="w"> </span><span class="cp">}}</span><span class="x">&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$isHidden</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">(</span><span class="na">.Param</span><span class="w"> </span><span class="s">&#34;cover.hiddenInList&#34;</span><span class="o">)</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">default</span><span class="w"> </span><span class="o">(</span><span class="na">.Param</span><span class="w"> </span><span class="s">&#34;cover.hidden&#34;</span><span class="o">)</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">default</span><span class="w"> </span><span class="k">false</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">partial</span><span class="w"> </span><span class="s">&#34;cover.html&#34;</span><span class="w"> </span><span class="o">(</span><span class="nx">dict</span><span class="w"> </span><span class="s">&#34;cxt&#34;</span><span class="w"> </span><span class="na">.</span><span class="w"> </span><span class="s">&#34;IsSingle&#34;</span><span class="w"> </span><span class="k">false</span><span class="w"> </span><span class="s">&#34;isHidden&#34;</span><span class="w"> </span><span class="nx">$isHidden</span><span class="o">)</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  &lt;header class=&#34;entry-header&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">    &lt;h2 class=&#34;entry-hint-parent&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">      </span><span class="cp">{{-</span><span class="w"> </span><span class="na">.Title</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">      </span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="na">.Draft</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">      &lt;span class=&#34;entry-hint&#34; title=&#34;Draft&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">        &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; height=&#34;20&#34; viewBox=&#34;0 -960 960 960&#34; fill=&#34;currentColor&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">          &lt;path
</span></span></span><span class="line"><span class="cl"><span class="x">            d=&#34;M160-410v-60h300v60H160Zm0-165v-60h470v60H160Zm0-165v-60h470v60H160Zm360 580v-123l221-220q9-9 20-13t22-4q12 0 23 4.5t20 13.5l37 37q9 9 13 20t4 22q0 11-4.5 22.5T862.09-380L643-160H520Zm300-263-37-37 37 37ZM580-220h38l121-122-18-19-19-18-122 121v38Zm141-141-19-18 37 37-18-19Z&#34; /&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">        &lt;/svg&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">      &lt;/span&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">      </span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">    &lt;/h2&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">  &lt;/header&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="o">(</span><span class="k">ne</span><span class="w"> </span><span class="o">(</span><span class="na">.Param</span><span class="w"> </span><span class="s">&#34;hideSummary&#34;</span><span class="o">)</span><span class="w"> </span><span class="k">true</span><span class="o">)</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  &lt;div class=&#34;entry-content&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">    &lt;p&gt;</span><span class="cp">{{</span><span class="w"> </span><span class="na">.Summary</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">plainify</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">htmlUnescape</span><span class="w"> </span><span class="cp">}}{{</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="na">.Truncated</span><span class="w"> </span><span class="cp">}}</span><span class="x">...</span><span class="cp">{{</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">&lt;/p&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">  &lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="o">(</span><span class="na">.Param</span><span class="w"> </span><span class="s">&#34;hideMeta&#34;</span><span class="o">)</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  &lt;footer class=&#34;entry-footer&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">partial</span><span class="w"> </span><span class="s">&#34;post_meta.html&#34;</span><span class="w"> </span><span class="na">.</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  &lt;/footer&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  &lt;a class=&#34;entry-link&#34; aria-label=&#34;post link to </span><span class="cp">{{</span><span class="w"> </span><span class="na">.Title</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">plainify</span><span class="w"> </span><span class="cp">}}</span><span class="x">&#34; href=&#34;</span><span class="cp">{{</span><span class="w"> </span><span class="na">.Permalink</span><span class="w"> </span><span class="cp">}}</span><span class="x">&#34;&gt;&lt;/a&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;/article&gt;
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">gt</span><span class="w"> </span><span class="o">(</span><span class="k">len</span><span class="w"> </span><span class="nx">$pages</span><span class="o">)</span><span class="w"> </span><span class="nx">3</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;footer class=&#34;page-footer&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">  &lt;nav class=&#34;pagination&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">    &lt;a class=&#34;next&#34; href=&#34;https://her-cat.com/posts/&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">      查看更多&amp;nbsp;»
</span></span></span><span class="line"><span class="cl"><span class="x">    &lt;/a&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">  &lt;/nav&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;/footer&gt;
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="cm">{{/* end profileMode */}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="cm">{{- /* end main */ -}}</span><span class="x"> 
</span></span></span></code></pre></div></details>
<h2 id="优化文章列表布局">优化文章列表布局</h2>
<p>PaperMod 的文章列表默认是图片在上、文字在下，看着信息密度很低。</p>
<p>调整了很久之后，最后选择了左右布局：文字内容在左，封面图片在右，既增加了视觉吸引力，又保持了页面的简洁性。</p>
<p><figure>
  <img alt="文章列表封面前后对比" decoding="async" height="1336" loading="lazy" sizes="(max-width: 768px) 100vw, (max-width: 1024px) 768px, 1024px" src="/posts/2025/10/08/hugo-paper-mod/cover.png" srcset="/posts/2025/10/08/hugo-paper-mod/cover_hu_75b7661d37679fed.png 384w, /posts/2025/10/08/hugo-paper-mod/cover_hu_5b630b5e74d05256.png 768w, /posts/2025/10/08/hugo-paper-mod/cover_hu_9477c6152c18e533.png 1024w, /posts/2025/10/08/hugo-paper-mod/cover_hu_d1b7cd2886fa0328.png 1536w, /posts/2025/10/08/hugo-paper-mod/cover.png 2388w" style="max-width: 100%; height: auto; aspect-ratio: 1.7874;" title="左：修改前，右：修改后" width="2388">
  <figcaption><p>左：修改前，右：修改后</p></figcaption>
</figure></p>
<ol>
<li>创建 <code>layouts/_default/list.html</code> 文件：</li>
</ol>
<details>
<summary>点击展开完整代码</summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go-template" data-lang="go-template"><span class="line"><span class="cl"><span class="cp">{{-</span><span class="w"> </span><span class="k">define</span><span class="w"> </span><span class="s">&#34;main&#34;</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="o">(</span><span class="k">and</span><span class="w"> </span><span class="nx">site</span><span class="na">.Params.profileMode.enabled</span><span class="w"> </span><span class="na">.IsHome</span><span class="o">)</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">partial</span><span class="w"> </span><span class="s">&#34;index_profile.html&#34;</span><span class="w"> </span><span class="na">.</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="cp">}}</span><span class="x"> </span><span class="cm">{{/* if not profileMode */}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="na">.IsHome</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">and</span><span class="w"> </span><span class="na">.Title</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;header class=&#34;page-header&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">partial</span><span class="w"> </span><span class="s">&#34;breadcrumbs.html&#34;</span><span class="w"> </span><span class="na">.</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  &lt;h1&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cp">{{</span><span class="w"> </span><span class="na">.Title</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">and</span><span class="w"> </span><span class="o">(</span><span class="k">or</span><span class="w"> </span><span class="o">(</span><span class="k">eq</span><span class="w"> </span><span class="na">.Kind</span><span class="w"> </span><span class="s">`term`</span><span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="k">eq</span><span class="w"> </span><span class="na">.Kind</span><span class="w"> </span><span class="s">`section`</span><span class="o">))</span><span class="w"> </span><span class="o">(</span><span class="na">.Param</span><span class="w"> </span><span class="s">&#34;ShowRssButtonInSectionTermList&#34;</span><span class="o">)</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cp">{{-</span><span class="w"> </span><span class="k">with</span><span class="w"> </span><span class="na">.OutputFormats.Get</span><span class="w"> </span><span class="s">&#34;rss&#34;</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">    &lt;a href=&#34;</span><span class="cp">{{</span><span class="w"> </span><span class="na">.RelPermalink</span><span class="w"> </span><span class="cp">}}</span><span class="x">&#34; title=&#34;RSS&#34; aria-label=&#34;RSS&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">      &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-width=&#34;2&#34;
</span></span></span><span class="line"><span class="cl"><span class="x">        stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; height=&#34;23&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">        &lt;path d=&#34;M4 11a9 9 0 0 1 9 9&#34; /&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">        &lt;path d=&#34;M4 4a16 16 0 0 1 16 16&#34; /&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">        &lt;circle cx=&#34;5&#34; cy=&#34;19&#34; r=&#34;1&#34; /&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">      &lt;/svg&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">    &lt;/a&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  &lt;/h1&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="na">.Description</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  &lt;div class=&#34;post-description&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cp">{{</span><span class="w"> </span><span class="na">.Description</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">markdownify</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  &lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;/header&gt;
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="na">.Content</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;div class=&#34;post-content&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="o">(</span><span class="na">.Param</span><span class="w"> </span><span class="s">&#34;disableAnchoredHeadings&#34;</span><span class="o">)</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">partial</span><span class="w"> </span><span class="s">&#34;anchored_headings.html&#34;</span><span class="w"> </span><span class="na">.Content</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="cp">}}{{</span><span class="w"> </span><span class="na">.Content</span><span class="w"> </span><span class="cp">}}{{</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$pages</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">union</span><span class="w"> </span><span class="na">.RegularPages</span><span class="w"> </span><span class="na">.Sections</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="na">.IsHome</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$pages</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">where</span><span class="w"> </span><span class="nx">site</span><span class="na">.RegularPages</span><span class="w"> </span><span class="s">&#34;Type&#34;</span><span class="w"> </span><span class="s">&#34;in&#34;</span><span class="w"> </span><span class="nx">site</span><span class="na">.Params.mainSections</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$pages</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">where</span><span class="w"> </span><span class="nx">$pages</span><span class="w"> </span><span class="s">&#34;Params.hiddenInHomeList&#34;</span><span class="w"> </span><span class="s">&#34;!=&#34;</span><span class="w"> </span><span class="s">&#34;true&#34;</span><span class="w">  </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$paginator</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="na">.Paginate</span><span class="w"> </span><span class="nx">$pages</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">and</span><span class="w"> </span><span class="na">.IsHome</span><span class="w"> </span><span class="nx">site</span><span class="na">.Params.homeInfoParams</span><span class="w"> </span><span class="o">(</span><span class="k">eq</span><span class="w"> </span><span class="nx">$paginator</span><span class="na">.PageNumber</span><span class="w"> </span><span class="nx">1</span><span class="o">)</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">partial</span><span class="w"> </span><span class="s">&#34;home_info.html&#34;</span><span class="w"> </span><span class="na">.</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$term</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="na">.Data.Term</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">$index</span><span class="o">,</span><span class="w"> </span><span class="nx">$page</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">$paginator</span><span class="na">.Pages</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$class</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="s">&#34;post-entry&#34;</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$user_preferred</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">or</span><span class="w"> </span><span class="nx">site</span><span class="na">.Params.disableSpecial1stPost</span><span class="w"> </span><span class="nx">site</span><span class="na">.Params.homeInfoParams</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="o">(</span><span class="k">and</span><span class="w"> </span><span class="na">$.IsHome</span><span class="w"> </span><span class="o">(</span><span class="k">eq</span><span class="w"> </span><span class="nx">$paginator</span><span class="na">.PageNumber</span><span class="w"> </span><span class="nx">1</span><span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="k">eq</span><span class="w"> </span><span class="nx">$index</span><span class="w"> </span><span class="nx">0</span><span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="k">not</span><span class="w"> </span><span class="nx">$user_preferred</span><span class="o">))</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$class</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;first-entry&#34;</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nx">$term</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$class</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;post-entry tag-entry&#34;</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;article class=&#34;</span><span class="cp">{{</span><span class="w"> </span><span class="nx">$class</span><span class="w"> </span><span class="cp">}}</span><span class="x">&#34;&gt;
</span></span></span><span class="line hl"><span class="cl"><span class="x">    &lt;div class=&#34;entry-content-wrapper&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">        &lt;header class=&#34;entry-header&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">            &lt;h2 class=&#34;entry-hint-parent&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">                </span><span class="cp">{{-</span><span class="w"> </span><span class="na">.Title</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">                </span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="na">.Draft</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">                &lt;span class=&#34;entry-hint&#34; title=&#34;Draft&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">          &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; height=&#34;20&#34; viewBox=&#34;0 -960 960 960&#34; fill=&#34;currentColor&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">            &lt;path
</span></span></span><span class="line"><span class="cl"><span class="x">                    d=&#34;M160-410v-60h300v60H160Zm0-165v-60h470v60H160Zm0-165v-60h470v60H160Zm360 580v-123l221-220q9-9 20-13t22-4q12 0 23 4.5t20 13.5l37 37q9 9 13 20t4 22q0 11-4.5 22.5T862.09-380L643-160H520Zm300-263-37-37 37 37ZM580-220h38l121-122-18-19-19-18-122 121v38Zm141-141-19-18 37 37-18-19Z&#34; /&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">          &lt;/svg&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">        &lt;/span&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">                </span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">            &lt;/h2&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">        &lt;/header&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">        </span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="o">(</span><span class="k">ne</span><span class="w"> </span><span class="o">(</span><span class="na">.Param</span><span class="w"> </span><span class="s">&#34;hideSummary&#34;</span><span class="o">)</span><span class="w"> </span><span class="k">true</span><span class="o">)</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">        &lt;div class=&#34;entry-content&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">            &lt;p&gt;</span><span class="cp">{{</span><span class="w"> </span><span class="na">.Summary</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">plainify</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">htmlUnescape</span><span class="w"> </span><span class="cp">}}{{</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="na">.Truncated</span><span class="w"> </span><span class="cp">}}</span><span class="x">...</span><span class="cp">{{</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">&lt;/p&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">        &lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">        </span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">        </span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="o">(</span><span class="na">.Param</span><span class="w"> </span><span class="s">&#34;hideMeta&#34;</span><span class="o">)</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">        &lt;footer class=&#34;entry-footer&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">            </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">partial</span><span class="w"> </span><span class="s">&#34;post_meta.html&#34;</span><span class="w"> </span><span class="na">.</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">        &lt;/footer&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">        </span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line hl"><span class="cl"><span class="x">    &lt;/div&gt;
</span></span></span><span class="line hl"><span class="cl"><span class="x">    &lt;div class=&#34;entry-cover-wrapper&#34;&gt;
</span></span></span><span class="line hl"><span class="cl"><span class="x">        </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$isHidden</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">(</span><span class="na">.Param</span><span class="w"> </span><span class="s">&#34;cover.hiddenInList&#34;</span><span class="o">)</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">default</span><span class="w"> </span><span class="o">(</span><span class="na">.Param</span><span class="w"> </span><span class="s">&#34;cover.hidden&#34;</span><span class="o">)</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">default</span><span class="w"> </span><span class="k">false</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line hl"><span class="cl"><span class="x">        </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">partial</span><span class="w"> </span><span class="s">&#34;cover.html&#34;</span><span class="w"> </span><span class="o">(</span><span class="nx">dict</span><span class="w"> </span><span class="s">&#34;cxt&#34;</span><span class="w"> </span><span class="na">.</span><span class="w"> </span><span class="s">&#34;IsSingle&#34;</span><span class="w"> </span><span class="k">false</span><span class="w"> </span><span class="s">&#34;isHidden&#34;</span><span class="w"> </span><span class="nx">$isHidden</span><span class="o">)</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">    &lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">    &lt;a class=&#34;entry-link&#34; aria-label=&#34;post link to </span><span class="cp">{{</span><span class="w"> </span><span class="na">.Title</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">plainify</span><span class="w"> </span><span class="cp">}}</span><span class="x">&#34; href=&#34;</span><span class="cp">{{</span><span class="w"> </span><span class="na">.Permalink</span><span class="w"> </span><span class="cp">}}</span><span class="x">&#34;&gt;&lt;/a&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;/article&gt;
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">gt</span><span class="w"> </span><span class="nx">$paginator</span><span class="na">.TotalPages</span><span class="w"> </span><span class="nx">1</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;footer class=&#34;page-footer&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">  &lt;nav class=&#34;pagination&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nx">$paginator</span><span class="na">.HasPrev</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">    &lt;a class=&#34;prev&#34; href=&#34;</span><span class="cp">{{</span><span class="w"> </span><span class="nx">$paginator</span><span class="na">.Prev.URL</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">absURL</span><span class="w"> </span><span class="cp">}}</span><span class="x">&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">      «&amp;nbsp;</span><span class="cp">{{</span><span class="w"> </span><span class="nx">i18n</span><span class="w"> </span><span class="s">&#34;prev_page&#34;</span><span class="w"> </span><span class="cp">}}</span><span class="x">&amp;nbsp;
</span></span></span><span class="line"><span class="cl"><span class="x">      </span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="o">(</span><span class="na">.Param</span><span class="w"> </span><span class="s">&#34;ShowPageNums&#34;</span><span class="o">)</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">      </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">sub</span><span class="w"> </span><span class="nx">$paginator</span><span class="na">.PageNumber</span><span class="w"> </span><span class="nx">1</span><span class="w"> </span><span class="cp">}}</span><span class="x">/</span><span class="cp">{{</span><span class="w"> </span><span class="nx">$paginator</span><span class="na">.TotalPages</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">      </span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">    &lt;/a&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nx">$paginator</span><span class="na">.HasNext</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">    &lt;a class=&#34;next&#34; href=&#34;</span><span class="cp">{{</span><span class="w"> </span><span class="nx">$paginator</span><span class="na">.Next.URL</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">absURL</span><span class="w"> </span><span class="cp">}}</span><span class="x">&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">      </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">i18n</span><span class="w"> </span><span class="s">&#34;next_page&#34;</span><span class="w"> </span><span class="cp">}}</span><span class="x">&amp;nbsp;
</span></span></span><span class="line"><span class="cl"><span class="x">      </span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="o">(</span><span class="na">.Param</span><span class="w"> </span><span class="s">&#34;ShowPageNums&#34;</span><span class="o">)</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">      </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">add</span><span class="w"> </span><span class="nx">1</span><span class="w"> </span><span class="nx">$paginator</span><span class="na">.PageNumber</span><span class="w"> </span><span class="cp">}}</span><span class="x">/</span><span class="cp">{{</span><span class="w"> </span><span class="nx">$paginator</span><span class="na">.TotalPages</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">      </span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">&amp;nbsp;»
</span></span></span><span class="line"><span class="cl"><span class="x">    &lt;/a&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  &lt;/nav&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;/footer&gt;
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="cm">{{/* end profileMode */}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="cm">{{- /* end main */ -}}</span><span class="x">
</span></span></span></code></pre></div></details>
<ol start="2">
<li>如果你按照「<a href="/posts/2025/10/08/hugo-paper-mod/#%E7%A7%BB%E9%99%A4%E4%B8%BB%E9%A1%B5%E5%86%97%E4%BD%99%E5%88%86%E9%A1%B5">移除主页冗余分页</a>」的步骤，创建了 <code>layouts/index.html</code> 文件，那么也需要修改该文件中文章列表的部分，修改内容与上面一样。否则可以直接跳过这一步骤。</li>
</ol>
<details>
<summary>点击展开完整代码</summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go-template" data-lang="go-template"><span class="line"><span class="cl"><span class="cp">{{-</span><span class="w"> </span><span class="k">define</span><span class="w"> </span><span class="s">&#34;main&#34;</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nx">site</span><span class="na">.Params.profileMode.enabled</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">partial</span><span class="w"> </span><span class="s">&#34;index_profile.html&#34;</span><span class="w"> </span><span class="na">.</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="cp">}}</span><span class="x"> </span><span class="cm">{{/* if not profileMode */}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="na">.Content</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;div class=&#34;post-content&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="o">(</span><span class="na">.Param</span><span class="w"> </span><span class="s">&#34;disableAnchoredHeadings&#34;</span><span class="o">)</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">partial</span><span class="w"> </span><span class="s">&#34;anchored_headings.html&#34;</span><span class="w"> </span><span class="na">.Content</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="cp">}}{{</span><span class="w"> </span><span class="na">.Content</span><span class="w"> </span><span class="cp">}}{{</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$pages</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">where</span><span class="w"> </span><span class="nx">site</span><span class="na">.RegularPages</span><span class="w"> </span><span class="s">&#34;Type&#34;</span><span class="w"> </span><span class="s">&#34;in&#34;</span><span class="w"> </span><span class="nx">site</span><span class="na">.Params.mainSections</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$pages</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">where</span><span class="w"> </span><span class="nx">$pages</span><span class="w"> </span><span class="s">&#34;Params.hiddenInHomeList&#34;</span><span class="w"> </span><span class="s">&#34;!=&#34;</span><span class="w"> </span><span class="s">&#34;true&#34;</span><span class="w">  </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nx">site</span><span class="na">.Params.homeInfoParams</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">partial</span><span class="w"> </span><span class="s">&#34;home_info.html&#34;</span><span class="w"> </span><span class="na">.</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$displayPages</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">first</span><span class="w"> </span><span class="nx">3</span><span class="w"> </span><span class="nx">$pages</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">$index</span><span class="o">,</span><span class="w"> </span><span class="nx">$page</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">$displayPages</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$class</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="s">&#34;post-entry&#34;</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$user_preferred</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">or</span><span class="w"> </span><span class="nx">site</span><span class="na">.Params.disableSpecial1stPost</span><span class="w"> </span><span class="nx">site</span><span class="na">.Params.homeInfoParams</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="o">(</span><span class="k">and</span><span class="w"> </span><span class="o">(</span><span class="k">eq</span><span class="w"> </span><span class="nx">$index</span><span class="w"> </span><span class="nx">0</span><span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="k">not</span><span class="w"> </span><span class="nx">$user_preferred</span><span class="o">))</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$class</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;first-entry&#34;</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;article class=&#34;</span><span class="cp">{{</span><span class="w"> </span><span class="nx">$class</span><span class="w"> </span><span class="cp">}}</span><span class="x">&#34;&gt;
</span></span></span><span class="line hl"><span class="cl"><span class="x">    &lt;div class=&#34;entry-content-wrapper&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">        &lt;header class=&#34;entry-header&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">            &lt;h2 class=&#34;entry-hint-parent&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">                </span><span class="cp">{{-</span><span class="w"> </span><span class="na">.Title</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">                </span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="na">.Draft</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">                &lt;span class=&#34;entry-hint&#34; title=&#34;Draft&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">          &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; height=&#34;20&#34; viewBox=&#34;0 -960 960 960&#34; fill=&#34;currentColor&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">            &lt;path
</span></span></span><span class="line"><span class="cl"><span class="x">                    d=&#34;M160-410v-60h300v60H160Zm0-165v-60h470v60H160Zm0-165v-60h470v60H160Zm360 580v-123l221-220q9-9 20-13t22-4q12 0 23 4.5t20 13.5l37 37q9 9 13 20t4 22q0 11-4.5 22.5T862.09-380L643-160H520Zm300-263-37-37 37 37ZM580-220h38l121-122-18-19-19-18-122 121v38Zm141-141-19-18 37 37-18-19Z&#34; /&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">          &lt;/svg&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">        &lt;/span&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">                </span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">            &lt;/h2&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">        &lt;/header&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">        </span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="o">(</span><span class="k">ne</span><span class="w"> </span><span class="o">(</span><span class="na">.Param</span><span class="w"> </span><span class="s">&#34;hideSummary&#34;</span><span class="o">)</span><span class="w"> </span><span class="k">true</span><span class="o">)</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">        &lt;div class=&#34;entry-content&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">            &lt;p&gt;</span><span class="cp">{{</span><span class="w"> </span><span class="na">.Summary</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">plainify</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">htmlUnescape</span><span class="w"> </span><span class="cp">}}{{</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="na">.Truncated</span><span class="w"> </span><span class="cp">}}</span><span class="x">...</span><span class="cp">{{</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">&lt;/p&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">        &lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">        </span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">        </span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="o">(</span><span class="na">.Param</span><span class="w"> </span><span class="s">&#34;hideMeta&#34;</span><span class="o">)</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">        &lt;footer class=&#34;entry-footer&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">            </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">partial</span><span class="w"> </span><span class="s">&#34;post_meta.html&#34;</span><span class="w"> </span><span class="na">.</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">        &lt;/footer&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">        </span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line hl"><span class="cl"><span class="x">    &lt;/div&gt;
</span></span></span><span class="line hl"><span class="cl"><span class="x">    &lt;div class=&#34;entry-cover-wrapper&#34;&gt;
</span></span></span><span class="line hl"><span class="cl"><span class="x">        </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$isHidden</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">(</span><span class="na">.Param</span><span class="w"> </span><span class="s">&#34;cover.hiddenInList&#34;</span><span class="o">)</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">default</span><span class="w"> </span><span class="o">(</span><span class="na">.Param</span><span class="w"> </span><span class="s">&#34;cover.hidden&#34;</span><span class="o">)</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">default</span><span class="w"> </span><span class="k">false</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line hl"><span class="cl"><span class="x">        </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">partial</span><span class="w"> </span><span class="s">&#34;cover.html&#34;</span><span class="w"> </span><span class="o">(</span><span class="nx">dict</span><span class="w"> </span><span class="s">&#34;cxt&#34;</span><span class="w"> </span><span class="na">.</span><span class="w"> </span><span class="s">&#34;IsSingle&#34;</span><span class="w"> </span><span class="k">false</span><span class="w"> </span><span class="s">&#34;isHidden&#34;</span><span class="w"> </span><span class="nx">$isHidden</span><span class="o">)</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line hl"><span class="cl"><span class="x">    &lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">    &lt;a class=&#34;entry-link&#34; aria-label=&#34;post link to </span><span class="cp">{{</span><span class="w"> </span><span class="na">.Title</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">plainify</span><span class="w"> </span><span class="cp">}}</span><span class="x">&#34; href=&#34;</span><span class="cp">{{</span><span class="w"> </span><span class="na">.Permalink</span><span class="w"> </span><span class="cp">}}</span><span class="x">&#34;&gt;&lt;/a&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;/article&gt;
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">gt</span><span class="w"> </span><span class="o">(</span><span class="k">len</span><span class="w"> </span><span class="nx">$pages</span><span class="o">)</span><span class="w"> </span><span class="nx">3</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;footer class=&#34;page-footer&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">  &lt;nav class=&#34;pagination&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">    &lt;a class=&#34;next&#34; href=&#34;https://her-cat.com/posts/&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">      查看更多&amp;nbsp;»
</span></span></span><span class="line"><span class="cl"><span class="x">    &lt;/a&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">  &lt;/nav&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;/footer&gt;
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="cm">{{/* end profileMode */}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="cm">{{- /* end main */ -}}</span><span class="x"> 
</span></span></span></code></pre></div></details>
<ol start="3">
<li>在 <code>assets/css/extended/blank.css</code> 中添加相关样式：</li>
</ol>
<details>
<summary>点击展开完整代码</summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="c">/* === 文章列表左右布局样式 === */</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-entry</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">display</span><span class="p">:</span> <span class="kc">flex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">gap</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">gap</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">align-items</span><span class="p">:</span> <span class="kc">stretch</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* 左侧内容区域 */</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-entry</span> <span class="p">.</span><span class="nc">entry-content-wrapper</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">flex</span><span class="p">:</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">display</span><span class="p">:</span> <span class="kc">flex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">flex-direction</span><span class="p">:</span> <span class="kc">column</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">min-width</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* 右侧图片区域 */</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-entry</span> <span class="p">.</span><span class="nc">entry-cover-wrapper</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">flex-shrink</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">width</span><span class="p">:</span> <span class="mi">225</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* 隐藏无图片容器 */</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-entry</span> <span class="p">.</span><span class="nc">entry-cover-wrapper</span><span class="p">:</span><span class="nd">empty</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-entry</span> <span class="p">.</span><span class="nc">entry-cover-wrapper</span><span class="p">:</span><span class="nd">not</span><span class="o">(</span><span class="p">:</span><span class="nd">has</span><span class="o">(</span><span class="p">.</span><span class="nc">entry-cover</span><span class="o">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">display</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* 图片样式 - 统一宽高比 */</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-entry</span> <span class="p">.</span><span class="nc">entry-cover</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin-bottom</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">aspect-ratio</span><span class="p">:</span> <span class="mi">2</span> <span class="o">/</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">overflow</span><span class="p">:</span> <span class="kc">hidden</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-entry</span> <span class="p">.</span><span class="nc">entry-cover</span> <span class="nt">img</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-entry</span> <span class="p">.</span><span class="nc">entry-cover</span> <span class="nt">picture</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-entry</span> <span class="p">.</span><span class="nc">entry-cover</span> <span class="nt">picture</span> <span class="nt">img</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">width</span><span class="p">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">height</span><span class="p">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">object-fit</span><span class="p">:</span> <span class="kc">cover</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">border-radius</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">radius</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* 内容间距 */</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-entry</span> <span class="p">.</span><span class="nc">entry-header</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin-bottom</span><span class="p">:</span> <span class="mi">8</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-entry</span> <span class="p">.</span><span class="nc">entry-content</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin</span><span class="p">:</span> <span class="mi">0</span> <span class="mi">0</span> <span class="mi">8</span><span class="kt">px</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-entry</span> <span class="p">.</span><span class="nc">entry-footer</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin-top</span><span class="p">:</span> <span class="kc">auto</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* 移动端 */</span>
</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">media</span> <span class="o">(</span><span class="nt">max-width</span><span class="o">:</span> <span class="nt">640px</span><span class="o">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c">/* 移动端隐藏封面图片 */</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="nc">post-entry</span> <span class="p">.</span><span class="nc">entry-cover-wrapper</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">display</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></details>
<ol start="4">
<li>在 <code>config.yaml</code> 中启用封面：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">cover</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">hiddenInList</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w"> </span><span class="c"># 在文章列表和首页显示封面</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">hiddenInSingle</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w"> </span><span class="c"># 在单页隐藏封面</span><span class="w">
</span></span></span></code></pre></div><h2 id="解决中文字数统计问题">解决中文字数统计问题</h2>
<p>Hugo 默认的字数统计对中日韩（CJK）文字不准确，需要在 <code>config.yaml</code> 中开启 <code>hasCJKLanguage</code> 选项：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">hasCJKLanguage</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span></code></pre></div><h2 id="解决图片加载抖动cls问题">解决图片加载抖动（CLS）问题</h2>
<p>使用 <a href="https://pagespeed.web.dev/">PageSpeed Insights</a> 检测博客时，发现 CLS（Cumulative Layout Shift，累积布局偏移）分数偏高，页面加载时图片会造成明显的抖动现象。</p>
<p>CLS 是 Google 评估网站用户体验的重要指标之一，分数过高通常是因为图片加载时浏览器不知道应该预留多大的空间，等图片加载完成后就会把下面的内容挤下去，导致页面跳动。</p>
<p>解决办法就是为图片添加正确的宽高属性，让浏览器在加载前预留空间。</p>
<p>创建 <code>layouts/_default/_markup/render-image.html</code> 文件：</p>
<details>
<summary>点击展开完整代码</summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go-template" data-lang="go-template"><span class="line"><span class="cl"><span class="cp">{{-</span><span class="w"> </span><span class="nx">$u</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">urls</span><span class="na">.Parse</span><span class="w"> </span><span class="na">.Destination</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$src</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">$u</span><span class="na">.String</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$img</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="s">&#34;&#34;</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$width</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="s">&#34;&#34;</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$height</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="s">&#34;&#34;</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$aspectRatio</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="s">&#34;&#34;</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="nx">$u</span><span class="na">.IsAbs</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$path</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">strings</span><span class="na">.TrimPrefix</span><span class="w"> </span><span class="s">&#34;./&#34;</span><span class="w"> </span><span class="nx">$u</span><span class="na">.Path</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cm">{{- /* 查找图片：优先页面资源，其次 assets 目录 */ -}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$img</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">or</span><span class="w"> </span><span class="o">(</span><span class="na">.PageInner.Resources.Get</span><span class="w"> </span><span class="nx">$path</span><span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="nx">resources</span><span class="na">.Get</span><span class="w"> </span><span class="o">(</span><span class="nx">strings</span><span class="na">.TrimPrefix</span><span class="w"> </span><span class="s">&#34;/&#34;</span><span class="w"> </span><span class="nx">$path</span><span class="o">))</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nx">$img</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cm">{{- /* 获取图片基本信息 */ -}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$src</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">$img</span><span class="na">.RelPermalink</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">    
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cm">{{- /* 只对栅格图片获取宽高，SVG 跳过 */ -}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">ne</span><span class="w"> </span><span class="nx">$img</span><span class="na">.MediaType.SubType</span><span class="w"> </span><span class="s">&#34;svg&#34;</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">      </span><span class="cm">{{- /* 确保宽高有效（大于 0） */ -}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">      </span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">and</span><span class="w"> </span><span class="o">(</span><span class="k">gt</span><span class="w"> </span><span class="nx">$img</span><span class="na">.Width</span><span class="w"> </span><span class="nx">0</span><span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="k">gt</span><span class="w"> </span><span class="nx">$img</span><span class="na">.Height</span><span class="w"> </span><span class="nx">0</span><span class="o">)</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">        </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">printf</span><span class="w"> </span><span class="s">&#34;%d&#34;</span><span class="w"> </span><span class="nx">$img</span><span class="na">.Width</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">        </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$height</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">printf</span><span class="w"> </span><span class="s">&#34;%d&#34;</span><span class="w"> </span><span class="nx">$img</span><span class="na">.Height</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">        </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$aspectRatio</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">printf</span><span class="w"> </span><span class="s">&#34;%.4f&#34;</span><span class="w"> </span><span class="o">(</span><span class="nx">div</span><span class="w"> </span><span class="o">(</span><span class="nx">float</span><span class="w"> </span><span class="nx">$img</span><span class="na">.Width</span><span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="nx">float</span><span class="w"> </span><span class="nx">$img</span><span class="na">.Height</span><span class="o">))</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">      </span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">    
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cm">{{- /* 保留原始 URL 的 query 和 fragment */ -}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cp">{{-</span><span class="w"> </span><span class="k">with</span><span class="w"> </span><span class="nx">$u</span><span class="na">.RawQuery</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">      </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$src</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">printf</span><span class="w"> </span><span class="s">&#34;%s?%s&#34;</span><span class="w"> </span><span class="nx">$src</span><span class="w"> </span><span class="na">.</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cp">{{-</span><span class="w"> </span><span class="k">with</span><span class="w"> </span><span class="nx">$u</span><span class="na">.Fragment</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">      </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$src</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">printf</span><span class="w"> </span><span class="s">&#34;%s#%s&#34;</span><span class="w"> </span><span class="nx">$src</span><span class="w"> </span><span class="na">.</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cm">{{- /* 如果找不到，保持原始路径（static 目录） */ -}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$src</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">$u</span><span class="na">.String</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cm">{{- /* 设置基础属性 */ -}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$attributes</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">dict</span><span class="w"> </span><span class="s">&#34;alt&#34;</span><span class="w"> </span><span class="na">.Text</span><span class="w"> </span><span class="s">&#34;src&#34;</span><span class="w"> </span><span class="nx">$src</span><span class="w"> </span><span class="s">&#34;loading&#34;</span><span class="w"> </span><span class="s">&#34;lazy&#34;</span><span class="w"> </span><span class="s">&#34;decoding&#34;</span><span class="w"> </span><span class="s">&#34;async&#34;</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cm">{{- /* 添加 title 属性（如果存在） */ -}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">with</span><span class="w"> </span><span class="na">.Title</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$attributes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">merge</span><span class="w"> </span><span class="nx">$attributes</span><span class="w"> </span><span class="o">(</span><span class="nx">dict</span><span class="w"> </span><span class="s">&#34;title&#34;</span><span class="w"> </span><span class="o">(</span><span class="na">.</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">transform</span><span class="na">.HTMLEscape</span><span class="o">))</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cm">{{- /* 如果获取到了尺寸信息，设置宽高和宽高比 */ -}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">and</span><span class="w"> </span><span class="nx">$width</span><span class="w"> </span><span class="nx">$height</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$attributes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">merge</span><span class="w"> </span><span class="nx">$attributes</span><span class="w"> </span><span class="o">(</span><span class="nx">dict</span><span class="w"> </span><span class="s">&#34;width&#34;</span><span class="w"> </span><span class="nx">$width</span><span class="w"> </span><span class="s">&#34;height&#34;</span><span class="w"> </span><span class="nx">$height</span><span class="o">)</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$style</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">printf</span><span class="w"> </span><span class="s">&#34;max-width: 100%%; height: auto; aspect-ratio: %s;&#34;</span><span class="w"> </span><span class="nx">$aspectRatio</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$attributes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">merge</span><span class="w"> </span><span class="nx">$attributes</span><span class="w"> </span><span class="o">(</span><span class="nx">dict</span><span class="w"> </span><span class="s">&#34;style&#34;</span><span class="w"> </span><span class="nx">$style</span><span class="o">)</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cm">{{- /* 如果没有尺寸信息，至少保持响应式 */ -}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$attributes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">merge</span><span class="w"> </span><span class="nx">$attributes</span><span class="w"> </span><span class="o">(</span><span class="nx">dict</span><span class="w"> </span><span class="s">&#34;style&#34;</span><span class="w"> </span><span class="s">&#34;max-width: 100%; height: auto;&#34;</span><span class="o">)</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cm">{{- /* 合并用户自定义属性 */ -}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$attributes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">merge</span><span class="w"> </span><span class="na">.Attributes</span><span class="w"> </span><span class="nx">$attributes</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="na">.Title</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;figure&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">  &lt;img
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cp">{{-</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">$k</span><span class="o">,</span><span class="w"> </span><span class="nx">$v</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">$attributes</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">      </span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nx">$v</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">        </span><span class="cp">{{-</span><span class="w"> </span><span class="k">printf</span><span class="w"> </span><span class="s">&#34; %s=%q&#34;</span><span class="w"> </span><span class="nx">$k</span><span class="w"> </span><span class="nx">$v</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">safeHTMLAttr</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">      </span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">-}}</span><span class="x">&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">  &lt;figcaption&gt;&lt;p&gt;</span><span class="cp">{{</span><span class="w"> </span><span class="na">.Title</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">markdownify</span><span class="w"> </span><span class="cp">}}</span><span class="x">&lt;/p&gt;&lt;/figcaption&gt;
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;/figure&gt;
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">&lt;img
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">$k</span><span class="o">,</span><span class="w"> </span><span class="nx">$v</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">$attributes</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nx">$v</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">      </span><span class="cp">{{-</span><span class="w"> </span><span class="k">printf</span><span class="w"> </span><span class="s">&#34; %s=%q&#34;</span><span class="w"> </span><span class="nx">$k</span><span class="w"> </span><span class="nx">$v</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">safeHTMLAttr</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">    </span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">  </span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">-}}</span><span class="x">&gt;
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span></code></pre></div></details>
<blockquote>
<p>提示：图片需要放在文章同目录（Page Bundle）或 <code>assets</code> 目录下，否则无法自动获取尺寸。</p></blockquote>
<h2 id="优化阅读体验">优化阅读体验</h2>
<p>对字体、排版、间距等进行了优化，主要借鉴了 <a href="https://dvel.me/posts/hugo-papermod-config/#markdown-%e6%b8%b2%e6%9f%93%e9%a3%8e%e6%a0%bc">Dvel</a> 和 <a href="https://atpx.com/blog/improving-online-reading-experience/">atpX</a> 的博客。</p>
<p>首先引入 Inter 字体，在 <code>layouts/partials/extend_head.html</code> 中添加：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!-- Inter 字体引入 --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;preconnect&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://fonts.googleapis.com&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;preconnect&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://fonts.gstatic.com&#34;</span> <span class="na">crossorigin</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://fonts.googleapis.com/css2?family=Inter:wght@400;700&amp;display=swap&#34;</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>接下来创建 <code>assets/css/extended/reading.css</code> 文件，定义详细的样式规则来优化文章排版、代码块、表格等元素的显示效果：</p>
<details>
<summary>点击展开完整代码</summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="c">/* === 1. CSS 变量定义 === */</span>
</span></span><span class="line"><span class="cl"><span class="p">:</span><span class="nd">root</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c">/* 颜色 */</span>
</span></span><span class="line"><span class="cl">    <span class="nv">--primary</span><span class="p">:</span> <span class="mh">#1a1b1c</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">--content</span><span class="p">:</span> <span class="mh">#333435</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">--secondary</span><span class="p">:</span> <span class="mh">#666</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">--sec-color</span><span class="p">:</span> <span class="mh">#f2f3f4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">--link-color</span><span class="p">:</span> <span class="mh">#2d8cdc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">--code-bg</span><span class="p">:</span> <span class="mh">#f5f5f5</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">--sec-note-color</span><span class="p">:</span> <span class="mh">#6e6e6e</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c">/* 字体 */</span>
</span></span><span class="line"><span class="cl">    <span class="nv">--font-fallback</span><span class="p">:</span> <span class="o">-</span><span class="n">apple-system</span><span class="p">,</span> <span class="n">BlinkMacSystemFont</span><span class="p">,</span> <span class="n">system-ui</span><span class="p">,</span> <span class="kc">sans-serif</span><span class="p">,</span> <span class="s1">&#39;Color Emoji&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">--font-family</span><span class="p">:</span> <span class="s1">&#39;Inter&#39;</span><span class="p">,</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">font</span><span class="o">-</span><span class="n">fallback</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nv">--code-font-family</span><span class="p">:</span> <span class="s1">&#39;Fira Code&#39;</span><span class="p">,</span> <span class="n">Menlo</span><span class="p">,</span> <span class="s1">&#39;Lucida Console&#39;</span><span class="p">,</span> <span class="s1">&#39;DejaVu Sans Mono&#39;</span><span class="p">,</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">font</span><span class="o">-</span><span class="n">fallback</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* 暗色模式 */</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">dark</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nv">--primary</span><span class="p">:</span> <span class="mh">#f2f2f2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">--content</span><span class="p">:</span> <span class="mh">#e3e3e3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">--sec-color</span><span class="p">:</span> <span class="mh">#2A2C2B</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nv">--sec-note-color</span><span class="p">:</span> <span class="mh">#808080</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* === 2. 全局字体设置 === */</span>
</span></span><span class="line"><span class="cl"><span class="nt">body</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-family</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">font</span><span class="o">-</span><span class="n">family</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="mi">18</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* 标题字重 */</span>
</span></span><span class="line"><span class="cl"><span class="nt">h1</span><span class="o">,</span> <span class="nt">h2</span><span class="o">,</span> <span class="nt">h3</span><span class="o">,</span> <span class="nt">h4</span><span class="o">,</span> <span class="nt">h5</span><span class="o">,</span> <span class="nt">h6</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-weight</span><span class="p">:</span> <span class="mi">700</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* 代码字体 */</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">code</span><span class="o">,</span> 
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">code</span> <span class="nt">span</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-family</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">code</span><span class="o">-</span><span class="n">font</span><span class="o">-</span><span class="n">family</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* === 3. 文章标题样式 === */</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-title</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="mi">34</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin</span><span class="p">:</span> <span class="mi">8</span><span class="kt">px</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">h1</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">h2</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">h3</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">h4</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">h5</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">h6</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin-bottom</span><span class="p">:</span> <span class="mi">18</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-weight</span><span class="p">:</span> <span class="mi">600</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">h1</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin-top</span><span class="p">:</span> <span class="mi">48</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">padding-bottom</span><span class="p">:</span> <span class="mi">13</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">border-bottom</span><span class="p">:</span> <span class="mi">1</span><span class="kt">px</span> <span class="kc">solid</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">sec</span><span class="o">-</span><span class="kc">color</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">h2</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="mi">24</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin-top</span><span class="p">:</span> <span class="mi">48</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">padding-bottom</span><span class="p">:</span> <span class="mi">13</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">border-bottom</span><span class="p">:</span> <span class="mi">1</span><span class="kt">px</span> <span class="kc">solid</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">sec</span><span class="o">-</span><span class="kc">color</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">h3</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="mi">22</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin-top</span><span class="p">:</span> <span class="mi">32</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">h4</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="mi">20</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin-top</span><span class="p">:</span> <span class="mi">23</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">h5</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="mi">16</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin-top</span><span class="p">:</span> <span class="mi">18</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">h6</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="mi">14</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin-top</span><span class="p">:</span> <span class="mi">16</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* === 4. 正文样式 === */</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">line-height</span><span class="p">:</span> <span class="mf">1.86</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">p</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">blockquote</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">figure</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">table</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin</span><span class="p">:</span> <span class="mi">18</span><span class="kt">px</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">blockquote</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">sec</span><span class="o">-</span><span class="n">note</span><span class="o">-</span><span class="kc">color</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">hr</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin</span><span class="p">:</span> <span class="mi">64</span><span class="kt">px</span> <span class="mi">128</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">ul</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">ol</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">dl</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">li</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin</span><span class="p">:</span> <span class="mi">8</span><span class="kt">px</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* === 5. 链接样式 === */</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">a</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">link</span><span class="o">-</span><span class="kc">color</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">box-shadow</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">text-decoration</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">a</span><span class="p">:</span><span class="nd">hover</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">text-decoration</span><span class="p">:</span> <span class="kc">underline</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* === 6. 行内代码样式 === */</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">code</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin</span><span class="p">:</span> <span class="kc">unset</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">padding</span><span class="p">:</span> <span class="mi">5</span><span class="kt">px</span> <span class="mi">7</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">border-radius</span><span class="p">:</span> <span class="mi">8</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* === 6.5. 折叠块样式 === */</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">details</span> <span class="nt">summary</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">cursor</span><span class="p">:</span> <span class="kc">zoom-in</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">user-select</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">details</span><span class="o">[</span><span class="nt">open</span><span class="o">]</span> <span class="nt">summary</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">cursor</span><span class="p">:</span> <span class="kc">zoom-out</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* === 7. 图片样式 === */</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">img</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin</span><span class="p">:</span> <span class="kc">auto</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">max-width</span><span class="p">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">height</span><span class="p">:</span> <span class="kc">auto</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">transition</span><span class="p">:</span> <span class="k">opacity</span> <span class="mf">0.3</span><span class="kt">s</span> <span class="kc">ease</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">figure</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin</span><span class="p">:</span> <span class="mi">27</span><span class="kt">px</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">text-align</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">figure</span> <span class="nt">img</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">border-radius</span><span class="p">:</span> <span class="mi">8</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">box-shadow</span><span class="p">:</span> <span class="mi">0</span> <span class="mi">4</span><span class="kt">px</span> <span class="mi">12</span><span class="kt">px</span> <span class="nb">rgba</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mf">0.1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">transition</span><span class="p">:</span> <span class="k">transform</span> <span class="mf">0.2</span><span class="kt">s</span> <span class="kc">ease</span><span class="p">,</span> <span class="k">box-shadow</span> <span class="mf">0.2</span><span class="kt">s</span> <span class="kc">ease</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">figure</span> <span class="nt">img</span><span class="p">:</span><span class="nd">hover</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">transform</span><span class="p">:</span> <span class="nb">translateY</span><span class="p">(</span><span class="mi">-2</span><span class="kt">px</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">box-shadow</span><span class="p">:</span> <span class="mi">0</span> <span class="mi">8</span><span class="kt">px</span> <span class="mi">24</span><span class="kt">px</span> <span class="nb">rgba</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mf">0.15</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-content</span> <span class="nt">figcaption</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin-top</span><span class="p">:</span> <span class="mi">9</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="mi">16</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">secondary</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-style</span><span class="p">:</span> <span class="kc">italic</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* === 8. 移动端响应式优化 === */</span>
</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">media</span> <span class="o">(</span><span class="nt">max-width</span><span class="o">:</span> <span class="nt">768px</span><span class="o">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="nc">post-content</span> <span class="nt">img</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">border-radius</span><span class="p">:</span> <span class="mi">4</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="nc">post-content</span> <span class="nt">figure</span> <span class="nt">img</span><span class="p">:</span><span class="nd">hover</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">transform</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* === 9. 防止滚动条导致页面抖动 === */</span>
</span></span><span class="line"><span class="cl"><span class="nt">html</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">overflow-y</span><span class="p">:</span> <span class="kc">scroll</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">:</span><span class="nd">root</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">overflow-y</span><span class="p">:</span> <span class="kc">auto</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">overflow-x</span><span class="p">:</span> <span class="kc">hidden</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">:</span><span class="nd">root</span> <span class="nt">body</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">position</span><span class="p">:</span> <span class="kc">absolute</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">width</span><span class="p">:</span> <span class="mi">100</span><span class="kt">vw</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">overflow</span><span class="p">:</span> <span class="kc">hidden</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></details>
<h2 id="字体本地化">字体本地化</h2>
<p>上面使用了 Inter 字体来优化阅读体验，但直接使用 Google Fonts 在国内访问会遇到加载缓慢甚至超时的问题，导致字体加载失败或页面渲染阻塞。因此需要将字体文件下载到本地托管，既能提升访问速度，也能保护用户隐私。</p>
<ol>
<li>创建 <code>download-fonts.py</code> 脚本，自动下载字体并生成本地 CSS：</li>
</ol>
<details>
<summary>点击展开完整代码</summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="ch">#!/usr/bin/env python3</span>
</span></span><span class="line"><span class="cl"><span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">下载 Google Fonts 到本地目录
</span></span></span><span class="line"><span class="cl"><span class="s2">使用方法: python3 download-fonts.py
</span></span></span><span class="line"><span class="cl"><span class="s2">&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">os</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">requests</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">urllib.parse</span> <span class="kn">import</span> <span class="n">urlparse</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 配置</span>
</span></span><span class="line"><span class="cl"><span class="n">GOOGLE_FONTS_URL</span> <span class="o">=</span> <span class="s2">&#34;https://fonts.googleapis.com/css2?family=Inter:wght@400;700&amp;display=swap&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">STATIC_DIR</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="s2">&#34;static&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">FONTS_DIR</span> <span class="o">=</span> <span class="n">STATIC_DIR</span> <span class="o">/</span> <span class="s2">&#34;fonts&#34;</span>  <span class="c1"># 字体文件放在 static 目录，Hugo 会自动复制</span>
</span></span><span class="line"><span class="cl"><span class="n">CSS_DIR</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="s2">&#34;assets&#34;</span><span class="p">)</span> <span class="o">/</span> <span class="s2">&#34;css&#34;</span> <span class="o">/</span> <span class="s2">&#34;extended&#34;</span>  <span class="c1"># CSS 放在 assets 目录</span>
</span></span><span class="line"><span class="cl"><span class="n">OUTPUT_CSS</span> <span class="o">=</span> <span class="n">CSS_DIR</span> <span class="o">/</span> <span class="s2">&#34;fonts.css&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 创建必要的目录</span>
</span></span><span class="line"><span class="cl"><span class="n">FONTS_DIR</span><span class="o">.</span><span class="n">mkdir</span><span class="p">(</span><span class="n">parents</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">exist_ok</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">CSS_DIR</span><span class="o">.</span><span class="n">mkdir</span><span class="p">(</span><span class="n">parents</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">exist_ok</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">download_file</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">dest_path</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;下载文件到指定路径&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;下载: </span><span class="si">{</span><span class="n">url</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">30</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span><span class="o">.</span><span class="n">raise_for_status</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">dest_path</span><span class="p">,</span> <span class="s1">&#39;wb&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;保存到: </span><span class="si">{</span><span class="n">dest_path</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">dest_path</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_google_fonts_css</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;获取 Google Fonts CSS&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;获取 Google Fonts CSS: </span><span class="si">{</span><span class="n">GOOGLE_FONTS_URL</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">headers</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="s1">&#39;User-Agent&#39;</span><span class="p">:</span> <span class="s1">&#39;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">response</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">GOOGLE_FONTS_URL</span><span class="p">,</span> <span class="n">headers</span><span class="o">=</span><span class="n">headers</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">30</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span><span class="o">.</span><span class="n">raise_for_status</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">response</span><span class="o">.</span><span class="n">text</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">extract_font_urls</span><span class="p">(</span><span class="n">css_content</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;从 CSS 中提取字体文件 URL&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">pattern</span> <span class="o">=</span> <span class="sa">r</span><span class="s1">&#39;url\((https://[^)]+)\)&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">urls</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="n">pattern</span><span class="p">,</span> <span class="n">css_content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">urls</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">download_fonts</span><span class="p">(</span><span class="n">css_content</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;下载所有字体文件并替换 CSS 中的 URL&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">font_urls</span> <span class="o">=</span> <span class="n">extract_font_urls</span><span class="p">(</span><span class="n">css_content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="ow">not</span> <span class="n">font_urls</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;❌ 未找到字体文件 URL&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">css_content</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;找到 </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">font_urls</span><span class="p">)</span><span class="si">}</span><span class="s2"> 个字体文件&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">idx</span><span class="p">,</span> <span class="n">url</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">font_urls</span><span class="p">,</span> <span class="mi">1</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">parsed</span> <span class="o">=</span> <span class="n">urlparse</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">url_path</span> <span class="o">=</span> <span class="n">parsed</span><span class="o">.</span><span class="n">path</span>
</span></span><span class="line"><span class="cl">        <span class="n">ext</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">splitext</span><span class="p">(</span><span class="n">url_path</span><span class="p">)[</span><span class="mi">1</span><span class="p">]</span> <span class="ow">or</span> <span class="s1">&#39;.woff2&#39;</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="n">filename</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34;inter-</span><span class="si">{</span><span class="n">idx</span><span class="si">}{</span><span class="n">ext</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">local_path</span> <span class="o">=</span> <span class="n">FONTS_DIR</span> <span class="o">/</span> <span class="n">filename</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">download_file</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">local_path</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">relative_url</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34;/fonts/</span><span class="si">{</span><span class="n">filename</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="n">css_content</span> <span class="o">=</span> <span class="n">css_content</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">relative_url</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;❌ 下载失败: </span><span class="si">{</span><span class="n">url</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;   错误: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">css_content</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">generate_local_css</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;生成本地字体 CSS 文件&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span> <span class="o">+</span> <span class="s2">&#34;=&#34;</span><span class="o">*</span><span class="mi">50</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;开始下载 Google Fonts&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;=&#34;</span><span class="o">*</span><span class="mi">50</span> <span class="o">+</span> <span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">css_content</span> <span class="o">=</span> <span class="n">get_google_fonts_css</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;✅ 成功获取 CSS (长度: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">css_content</span><span class="p">)</span><span class="si">}</span><span class="s2"> 字节)</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="n">local_css</span> <span class="o">=</span> <span class="n">download_fonts</span><span class="p">(</span><span class="n">css_content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="n">header</span> <span class="o">=</span> <span class="s2">&#34;&#34;&#34;/* 
</span></span></span><span class="line"><span class="cl"><span class="s2"> * Google Fonts - Inter
</span></span></span><span class="line"><span class="cl"><span class="s2"> * 本地托管版本，自动生成于 download-fonts.py
</span></span></span><span class="line"><span class="cl"><span class="s2"> */
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">local_css</span> <span class="o">=</span> <span class="n">header</span> <span class="o">+</span> <span class="n">local_css</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">OUTPUT_CSS</span><span class="p">,</span> <span class="s1">&#39;w&#39;</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s1">&#39;utf-8&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">local_css</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">✅ CSS 文件已保存到: </span><span class="si">{</span><span class="n">OUTPUT_CSS</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;✅ 字体文件已保存到: </span><span class="si">{</span><span class="n">FONTS_DIR</span><span class="si">}</span><span class="s2">/&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">重新构建网站: hugo --gc --minify</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">    <span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">❌ 错误: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="kn">import</span> <span class="nn">traceback</span>
</span></span><span class="line"><span class="cl">        <span class="n">traceback</span><span class="o">.</span><span class="n">print_exc</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&#34;__main__&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="kn">import</span> <span class="nn">requests</span>
</span></span><span class="line"><span class="cl">    <span class="k">except</span> <span class="ne">ImportError</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;❌ 请先安装 requests 库: pip install requests&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">generate_local_css</span><span class="p">()</span>
</span></span></code></pre></div></details>
<ol start="2">
<li>运行脚本</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 1. 安装依赖</span>
</span></span><span class="line"><span class="cl">pip install requests
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 2. 运行脚本</span>
</span></span><span class="line"><span class="cl">python3 download-fonts.py
</span></span></code></pre></div><p>脚本会自动：</p>
<ul>
<li>下载字体文件到 <code>static/fonts/</code> 目录</li>
<li>生成本地 CSS 到 <code>assets/css/extended/fonts.css</code></li>
<li>Hugo 会自动加载 <code>extended</code> 目录中的 CSS</li>
</ul>
<ol start="3">
<li>更新 <code>layouts/partials/extend_head.html</code>，移除 Google Fonts 的引用，改用本地字体预加载：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="cl"><span class="gd">- &lt;!-- Inter 字体引入 --&gt;
</span></span></span><span class="line"><span class="cl"><span class="gd">- &lt;link rel=&#34;preconnect&#34; href=&#34;https://fonts.googleapis.com&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="gd">- &lt;link rel=&#34;preconnect&#34; href=&#34;https://fonts.gstatic.com&#34; crossorigin&gt;
</span></span></span><span class="line"><span class="cl"><span class="gd">- &lt;link href=&#34;https://fonts.googleapis.com/css2?family=Inter:wght@400;700&amp;display=swap&#34; rel=&#34;stylesheet&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="gd"></span><span class="gi">+ &lt;!-- 预加载关键字体文件，提升首屏渲染速度 --&gt;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ &lt;link rel=&#34;preload&#34; href=&#34;https://her-cat.com/fonts/inter-7.woff2&#34; as=&#34;font&#34; type=&#34;font/woff2&#34; crossorigin&gt;
</span></span></span></code></pre></div><p>通过 <code>preload</code> 提示浏览器优先加载最常用的字体文件（<code>inter-7.woff2</code>），避免字体加载延迟导致的页面重排。</p>
<h2 id="显示文章所属的分类和系列">显示文章所属的分类和系列</h2>
<p>为了方便快速查看相关内容，我在文章标题下方的元数据区域增加了分类和系列的链接。</p>
<ol>
<li>创建 <code>layouts/partials/post_meta.html</code> 文件：</li>
</ol>
<details>
<summary>点击展开完整代码</summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go-template" data-lang="go-template"><span class="line"><span class="cl"><span class="cp">{{-</span><span class="w"> </span><span class="nx">$scratch</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">newScratch</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="na">.Date.IsZero</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$scratch</span><span class="na">.Add</span><span class="w"> </span><span class="s">&#34;meta&#34;</span><span class="w"> </span><span class="o">(</span><span class="k">slice</span><span class="w"> </span><span class="o">(</span><span class="k">printf</span><span class="w"> </span><span class="s">&#34;&lt;span title=&#39;%s&#39;&gt;%s&lt;/span&gt;&#34;</span><span class="w"> </span><span class="o">(</span><span class="na">.Date</span><span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="na">.Date</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">time</span><span class="na">.Format</span><span class="w"> </span><span class="o">(</span><span class="nx">default</span><span class="w"> </span><span class="s">&#34;January 2, 2006&#34;</span><span class="w"> </span><span class="nx">site</span><span class="na">.Params.DateFormat</span><span class="o">))))</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="o">(</span><span class="na">.Param</span><span class="w"> </span><span class="s">&#34;ShowReadingTime&#34;</span><span class="o">)</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$scratch</span><span class="na">.Add</span><span class="w"> </span><span class="s">&#34;meta&#34;</span><span class="w"> </span><span class="o">(</span><span class="k">slice</span><span class="w"> </span><span class="o">(</span><span class="nx">i18n</span><span class="w"> </span><span class="s">&#34;read_time&#34;</span><span class="w"> </span><span class="na">.ReadingTime</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">default</span><span class="w"> </span><span class="o">(</span><span class="k">printf</span><span class="w"> </span><span class="s">&#34;%d min&#34;</span><span class="w"> </span><span class="na">.ReadingTime</span><span class="o">)))</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="o">(</span><span class="na">.Param</span><span class="w"> </span><span class="s">&#34;ShowWordCount&#34;</span><span class="o">)</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$scratch</span><span class="na">.Add</span><span class="w"> </span><span class="s">&#34;meta&#34;</span><span class="w"> </span><span class="o">(</span><span class="k">slice</span><span class="w"> </span><span class="o">(</span><span class="nx">i18n</span><span class="w"> </span><span class="s">&#34;words&#34;</span><span class="w"> </span><span class="na">.WordCount</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">default</span><span class="w"> </span><span class="o">(</span><span class="k">printf</span><span class="w"> </span><span class="s">&#34;%d words&#34;</span><span class="w"> </span><span class="na">.WordCount</span><span class="o">)))</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="o">(</span><span class="na">.Param</span><span class="w"> </span><span class="s">&#34;hideAuthor&#34;</span><span class="o">)</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">with</span><span class="w"> </span><span class="o">(</span><span class="nx">partial</span><span class="w"> </span><span class="s">&#34;author.html&#34;</span><span class="w"> </span><span class="na">.</span><span class="o">)</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$scratch</span><span class="na">.Add</span><span class="w"> </span><span class="s">&#34;meta&#34;</span><span class="w"> </span><span class="o">(</span><span class="k">slice</span><span class="w"> </span><span class="na">.</span><span class="o">)</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$categories</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="na">.Language.Params.Taxonomies.category</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">default</span><span class="w"> </span><span class="s">&#34;categories&#34;</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">with</span><span class="w"> </span><span class="o">(</span><span class="na">$.GetTerms</span><span class="w"> </span><span class="nx">$categories</span><span class="o">)</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$categoryLinks</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">slice</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="na">.</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$categoryLinks</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">$categoryLinks</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">append</span><span class="w"> </span><span class="o">(</span><span class="k">printf</span><span class="w"> </span><span class="s">&#34;&lt;a href=\&#34;%s\&#34;&gt;%s&lt;/a&gt;&#34;</span><span class="w"> </span><span class="na">.Permalink</span><span class="w"> </span><span class="na">.LinkTitle</span><span class="o">)</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$categoryString</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">delimit</span><span class="w"> </span><span class="nx">$categoryLinks</span><span class="w"> </span><span class="s">&#34;&amp;nbsp;&#34;</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">safeHTML</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$scratch</span><span class="na">.Add</span><span class="w"> </span><span class="s">&#34;meta&#34;</span><span class="w"> </span><span class="o">(</span><span class="k">slice</span><span class="w"> </span><span class="o">(</span><span class="nx">string</span><span class="w"> </span><span class="nx">$categoryString</span><span class="o">))</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$series</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="na">.Language.Params.Taxonomies.series</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">default</span><span class="w"> </span><span class="s">&#34;series&#34;</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">with</span><span class="w"> </span><span class="o">(</span><span class="na">$.GetTerms</span><span class="w"> </span><span class="nx">$series</span><span class="o">)</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$seriesLinks</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">slice</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="na">.</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$seriesLinks</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">$seriesLinks</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">append</span><span class="w"> </span><span class="o">(</span><span class="k">printf</span><span class="w"> </span><span class="s">&#34;&lt;a href=\&#34;%s\&#34;&gt;%s&lt;/a&gt;&#34;</span><span class="w"> </span><span class="na">.Permalink</span><span class="w"> </span><span class="na">.LinkTitle</span><span class="o">)</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$seriesString</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">delimit</span><span class="w"> </span><span class="nx">$seriesLinks</span><span class="w"> </span><span class="s">&#34;&amp;nbsp;&#34;</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">safeHTML</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">$scratch</span><span class="na">.Add</span><span class="w"> </span><span class="s">&#34;meta&#34;</span><span class="w"> </span><span class="o">(</span><span class="k">slice</span><span class="w"> </span><span class="o">(</span><span class="nx">string</span><span class="w"> </span><span class="nx">$seriesString</span><span class="o">))</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">with</span><span class="w"> </span><span class="o">(</span><span class="nx">$scratch</span><span class="na">.Get</span><span class="w"> </span><span class="s">&#34;meta&#34;</span><span class="o">)</span><span class="w"> </span><span class="cp">}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="nx">delimit</span><span class="w"> </span><span class="na">.</span><span class="w"> </span><span class="s">&#34;&amp;nbsp;·&amp;nbsp;&#34;</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nx">safeHTML</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span><span class="line"><span class="cl"><span class="x"></span><span class="cp">{{-</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">-}}</span><span class="x">
</span></span></span></code></pre></div></details>
<ol start="2">
<li>在 <code>assets/css/extended/blank.css</code> 中添加以下样式：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-meta</span> <span class="nt">a</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">archive-meta</span> <span class="nt">a</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">entry-footer</span> <span class="nt">a</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">secondary</span><span class="p">)</span> <span class="cp">!important</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">text-decoration</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">transition</span><span class="p">:</span> <span class="kc">color</span> <span class="mf">0.2</span><span class="kt">s</span> <span class="kc">ease</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">post-meta</span> <span class="nt">a</span><span class="p">:</span><span class="nd">hover</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">archive-meta</span> <span class="nt">a</span><span class="p">:</span><span class="nd">hover</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">entry-footer</span> <span class="nt">a</span><span class="p">:</span><span class="nd">hover</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">primary</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">text-decoration</span><span class="p">:</span> <span class="kc">underline</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="使用-waline-评论系统">使用 Waline 评论系统</h2>
<p><a href="https://waline.js.org">Waline</a> 是一款简洁、安全的评论系统，提供了多种部署方式。在体验了 LeanCloud 和 Vercel 这两种无服务部署方式后，发现速度太慢，最后用 Docker 自建了服务。</p>
<h3 id="部署服务端">部署服务端</h3>
<p>如果你也想使用 Docker 部署服务端，可以参考我的 <code>docker-compose.yaml</code>：</p>
<details>
<summary>点击展开完整配置</summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">waline</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">container_name</span><span class="p">:</span><span class="w"> </span><span class="l">waline</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">lizheming/waline:latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">always</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="m">8360</span><span class="p">:</span><span class="m">8360</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">${PWD}/data:/app/data</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c"># 时区设置</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">TZ</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;Asia/Shanghai&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c"># 数据库配置（使用 SQLite）</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">SQLITE_PATH</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;/app/data&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c"># 用户认证配置</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">JWT_TOKEN</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;用户登录密钥，随机字符串即可&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c"># 站点信息</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">SITE_NAME</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;她和她的猫&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">SITE_URL</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;https://her-cat.com&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">SECURE_DOMAINS</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;her-cat.com&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c"># 邮件通知配置</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">AUTHOR_EMAIL</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;hxhsoft@foxmail.com&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">SMTP_SERVICE</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;163&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">SMTP_USER</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;hercat2025@163.com&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">SMTP_PASS</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;邮箱密码&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">SMTP_SECURE</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;true&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">SENDER_NAME</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;她和她的猫&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c"># 隐私保护配置</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">DISABLE_USERAGENT</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">DISABLE_REGION</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c"># 头像服务配置</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">GRAVATAR_STR</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;https://cn.cravatar.com/avatar/{{mail|lower|trim|md5}}?s=150&amp;d=retro&#39;</span><span class="w">
</span></span></span></code></pre></div></details>
<blockquote>
<p>提示：记得将上面的配置中的站点信息、邮箱账号和密码替换成你自己的。<code>JWT_TOKEN</code> 使用任意随机字符串即可。</p></blockquote>
<p>然后执行 <code>docker-compose up -d</code> 启动服务，Waline 会自动创建 SQLite 数据库并监听 8360 端口。</p>
<p><strong>关于头像服务的选择</strong></p>
<p>在 <code>GRAVATAR_STR</code> 环境变量中，我使用 <a href="https://cravatar.com/">Cravatar</a> 代替了 Waline 默认的 Libravatar。Libravatar 加载头像时会返回 302 重定向，这个 302 状态码会导致<a href="https://web.developers.google.cn/articles/bfcache?hl=zh-cn">后退/前进缓存</a>失效。Cravatar 是国内镜像服务，访问速度快且直接返回头像，没有重定向问题。</p>
<h3 id="集成客户端">集成客户端</h3>
<p>服务端配置完成后，接下来需要在博客中集成 Waline 的前端组件。</p>
<p>创建 <code>layouts/partials/comments.html</code> 文件：</p>
<details>
<summary>点击展开完整代码</summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">noscript</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;text-align: center; padding: 20px; color: var(--secondary);&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>💬 评论功能需要启用 JavaScript 才能使用<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">noscript</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;waline&#34;</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;margin-top: 30px;&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// 使用 IntersectionObserver 在评论区域接近可视区域时才加载
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">walineElement</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">&#39;waline&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">loaded</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">function</span> <span class="nx">loadWaline</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="nx">loaded</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="nx">loaded</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      
</span></span><span class="line"><span class="cl">      <span class="c1">// 动态加载 CSS
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="kr">const</span> <span class="nx">link</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">&#39;link&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="nx">link</span><span class="p">.</span><span class="nx">rel</span> <span class="o">=</span> <span class="s1">&#39;stylesheet&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="nx">link</span><span class="p">.</span><span class="nx">href</span> <span class="o">=</span> <span class="s1">&#39;https://unpkg.com/@waline/client@v3/dist/waline.css&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="nb">document</span><span class="p">.</span><span class="nx">head</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">link</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      
</span></span><span class="line"><span class="cl">      <span class="c1">// 动态加载 JS
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="kr">import</span><span class="p">(</span><span class="s1">&#39;https://unpkg.com/@waline/client@v3/dist/waline.js&#39;</span><span class="p">).</span><span class="nx">then</span><span class="p">(({</span> <span class="nx">init</span> <span class="p">})</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">init</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">          <span class="nx">el</span><span class="o">:</span> <span class="s1">&#39;#waline&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">serverURL</span><span class="o">:</span> <span class="s1">&#39;https://你的 Waline 服务端地址&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">reaction</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">imageUploader</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">search</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">lang</span><span class="o">:</span> <span class="s1">&#39;zh-CN&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">dark</span><span class="o">:</span> <span class="s1">&#39;body[class=&#34;dark&#34;]&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">emoji</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;https://unpkg.com/@waline/emojis@1.2.0/alus&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;/images/waline/emoji/huaji&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">      <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 如果支持 IntersectionObserver，在元素接近可视区域时加载
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="s1">&#39;IntersectionObserver&#39;</span> <span class="k">in</span> <span class="nb">window</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">observer</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">IntersectionObserver</span><span class="p">((</span><span class="nx">entries</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">entries</span><span class="p">.</span><span class="nx">forEach</span><span class="p">((</span><span class="nx">entry</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">if</span> <span class="p">(</span><span class="nx">entry</span><span class="p">.</span><span class="nx">isIntersecting</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">loadWaline</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">            <span class="nx">observer</span><span class="p">.</span><span class="nx">disconnect</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">          <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">rootMargin</span><span class="o">:</span> <span class="s1">&#39;200px&#39;</span> <span class="c1">// 提前 200px 开始加载
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="p">});</span>
</span></span><span class="line"><span class="cl">      
</span></span><span class="line"><span class="cl">      <span class="nx">observer</span><span class="p">.</span><span class="nx">observe</span><span class="p">(</span><span class="nx">walineElement</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// 降级方案：延迟加载
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="nx">setTimeout</span><span class="p">(</span><span class="nx">loadWaline</span><span class="p">,</span> <span class="mi">1000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">})();</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div></details>
<p>在上面的代码中，针对 <a href="https://pagespeed.web.dev/">PageSpeed Insights</a> 的性能检测做了优化，使用 <code>IntersectionObserver</code> 实现懒加载，只有当滚动到评论区附近时才动态加载 CSS 和 JS 资源，不影响首屏渲染。</p>
<p>更多配置项请参考 <a href="https://waline.js.org/reference/client/props.html">Waline 客户端配置文档</a>。如果你也想使用滑稽表情包，可以参考 <a href="https://github.com/qwqcode/huaji">qwqcode/huaji</a> 和我的 <a href="https://her-cat.com/images/waline/emoji/huaji/info.json">info.json</a>。</p>
<h2 id="添加图片画廊组件">添加图片画廊组件</h2>
<p>PaperMod 主题对图片的支持比较基础，文章中的图片既不能点击放大查看，也不能排列布局。为了优化一下体验，我找到了 <a href="https://github.com/mfg92/hugo-shortcode-gallery">mfg92/hugo-shortcode-gallery</a> 这个项目，它可以在文章中以画廊形式展示图片，支持响应式布局和点击预览。</p>
<p>具体效果可以看<a href="/posts/2025/06/30/chuanloo/">去长鹿旅游休博园</a>这篇文章。</p>
<ol>
<li>我使用 Git 子模块的方式安装（与主题的安装方式一致），在项目根目录中执行：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git submodule add https://github.com/mfg92/hugo-shortcode-gallery.git themes/hugo-shortcode-gallery
</span></span></code></pre></div><ol start="2">
<li>在 <code>config.yaml</code> 中将该组件添加到 <code>theme</code> 字段：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">theme</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">PaperMod, hugo-shortcode-gallery]</span><span class="w">
</span></span></span></code></pre></div><ol start="3">
<li>在文章中使用 <code>gallery</code> 加载图片：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="p">{{</span><span class="o">&lt;</span> <span class="n">gallery</span> <span class="n">match</span><span class="o">=</span><span class="s2">&#34;images/*&#34;</span> <span class="n">sortOrder</span><span class="o">=</span><span class="s2">&#34;asc&#34;</span> <span class="n">rowHeight</span><span class="o">=</span><span class="s2">&#34;150&#34;</span> <span class="n">margins</span><span class="o">=</span><span class="s2">&#34;5&#34;</span> <span class="n">thumbnailResizeOptions</span><span class="o">=</span><span class="s2">&#34;600x600 q90 Lanczos&#34;</span> <span class="n">previewType</span><span class="o">=</span><span class="s2">&#34;blur&#34;</span> <span class="n">embedPreview</span><span class="o">=</span><span class="bp">true</span> <span class="n">loadJQuery</span><span class="o">=</span><span class="bp">true</span> <span class="o">&gt;</span><span class="p">}}</span>
</span></span></code></pre></div><p>参数说明：</p>
<ul>
<li><code>match</code>: 图片路径匹配规则</li>
<li><code>rowHeight</code>: 缩略图行高</li>
<li><code>margins</code>: 图片间距</li>
<li><code>thumbnailResizeOptions</code>: 缩略图生成选项</li>
<li><code>previewType</code>: 预览效果（blur 为模糊过渡）</li>
</ul>
<p>需要注意的是，这个组件支持展示图片的 Exif 信息（默认关闭）。如果启用该功能，构建时会扫描所有图片并提取元数据，构建时间会随图片数量和大小显著增加。如果你的图片比较多，建议关闭以提升构建速度。</p>
<h2 id="更新日志">更新日志</h2>
<ul>
<li>2026-01-11：优化文章列表布局，封面图片改为右侧展示</li>
<li>2025-10-20：添加图片画廊组件</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>将博客迁移到又拍云</title>
      <link>https://her-cat.com/posts/2023/01/31/blog-migration-to-upyun/</link>
      <pubDate>Tue, 31 Jan 2023 23:55:06 +0800</pubDate>
      <guid>https://her-cat.com/posts/2023/01/31/blog-migration-to-upyun/</guid>
      <description>&lt;p&gt;考虑到使用一台独立的服务器来部署静态博客多少有点浪费，所以决定将博客迁移到又拍云上，一是可以节省服务器成本；二是可以利用又拍云多节点 CDN 提升博客的访问速度。之前为了将博客图片托管到又拍云上，参加了又拍云的联盟活动，每个月可以免费获得 10GB 存储空间 + 15GB CDN 流量，对于我这个小博客是完全够用的。&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>考虑到使用一台独立的服务器来部署静态博客多少有点浪费，所以决定将博客迁移到又拍云上，一是可以节省服务器成本；二是可以利用又拍云多节点 CDN 提升博客的访问速度。之前为了将博客图片托管到又拍云上，参加了又拍云的联盟活动，每个月可以免费获得 10GB 存储空间 + 15GB CDN 流量，对于我这个小博客是完全够用的。</p>
<h2 id="编写部署脚本">编写部署脚本</h2>
<p>首先，为了能够通过 Github Actions 将博客文件自动同步到又拍云上，需要写一个脚本来实现上传文件的逻辑。</p>
<p>在此之前，我已经写过一个这样的脚本，用来将博客图片同步到又拍云上。这次用 Go 重写了这个脚本，旧版本的脚本执行需要四十多秒，而新的脚本执行只需要三秒钟。</p>
<p>主要是因为使用了 Go 语言自带的协程和 Channel，并将上传文件的逻辑放在协程中，使得主流程不会被上传文件的动作阻塞，实现并发上传文件，从而达到提升执行速度的目的。</p>
<p>相关代码已经上传到 GitHub：<a href="https://github.com/her-cat/upyun-deployer">https://github.com/her-cat/upyun-deployer</a>，感兴趣的话可以看一下。</p>
<p>脚本的逻辑分为两部分：上传文件、清理已删除的文件和空目录。</p>
<h3 id="上传文件">上传文件</h3>
<p>上传文件时，先检查文件在又拍云存储库中是否已经存在，如果不存在则直接上传；存在的话就检查文件的 md5 值是否一致，一致则说明该文件没有被修改，不需要处理该文件，否则重新上传该文件。</p>
<h3 id="清理已删除的文件和目录">清理已删除的文件和目录</h3>
<p>当本地删除了某个文件后，又拍云的存储库要同步删除该文件；当目录中没有任何文件时，需要删除该目录。</p>
<p>为了实现这一点，在上传文件之前，我们需要从又拍云拿到所有的文件及目录的相对路径，并保存到 files 和 dirs 数组中。 每次上传本地文件时，先将本地文件的相对路径从 files 数组中删除，并将该路径对应的多级目录从 dirs 数组中删除。</p>
<p>当所有的本地文件都上传完毕后，files 和 dirs 数组中剩余的文件和目录就是我们要清理的内容，直接调用又拍云删除接口即可。</p>
<p>要注意的是，删除目录时，需要从最深层的目录开始删除，否则就会由于目录中存在子目录导致删除失败，即使子目录是空的。</p>
<p>比如有 <code>/a/b</code>、<code>/a</code>、<code>/a/b/c</code> 三个目录，一定要按照 <code>/a/b/c</code>、<code>/a/b</code>、<code>/a</code> 的顺序依次删除。</p>
<h2 id="设置边缘规则">设置边缘规则</h2>
<p>将所有文件都上传到又拍云之后，我们就可以直接在浏览器中访问博客了。但是，有时候需要配置一些重定向规则，比如旧文章的 URL 重定向、资源不存在时自动跳转到 404 页面等等。在使用服务器部署的时候，都是通过编写 Nginx 配置来实现这些规则，而在又拍云中则是通过设置「<a href="https://help.upyun.com/docs/edgerules/">边缘规则</a>」。</p>
<p>边缘规则支持两种设置方式，第一种是通用模式，这种模式上手简单，不需要了解边缘规则的语法规则；第二种是编程模式，比前一种模式更加灵活、强大，前提是你要熟悉它的语法规则。</p>
<p>下面是我配置的一些边缘规则。</p>
<h3 id="统一域名">统一域名</h3>
<p>第一个规则是为了统一域名，我的博客域名是 her-cat.com，而有的人则喜欢使用 <code>www.her-cat.com</code> 进行访问。为了兼容这两种方式，我将 her-cat.com 和 <code>www.her-cat.com</code> 都解析到了又拍云上，然后使用边缘规则对请求的域名进行检测，当域名等于 <code>www.her-cat.com</code> 时，就使用 301 跳转到 her-cat.com。</p>
<p>这个规则我使用的是编程模式，规则内容如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$WHEN($EQ($_HOST,www.her-cat.com))$REDIRECT(https://her-cat.com$_URI,301)
</span></span></code></pre></div><p>语法说明：</p>
<ul>
<li>$_HOST：请求的域名</li>
<li>$EQ：判断两个值是否相等，返回 true 和 false</li>
<li>$WHEN：条件判断语句，当括号中为 true 时，执行后面的语句</li>
<li>$REDIRECT：执行重定向，第一个地址重定向的地址，第二个是状态码</li>
</ul>
<h3 id="旧文章重定向">旧文章重定向</h3>
<p>在国庆的时候，我将静态博客的构建工具从 hexo 换成了 hugo，带来的问题就是博客中所有文章的地址都发生了变化。原来的地址是 <code>https://her-cat.com/年/月/日/slug.html</code>，新的地址是 <code>https://her-cat.com/posts/年/月/日/slug/</code>。可以看到，域名后面多了 <code>posts</code> 并且删除了 <code>.html</code> 文件后缀，变成了 <code>/</code> 结尾。由于一些文章已经被收录或者其它站点所引用，如果不进行重定向就会导致无法使用原来的地址访问。</p>
<p>这里使用的也是编程模式，与上一个规则相比多了一个步骤，需要先 URL 中提取出年、月、日、slug，然后再进行 301 重定向。</p>
<p>URL 字符串提取：</p>
<pre><code>^/(\d+)/(\d+)/(\d+)/([\w-]+).html$
</code></pre>
<p>规则内容：</p>
<pre><code>$REDIRECT(https://her-cat.com/posts/$1/$2/$3/$4/,301)
</code></pre>
<h3 id="在-uri-结尾追加-">在 URI 结尾追加 /</h3>
<p>在 hugo 中，强制要求所有页面的 URI 都必须以 / 结尾，否则无法找到对应的页面。</p>
<p>解决方法是当检测到 URI 不是以 / 结尾时就自动追加 / 并 301 重定向，当然还有一些特殊情况需要处理，比如请求的是文件时不做处理。</p>
<p>这里使用的是通用模式。</p>
<p>条件判断：</p>
<ul>
<li>当 <code>请求 URI</code> 正则不匹配（不区分大小写）<code>^/.*/$</code></li>
<li>并且 <code>请求 URI</code> 正则不匹配（不区分大小写）<code>(jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|txt|html|xml|js|css|woff2)$</code></li>
<li>并且 <code>请求 URI</code> 正则不匹配（不区分大小写） <code>/$</code></li>
<li>并且 <code>请求 URI</code> 不等于 <code>/</code></li>
</ul>
<p>满足以上条件时，执行功能：</p>
<ul>
<li>执行 <code>边缘重定向</code> 动作，响应 <code>301</code> 状态码，重定向地址为 <code>https://her-cat.com$_URI/</code>。</li>
</ul>
<h3 id="404-页面">404 页面</h3>
<p>最后一个规则是当请求的页面不存在时，自动重定向到 404 页面。执行重定向动作之前，需要先判断请求的文件类型，如果是资源文件则不处理，只重定向对页面的请求。</p>
<p>这里使用的是通用模式。</p>
<p>条件判断：</p>
<ul>
<li>当 <code>请求 URI</code> 正则不匹配（不区分大小写）<code>(jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|txt|html|xml|js|css|woff2)$</code></li>
</ul>
<p>满足以上条件时，执行功能：</p>
<ul>
<li>执行 <code>自定义错误页面</code> 动作，响应 <code>404</code> 状态码，跳转地址为 <code>https://her-cat.com/404.html</code>。</li>
</ul>
<p>到这里边缘规则就全部设置完了，在使用正则表达式的时候需要注意，边缘规则并不支持标准的正则表达式。</p>
<p>比如匹配以 .html 结尾的 URI 时，正常使用 <code>\.html$</code> 就可以了，但是又拍云不支持 <code>\.</code> 这种写法，并且没有任何错误提示，需要不断地编辑、保存规则然后进行调试才能找到问题。</p>
<h3 id="其它设置">其它设置</h3>
<p>下面是又拍云提供的一些功能，可以提升博客的体验以及访问速度。</p>
<ul>
<li>浏览器缓存：启用配置图片、字体、样式等文件的缓存。</li>
<li>智能压缩：开启 Gzip 和 Brotli 压缩，减少传输内容大小。</li>
<li>页面压缩：自动去除页面文件中非必要的字符（空白、注释等），加快传输速度。</li>
<li>TLS 1.3 加密协议：一种全新的加密协议，它既能提高终端用户的访问速度，又能增强安全性。</li>
<li>HTTP/2 传输协议：支持包括采用二进制格式传输数据、对消息头采用 HPACK 进行压缩传输、多路复用等特性。</li>
<li>WebP 自适应：智能判断浏览器是否支持 WebP，来决定返回 WebP 格式图片还是原图，从而减少网络传输消耗。</li>
<li>HTTPS：可以使用 Let&rsquo;s Encrypt 免费的证书，并且支持自动续费。</li>
</ul>
<h2 id="优化博客访问速度">优化博客访问速度</h2>
<p>使用 <a href="https://pagespeed.web.dev/">PageSpeed Insights</a> 工具对博客进行分析后，发现博客有很多可以优化的地方，比如字体文件、图片等资源的加载。下面是优化前后的评分对比。</p>
<p>优化前：</p>
<p><img decoding="async" height="1052" loading="lazy" sizes="(max-width: 768px) 100vw, (max-width: 1024px) 768px, 1024px" src="/posts/2023/01/31/blog-migration-to-upyun/before-optimizing-blog.jpg" srcset="/posts/2023/01/31/blog-migration-to-upyun/before-optimizing-blog_hu_335afcb57dae6279.jpg 384w, /posts/2023/01/31/blog-migration-to-upyun/before-optimizing-blog_hu_e573aa4b96c4e37c.jpg 768w, /posts/2023/01/31/blog-migration-to-upyun/before-optimizing-blog_hu_644f2071ef499c12.jpg 1024w" style="max-width: 100%; height: auto; aspect-ratio: 1.1473;" width="1207"></p>
<p>优化后：</p>
<p><img decoding="async" height="1071" loading="lazy" sizes="(max-width: 768px) 100vw, (max-width: 1024px) 768px, 1024px" src="/posts/2023/01/31/blog-migration-to-upyun/after-optimizing-blog.jpg" srcset="/posts/2023/01/31/blog-migration-to-upyun/after-optimizing-blog_hu_12c95ffaae0fed80.jpg 384w, /posts/2023/01/31/blog-migration-to-upyun/after-optimizing-blog_hu_8ad1accdcbb52ca5.jpg 768w, /posts/2023/01/31/blog-migration-to-upyun/after-optimizing-blog_hu_1e22c08ca15c5eba.jpg 1024w" style="max-width: 100%; height: auto; aspect-ratio: 1.1261;" width="1206"></p>
<h3 id="优化字体加载">优化字体加载</h3>
<p>Eureka 主题默认使用 Google fonts 来加载所需的字体文件：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">https://fonts.googleapis.com/css2?family=Lora:wght@400;600;700&amp;family=Noto+Serif+SC:wght@400;600;700&amp;display=swap
</span></span></code></pre></div><p>可以看到，主题使用了两种字体：Lora 和 Noto Serif SC，后面的 400;600;700 表示需要三种粗细值，display=swap 表示在 Google fonts 加载完成之前，先用系统自带的字体完成页面渲染，字体加载完成之后再用相应的字体进行渲染，避免加载字体时白屏闪烁的问题。</p>
<p>下面是该请求返回的 CSS 样式（部分）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="c">/* cyrillic-ext */</span>
</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">font-face</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">font-family</span><span class="o">:</span> <span class="s1">&#39;Lora&#39;</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">font-style</span><span class="o">:</span> <span class="nt">normal</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">font-weight</span><span class="o">:</span> <span class="nt">400</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">font-display</span><span class="o">:</span> <span class="nt">swap</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">src</span><span class="o">:</span> <span class="nt">url</span><span class="o">(</span><span class="nt">https</span><span class="o">://</span><span class="nt">fonts</span><span class="p">.</span><span class="nc">gstatic</span><span class="p">.</span><span class="nc">com</span><span class="o">/</span><span class="nt">s</span><span class="o">/</span><span class="nt">lora</span><span class="o">/</span><span class="nt">v26</span><span class="o">/</span><span class="nt">0QIvMX1D_JOuMwf7I_FMl_GW8g</span><span class="p">.</span><span class="nc">woff2</span><span class="o">)</span> <span class="nt">format</span><span class="o">(</span><span class="s1">&#39;woff2&#39;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">  <span class="nt">unicode-range</span><span class="o">:</span> <span class="nt">U</span><span class="o">+</span><span class="nt">0460-052F</span><span class="o">,</span> <span class="nt">U</span><span class="o">+</span><span class="nt">1C80-1C88</span><span class="o">,</span> <span class="nt">U</span><span class="o">+</span><span class="nt">20B4</span><span class="o">,</span> <span class="nt">U</span><span class="o">+</span><span class="nt">2DE0-2DFF</span><span class="o">,</span> <span class="nt">U</span><span class="o">+</span><span class="nt">A640-A69F</span><span class="o">,</span> <span class="nt">U</span><span class="o">+</span><span class="nt">FE2E-FE2F</span><span class="o">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c">/* cyrillic */</span>
</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">font-face</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">font-family</span><span class="o">:</span> <span class="s1">&#39;Lora&#39;</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">font-style</span><span class="o">:</span> <span class="nt">normal</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">font-weight</span><span class="o">:</span> <span class="nt">400</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">font-display</span><span class="o">:</span> <span class="nt">swap</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">src</span><span class="o">:</span> <span class="nt">url</span><span class="o">(</span><span class="nt">https</span><span class="o">://</span><span class="nt">fonts</span><span class="p">.</span><span class="nc">gstatic</span><span class="p">.</span><span class="nc">com</span><span class="o">/</span><span class="nt">s</span><span class="o">/</span><span class="nt">lora</span><span class="o">/</span><span class="nt">v26</span><span class="o">/</span><span class="nt">0QIvMX1D_JOuMw77I_FMl_GW8g</span><span class="p">.</span><span class="nc">woff2</span><span class="o">)</span> <span class="nt">format</span><span class="o">(</span><span class="s1">&#39;woff2&#39;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">  <span class="nt">unicode-range</span><span class="o">:</span> <span class="nt">U</span><span class="o">+</span><span class="nt">0301</span><span class="o">,</span> <span class="nt">U</span><span class="o">+</span><span class="nt">0400-045F</span><span class="o">,</span> <span class="nt">U</span><span class="o">+</span><span class="nt">0490-0491</span><span class="o">,</span> <span class="nt">U</span><span class="o">+</span><span class="nt">04B0-04B1</span><span class="o">,</span> <span class="nt">U</span><span class="o">+</span><span class="nt">2116</span><span class="o">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>像这样的 @font-face 定义一共有 306 个，也就是说最坏的情况需要发起 306 个请求才能完成页面上所有字体的渲染，为什么要分成这么多字体文件呢？</p>
<p>目的是按需加载字体文件，提高字体的加载速度。在 unicode-range 中定义了字体文件包含的字符编码范围，当页面上需要用到相应的字符时，浏览器就会请求 src 中的地址加载字体文件进行渲染。</p>
<p>分析网络请求后发现，首次打开博客大概需要发起 30 多个请求来获取字体文件，由于 Google fonts 的服务器在国外，所以即使开启了浏览器缓存，首次访问还是需要花费较长的时间。为了解决这个问题，我将字体文件上传到了又拍云，通过又拍云的 CDN 节点提升字体的加载速度。</p>
<p>操作步骤：</p>
<ul>
<li>将 CSS 文件中所有的字体按照路径下载到本地。</li>
<li>将 CSS 文件中所有的 fonts.gstatic.com 替换成又拍云的访问域名。</li>
<li>将 CSS 文件和字体文件按照路径上传到又拍云。</li>
</ul>
<p>打开 <code>/fonts/fonts-family.css</code> 可以看到最终效果。</p>
<p>除此之外，还需要在又拍云中为字体文件配置浏览器缓存，避免每次都从又拍云加载字体文件。</p>
<h3 id="优化图片加载">优化图片加载</h3>
<p>关于图片的优化主要是首页的头像和底部的又拍云 Logo。</p>
<p>头像的问题在于没有给图片设置宽度和高度，如果图片在页面渲染完成之后才加载出来，会导致图片附近的元素会发生位移，会感觉页面好像抖了一下。</p>
<p>又拍云 Logo 问题在于加载的图片很大，但是实际上又不需要这么大的图片，可以使用又拍云的图片处理对图片进行压缩。</p>
<h3 id="优化资源文件加载">优化资源文件加载</h3>
<p>在查看分析报告的时候，发现博客中所有的页面都加载了代码高亮和评论模块的 JS 以及 CSS 文件，但实际上除了详情页面以外，其它页面都不需要代码高亮以及评论功能。为了解决这个问题，我们只需要在 baseof.html 页面中判断一下的页面类型，只有详情页面才加载代码高亮和评论模块相关资源。</p>
<p>首先将主题下的 baseof.html 文件拷贝到博客的 layouts/_default/ 目录下，对 baseof.html 文件进行重写，然后将文件中代码高亮和评论相关的代码删除，替换成以下内容：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">{{ $currentPage := . }}
</span></span><span class="line"><span class="cl">{{/* 判断是否为详情页面 */}}
</span></span><span class="line"><span class="cl">{{ if eq $currentPage.Kind &#34;page&#34; }}
</span></span><span class="line"><span class="cl">{{/* 引入评论相关资源 */}}
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://unpkg.com/gitalk@1.8.0/dist/gitalk.css&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;gitalk_js&#34;</span> <span class="na">async</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://unpkg.com/gitalk@1.8.0/dist/gitalk.min.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{{/* 引入代码高亮相关资源 */}}
</span></span><span class="line"><span class="cl">{{- $assets := .Site.Data.assets }}
</span></span><span class="line"><span class="cl">{{- if eq .Site.Params.highlight.handler &#34;chroma&#34; }}
</span></span><span class="line"><span class="cl">{{- $highlightCSS := resources.Get &#34;css/syntax.css&#34; | minify | fingerprint &#34;sha384&#34; }}
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;{{ $highlightCSS.Permalink }}&#34;</span> <span class="na">media</span><span class="o">=</span><span class="s">&#34;print&#34;</span> <span class="na">onload</span><span class="o">=</span><span class="s">&#34;this.media=&#39;all&#39;;this.onload=null&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{{- else if eq .Site.Params.highlight.handler &#34;highlightjs&#34; }}
</span></span><span class="line"><span class="cl">{{- $highlightjsStyle := .Site.Params.highlight.highlightjs.style | default &#34;base16/solarized-light&#34; }}
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;{{ printf $assets.highlightjs.css.url $assets.highlightjs.version $highlightjsStyle }}&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="err">{{</span> <span class="na">with</span> <span class="err">$</span><span class="na">assets</span><span class="err">.</span><span class="na">highlightjs</span><span class="err">.</span><span class="na">css</span><span class="err">.</span><span class="na">sri</span> <span class="err">}}</span> <span class="na">integrity</span><span class="o">=</span><span class="s">&#34;{{ . }}&#34;</span> <span class="err">{{</span> <span class="na">end</span> <span class="err">}}</span> <span class="na">media</span><span class="o">=</span><span class="s">&#34;print&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">onload</span><span class="o">=</span><span class="s">&#34;this.media=&#39;all&#39;;this.onload=null&#34;</span> <span class="na">crossorigin</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">defer</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;{{ printf $assets.highlightjs.js.url $assets.highlightjs.version }}&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="err">{{</span> <span class="na">with</span> <span class="err">$</span><span class="na">assets</span><span class="err">.</span><span class="na">highlightjs</span><span class="err">.</span><span class="na">js</span><span class="err">.</span><span class="na">sri</span> <span class="err">}}</span> <span class="na">integrity</span><span class="o">=</span><span class="s">&#34;{{ . }}&#34;</span> <span class="err">{{</span> <span class="na">end</span> <span class="err">}}</span> <span class="na">crossorigin</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{{- range .Site.Params.highlight.highlightjs.languages }}
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">defer</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;{{ printf $assets.highlightjs.languages.url $assets.highlightjs.version . }}&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="err">{{</span> <span class="na">with</span> <span class="err">$</span><span class="na">assets</span><span class="err">.</span><span class="na">highlightjs</span><span class="err">.</span><span class="na">languages</span><span class="err">.</span><span class="na">sri</span> <span class="err">}}</span> <span class="na">integrity</span><span class="o">=</span><span class="s">&#34;{{ . }}&#34;</span> <span class="err">{{</span> <span class="na">end</span> <span class="err">}}</span> <span class="na">crossorigin</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{{- end }}
</span></span><span class="line"><span class="cl">{{- $highlightjsCSS := resources.Get &#34;css/highlightjs.css&#34; | minify | fingerprint &#34;sha384&#34; }}
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;{{ $highlightjsCSS.Permalink }}&#34;</span> <span class="na">media</span><span class="o">=</span><span class="s">&#34;print&#34;</span> <span class="na">onload</span><span class="o">=</span><span class="s">&#34;this.media=&#39;all&#39;;this.onload=null&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{{- end }}
</span></span><span class="line"><span class="cl">{{ end }}
</span></span></code></pre></div><h2 id="总结">总结</h2>
<p>这篇文章介绍了我是如何将博客迁移到又拍云并实现自动化更新博客的，顺便介绍了如何使用边缘规则来实现对请求的处理，最后提到了我对博客的一些优化，虽然优化的手段都比较常规，但实际效果还是很不错的。</p>
<p>最后，祝大家兔年大吉！</p>
]]></content:encoded>
    </item>
    <item>
      <title>基于 GitHub Actions 定时推送网址到百度站长平台</title>
      <link>https://her-cat.com/posts/2021/05/17/github-actions-baidu-push-url/</link>
      <pubDate>Mon, 17 May 2021 21:09:24 +0800</pubDate>
      <guid>https://her-cat.com/posts/2021/05/17/github-actions-baidu-push-url/</guid>
      <description>刚学会 PHP 的时候写了一个笑话类型的网站，网站的数据是定时从另外一个网站上采集的。但是网站部署在虚拟主机上</description>
      <content:encoded><![CDATA[<h2 id="前言">前言</h2>
<p>刚学会 PHP 的时候写了一个笑话类型的网站，网站的数据是定时从另外一个网站上采集的。但是网站部署在虚拟主机上，所以用不了 crontab 执行定时任务。</p>
<p>解决办法是使用监控宝，定时请求我网站的一个地址，在这个地址里面编写采集数据的逻辑。到了现在已经有很多解决办法，比如 Workerman/Swoole 的定时器组件、GitHub Actions。</p>
<p>本文就是介绍如何用 GitHub Actions 的定时任务将网址推送到百度站长平台，提高文章被收录的速度。</p>
<h2 id="配置-github-actions">配置 GitHub Actions</h2>
<p>首先设置工作流触发条件， push 和 schedule 表示推送代码及定时计划都会触发。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">name: &#39;Push Baidu&#39;
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">on:
</span></span><span class="line"><span class="cl">  push:
</span></span><span class="line"><span class="cl">    branches:
</span></span><span class="line"><span class="cl">      - gh-pages
</span></span><span class="line"><span class="cl">  schedule:
</span></span><span class="line"><span class="cl">    - cron: &#39;*/5 * * * *&#39;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">jobs:
</span></span><span class="line"><span class="cl">  start:
</span></span><span class="line"><span class="cl">    runs-on: ubuntu-latest
</span></span><span class="line"><span class="cl">    steps:
</span></span><span class="line"><span class="cl">      # 检查工作流是否可以访问 actions
</span></span><span class="line"><span class="cl">      - name: Checkout Repository master branch
</span></span><span class="line"><span class="cl">        uses: actions/checkout@v1
</span></span><span class="line"><span class="cl">      # 执行仓库中的脚本文件
</span></span><span class="line"><span class="cl">      - name: Execute script
</span></span><span class="line"><span class="cl">        env:
</span></span><span class="line"><span class="cl">          SITEMAP_URL: ${{ secrets.SITEMAP_URL }}
</span></span><span class="line"><span class="cl">          PUSH_URL: ${{ secrets.PUSH_URL }}
</span></span><span class="line"><span class="cl">        run: php ./.github/push.php -s ${SITEMAP_URL} -p ${PUSH_URL}
</span></span></code></pre></div><p><code>*/5 * * * *</code> 表示每五分钟执行一次。</p>
<p>runs-on 的意思是指定工作流运行在什么环境上，ubuntu-latest 表示 Ubuntu 最新版。
steps 中第一个步骤是检查工作流是否可以访问 actions，第二个步骤是执行我们的 PHP 脚本，在执行脚本之前，需要将密钥库中的 SITEMAP_URL 和 PUSH_URL 以环境变量的方式传给脚本。</p>
<p>在 <a href="https://github.com/">https://github.com/</a>用户名/仓库名/settings/secrets/actions 中新增两个配置项：</p>
<table>
  <thead>
      <tr>
          <th>Name</th>
          <th>Value</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>SITEMAP_URL</td>
          <td>网站的 sitemap 地址， <code>https://yourdomain/baidusitemap.xml</code></td>
      </tr>
      <tr>
          <td>PUSH_URL</td>
          <td>百度站长平台的推送地址，可以在 <a href="https://ziyuan.baidu.com/linksubmit/index">百度站长平台</a> 普通收录 =&gt; API提交页面中找到，类似于：http://data.zz.baidu.com/urls?site=https://her-cat.com&amp;token=xxxxx</td>
      </tr>
  </tbody>
</table>
<p>将上面的工作流保存到 .github/workflows 目录下，命名为 push.yml。</p>
<h2 id="编写推送脚本">编写推送脚本</h2>
<p>百度站长平台已经提供了 PHP 推送的例子，我基于这个例子添加了自动获取网址的函数并适配了工作流。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="o">&lt;</span><span class="err">?</span><span class="n">php</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">//</span> <span class="err">获取</span> <span class="n">sitemap</span> <span class="err">地址和推送地址</span>
</span></span><span class="line"><span class="cl"><span class="o">$</span><span class="n">opt</span> <span class="o">=</span> <span class="n">getopt</span><span class="p">(</span><span class="s1">&#39;s:p:&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">empty</span><span class="p">(</span><span class="o">$</span><span class="n">opt</span><span class="p">[</span><span class="s1">&#39;s&#39;</span><span class="p">])</span> <span class="o">||</span> <span class="n">empty</span><span class="p">(</span><span class="o">$</span><span class="n">opt</span><span class="p">[</span><span class="s1">&#39;p&#39;</span><span class="p">]))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">throw</span> <span class="n">new</span> \<span class="n">Exception</span><span class="p">(</span><span class="s1">&#39;关键参数不能为空&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">//</span> <span class="err">从</span> <span class="n">sitemap</span> <span class="err">中解析出网址列表</span>
</span></span><span class="line"><span class="cl"><span class="n">function</span> <span class="n">get_site_urls</span><span class="p">(</span><span class="o">$</span><span class="n">sitemap_url</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="o">$</span><span class="n">content</span> <span class="o">=</span> <span class="n">file_get_contents</span><span class="p">(</span><span class="o">$</span><span class="n">sitemap_url</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">empty</span><span class="p">(</span><span class="o">$</span><span class="n">content</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="o">$</span><span class="n">xml</span> <span class="o">=</span> <span class="n">simplexml_load_string</span><span class="p">(</span><span class="o">$</span><span class="n">content</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!$</span><span class="n">xml</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="o">$</span><span class="n">urls</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">    <span class="n">foreach</span> <span class="p">(</span><span class="o">$</span><span class="n">xml</span><span class="o">-&gt;</span><span class="n">url</span> <span class="n">as</span> <span class="o">$</span><span class="n">url</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="o">$</span><span class="n">urls</span><span class="p">[]</span> <span class="o">=</span> <span class="p">(</span><span class="n">string</span><span class="p">)</span> <span class="o">$</span><span class="n">url</span><span class="o">-&gt;</span><span class="n">loc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="o">$</span><span class="n">urls</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">//</span> <span class="err">将网址列表推送到百度</span>
</span></span><span class="line"><span class="cl"><span class="n">function</span> <span class="n">push_to_baidu</span><span class="p">(</span><span class="o">$</span><span class="n">push_url</span><span class="p">,</span> <span class="o">$</span><span class="n">urls</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="o">$</span><span class="n">ch</span> <span class="o">=</span> <span class="n">curl_init</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="o">$</span><span class="n">options</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="n">CURLOPT_URL</span> <span class="o">=&gt;</span> <span class="o">$</span><span class="n">push_url</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">CURLOPT_POST</span> <span class="o">=&gt;</span> <span class="bp">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">CURLOPT_TIMEOUT</span> <span class="o">=&gt;</span> <span class="mi">15</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">CURLOPT_RETURNTRANSFER</span> <span class="o">=&gt;</span> <span class="bp">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">CURLOPT_CONNECTTIMEOUT</span> <span class="o">=&gt;</span> <span class="mi">15</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">CURLOPT_POSTFIELDS</span> <span class="o">=&gt;</span> <span class="n">implode</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">,</span> <span class="o">$</span><span class="n">urls</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">CURLOPT_HTTPHEADER</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="s1">&#39;Content-Type: text/plain&#39;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">curl_setopt_array</span><span class="p">(</span><span class="o">$</span><span class="n">ch</span><span class="p">,</span> <span class="o">$</span><span class="n">options</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">curl_exec</span><span class="p">(</span><span class="o">$</span><span class="n">ch</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">//</span> <span class="err">获取需要推送的网址列表</span>
</span></span><span class="line"><span class="cl"><span class="o">$</span><span class="n">urls</span> <span class="o">=</span> <span class="n">get_site_urls</span><span class="p">(</span><span class="o">$</span><span class="n">opt</span><span class="p">[</span><span class="s1">&#39;s&#39;</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">empty</span><span class="p">(</span><span class="o">$</span><span class="n">urls</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">throw</span> <span class="n">new</span> \<span class="n">Exception</span><span class="p">(</span><span class="s1">&#39;网址列表为空&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">//</span> <span class="err">调用推送函数</span>
</span></span><span class="line"><span class="cl"><span class="n">echo</span> <span class="n">push_to_baidu</span><span class="p">(</span><span class="o">$</span><span class="n">opt</span><span class="p">[</span><span class="s1">&#39;p&#39;</span><span class="p">],</span> <span class="o">$</span><span class="n">urls</span><span class="p">)</span><span class="o">.</span><span class="n">PHP_EOL</span><span class="p">;</span>
</span></span></code></pre></div><p>将上面的代码保存到 .github/ 目录下，命名为 push.php。</p>
<p>然后提交代码就可以在 GitHub Actions 中查看工作流的运行状态。</p>
<h2 id="总结">总结</h2>
<p>需要注意，如果你的 sitemap 格式和我的不一样，需要修改 get_site_urls 函数解析网址列表的逻辑。</p>
<p>PS：截止到本文发出来，定时计划还是一次都没有运行，我一度以为配置没写对，后来才发现不是到了计划时间就一定会运行！</p>
<p>当定时计划到达执行时间时，会将本次任务放到一个队列里面，每当 GitHub <strong>有可用的机器时</strong>才会运行，一般延迟时间约为 3 到 10 分钟。有时可能是更多的时间，甚至几十分钟或一个多小时。但是，如果延迟时间过长，则当天可能不会触发计划的工作流。除了使用外部调度器手动触发工作流之外，没有别的办法。</p>
<p>相关文章：</p>
<ul>
<li><a href="https://github.community/t/no-assurance-on-scheduled-jobs/133753">No assurance on scheduled jobs?</a></li>
<li><a href="https://upptime.js.org/blog/2021/01/22/github-actions-schedule-not-working/">GitHub Actions workflow not triggering at scheduled time</a></li>
<li><a href="https://her-cat.com/posts/2021/03/30/github-action-deploy-hexo/">使用 GitHub Actions 自动部署 Hexo</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>使用 GitHub Actions 自动部署 Hexo</title>
      <link>https://her-cat.com/posts/2021/03/30/github-action-deploy-hexo/</link>
      <pubDate>Tue, 30 Mar 2021 16:02:45 +0800</pubDate>
      <guid>https://her-cat.com/posts/2021/03/30/github-action-deploy-hexo/</guid>
      <description>目前部署 Hexo 的操作比较麻烦，先在本地执行 hexo clean &amp;amp;&amp;amp; hexo d 生成博客静态文件并发布到 GitHub 上，然后通过 XShell 登录</description>
      <content:encoded><![CDATA[<p>目前部署 Hexo 的操作比较麻烦，先在本地执行 <strong>hexo clean &amp;&amp; hexo d</strong> 生成博客静态文件并发布到 GitHub 上，然后通过 XShell 登录到服务器，切换到博客站点目录下 git pull 拉取最新的代码，最近网络也不太不稳定，拉取代码也经常超时，更新的时候还得看运气。</p>
<p>有两种方案可以自动更新：GitHub WebHook 和 GitHub Actions。</p>
<p>WebHook 是一种比较简单且常见的方式，在仓库配置一个回调地址并指定需要回调的事件类型即可。</p>
<p>比如指定了 push 事件，往该仓库 push 代码时，GitHub 就会请求配置的回调地址并携带一些信息，通过这些信息就可以检查请求是否合法、是哪个仓库的谁提交了代码，从而决定是否需要更新服务器端的代码。</p>
<p>详情可以看官方的文档：<a href="https://docs.github.com/cn/developers/webhooks-and-events/about-webhooks">https://docs.github.com/cn/developers/webhooks-and-events/about-webhooks</a></p>
<p>Actions 是 GitHub 新推出来（快两年，也不算新了）的一种持续集成的方案，将上面这些操作变成一个个的任务（Job）组成工作流，当发生指定事件后触发该工作流，也可以用别人写好的工作流。</p>
<p>更详细的内容可以看 <a href="https://docs.github.com/cn/actions">官方文档</a> 和阮老师的 <a href="http://www.ruanyifeng.com/blog/2019/09/getting-started-with-github-actions.html">GitHub Actions 入门教程</a>。</p>
<p>话不多说，直接贴 GitHub Actions 的配置。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="c1"># 工作流的名称</span>
</span></span><span class="line"><span class="cl"><span class="n">name</span><span class="p">:</span> <span class="n">Hexo</span> <span class="n">Blog</span> <span class="n">CI</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 指定触发的条件，这里是打标签才触发该工作流，你也可以改成 on: push，只要提交代码就触发</span>
</span></span><span class="line"><span class="cl"><span class="n">on</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">push</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">tags</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="o">-</span> <span class="s1">&#39;v*&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">jobs</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">build</span><span class="p">:</span> 
</span></span><span class="line"><span class="cl">    <span class="n">runs</span><span class="o">-</span><span class="n">on</span><span class="p">:</span> <span class="n">ubuntu</span><span class="o">-</span><span class="n">latest</span> 
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">    <span class="n">steps</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># 检查工作流是否可以访问 actions</span>
</span></span><span class="line"><span class="cl">    <span class="o">-</span> <span class="n">name</span><span class="p">:</span> <span class="n">Checkout</span> <span class="n">Repository</span> <span class="n">master</span> <span class="n">branch</span>
</span></span><span class="line"><span class="cl">      <span class="n">uses</span><span class="p">:</span> <span class="n">actions</span><span class="o">/</span><span class="n">checkout</span><span class="err">@</span><span class="n">master</span> 
</span></span><span class="line"><span class="cl">      
</span></span><span class="line"><span class="cl">    <span class="c1"># 安装 NodeJs</span>
</span></span><span class="line"><span class="cl">    <span class="o">-</span> <span class="n">name</span><span class="p">:</span> <span class="n">Setup</span> <span class="ne">Node</span><span class="o">.</span><span class="n">js</span> <span class="mf">12.</span><span class="n">x</span> 
</span></span><span class="line"><span class="cl">      <span class="n">uses</span><span class="p">:</span> <span class="n">actions</span><span class="o">/</span><span class="n">setup</span><span class="o">-</span><span class="n">node</span><span class="err">@</span><span class="n">master</span>
</span></span><span class="line"><span class="cl">      <span class="n">with</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">node</span><span class="o">-</span><span class="n">version</span><span class="p">:</span> <span class="s2">&#34;12.x&#34;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># 安装 Hexo 的依赖库</span>
</span></span><span class="line"><span class="cl">    <span class="o">-</span> <span class="n">name</span><span class="p">:</span> <span class="n">Setup</span> <span class="n">Hexo</span> <span class="n">Dependencies</span>
</span></span><span class="line"><span class="cl">      <span class="n">run</span><span class="p">:</span> <span class="o">|</span>
</span></span><span class="line"><span class="cl">        <span class="n">npm</span> <span class="n">install</span> <span class="n">hexo</span><span class="o">-</span><span class="n">cli</span> <span class="o">-</span><span class="n">g</span>
</span></span><span class="line"><span class="cl">        <span class="n">npm</span> <span class="n">install</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># 设置 Hexo 部署的私钥</span>
</span></span><span class="line"><span class="cl">    <span class="o">-</span> <span class="n">name</span><span class="p">:</span> <span class="n">Setup</span> <span class="n">Deploy</span> <span class="n">Private</span> <span class="n">Key</span>
</span></span><span class="line"><span class="cl">      <span class="n">env</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">HEXO_DEPLOY_PRIVATE_KEY</span><span class="p">:</span> <span class="o">$</span><span class="p">{{</span> <span class="n">secrets</span><span class="o">.</span><span class="n">HEXO_DEPLOY_PRIVATE_KEY</span> <span class="p">}}</span>
</span></span><span class="line"><span class="cl">      <span class="n">run</span><span class="p">:</span> <span class="o">|</span>
</span></span><span class="line"><span class="cl">        <span class="n">mkdir</span> <span class="o">-</span><span class="n">p</span> <span class="o">~/.</span><span class="n">ssh</span><span class="o">/</span>
</span></span><span class="line"><span class="cl">        <span class="n">echo</span> <span class="s2">&#34;$HEXO_DEPLOY_PRIVATE_KEY&#34;</span> <span class="o">&gt;</span> <span class="o">~/.</span><span class="n">ssh</span><span class="o">/</span><span class="n">id_rsa</span> 
</span></span><span class="line"><span class="cl">        <span class="n">chmod</span> <span class="mi">600</span> <span class="o">~/.</span><span class="n">ssh</span><span class="o">/</span><span class="n">id_rsa</span>
</span></span><span class="line"><span class="cl">        <span class="n">ssh</span><span class="o">-</span><span class="n">keyscan</span> <span class="n">github</span><span class="o">.</span><span class="n">com</span> <span class="o">&gt;&gt;</span> <span class="o">~/.</span><span class="n">ssh</span><span class="o">/</span><span class="n">known_hosts</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1"># 设置 Git 信息</span>
</span></span><span class="line"><span class="cl">    <span class="o">-</span> <span class="n">name</span><span class="p">:</span> <span class="n">Setup</span> <span class="n">Git</span> <span class="n">Infomation</span>
</span></span><span class="line"><span class="cl">      <span class="n">run</span><span class="p">:</span> <span class="o">|</span> 
</span></span><span class="line"><span class="cl">        <span class="n">git</span> <span class="n">config</span> <span class="o">--</span><span class="n">global</span> <span class="n">user</span><span class="o">.</span><span class="n">name</span> <span class="s2">&#34;Git 用户名&#34;</span> 
</span></span><span class="line"><span class="cl">        <span class="n">git</span> <span class="n">config</span> <span class="o">--</span><span class="n">global</span> <span class="n">user</span><span class="o">.</span><span class="n">email</span> <span class="s2">&#34;Git 邮箱&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 部署 Hexo 静态文件到博客所在的分支</span>
</span></span><span class="line"><span class="cl">    <span class="o">-</span> <span class="n">name</span><span class="p">:</span> <span class="n">Deploy</span> <span class="n">Hexo</span> 
</span></span><span class="line"><span class="cl">      <span class="n">run</span><span class="p">:</span> <span class="o">|</span>
</span></span><span class="line"><span class="cl">        <span class="n">hexo</span> <span class="n">clean</span>
</span></span><span class="line"><span class="cl">        <span class="n">hexo</span> <span class="n">generate</span> 
</span></span><span class="line"><span class="cl">        <span class="n">hexo</span> <span class="n">deploy</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 部署到服务器</span>
</span></span><span class="line"><span class="cl">    <span class="o">-</span> <span class="n">name</span><span class="p">:</span> <span class="n">Deploy</span> <span class="n">Server</span>
</span></span><span class="line"><span class="cl">      <span class="n">uses</span><span class="p">:</span> <span class="n">appleboy</span><span class="o">/</span><span class="n">ssh</span><span class="o">-</span><span class="n">action</span><span class="err">@</span><span class="n">master</span>
</span></span><span class="line"><span class="cl">      <span class="n">env</span><span class="p">:</span> 
</span></span><span class="line"><span class="cl">        <span class="n">SITE_DIR</span><span class="p">:</span> <span class="err">站点目录（如：</span><span class="o">/</span><span class="k">var</span><span class="o">/</span><span class="n">www</span><span class="o">/</span><span class="n">blog</span><span class="err">）</span>
</span></span><span class="line"><span class="cl">      <span class="n">with</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">host</span><span class="p">:</span> <span class="o">$</span><span class="p">{{</span> <span class="n">secrets</span><span class="o">.</span><span class="n">SERVER_IP</span> <span class="p">}}</span>
</span></span><span class="line"><span class="cl">        <span class="n">port</span><span class="p">:</span> <span class="o">$</span><span class="p">{{</span> <span class="n">secrets</span><span class="o">.</span><span class="n">SERVER_PORT</span> <span class="p">}}</span>
</span></span><span class="line"><span class="cl">        <span class="n">username</span><span class="p">:</span> <span class="o">$</span><span class="p">{{</span> <span class="n">secrets</span><span class="o">.</span><span class="n">SERVER_USERNAME</span> <span class="p">}}</span>
</span></span><span class="line"><span class="cl">        <span class="n">key</span><span class="p">:</span> <span class="o">$</span><span class="p">{{</span> <span class="n">secrets</span><span class="o">.</span><span class="n">SERVER_PRIVATE_KEY</span> <span class="p">}}</span>
</span></span><span class="line"><span class="cl">        <span class="n">envs</span><span class="p">:</span> <span class="n">SITE_DIR</span>
</span></span><span class="line"><span class="cl">        <span class="n">script</span><span class="p">:</span> <span class="o">|</span>
</span></span><span class="line"><span class="cl">          <span class="n">cd</span> <span class="o">$</span><span class="n">SITE_DIR</span>
</span></span><span class="line"><span class="cl">          <span class="n">sudo</span> <span class="n">git</span> <span class="n">config</span> <span class="o">--</span><span class="n">global</span> <span class="n">pull</span><span class="o">.</span><span class="n">rebase</span> <span class="bp">true</span>
</span></span><span class="line"><span class="cl">          <span class="n">sudo</span> <span class="n">git</span> <span class="n">pull</span> <span class="n">origin</span> <span class="n">master</span> <span class="o">--</span><span class="n">allow</span><span class="o">-</span><span class="n">unrelated</span><span class="o">-</span><span class="n">histories</span>
</span></span></code></pre></div><p>其中 <strong>Git 用户名</strong>、<strong>Git 邮箱</strong>、<strong>站点目录</strong> 都要替换成你自己的。</p>
<p>首先打开 <a href="https://github.com/">https://github.com/</a>用户名/仓库名/settings/secrets/actions 新增几个 Action secret，在上面的执行工作流的时候会用到。</p>
<p>需要新增以下几项：</p>
<table>
  <thead>
      <tr>
          <th>Name</th>
          <th>Value</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>SERVER_IP</td>
          <td>服务器 IP</td>
      </tr>
      <tr>
          <td>SERVER_PORT</td>
          <td>服务器端口号</td>
      </tr>
      <tr>
          <td>SERVER_USERNAME</td>
          <td>用户名</td>
      </tr>
      <tr>
          <td>SERVER_PRIVATE_KEY</td>
          <td>服务器私钥（服务器的 ~/.ssh/id_rsa）</td>
      </tr>
      <tr>
          <td>HEXO_DEPLOY_PRIVATE_KEY</td>
          <td>部署私钥（本地的 ~/.ssh/id_rsa）</td>
      </tr>
  </tbody>
</table>
<p>前三项就不多说，主要讲下后面两项怎么设置。</p>
<blockquote>
<p>HEXO_DEPLOY_PRIVATE_KEY 是为了在工作流中能够提交代码到 GitHub，SERVER_PRIVATE_KEY 是为了能在工作流中登录到服务器上执行命令。</p></blockquote>
<p>工作流被触发后，先生成博客的静态文件，并通过 hexo deploy 提交到博客所在的分支（由 Hexo 配置文件中的 deploy.branch 指定），这里涉及到提交代码，所以要配置 SSH Key。</p>
<p>查看电脑上是否已经生成了密钥对。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$ ll ~/.ssh
</span></span><span class="line"><span class="cl"># 输出： id_rsa  id_rsa.pub  known_hosts
</span></span></code></pre></div><p>如果为空则执行以下命令，一路回车就可以了：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$ ssh-keygen -t rsa -C &#34;你的邮箱&#34;
</span></span></code></pre></div><p>这时候电脑上就会有两个文件：~/.ssh/id_rsa、~/.ssh/id_rsa.pub，分别是是私钥和公钥。</p>
<p>id_rsa 文件的内容就是 HEXO_DEPLOY_PRIVATE_KEY 的 Value。</p>
<p>然后打开 <a href="https://github.com/settings/ssh/new">https://github.com/settings/ssh/new</a>，将 id_rsa.pub 的内容填到 Key 里面，Title 可以随便去，然后保存。</p>
<p>接下来就是 SERVER_PRIVATE_KEY，登录到服务器拿到密钥对，步骤跟上面差不多，没有的话就生成。</p>
<p>服务器中 ~/.ssh/id_rsa 文件的内容就是 SERVER_PRIVATE_KEY 的 Value，然后将 ~/.ssh/id_rsa.pub 的内容追加到 ~/.ssh/authorized_keys 文件中。</p>
<p>需要注意：</p>
<ul>
<li>服务器中必须已经存在 SITE_DIR 目录，且已经将仓库 clone 下来了。</li>
<li>在服务器上可以免密执行 git pull。</li>
</ul>
<p>更新博客时只需要打标签，然后将标签推送到云端就会触发工作流。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$ git tag v1.0.0
</span></span><span class="line"><span class="cl">$ git push origin v1.0.0
</span></span></code></pre></div><p><img alt="GitHub Actions Log" decoding="async" height="869" loading="lazy" sizes="(max-width: 768px) 100vw, (max-width: 1024px) 768px, 1024px" src="/posts/2021/03/30/github-action-deploy-hexo/github-actions-log.png" srcset="/posts/2021/03/30/github-action-deploy-hexo/github-actions-log_hu_37b79eb5e295c66d.png 384w, /posts/2021/03/30/github-action-deploy-hexo/github-actions-log_hu_4630f40328e043eb.png 768w, /posts/2021/03/30/github-action-deploy-hexo/github-actions-log_hu_b0ff64478b3ef28e.png 1024w, /posts/2021/03/30/github-action-deploy-hexo/github-actions-log_hu_7ff4ce1cd4b71578.png 1536w, /posts/2021/03/30/github-action-deploy-hexo/github-actions-log.png 1897w" style="max-width: 100%; height: auto; aspect-ratio: 2.1830;" width="1897"></p>
]]></content:encoded>
    </item><follow_challenge>
      <feedId>58021783493571598</feedId>
      <userId>56882619875632128</userId>
    </follow_challenge>
  </channel>
</rss>
