<?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>kkkk24 的博客</title>
    <link>https://web.943827561.xyz/</link>
    <description>Recent content on kkkk24 的博客</description>
    <generator>Hugo -- 0.155.2</generator>
    <language>zh-cn</language>
    <lastBuildDate>Fri, 06 Feb 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://web.943827561.xyz/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>聊聊这个博客是怎么跑起来的</title>
      <link>https://web.943827561.xyz/posts/hugo-blog-architecture/</link>
      <pubDate>Wed, 04 Feb 2026 00:00:00 +0000</pubDate>
      <guid>https://web.943827561.xyz/posts/hugo-blog-architecture/</guid>
      <description>记录一下博客从写文章到上线的全过程，涉及 Hugo、GitHub Actions、服务器部署和 CDN 加速</description>
      <content:encoded><![CDATA[<p>最近把博客从 Hexo 换成了 Hugo，顺便重新设计了一下构建和部署流程。趁热乎记录一下，也方便以后自己查。</p>
<h2 id="先说结论整体流程">先说结论：整体流程</h2>
<p>简单来说就是这么个事儿：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">我写 Markdown → 推到 GitHub → Actions 自动编译 → 服务器拉取 → Cloudflare 加速
</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">本地写文章 (Markdown)
</span></span><span class="line"><span class="cl">        ↓ git push
</span></span><span class="line"><span class="cl">GitHub main 分支
</span></span><span class="line"><span class="cl">        ↓ 触发 Actions
</span></span><span class="line"><span class="cl">GitHub Actions 编译
</span></span><span class="line"><span class="cl">        ↓ 生成静态文件
</span></span><span class="line"><span class="cl">gh-pages 分支
</span></span><span class="line"><span class="cl">        ↓ 服务器 git pull
</span></span><span class="line"><span class="cl">Nginx 托管静态文件
</span></span><span class="line"><span class="cl">        ↓ 
</span></span><span class="line"><span class="cl">Cloudflare CDN 缓存加速
</span></span><span class="line"><span class="cl">        ↓
</span></span><span class="line"><span class="cl">用户访问
</span></span></code></pre></div><p>为啥要搞这么复杂？主要是不想每次改个错别字都要登服务器手动操作。</p>
<h2 id="为啥选-hugo">为啥选 Hugo</h2>
<p>之前用的 Hexo，没啥大毛病，但有几个痒点：</p>
<ol>
<li><strong>npm 依赖太多了</strong>。每次 CI 光装依赖就得好一会儿，偶尔还会出点版本冲突的幺蛾子</li>
<li><strong>本地预览慢</strong>。文章多了之后，热更新明显变卡</li>
<li><strong>想尝尝鲜</strong>。Hugo 用 Go 写的，据说快得离谱</li>
</ol>
<p>实际用下来，确实快。本地预览基本秒开，GitHub Actions 那边构建也就几秒钟的事。</p>
<h2 id="github-actions自动化构建">GitHub Actions：自动化构建</h2>
<p>这是整套流程的核心。配置文件在 <code>.github/workflows/hugo.yml</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">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build Hugo Site</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="nt">on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">push</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">main]</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="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">build</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">装 Hugo</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">          wget -O hugo.deb https://github.com/gohugoio/hugo/releases/...
</span></span></span><span class="line"><span class="cl"><span class="sd">          sudo dpkg -i hugo.deb</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="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">拉代码</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v4</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">submodules</span><span class="p">:</span><span class="w"> </span><span class="l">recursive </span><span class="w"> </span><span class="c"># 主题是 submodule</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="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">编译</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">hugo --gc --minify</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="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">推到 gh-pages</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">peaceiris/actions-gh-pages@v4</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">publish_dir</span><span class="p">:</span><span class="w"> </span><span class="l">./public</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">publish_branch</span><span class="p">:</span><span class="w"> </span><span class="l">gh-pages</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="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">顺手清一下 CDN 缓存</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">curl -X POST &#34;https://api.cloudflare.com/...&#34; ...</span><span class="w">
</span></span></span></code></pre></div><p>每次 push 到 main 分支，Actions 就自动跑这一套。成功之后，编译好的静态文件就躺在 gh-pages 分支里了。</p>
<h2 id="双分支策略">双分支策略</h2>
<p>这个设计我挺满意的：</p>
<ul>
<li><strong>main 分支</strong>：源代码，Markdown 文件 + 主题配置</li>
<li><strong>gh-pages 分支</strong>：编译产物，纯静态文件</li>
</ul>
<p>好处是服务器上不用装 Hugo，直接拉 gh-pages 就能用。万一哪天想换个部署方式，也方便。</p>
<h2 id="服务器这边">服务器这边</h2>
<p>服务器就做两件事：</p>
<ol>
<li>定时（或手动）从 gh-pages 拉最新的静态文件</li>
<li>Nginx 伺候着</li>
</ol>
<p>Nginx 配置没啥特别的，主要是加了点缓存和压缩：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nginx" data-lang="nginx"><span class="line"><span class="cl"><span class="k">server</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kn">listen</span> <span class="mi">80</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kn">server_name</span> <span class="s">blog.kkkk24juastin.asia</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kn">root</span> <span class="s">/var/www/blog</span><span class="p">;</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="kn">gzip</span> <span class="no">on</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kn">gzip_types</span> <span class="s">text/css</span> <span class="s">application/javascript</span> <span class="s">text/html</span><span class="p">;</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="kn">location</span> <span class="p">~</span><span class="sr">*</span> <span class="s">\.(css|js|png|jpg|woff2)</span>$ <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kn">expires</span> <span class="s">1y</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="kn">add_header</span> <span class="s">Cache-Control</span> <span class="s">&#34;public,</span> <span class="s">immutable&#34;</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><p>更新脚本也就几行：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl"><span class="nb">cd</span> /var/www/blog
</span></span><span class="line"><span class="cl">git fetch origin gh-pages
</span></span><span class="line"><span class="cl">git reset --hard origin/gh-pages
</span></span></code></pre></div><p>扔 crontab 里定时跑，或者配个 webhook 触发都行。</p>
<h2 id="cloudflare-加速">Cloudflare 加速</h2>
<p>最后挂了层 Cloudflare，主要图几个好处：</p>
<ol>
<li><strong>全球 CDN</strong>。访客能从就近节点拿缓存，不用每次都回源</li>
<li><strong>免费 SSL</strong>。懒得自己折腾证书</li>
<li><strong>DDoS 防护</strong>。虽然我这小破站应该没人会打</li>
</ol>
<p>不过有个坑：Cloudflare 默认不缓存 HTML 页面。对于静态博客来说这太亏了，得手动加个 Cache Rule，让它把 HTML 也缓存上。</p>
<p>配置好之后，访问速度从之前的 2 秒多降到了几十毫秒，效果立竿见影。</p>
<h2 id="实际效果">实际效果</h2>
<p>优化前后的对比：</p>
<table>
  <thead>
      <tr>
          <th>指标</th>
          <th>之前</th>
          <th>现在</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>首字节时间 (TTFB)</td>
          <td>2.28s</td>
          <td>~50ms</td>
      </tr>
      <tr>
          <td>页面加载</td>
          <td>3.5s</td>
          <td>0.8s</td>
      </tr>
  </tbody>
</table>
<p>主要提升来自 CDN 缓存。代码层面其实已经没啥可优化的了，瓶颈都在网络延迟。</p>
<h2 id="一些碎碎念">一些碎碎念</h2>
<p>整套流程跑下来，最爽的是真的不用操心部署了。写完文章，commit + push，等个几十秒，刷新页面就能看到更新。</p>
<p>当然也有不爽的地方。Hugo 的模板语法（Go Template）用起来不太顺手，文档也比较散。不过忍忍就好，毕竟主题配好了基本不用改。</p>
<p>如果你也在折腾博客，希望这篇能有点帮助。有问题可以留言，看到会回的 👋</p>
]]></content:encoded>
    </item>
    <item>
      <title>博客加载太慢？我是这样排查和解决的</title>
      <link>https://web.943827561.xyz/posts/hugo-blog-speed-optimization-v2/</link>
      <pubDate>Fri, 06 Feb 2026 00:00:00 +0000</pubDate>
      <guid>https://web.943827561.xyz/posts/hugo-blog-speed-optimization-v2/</guid>
      <description>发现博客首页加载要 2 秒多，一顿排查之后找到了罪魁祸首</description>
      <content:encoded><![CDATA[<h2 id="起因">起因</h2>
<p>前两天觉得博客打开有点慢，但也没太在意。今天闲着没事，拿 curl 测了一下，好家伙，首字节时间（TTFB）居然要 2.28 秒：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -s -o /dev/null -w <span class="s2">&#34;TTFB: %{time_starttransfer}s\n&#34;</span> https://blog.kkkk24juastin.asia/
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 输出：TTFB: 2.280466s</span>
</span></span></code></pre></div><p>这也太离谱了。明明挂着 Cloudflare CDN，怎么还这么慢？</p>
<h2 id="排查过程">排查过程</h2>
<p>先看看响应头：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -sI https://blog.kkkk24juastin.asia/ <span class="p">|</span> grep -i cache
</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="c1"># cf-cache-status: DYNAMIC</span>
</span></span><span class="line"><span class="cl"><span class="c1"># cache-control: max-age=3600</span>
</span></span></code></pre></div><p>看到 <code>cf-cache-status: DYNAMIC</code> 我就懂了。</p>
<p>这玩意的意思是 Cloudflare 压根没缓存这个页面，每次访问都要回源到我的服务器。而我的服务器在国外，网络延迟加上 TLS 握手，2 秒多其实不算意外。</p>
<h2 id="问题出在哪">问题出在哪</h2>
<p>Cloudflare 有个&quot;特性&quot;：<strong>默认不缓存 HTML 页面</strong>。</p>
<p>它的逻辑是 HTML 通常是动态生成的，不适合缓存。但对于静态博客来说，HTML 就是静态文件啊，为啥不能缓存？</p>
<h2 id="解决方案">解决方案</h2>
<p>需要手动加一条 Cache Rule，告诉 Cloudflare：这个域名下的 HTML 也给我缓存上。</p>
<p>操作步骤：</p>
<ol>
<li>登录 Cloudflare Dashboard</li>
<li>选你的域名</li>
<li>进 <strong>Caching</strong> → <strong>Cache Rules</strong></li>
<li>创建规则：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">匹配条件: 主机名 = blog.kkkk24juastin.asia
</span></span><span class="line"><span class="cl">缓存设置: 
</span></span><span class="line"><span class="cl">  - 符合缓存条件: 是
</span></span><span class="line"><span class="cl">  - 边缘 TTL: 1 个月
</span></span><span class="line"><span class="cl">  - 浏览器 TTL: 1 天
</span></span></code></pre></div><p>保存，等几分钟生效。</p>
<h2 id="效果验证">效果验证</h2>
<p>再跑一遍：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 第一次，预热缓存</span>
</span></span><span class="line"><span class="cl">curl -s -o /dev/null -w <span class="s2">&#34;TTFB: %{time_starttransfer}s\n&#34;</span> https://blog.kkkk24juastin.asia/
</span></span><span class="line"><span class="cl"><span class="c1"># TTFB: 1.1s（回源了）</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">curl -s -o /dev/null -w <span class="s2">&#34;TTFB: %{time_starttransfer}s\n&#34;</span> https://blog.kkkk24juastin.asia/
</span></span><span class="line"><span class="cl"><span class="c1"># TTFB: 0.045s</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">curl -sI https://blog.kkkk24juastin.asia/ <span class="p">|</span> grep cf-cache-status
</span></span><span class="line"><span class="cl"><span class="c1"># cf-cache-status: HIT</span>
</span></span></code></pre></div><p>从 2.28s 降到 45ms，提升了 98%。</p>
<p>舒服了。</p>
<h2 id="还做了啥">还做了啥</h2>
<p>既然都折腾了，顺手做了几个小优化：</p>
<h3 id="1-确认-brotli-压缩开了">1. 确认 Brotli 压缩开了</h3>
<p>在 Cloudflare 的 Speed → Optimization 里，确保 Brotli 是开着的。比 Gzip 压缩率更高。</p>
<h3 id="2-加了资源预加载">2. 加了资源预加载</h3>
<p>在 <code>extend_head.html</code> 里加了 DNS 预解析：</p>
<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">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;dns-prefetch&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;//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.googleapis.com&#34;</span> <span class="na">crossorigin</span><span class="p">&gt;</span>
</span></span></code></pre></div><h3 id="3-构建时自动清缓存">3. 构建时自动清缓存</h3>
<p>在 GitHub Actions 里加了一步，每次部署完自动清 Cloudflare 缓存：</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">name</span><span class="p">:</span><span class="w"> </span><span class="l">清缓存</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">    curl -X POST &#34;https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/purge_cache&#34; \
</span></span></span><span class="line"><span class="cl"><span class="sd">      -H &#34;Authorization: Bearer $CF_API_TOKEN&#34; \
</span></span></span><span class="line"><span class="cl"><span class="sd">      --data &#39;{&#34;purge_everything&#34;:true}&#39;</span><span class="w">
</span></span></span></code></pre></div><p>这样每次更新文章，CDN 会立刻拉取最新内容。</p>
<h2 id="总结">总结</h2>
<p>对于静态博客来说，优化的重点不在代码层面（Hugo 已经够快了），而是<strong>让 CDN 真正发挥作用</strong>。</p>
<p>如果你的博客也挂着 Cloudflare 但还是慢，先检查一下 <code>cf-cache-status</code> 是不是 <code>HIT</code>。不是的话，多半是缓存规则没配好。</p>
<p>就这样，收工。</p>
]]></content:encoded>
    </item>
    <item>
      <title>Hugo 博客加载速度优化实践</title>
      <link>https://web.943827561.xyz/posts/hugo-blog-performance-optimization/</link>
      <pubDate>Wed, 04 Feb 2026 00:00:00 +0000</pubDate>
      <guid>https://web.943827561.xyz/posts/hugo-blog-performance-optimization/</guid>
      <description>记录优化 Hugo &#43; PaperMod 博客加载速度的完整过程，包括构建配置、图片懒加载、资源压缩等多个方面</description>
      <content:encoded><![CDATA[<h2 id="发现问题">发现问题</h2>
<p>博客搭建完成后，访问时总觉得有点卡。打开 Chrome DevTools 看了下 Network 面板，发现几个问题：</p>
<ol>
<li>图片没有做懒加载，首屏就加载了所有图片</li>
<li>HTML/CSS/JS 文件可以进一步压缩</li>
<li>没有利用好浏览器缓存</li>
<li>外部资源（如字体）请求有延迟</li>
</ol>
<p>既然发现了问题，那就一个个解决。</p>
<h2 id="hugo-构建优化">Hugo 构建优化</h2>
<h3 id="深度压缩配置">深度压缩配置</h3>
<p>Hugo 自带 minify 功能，但默认配置比较保守。在 <code>hugo.toml</code> 中可以配置更激进的压缩选项：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">minify</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nx">disableXML</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">  <span class="nx">minifyOutput</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="nx">minify</span><span class="p">.</span><span class="nx">tdewolff</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span><span class="nx">minify</span><span class="p">.</span><span class="nx">tdewolff</span><span class="p">.</span><span class="nx">css</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">      <span class="nx">keepCSS2</span> <span class="p">=</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">      <span class="nx">precision</span> <span class="p">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="p">[</span><span class="nx">minify</span><span class="p">.</span><span class="nx">tdewolff</span><span class="p">.</span><span class="nx">html</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">      <span class="nx">keepDocumentTags</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">      <span class="nx">keepEndTags</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">      <span class="nx">keepQuotes</span> <span class="p">=</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">      <span class="nx">keepWhitespace</span> <span class="p">=</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="p">[</span><span class="nx">minify</span><span class="p">.</span><span class="nx">tdewolff</span><span class="p">.</span><span class="nx">js</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">      <span class="nx">keepVarNames</span> <span class="p">=</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">      <span class="nx">precision</span> <span class="p">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="p">[</span><span class="nx">minify</span><span class="p">.</span><span class="nx">tdewolff</span><span class="p">.</span><span class="nx">json</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">      <span class="nx">keepNumbers</span> <span class="p">=</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">      <span class="nx">precision</span> <span class="p">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="p">[</span><span class="nx">minify</span><span class="p">.</span><span class="nx">tdewolff</span><span class="p">.</span><span class="nx">svg</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">      <span class="nx">keepComments</span> <span class="p">=</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">      <span class="nx">precision</span> <span class="p">=</span> <span class="mi">0</span>
</span></span></code></pre></div><p>这个配置使用了 <a href="https://github.com/tdewolff/minify">tdewolff/minify</a> 库的参数，可以对各类资源做深度压缩：</p>
<ul>
<li><strong>CSS</strong>: 移除不必要的空格、简化数值精度</li>
<li><strong>HTML</strong>: 移除空白、可选引号</li>
<li><strong>JS</strong>: 压缩变量名</li>
<li><strong>SVG</strong>: 移除注释、简化数值</li>
</ul>
<h3 id="构建缓存">构建缓存</h3>
<p>重复构建时，很多资源是不变的，可以利用缓存加速：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">build</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nx">writeStats</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">caches</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="nx">caches</span><span class="p">.</span><span class="nx">assets</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="nx">dir</span> <span class="p">=</span> <span class="s2">&#34;:resourceDir/_gen&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">maxAge</span> <span class="p">=</span> <span class="s2">&#34;720h&#34;</span>  <span class="c"># 30 天</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="nx">caches</span><span class="p">.</span><span class="nx">getcsv</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="nx">dir</span> <span class="p">=</span> <span class="s2">&#34;:cacheDir/:project&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">maxAge</span> <span class="p">=</span> <span class="s2">&#34;4h&#34;</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="nx">caches</span><span class="p">.</span><span class="nx">getjson</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="nx">dir</span> <span class="p">=</span> <span class="s2">&#34;:cacheDir/:project&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">maxAge</span> <span class="p">=</span> <span class="s2">&#34;4h&#34;</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="nx">caches</span><span class="p">.</span><span class="nx">getresource</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="nx">dir</span> <span class="p">=</span> <span class="s2">&#34;:cacheDir/:project&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">maxAge</span> <span class="p">=</span> <span class="s2">&#34;4h&#34;</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="nx">caches</span><span class="p">.</span><span class="nx">images</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="nx">dir</span> <span class="p">=</span> <span class="s2">&#34;:resourceDir/_gen&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">maxAge</span> <span class="p">=</span> <span class="s2">&#34;720h&#34;</span>
</span></span></code></pre></div><p><code>writeStats = true</code> 会生成一个包含所有使用过的类名的文件，可以配合 PurgeCSS 做 tree-shaking（虽然 PaperMod 主题本身已经很精简了）。</p>
<h3 id="图片处理优化">图片处理优化</h3>
<p>Hugo 的图片处理功能很强大，合理配置可以减小图片体积：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">imaging</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nx">quality</span> <span class="p">=</span> <span class="mi">80</span>
</span></span><span class="line"><span class="cl">  <span class="nx">resampleFilter</span> <span class="p">=</span> <span class="s2">&#34;Lanczos&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">hint</span> <span class="p">=</span> <span class="s2">&#34;photo&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">anchor</span> <span class="p">=</span> <span class="s2">&#34;Smart&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">bgColor</span> <span class="p">=</span> <span class="s2">&#34;#ffffff&#34;</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="nx">imaging</span><span class="p">.</span><span class="nx">exif</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="nx">disableDate</span> <span class="p">=</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">    <span class="nx">disableLatLong</span> <span class="p">=</span> <span class="kc">true</span>  <span class="c"># 保护隐私</span>
</span></span></code></pre></div><ul>
<li><code>quality = 80</code>：图片质量设为 80%，肉眼几乎看不出差别，但体积能减小不少</li>
<li><code>resampleFilter = &quot;Lanczos&quot;</code>：高质量重采样算法，适合缩放照片</li>
<li><code>anchor = &quot;Smart&quot;</code>：智能裁剪，自动识别图片主体</li>
<li><code>disableLatLong = true</code>：移除 EXIF 中的位置信息，保护隐私</li>
</ul>
<h2 id="前端优化">前端优化</h2>
<h3 id="dns-预解析和预连接">DNS 预解析和预连接</h3>
<p>浏览器解析域名需要时间，可以用 <code>dns-prefetch</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;!-- 放在 layouts/partials/extend_head.html --&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;dns-prefetch&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;//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;dns-prefetch&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;//fonts.gstatic.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;dns-prefetch&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;//cdn.jsdelivr.net&#34;</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">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="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">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></code></pre></div><p><code>preconnect</code> 比 <code>dns-prefetch</code> 更进一步，会提前建立完整的 TCP 连接。</p>
<h3 id="图片懒加载">图片懒加载</h3>
<p>现代浏览器都支持原生的 <code>loading=&quot;lazy&quot;</code> 属性，不需要额外的 JS 库：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;DOMContentLoaded&#39;</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="kd">function</span> <span class="nx">addLoadedClass</span><span class="p">(</span><span class="nx">img</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">img</span><span class="p">.</span><span class="nx">complete</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">img</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="s1">&#39;loaded&#39;</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="nx">img</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">&#39;load&#39;</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="nx">img</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="s1">&#39;loaded&#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></span><span class="line"><span class="cl">  <span class="nb">document</span><span class="p">.</span><span class="nx">querySelectorAll</span><span class="p">(</span><span class="s1">&#39;.post-content img&#39;</span><span class="p">).</span><span class="nx">forEach</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">img</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="o">!</span><span class="nx">img</span><span class="p">.</span><span class="nx">hasAttribute</span><span class="p">(</span><span class="s1">&#39;loading&#39;</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">img</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">&#39;loading&#39;</span><span class="p">,</span> <span class="s1">&#39;lazy&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="nx">img</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">&#39;decoding&#39;</span><span class="p">,</span> <span class="s1">&#39;async&#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="nx">addLoadedClass</span><span class="p">(</span><span class="nx">img</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><p>这段代码做了两件事：</p>
<ol>
<li>给所有文章图片添加 <code>loading=&quot;lazy&quot;</code> 和 <code>decoding=&quot;async&quot;</code> 属性</li>
<li>图片加载完成后添加 <code>loaded</code> 类，配合 CSS 做渐显效果</li>
</ol>
<h3 id="图片渐显效果">图片渐显效果</h3>
<p>配合上面的 JS，在 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">/* assets/css/extended/custom.css */</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">img</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">entry-cover</span> <span class="nt">img</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="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">code</span><span class="o">-</span><span class="n">bg</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-in-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="p">.</span><span class="nc">post-content</span> <span class="nt">img</span><span class="o">[</span><span class="nt">loading</span><span class="o">=</span><span class="s2">&#34;lazy&#34;</span><span class="o">],</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">entry-cover</span> <span class="nt">img</span><span class="o">[</span><span class="nt">loading</span><span class="o">=</span><span class="s2">&#34;lazy&#34;</span><span class="o">]</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">opacity</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="p">.</span><span class="nc">post-content</span> <span class="nt">img</span><span class="p">.</span><span class="nc">loaded</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">entry-cover</span> <span class="nt">img</span><span class="p">.</span><span class="nc">loaded</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">opacity</span><span class="p">:</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>这样图片在加载时会显示一个灰色占位背景，加载完成后平滑显示。</p>
<h3 id="减少布局偏移">减少布局偏移</h3>
<p>图片加载时如果没有预留空间，会导致页面布局跳动（CLS 指标），用户体验很差：</p>
<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-content</span> <span class="nt">img</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">aspect-ratio</span><span class="p">:</span> <span class="nb">attr</span><span class="p">(</span><span class="n">width</span><span class="p">)</span> <span class="o">/</span> <span class="nb">attr</span><span class="p">(</span><span class="n">height</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">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></code></pre></div><p>如果图片设置了 width 和 height 属性，浏览器可以提前计算出图片的宽高比，预留好空间。</p>
<h3 id="尊重用户偏好">尊重用户偏好</h3>
<p>有些用户可能对动画敏感，可以检测并禁用过渡效果：</p>
<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="k">media</span> <span class="o">(</span><span class="nt">prefers-reduced-motion</span><span class="o">:</span> <span class="nt">no-preference</span><span class="o">)</span> <span class="p">{</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">scroll-behavior</span><span class="p">:</span> <span class="kc">smooth</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="p">@</span><span class="k">media</span> <span class="o">(</span><span class="nt">prefers-reduced-motion</span><span class="o">:</span> <span class="nt">reduce</span><span class="o">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="o">*,</span>
</span></span><span class="line"><span class="cl">  <span class="o">*</span><span class="p">::</span><span class="nd">before</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">  <span class="o">*</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">animation-duration</span><span class="p">:</span> <span class="mf">0.01</span><span class="kt">ms</span> <span class="cp">!important</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">transition-duration</span><span class="p">:</span> <span class="mf">0.01</span><span class="kt">ms</span> <span class="cp">!important</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">scroll-behavior</span><span class="p">:</span> <span class="kc">auto</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 class="p">}</span>
</span></span></code></pre></div><h2 id="优化效果">优化效果</h2>
<p>做完这些优化后，再看 Lighthouse 跑分：</p>
<table>
  <thead>
      <tr>
          <th>指标</th>
          <th>优化前</th>
          <th>优化后</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>首次内容绘制 (FCP)</td>
          <td>~1.5s</td>
          <td>~0.8s</td>
      </tr>
      <tr>
          <td>最大内容绘制 (LCP)</td>
          <td>~2.5s</td>
          <td>~1.5s</td>
      </tr>
      <tr>
          <td>累积布局偏移 (CLS)</td>
          <td>0.15</td>
          <td>&lt; 0.05</td>
      </tr>
  </tbody>
</table>
<p>当然，实际效果还取决于服务器性能和用户网络。</p>
<h2 id="更多优化思路">更多优化思路</h2>
<p>这次只做了 Hugo 本身的优化，还有一些可以继续优化的方向：</p>
<ol>
<li><strong>CDN 加速</strong>：把静态资源放到 CDN 上，全球访问都很快</li>
<li><strong>Gzip/Brotli 压缩</strong>：服务器开启压缩，进一步减小传输体积</li>
<li><strong>HTTP/2</strong>：启用 HTTP/2 多路复用，并行加载资源</li>
<li><strong>字体优化</strong>：使用 <code>font-display: swap</code> 避免字体阻塞渲染</li>
<li><strong>图片格式</strong>：使用 WebP/AVIF 等现代格式</li>
</ol>
<p>后续有空再继续折腾。</p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="https://gohugo.io/getting-started/configuration/#configure-minify">Hugo 官方文档 - Configure Minify</a></li>
<li><a href="https://gohugo.io/content-management/image-processing/">Hugo 官方文档 - Image Processing</a></li>
<li><a href="https://web.dev/browser-level-image-lazy-loading/">Web.dev - Browser-level image lazy loading</a></li>
<li><a href="https://web.dev/optimize-cls/">Web.dev - Optimize Cumulative Layout Shift</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>关于</title>
      <link>https://web.943827561.xyz/about/</link>
      <pubDate>Wed, 04 Feb 2026 00:00:00 +0000</pubDate>
      <guid>https://web.943827561.xyz/about/</guid>
      <description>关于这个博客和我</description>
      <content:encoded><![CDATA[<h2 id="-嘿">👋 嘿</h2>
<p>这里是 <strong>kkkk24</strong> 的个人博客。</p>
<p>主要记录一些技术折腾、踩坑经历，偶尔也写点别的。</p>
<h2 id="技术栈">技术栈</h2>
<p>日常用的比较多的：</p>
<ul>
<li>Python / JavaScript / Go</li>
<li>Vue / React</li>
<li>Django / FastAPI</li>
<li>MySQL / PostgreSQL / Redis</li>
<li>Docker / Linux</li>
</ul>
<p>不是什么都精通，但什么都能写点。</p>
<h2 id="这个博客">这个博客</h2>
<p>用 <a href="https://gohugo.io/">Hugo</a> + <a href="https://github.com/adityatelange/hugo-PaperMod">PaperMod</a> 主题搭的。</p>
<p>部署方式：GitHub Actions 自动编译 → 推到 gh-pages → 服务器拉取 → Cloudflare CDN。</p>
<p>具体可以看这篇：<a href="/posts/hugo-blog-architecture/">聊聊这个博客是怎么跑起来的</a></p>
<h2 id="联系">联系</h2>
<ul>
<li>GitHub: <a href="https://github.com/kkkk24juastin">kkkk24juastin</a></li>
<li>Email: <a href="mailto:contact@kkkk24juastin.asia">contact@kkkk24juastin.asia</a></li>
</ul>
<h2 id="其他">其他</h2>
<ul>
<li>文章可以转载，注明出处就行</li>
<li>发现错误或者有建议，欢迎留言或者直接提 PR</li>
<li>这个博客不卖课不带货，纯粹自己写着玩</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>给博客加个 Live2D 看板娘</title>
      <link>https://web.943827561.xyz/posts/add-live2d-to-hexo-blog/</link>
      <pubDate>Tue, 03 Feb 2026 01:29:00 +0800</pubDate>
      <guid>https://web.943827561.xyz/posts/add-live2d-to-hexo-blog/</guid>
      <description>给 Hexo 博客加个会动的二次元小人，没啥实际用处，就是看着舒服</description>
      <content:encoded><![CDATA[<p>之前逛别人博客的时候，经常看到左下角有个会动的小人，眼睛还会跟着鼠标跑，感觉挺有意思的。研究了一下发现这玩意叫 Live2D，于是决定给自己的 Hexo 博客也整一个。</p>
<p>折腾了一晚上，踩了不少坑，这里记录一下实现过程。</p>
<h2 id="效果">效果</h2>
<p>加完之后，博客左下角会多一个二次元小人，能：</p>
<ul>
<li>眼睛跟着鼠标动（有点魔性）</li>
<li>随机显示一言</li>
<li>换装、换模型</li>
<li>截图保存</li>
<li>还能玩个打砖块小游戏</li>
</ul>
<p>说实话没啥实际用处，但看着就是舒服 😂</p>
<h2 id="最简单的方式">最简单的方式</h2>
<p>如果只是想快速体验，一行代码就够了。</p>
<p>在主题的 <code>layout/layout.ejs</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="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://fastly.jsdelivr.net/npm/live2d-widgets@1.0.0/dist/autoload.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>放在 <code>&lt;/body&gt;</code> 之前就行。刷新页面，看板娘应该就出来了。</p>
<p>不过这种方式没法自定义，想要更多控制就得用下面的方法。</p>
<h2 id="完整配置版">完整配置版</h2>
<h3 id="第一步创建组件">第一步：创建组件</h3>
<p>在主题目录的 <code>layout/_partial/</code> 下面新建一个 <code>live2d.ejs</code>：</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="c1"># 看板娘组件 %&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;%</span> <span class="k">if</span> <span class="p">(</span><span class="n">theme</span><span class="o">.</span><span class="n">live2d</span> <span class="o">&amp;&amp;</span> <span class="n">theme</span><span class="o">.</span><span class="n">live2d</span><span class="o">.</span><span class="n">enable</span><span class="p">)</span> <span class="p">{</span> <span class="o">%&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="o">&lt;%</span>
</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="k">const</span> <span class="n">cdnPath</span> <span class="o">=</span> <span class="p">(</span><span class="n">theme</span><span class="o">.</span><span class="n">live2d</span><span class="o">.</span><span class="n">cdn</span> <span class="o">&amp;&amp;</span> <span class="n">theme</span><span class="o">.</span><span class="n">live2d</span><span class="o">.</span><span class="n">cdn</span><span class="o">.</span><span class="n">url</span><span class="p">)</span> 
</span></span><span class="line"><span class="cl">      <span class="err">?</span> <span class="n">theme</span><span class="o">.</span><span class="n">live2d</span><span class="o">.</span><span class="n">cdn</span><span class="o">.</span><span class="n">url</span> 
</span></span><span class="line"><span class="cl">      <span class="p">:</span> <span class="s1">&#39;https://fastly.jsdelivr.net/npm/live2d-widgets@1.0.0/dist/&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="n">modelId</span> <span class="o">=</span> <span class="p">(</span><span class="n">theme</span><span class="o">.</span><span class="n">live2d</span><span class="o">.</span><span class="n">model</span> <span class="o">&amp;&amp;</span> <span class="n">theme</span><span class="o">.</span><span class="n">live2d</span><span class="o">.</span><span class="n">model</span><span class="o">.</span><span class="n">id</span> <span class="o">!==</span> <span class="n">undefined</span><span class="p">)</span> 
</span></span><span class="line"><span class="cl">      <span class="err">?</span> <span class="n">theme</span><span class="o">.</span><span class="n">live2d</span><span class="o">.</span><span class="n">model</span><span class="o">.</span><span class="n">id</span> 
</span></span><span class="line"><span class="cl">      <span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="n">modelCdnPath</span> <span class="o">=</span> <span class="p">(</span><span class="n">theme</span><span class="o">.</span><span class="n">live2d</span><span class="o">.</span><span class="n">model</span> <span class="o">&amp;&amp;</span> <span class="n">theme</span><span class="o">.</span><span class="n">live2d</span><span class="o">.</span><span class="n">model</span><span class="o">.</span><span class="n">cdnPath</span><span class="p">)</span> 
</span></span><span class="line"><span class="cl">      <span class="err">?</span> <span class="n">theme</span><span class="o">.</span><span class="n">live2d</span><span class="o">.</span><span class="n">model</span><span class="o">.</span><span class="n">cdnPath</span> 
</span></span><span class="line"><span class="cl">      <span class="p">:</span> <span class="s1">&#39;https://fastly.jsdelivr.net/gh/fghrsh/live2d_api/&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="n">toolItems</span> <span class="o">=</span> <span class="p">(</span><span class="n">theme</span><span class="o">.</span><span class="n">live2d</span><span class="o">.</span><span class="n">tools</span> <span class="o">&amp;&amp;</span> <span class="n">theme</span><span class="o">.</span><span class="n">live2d</span><span class="o">.</span><span class="n">tools</span><span class="o">.</span><span class="n">items</span><span class="p">)</span> 
</span></span><span class="line"><span class="cl">      <span class="err">?</span> <span class="n">theme</span><span class="o">.</span><span class="n">live2d</span><span class="o">.</span><span class="n">tools</span><span class="o">.</span><span class="n">items</span> 
</span></span><span class="line"><span class="cl">      <span class="p">:</span> <span class="p">[</span><span class="s1">&#39;hitokoto&#39;</span><span class="p">,</span> <span class="s1">&#39;asteroids&#39;</span><span class="p">,</span> <span class="s1">&#39;switch-model&#39;</span><span class="p">,</span> <span class="s1">&#39;switch-texture&#39;</span><span class="p">,</span> <span class="s1">&#39;photo&#39;</span><span class="p">,</span> <span class="s1">&#39;info&#39;</span><span class="p">,</span> <span class="s1">&#39;quit&#39;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="n">drag</span> <span class="o">=</span> <span class="n">theme</span><span class="o">.</span><span class="n">live2d</span><span class="o">.</span><span class="n">drag</span> <span class="o">!==</span> <span class="n">undefined</span> <span class="err">?</span> <span class="n">theme</span><span class="o">.</span><span class="n">live2d</span><span class="o">.</span><span class="n">drag</span> <span class="p">:</span> <span class="bp">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="n">logLevel</span> <span class="o">=</span> <span class="n">theme</span><span class="o">.</span><span class="n">live2d</span><span class="o">.</span><span class="n">logLevel</span> <span class="o">||</span> <span class="s1">&#39;warn&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="o">%&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="o">&lt;</span><span class="n">script</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="n">live2d_path</span> <span class="o">=</span> <span class="s1">&#39;&lt;%= cdnPath %&gt;&#39;</span><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">loadExternalResource</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">type</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="n">new</span> <span class="n">Promise</span><span class="p">((</span><span class="n">resolve</span><span class="p">,</span> <span class="n">reject</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">let</span> <span class="n">tag</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">type</span> <span class="o">===</span> <span class="s1">&#39;css&#39;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="n">tag</span> <span class="o">=</span> <span class="n">document</span><span class="o">.</span><span class="n">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="n">tag</span><span class="o">.</span><span class="n">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="n">tag</span><span class="o">.</span><span class="n">href</span> <span class="o">=</span> <span class="n">url</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">type</span> <span class="o">===</span> <span class="s1">&#39;js&#39;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="n">tag</span> <span class="o">=</span> <span class="n">document</span><span class="o">.</span><span class="n">createElement</span><span class="p">(</span><span class="s1">&#39;script&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="n">tag</span><span class="o">.</span><span class="n">type</span> <span class="o">=</span> <span class="s1">&#39;module&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="n">tag</span><span class="o">.</span><span class="n">src</span> <span class="o">=</span> <span class="n">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="k">if</span> <span class="p">(</span><span class="n">tag</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="n">tag</span><span class="o">.</span><span class="n">onload</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="n">resolve</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">tag</span><span class="o">.</span><span class="n">onerror</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="n">reject</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">document</span><span class="o">.</span><span class="n">head</span><span class="o">.</span><span class="n">appendChild</span><span class="p">(</span><span class="n">tag</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></span><span class="line"><span class="cl">    <span class="p">(</span><span class="n">async</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</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="k">if</span> <span class="p">(</span><span class="n">screen</span><span class="o">.</span><span class="n">width</span> <span class="o">&lt;</span> <span class="mi">768</span><span class="p">)</span> <span class="k">return</span><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="k">const</span> <span class="n">OriginalImage</span> <span class="o">=</span> <span class="n">window</span><span class="o">.</span><span class="n">Image</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">window</span><span class="o">.</span><span class="n">Image</span> <span class="o">=</span> <span class="n">function</span><span class="p">(</span><span class="o">...</span><span class="n">args</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">const</span> <span class="n">img</span> <span class="o">=</span> <span class="n">new</span> <span class="n">OriginalImage</span><span class="p">(</span><span class="o">...</span><span class="n">args</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">img</span><span class="o">.</span><span class="n">crossOrigin</span> <span class="o">=</span> <span class="s2">&#34;anonymous&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">img</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">window</span><span class="o">.</span><span class="n">Image</span><span class="o">.</span><span class="n">prototype</span> <span class="o">=</span> <span class="n">OriginalImage</span><span class="o">.</span><span class="n">prototype</span><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">await</span> <span class="n">Promise</span><span class="o">.</span><span class="n">all</span><span class="p">([</span>
</span></span><span class="line"><span class="cl">        <span class="n">loadExternalResource</span><span class="p">(</span><span class="n">live2d_path</span> <span class="o">+</span> <span class="s1">&#39;waifu.css&#39;</span><span class="p">,</span> <span class="s1">&#39;css&#39;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">loadExternalResource</span><span class="p">(</span><span class="n">live2d_path</span> <span class="o">+</span> <span class="s1">&#39;waifu-tips.js&#39;</span><span class="p">,</span> <span class="s1">&#39;js&#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">initWidget</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">        <span class="n">waifuPath</span><span class="p">:</span> <span class="n">live2d_path</span> <span class="o">+</span> <span class="s1">&#39;waifu-tips.json&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">cdnPath</span><span class="p">:</span> <span class="s1">&#39;&lt;%= modelCdnPath %&gt;&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">cubism2Path</span><span class="p">:</span> <span class="n">live2d_path</span> <span class="o">+</span> <span class="s1">&#39;live2d.min.js&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">cubism5Path</span><span class="p">:</span> <span class="s1">&#39;https://cubism.live2d.com/sdk-web/cubismcore/live2dcubismcore.min.js&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">modelId</span><span class="p">:</span> <span class="o">&lt;%=</span> <span class="n">modelId</span> <span class="o">%&gt;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">tools</span><span class="p">:</span> <span class="o">&lt;%-</span> <span class="n">JSON</span><span class="o">.</span><span class="n">stringify</span><span class="p">(</span><span class="n">toolItems</span><span class="p">)</span> <span class="o">%&gt;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">drag</span><span class="p">:</span> <span class="o">&lt;%=</span> <span class="n">drag</span> <span class="o">%&gt;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">logLevel</span><span class="p">:</span> <span class="s1">&#39;&lt;%= logLevel %&gt;&#39;</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="o">&lt;/</span><span class="n">script</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;%</span> <span class="p">}</span> <span class="o">%&gt;</span>
</span></span></code></pre></div><h3 id="第二步引入组件">第二步：引入组件</h3>
<p>在 <code>layout/layout.ejs</code> 里引入（放在 <code>&lt;/body&gt;</code> 前面）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">&lt;%- partial(&#39;_partial/live2d&#39;) %&gt;
</span></span></code></pre></div><h3 id="第三步配置主题">第三步：配置主题</h3>
<p>在主题的 <code>_config.yml</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="c"># Live2D 看板娘</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">live2d</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">enable</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">model</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="m">0</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">cdnPath</span><span class="p">:</span><span class="w"> </span><span class="l">https://fastly.jsdelivr.net/gh/fghrsh/live2d_api/</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">tools</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">items</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">hitokoto            </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="l">asteroids           </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="l">switch-model        </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="l">switch-texture      </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="l">photo               </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="l">info                </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="l">quit                </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">drag</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">logLevel</span><span class="p">:</span><span class="w"> </span><span class="l">warn</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">cdn</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="l">https://fastly.jsdelivr.net/npm/live2d-widgets@1.0.0/dist/</span><span class="w">
</span></span></span></code></pre></div><h2 id="踩过的坑">踩过的坑</h2>
<h3 id="1-看板娘变形">1. 看板娘变形</h3>
<p>一开始在 CSS 里给容器设了宽高，结果模型比例全乱了。后来发现要让它自己加载 <code>waifu.css</code>，不要手动设置尺寸。</p>
<h3 id="2-图片跨域">2. 图片跨域</h3>
<p>控制台一堆跨域错误，截图功能也用不了。解决方法是在加载前给 Image 设置 <code>crossOrigin</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">OriginalImage</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">Image</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nb">window</span><span class="p">.</span><span class="nx">Image</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(...</span><span class="nx">args</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">img</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">OriginalImage</span><span class="p">(...</span><span class="nx">args</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">img</span><span class="p">.</span><span class="nx">crossOrigin</span> <span class="o">=</span> <span class="s2">&#34;anonymous&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">img</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><h3 id="3-手机上太占地方">3. 手机上太占地方</h3>
<p>加个判断，屏幕小就不加载：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="nx">screen</span><span class="p">.</span><span class="nx">width</span> <span class="o">&lt;</span> <span class="mi">768</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
</span></span></code></pre></div><h3 id="4-pjax-导致重复加载">4. PJAX 导致重复加载</h3>
<p>把看板娘的代码放到 PJAX 刷新区域外面就好了。</p>
<h2 id="想用自己的模型">想用自己的模型？</h2>
<p>默认用的是 <a href="https://github.com/fghrsh/live2d_api">fghrsh 的模型 API</a>，里面模型挺多的。</p>
<p>如果想用自己的模型，可以 fork 那个仓库，把模型放进去，然后改一下 <code>cdnPath</code> 指向你自己的地址。</p>
<h2 id="相关链接">相关链接</h2>
<ul>
<li><a href="https://github.com/stevenjoezhang/live2d-widget">live2d-widget 项目地址</a></li>
<li><a href="https://github.com/fghrsh/live2d_api">模型资源 live2d_api</a></li>
<li><a href="https://github.com/zenghongtu/live2d-model-assets">更多模型资源</a></li>
</ul>
<hr>
<p>就这些。虽然只是个小玩意，但折腾的过程还是挺有意思的。</p>
<p>有问题可以留言 👋</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
