<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Bruce's Blog</title>
    <link>https://swkajnbruceee.github.io/</link>
    <description>With each passing breeze,we exchange gentle greetings</description>
    <language>zh-CN</language>
    <copyright>All rights reserved 2026, BruceLee</copyright>
    <lastBuildDate>Sun, 30 Aug 2026 05:31:29 GMT</lastBuildDate>
    <generator>Hexo</generator>
    <atom:link href="https://swkajnbruceee.github.io/feed.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>Orin NX 远程连接与网线共享上网实战：网线供网 + 手机 USB 共享</title>
      <link>https://swkajnbruceee.github.io/2026/01/26/OrinNX%E8%BF%9C%E7%A8%8B%E8%BF%9E%E6%8E%A5-%E7%BD%91%E7%BA%BF%E5%85%B1%E4%BA%AB%E4%B8%8A%E7%BD%91-USB%E5%85%B1%E4%BA%AB/</link>
      <description>记录在远程连接场景下，通过电脑为 Jetson Orin NX 走网线上网的完整配置流程，涵盖 IP 转发、NAT、静态 IP、DNS、手机 USB 共享等细节与排错思路。</description>
      <author>BruceLee</author>
      <category domain="https://swkajnbruceee.github.io/categories/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/">技术分享</category>
      <category domain="https://swkajnbruceee.github.io/tags/Jetson/">Jetson</category>
      <category domain="https://swkajnbruceee.github.io/tags/Orin-NX/">Orin NX</category>
      <category domain="https://swkajnbruceee.github.io/tags/Linux/">Linux</category>
      <category domain="https://swkajnbruceee.github.io/tags/%E7%BD%91%E7%BB%9C%E5%85%B1%E4%BA%AB/">网络共享</category>
      <category domain="https://swkajnbruceee.github.io/tags/IP-%E8%BD%AC%E5%8F%91/">IP 转发</category>
      <category domain="https://swkajnbruceee.github.io/tags/NAT/">NAT</category>
      <category domain="https://swkajnbruceee.github.io/tags/SSH/">SSH</category>
      <pubDate>Mon, 26 Jan 2026 18:42:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h1 id="Orin-NX-远程连接与网线共享上网实战：网线供网-手机-USB-共享"><a href="#Orin-NX-远程连接与网线共享上网实战：网线供网-手机-USB-共享" class="headerlink" title="Orin NX 远程连接与网线共享上网实战：网线供网 + 手机 USB 共享"></a>Orin NX 远程连接与网线共享上网实战：网线供网 + 手机 USB 共享</h1><p>在没有显示器的情况下，我经常用 <strong>SSH 远程连接 Jetson Orin NX</strong>。问题来了：NX 通过网线连到电脑时，如何让它也能上网？更复杂一点，如果电脑的网络来自 <strong>手机 USB 共享</strong>，还能不能把这条网络再“转发”给 NX？这篇就把完整流程、关键命令与常见坑一次讲清楚。</p><p>适用场景：</p><ul><li>NX 无显示器，通过网线直连电脑</li><li>电脑上网方式为 WiFi 或手机 USB 共享</li><li>希望 NX 能正常 <code>apt</code>、拉代码、访问外网</li></ul><span id="more"></span><h1 id="🧭-网络拓扑"><a href="#🧭-网络拓扑" class="headerlink" title="🧭 网络拓扑"></a>🧭 网络拓扑</h1><p>我这里的典型拓扑如下（你也可以替换为自己的网卡名）：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">手机(4G/5G)</span><br><span class="line">   │ USB 共享</span><br><span class="line">   ▼</span><br><span class="line">电脑 (上网接口：usb0 / wlan0)</span><br><span class="line">   │ 网线</span><br><span class="line">   ▼</span><br><span class="line">Jetson Orin NX (接口：eth0)</span><br></pre></td></tr></table></figure><p>关键点：<strong>电脑作为网关</strong>，对外走 WiFi&#x2F;USB，对内把网络共享给 NX。</p><h1 id="✅-一、确认网卡名称"><a href="#✅-一、确认网卡名称" class="headerlink" title="✅ 一、确认网卡名称"></a>✅ 一、确认网卡名称</h1><p>不同系统网卡名可能不一样，先确认一下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ip -br a</span><br></pre></td></tr></table></figure><p>常见情况：</p><ul><li>电脑上网接口：<code>wlan0</code>（WiFi）或 <code>usb0</code> &#x2F; <code>enx...</code>（手机 USB 共享）</li><li>电脑连 NX 的口：<code>eth0</code> &#x2F; <code>enp3s0</code> &#x2F; <code>enx...</code></li><li>NX 侧接口：一般是 <code>eth0</code></li></ul><p>下文我用：</p><ul><li>电脑上网接口：<code>wlan0</code></li><li>电脑对 NX 的接口：<code>eth0</code></li></ul><p>你只需要把网卡名替换成自己的即可。</p><h1 id="✅-二、配置静态-IP（电脑与-NX）"><a href="#✅-二、配置静态-IP（电脑与-NX）" class="headerlink" title="✅ 二、配置静态 IP（电脑与 NX）"></a>✅ 二、配置静态 IP（电脑与 NX）</h1><p>为了稳定远程连接，建议给电脑和 NX 配置固定 IP。</p><p><strong>电脑（eth0）临时配置：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> ip addr add 192.168.55.1/24 dev eth0</span><br></pre></td></tr></table></figure><p><strong>NX（eth0）临时配置：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> ip addr add 192.168.55.2/24 dev eth0</span><br><span class="line"><span class="built_in">sudo</span> ip route add default via 192.168.55.1</span><br></pre></td></tr></table></figure><p><strong>DNS（NX 端）：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> resolvectl dns eth0 8.8.8.8 1.1.1.1</span><br></pre></td></tr></table></figure><p>如果你的系统没有 <code>resolvectl</code>，可以先用：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> bash -c <span class="string">&#x27;echo &quot;nameserver 8.8.8.8&quot; &gt; /etc/resolv.conf&#x27;</span></span><br></pre></td></tr></table></figure><blockquote><p>小贴士：临时 IP 重启会失效，长期使用建议用 NetworkManager 或 netplan 设为永久。</p></blockquote><h1 id="✅-三、开启-IP-转发（电脑上）"><a href="#✅-三、开启-IP-转发（电脑上）" class="headerlink" title="✅ 三、开启 IP 转发（电脑上）"></a>✅ 三、开启 IP 转发（电脑上）</h1><p>这是让电脑“当路由”的关键开关：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> sysctl -w net.ipv4.ip_forward=1</span><br></pre></td></tr></table></figure><p><strong>（可选，永久生效）</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">echo</span> <span class="string">&quot;net.ipv4.ip_forward=1&quot;</span> | <span class="built_in">sudo</span> <span class="built_in">tee</span> -a /etc/sysctl.conf</span><br></pre></td></tr></table></figure><h1 id="✅-四、配置-NAT（电脑上）"><a href="#✅-四、配置-NAT（电脑上）" class="headerlink" title="✅ 四、配置 NAT（电脑上）"></a>✅ 四、配置 NAT（电脑上）</h1><p>让 NX 的流量通过电脑的上网接口“伪装”出去：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE</span><br><span class="line"><span class="built_in">sudo</span> iptables -A FORWARD -i wlan0 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT</span><br><span class="line"><span class="built_in">sudo</span> iptables -A FORWARD -i eth0 -o wlan0 -j ACCEPT</span><br></pre></td></tr></table></figure><p>如果你的上网接口是手机 USB 共享，请把 <code>wlan0</code> 换成 <code>usb0</code> 或 <code>enxXXXX</code>。</p><h1 id="✅-五、手机-USB-共享的正确打开方式"><a href="#✅-五、手机-USB-共享的正确打开方式" class="headerlink" title="✅ 五、手机 USB 共享的正确打开方式"></a>✅ 五、手机 USB 共享的正确打开方式</h1><ol><li>手机连接电脑 USB</li><li>打开 <strong>USB 共享网络</strong>（不同手机菜单略有差异）</li><li>电脑会出现一个新的网卡（通常叫 <code>usb0</code> 或 <code>enx...</code>）</li><li>把这张网卡作为 <strong>上网接口</strong> 填到上面的 iptables 里</li></ol><p>你可以用下面命令确认：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ip -br a | grep -E <span class="string">&#x27;usb|enx&#x27;</span></span><br></pre></td></tr></table></figure><h1 id="✅-六、测试连通性"><a href="#✅-六、测试连通性" class="headerlink" title="✅ 六、测试连通性"></a>✅ 六、测试连通性</h1><p>在 NX 上依次测试：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">ping -c 4 192.168.55.1   <span class="comment"># 能否 ping 通电脑</span></span><br><span class="line">ping -c 4 8.8.8.8        <span class="comment"># 能否出公网</span></span><br><span class="line">ping -c 4 github.com     <span class="comment"># DNS 是否正常</span></span><br></pre></td></tr></table></figure><p>如果三步全通，说明共享成功。</p><h1 id="🧩-常见问题与排查思路"><a href="#🧩-常见问题与排查思路" class="headerlink" title="🧩 常见问题与排查思路"></a>🧩 常见问题与排查思路</h1><h2 id="1-能-ping-通电脑，不能上网"><a href="#1-能-ping-通电脑，不能上网" class="headerlink" title="1. 能 ping 通电脑，不能上网"></a>1. 能 ping 通电脑，不能上网</h2><ul><li>检查电脑是否开启 <code>ip_forward</code></li><li>检查 NAT 规则是否生效（网卡名是否写错）</li><li>检查 NX 默认网关是否指向 <code>192.168.55.1</code></li></ul><h2 id="2-能-ping-通公网-IP，不能解析域名"><a href="#2-能-ping-通公网-IP，不能解析域名" class="headerlink" title="2. 能 ping 通公网 IP，不能解析域名"></a>2. 能 ping 通公网 IP，不能解析域名</h2><ul><li>DNS 没配置好，重新设置 <code>resolvectl</code> 或 <code>/etc/resolv.conf</code></li></ul><h2 id="3-重启后规则失效"><a href="#3-重启后规则失效" class="headerlink" title="3. 重启后规则失效"></a>3. 重启后规则失效</h2><ul><li><code>ip addr</code>、<code>iptables</code> 都是临时的</li><li>可考虑安装 <code>iptables-persistent</code> 保存规则</li></ul><h2 id="4-UFW-防火墙拦截"><a href="#4-UFW-防火墙拦截" class="headerlink" title="4. UFW&#x2F;防火墙拦截"></a>4. UFW&#x2F;防火墙拦截</h2><ul><li>如果电脑启用了 UFW，可能会拦截转发</li><li>可临时关闭测试：</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> ufw <span class="built_in">disable</span></span><br></pre></td></tr></table></figure><h1 id="✅-一句话总结"><a href="#✅-一句话总结" class="headerlink" title="✅ 一句话总结"></a>✅ 一句话总结</h1><p><strong>电脑作为网关 + NX 静态 IP + IP 转发 + NAT</strong>，就能让 Orin NX 在网线直连的情况下稳定上网，即使电脑是靠手机 USB 共享也没问题。</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>LSTM + Kalman 分层融合自瞄：5 帧预判 20 帧的高精度坐标预测</title>
      <link>https://swkajnbruceee.github.io/2025/12/03/%E8%87%AA%E7%9E%84%E5%9D%90%E6%A0%87%E9%A2%84%E6%B5%8B-LSTM-Kalman%E5%88%86%E5%B1%82%E8%9E%8D%E5%90%88/</link>
      <description>
        <![CDATA[<h2 id="1-背景与目标"><a href="#1-背景与目标" class="headerlink" title="1. 背景与目标"></a>1. 背景与目标</h2><p>在自瞄&#x2F;跟踪场景里，我们希望只用前 5 帧 ROI，就能给出第 20 帧的边界框 <c]]>
      </description>
      <author>BruceLee</author>
      <category domain="https://swkajnbruceee.github.io/categories/CV/">CV</category>
      <category domain="https://swkajnbruceee.github.io/tags/%E8%87%AA%E7%9E%84/">自瞄</category>
      <category domain="https://swkajnbruceee.github.io/tags/%E6%97%B6%E5%BA%8F%E9%A2%84%E6%B5%8B/">时序预测</category>
      <category domain="https://swkajnbruceee.github.io/tags/Kalman-Filter/">Kalman Filter</category>
      <category domain="https://swkajnbruceee.github.io/tags/LSTM/">LSTM</category>
      <category domain="https://swkajnbruceee.github.io/tags/YOLO/">YOLO</category>
      <pubDate>Wed, 03 Dec 2025 08:30:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h2 id="1-背景与目标"><a href="#1-背景与目标" class="headerlink" title="1. 背景与目标"></a>1. 背景与目标</h2><p>在自瞄&#x2F;跟踪场景里，我们希望只用前 5 帧 ROI，就能给出第 20 帧的边界框 <code>(x, y, w, h)</code>，而且：</p><ul><li>精度高：R² 接近 1，MSE 够低。</li><li>实时：端到端毫秒级。</li><li>稳定：遮挡、噪声、尺度变化下抖动小。</li></ul><p>本文记录的方案把 LSTM 的时序感知和 Kalman 的稳健估计分层融合，同时保持极轻的推理开销。</p><h2 id="2-系统总览"><a href="#2-系统总览" class="headerlink" title="2. 系统总览"></a>2. 系统总览</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">视频帧 → YOLO 检测 → 64×64 ROI+上下文 → LSTM 时序编码</span><br><span class="line">              ↘（检测掩码）</span><br><span class="line">LSTM 隐状态 → 自适应 Kalman 滤波 → 分层门控融合 → 坐标/置信度头 → 预测第20帧</span><br></pre></td></tr></table></figure><ul><li>输入：5 帧 ROI（灰度 64×64 展平 4096 维）。</li><li>训练：20 帧序列（5 输入 + 15 间隔 + 1 目标）。</li><li>推理：仅需 5 帧，直接输出第 20 帧坐标。</li></ul><h2 id="3-关键代码与设计"><a href="#3-关键代码与设计" class="headerlink" title="3. 关键代码与设计"></a>3. 关键代码与设计</h2><h3 id="3-1-分层融合（门控-投影）"><a href="#3-1-分层融合（门控-投影）" class="headerlink" title="3.1 分层融合（门控 + 投影）"></a>3.1 分层融合（门控 + 投影）</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">FusionModule</span>(nn.Module):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, hidden_size</span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__()</span><br><span class="line">        <span class="variable language_">self</span>.lstm_enc = nn.Sequential(</span><br><span class="line">            nn.Linear(hidden_size, hidden_size), nn.ReLU(), nn.Dropout(<span class="number">0.1</span>))</span><br><span class="line">        <span class="variable language_">self</span>.kf_enc = nn.Sequential(</span><br><span class="line">            nn.Linear(hidden_size, hidden_size), nn.ReLU(), nn.Dropout(<span class="number">0.1</span>))</span><br><span class="line">        <span class="variable language_">self</span>.gate = nn.Sequential(</span><br><span class="line">            nn.Linear(hidden_size * <span class="number">2</span>, hidden_size), nn.ReLU(),</span><br><span class="line">            nn.Linear(hidden_size, <span class="number">1</span>), nn.Sigmoid())</span><br><span class="line">        <span class="variable language_">self</span>.proj = nn.Sequential(</span><br><span class="line">            nn.Linear(hidden_size, hidden_size), nn.ReLU(),</span><br><span class="line">            nn.Dropout(<span class="number">0.1</span>), nn.Linear(hidden_size, hidden_size))</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self, h_lstm, h_kf</span>):</span><br><span class="line">        hl = <span class="variable language_">self</span>.lstm_enc(h_lstm)</span><br><span class="line">        hk = <span class="variable language_">self</span>.kf_enc(h_kf)</span><br><span class="line">        g = <span class="variable language_">self</span>.gate(torch.cat([hl, hk], dim=-<span class="number">1</span>))</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.proj(g * hl + (<span class="number">1</span> - g) * hk)</span><br></pre></td></tr></table></figure><p>门控动态决定“信 LSTM 还是信 Kalman”，再统一投影，兼顾敏捷与平滑。</p><h3 id="3-2-自适应-Kalman（可学习噪声-状态转移）"><a href="#3-2-自适应-Kalman（可学习噪声-状态转移）" class="headerlink" title="3.2 自适应 Kalman（可学习噪声 + 状态转移）"></a>3.2 自适应 Kalman（可学习噪声 + 状态转移）</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">AdaptiveKalmanFilter</span>(nn.Module):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, hidden_size</span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__()</span><br><span class="line">        <span class="variable language_">self</span>.log_q = nn.Parameter(torch.tensor(-<span class="number">2.0</span>))</span><br><span class="line">        <span class="variable language_">self</span>.log_r = nn.Parameter(torch.tensor(-<span class="number">2.0</span>))</span><br><span class="line">        <span class="variable language_">self</span>.A = nn.Parameter(torch.eye(hidden_size) * <span class="number">0.95</span>)</span><br><span class="line">        <span class="variable language_">self</span>.P0 = torch.eye(hidden_size) * <span class="number">1.0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self, h_prev, h_obs</span>):</span><br><span class="line">        Q = torch.eye(<span class="variable language_">self</span>.A.size(<span class="number">0</span>), device=h_prev.device) * torch.exp(<span class="variable language_">self</span>.log_q)</span><br><span class="line">        R = torch.eye(<span class="variable language_">self</span>.A.size(<span class="number">0</span>), device=h_prev.device) * torch.exp(<span class="variable language_">self</span>.log_r)</span><br><span class="line">        P = <span class="variable language_">self</span>.P0.to(h_prev.device)</span><br><span class="line"></span><br><span class="line">        h_pred = torch.mm(h_prev, <span class="variable language_">self</span>.A.t())</span><br><span class="line">        P_pred = <span class="variable language_">self</span>.A @ P @ <span class="variable language_">self</span>.A.t() + Q</span><br><span class="line">        P_pred = P_pred.unsqueeze(<span class="number">0</span>).expand(h_prev.size(<span class="number">0</span>), -<span class="number">1</span>, -<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">        P_plus_R = P_pred + R.unsqueeze(<span class="number">0</span>).expand_as(P_pred)</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            L = torch.linalg.cholesky(P_plus_R)</span><br><span class="line">            inv = torch.cholesky_inverse(L)</span><br><span class="line">        <span class="keyword">except</span>:</span><br><span class="line">            inv = torch.pinverse(P_plus_R)</span><br><span class="line"></span><br><span class="line">        K = torch.bmm(P_pred, inv)</span><br><span class="line">        <span class="keyword">return</span> h_pred + torch.bmm(K, (h_obs - h_pred).unsqueeze(-<span class="number">1</span>)).squeeze(-<span class="number">1</span>)</span><br></pre></td></tr></table></figure><p>噪声与状态转移矩阵参与训练，对分布漂移更稳。</p><h3 id="3-3-主干与双头输出"><a href="#3-3-主干与双头输出" class="headerlink" title="3.3 主干与双头输出"></a>3.3 主干与双头输出</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">CoordinatePredictionModel</span>(nn.Module):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, input_size=<span class="number">4096</span>, hidden_size=<span class="number">128</span>, num_layers=<span class="number">2</span>, dropout=<span class="number">0.1</span></span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__()</span><br><span class="line">        <span class="variable language_">self</span>.lstm = LSTMFeatureExtractor(input_size, hidden_size, num_layers, dropout)</span><br><span class="line">        <span class="variable language_">self</span>.kf = AdaptiveKalmanFilter(hidden_size)</span><br><span class="line">        <span class="variable language_">self</span>.fusion = FusionModule(hidden_size)</span><br><span class="line">        <span class="variable language_">self</span>.coord_head = nn.Sequential(</span><br><span class="line">            nn.Linear(hidden_size, hidden_size//<span class="number">2</span>), nn.ReLU(), nn.Dropout(dropout),</span><br><span class="line">            nn.Linear(hidden_size//<span class="number">2</span>, hidden_size//<span class="number">4</span>), nn.ReLU(),</span><br><span class="line">            nn.Linear(hidden_size//<span class="number">4</span>, <span class="number">4</span>), nn.Sigmoid())</span><br><span class="line">        <span class="variable language_">self</span>.conf_head = nn.Sequential(</span><br><span class="line">            nn.Linear(hidden_size, hidden_size//<span class="number">4</span>), nn.ReLU(),</span><br><span class="line">            nn.Linear(hidden_size//<span class="number">4</span>, <span class="number">1</span>), nn.Sigmoid())</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self, x, h_prev=<span class="literal">None</span></span>):</span><br><span class="line">        <span class="keyword">if</span> h_prev <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">            h_prev = torch.zeros(x.size(<span class="number">0</span>), <span class="variable language_">self</span>.kf.A.size(<span class="number">0</span>), device=x.device)</span><br><span class="line">        h_lstm, _ = <span class="variable language_">self</span>.lstm(x)</span><br><span class="line">        h_kf = <span class="variable language_">self</span>.kf(h_prev, h_lstm)</span><br><span class="line">        h_fused = <span class="variable language_">self</span>.fusion(h_lstm, h_kf)</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.coord_head(h_fused), <span class="variable language_">self</span>.conf_head(h_fused)</span><br></pre></td></tr></table></figure><p>坐标头 + 置信度头分开，便于后处理&#x2F;可视化。</p><h3 id="3-4-ROI-与掩码"><a href="#3-4-ROI-与掩码" class="headerlink" title="3.4 ROI 与掩码"></a>3.4 ROI 与掩码</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)</span><br><span class="line">resized = cv2.resize(gray, (<span class="number">64</span>, <span class="number">64</span>))</span><br><span class="line"><span class="keyword">if</span> use_detection:</span><br><span class="line">    mask = create_mask_from_yolo(boxes, padding=<span class="number">15</span>)</span><br><span class="line">    resized = resized * (mask/<span class="number">255.0</span>) + resized * (<span class="number">1</span> - mask/<span class="number">255.0</span>) * <span class="number">0.1</span>  <span class="comment"># 留少量上下文</span></span><br><span class="line">normalized = resized.astype(np.float32) / <span class="number">255.0</span></span><br></pre></td></tr></table></figure><p>检测掩码 + padding 保留目标上下文，抑制背景噪声。</p><h2 id="4-训练与推理配置"><a href="#4-训练与推理配置" class="headerlink" title="4. 训练与推理配置"></a>4. 训练与推理配置</h2><ul><li>输入：5 帧 ROI（4096 维），预测第 20 帧。</li><li>模型：hidden&#x3D;128，LSTM 层&#x3D;2，dropout&#x3D;0.1。</li><li>优化：batch&#x3D;32，lr&#x3D;1e-3，epoch&#x3D;100。</li><li>数据：小视频训练（<del>668 帧），大视频验证（</del>7669 帧），检验泛化。</li><li>推理：端到端 2–3 ms（检测为主要开销，轻量化&#x2F;缓存可再降）。</li></ul><p>运行命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">conda create -n py310 python=3.10</span><br><span class="line">conda activate py310</span><br><span class="line">pip install -r requirements.txt</span><br><span class="line">pip install scikit-learn joblib scipy pandas tqdm seaborn</span><br><span class="line"></span><br><span class="line"><span class="comment"># 训练</span></span><br><span class="line">python train_coordinate_prediction.py</span><br><span class="line"></span><br><span class="line"><span class="comment"># 三列演示（原始 / 检测 / 预测）</span></span><br><span class="line">python coordinate_prediction_demo.py</span><br><span class="line"></span><br><span class="line"><span class="comment"># 多模型对比</span></span><br><span class="line">python coordinate_prediction_comparison.py</span><br></pre></td></tr></table></figure><h2 id="5-结果与对比（5→20-帧）"><a href="#5-结果与对比（5→20-帧）" class="headerlink" title="5. 结果与对比（5→20 帧）"></a>5. 结果与对比（5→20 帧）</h2><p><img src="/img/vision/coordinate_prediction_metrics_comparison.png" alt="多模型指标对比"><br><img src="/img/vision/coordinate_prediction_distribution_comparison.png" alt="误差分布对比"></p><table><thead><tr><th>模型</th><th>MSE</th><th>RMSE</th><th>MAE</th><th>R²</th><th>推理(ms)</th></tr></thead><tbody><tr><td><strong>LSTM-KF-Fusion-Hierarchical</strong></td><td><strong>0.000425 ± 0.000551</strong></td><td><strong>0.01709 ± 0.01152</strong></td><td><strong>0.01205 ± 0.00805</strong></td><td><strong>0.9934 ± 0.0091</strong></td><td><strong>2.46 ± 1.71</strong></td></tr><tr><td>DT</td><td>0.00312 ± 0.00321</td><td>0.04746 ± 0.02945</td><td>0.03852 ± 0.02869</td><td>0.9579 ± 0.0380</td><td>48.94 ± 0.11</td></tr><tr><td>AR</td><td>0.00425 ± 0.00347</td><td>0.05923 ± 0.02714</td><td>0.04603 ± 0.02644</td><td>0.9422 ± 0.0410</td><td>1.13 ± 0.07</td></tr><tr><td>SVR</td><td>0.00719 ± 0.00166</td><td>0.08426 ± 0.00967</td><td>0.07866 ± 0.01117</td><td>0.8960 ± 0.0131</td><td>19.94 ± 0.24</td></tr><tr><td>KNN</td><td>0.00160 ± 0.00171</td><td>0.03425 ± 0.02059</td><td>0.02613 ± 0.01619</td><td>0.9786 ± 0.0201</td><td>52.49 ± 1.88</td></tr><tr><td>Serial Kalman→LSTM</td><td>0.00551 ± 0.00726</td><td>0.06188 ± 0.04095</td><td>0.05149 ± 0.03431</td><td>0.9192 ± 0.1105</td><td>1.62 ± 0.18</td></tr><tr><td>Serial LSTM→Kalman</td><td>0.00485 ± 0.00585</td><td>0.06030 ± 0.03488</td><td>0.05038 ± 0.02966</td><td>0.9280 ± 0.0893</td><td>1.74 ± 0.19</td></tr><tr><td>ANN</td><td>0.01929 ± 0.01547</td><td>0.12179 ± 0.06678</td><td>0.10229 ± 0.05234</td><td>0.7369 ± 0.1843</td><td>25.88 ± 0.27</td></tr><tr><td>Standalone LSTM</td><td>0.02593 ± 0.00653</td><td>0.15929 ± 0.02356</td><td>0.14124 ± 0.02395</td><td>0.6248 ± 0.0742</td><td>2.54 ± 0.14</td></tr><tr><td>Standalone Kalman</td><td>0.02667 ± 0.00655</td><td>0.16163 ± 0.02346</td><td>0.14362 ± 0.02396</td><td>0.6137 ± 0.0746</td><td>2.51 ± 0.12</td></tr></tbody></table><h2 id="6-更多细节与亮点"><a href="#6-更多细节与亮点" class="headerlink" title="6. 更多细节与亮点"></a>6. 更多细节与亮点</h2><ul><li>为什么要分层融合：Kalman 擅长平滑与抗噪，LSTM 擅长捕捉序列模式，门控让二者动态取长补短。</li><li>5→20 的设定：比常见的短期预测更具挑战，能验证模型对长跨度运动的外推能力。</li><li>ROI + 上下文：在小目标&#x2F;遮挡场景中，少量上下文能显著降低误检与漂移。</li><li>置信度头：可用于过滤低置信度预测，或在可视化中用透明度&#x2F;颜色区分。</li></ul><h2 id="7-消融与经验"><a href="#7-消融与经验" class="headerlink" title="7. 消融与经验"></a>7. 消融与经验</h2><ul><li>去掉 Kalman：MSE 上升一个数量级，远期抖动明显。</li><li>去掉融合门控：快变&#x2F;遮挡下不稳，误差方差增大。</li><li>关闭上下文 padding：小目标易漏检，R² 明显下降。</li><li>序列长度减到 10：实时性更好但远期精度显著下降。</li></ul><p>调参提示：</p><ul><li>目标小且易抖：增大 padding，抬高置信度阈值。</li><li>剧烈运动：放宽 Kalman 噪声初值或增大 hidden。</li><li>部署：检测是瓶颈，先轻量化&#x2F;缓存检测；融合头极轻，可 ONNX&#x2F;TensorRT。</li></ul><h2 id="8-复现路线"><a href="#8-复现路线" class="headerlink" title="8. 复现路线"></a>8. 复现路线</h2><ol><li>Python 3.10，安装依赖：<code>requirements.txt</code> + <code>scikit-learn joblib scipy pandas tqdm seaborn</code>。</li><li>放置视频与 <code>best.pt</code> 权重。</li><li>训练：<code>python train_coordinate_prediction.py</code>。</li><li>演示：<code>python coordinate_prediction_demo.py</code> 生成三列视频（原始&#x2F;检测&#x2F;预测）。</li><li>评测：<code>python coordinate_prediction_comparison.py</code> 自动产出表和图。</li><li>无 GPU：降低 <code>num_sequences</code> 与 <code>sequence_length</code>，逻辑仍可跑通。</li></ol><h2 id="9-结语"><a href="#9-结语" class="headerlink" title="9. 结语"></a>9. 结语</h2><p>分层融合让 LSTM 的时序感知与 Kalman 的稳健估计互补，在“5 帧看穿 20 帧”的自瞄任务里兼顾了精度、延迟与稳定性。接下来可以尝试多尺度金字塔、检测-预测联合训练，以及端侧加速（多线程解码 + 轻量化检测）进一步压榨时延。</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>机器学习全篇笔记</title>
      <link>https://swkajnbruceee.github.io/2025/11/06/machine-learning-complete/</link>
      <description>梳理机器学习入门阶段最常遇到的八个知识点，从逻辑回归、激活函数到偏差与方差、迁移学习，配合公式与实战建议快速建立全景认知。</description>
      <author>BruceLee</author>
      <category domain="https://swkajnbruceee.github.io/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/">技术笔记</category>
      <category domain="https://swkajnbruceee.github.io/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/">机器学习</category>
      <category domain="https://swkajnbruceee.github.io/tags/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      <category domain="https://swkajnbruceee.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/">深度学习</category>
      <pubDate>Thu, 06 Nov 2025 13:30:43 GMT</pubDate>
      <content:encoded>
        <![CDATA[<p>在整理近期的学习笔记时，我把机器学习入门阶段最常被问到的几个模块重新梳理了一遍：从逻辑回归的概率建模，到激活函数为何能让网络“弯起来”；从梯度下降的优化逻辑，到成本函数与正则化之间的取舍；再到数据集划分、偏差方差调试，以及最后的迁移学习实践。希望这篇文章能成为你快速复盘和查缺补漏的随身卡片。</p><span id="more"></span><h1 id="一、逻辑回归"><a href="#一、逻辑回归" class="headerlink" title="一、逻辑回归"></a>一、逻辑回归</h1><p>逻辑回归（Logistic Regression）是一种用于解决<strong>二分类问题</strong>的经典机器学习算法，其核心思想是通过线性模型结合 Sigmoid 函数，将线性输出映射到 0 到 1 之间的概率值，从而实现对样本类别的概率预测【1†source】。尽管名称中包含“回归”，但它实际上是一种分类算法。</p><h3 id="1-核心原理"><a href="#1-核心原理" class="headerlink" title="1.核心原理"></a>1.核心原理</h3><ol><li><p><strong>线性模型</strong>：<br>逻辑回归首先计算输入特征（(X)）与权重（(W)）的线性组合：<br>[ z &#x3D; W^T X + b ]<br>其中 (b) 为偏置项。</p></li><li><p><strong>Sigmoid 函数</strong>：<br>通过 Sigmoid 函数将线性输出 (z) 映射到概率区间 ([0, 1])：<br>sigma(z) &#x3D; 1 &#x2F; (1 + e^(-z))</p><p>输出值表示样本属于正类的概率（例如 (P(y&#x3D;1|X))）。</p></li><li><p><strong>决策边界</strong>：<br>通常以 0.5 为阈值：  </p><ul><li>若 sigma(z) &gt;&#x3D; 0.5，预测为正类（1）；  </li><li>若 sigma(z) &lt;&#x3D; 0.5，预测为负类（0）。</li></ul></li></ol><h3 id="2-训练与损失函数"><a href="#2-训练与损失函数" class="headerlink" title="2.训练与损失函数"></a>2.训练与损失函数</h3><ul><li><p><strong>损失函数</strong>：使用交叉熵损失（Log Loss）衡量预测概率与真实标签的差异：  </p></li><li><p><img src="/img/machine-learning/image-20250924135209795.png" alt="image-20250924135209795"></p><p>其中 (y) 为真实标签（0 或 1），(\hat{y}) 为预测概率。</p></li><li><p><strong>参数优化</strong>：<br>通过梯度下降等优化算法最小化损失函数，更新权重 (W) 和偏置 (b)。</p></li></ul><h3 id="3-特点与适用场景"><a href="#3-特点与适用场景" class="headerlink" title="3.特点与适用场景"></a>3.特点与适用场景</h3><ul><li><p><strong>优点</strong>：  </p><ul><li>计算高效，可解释性强（权重反映特征重要性）；  </li><li>输出为概率，支持不确定性评估；  </li><li>适用于线性可分或近似线性可分的数据。</li></ul></li><li><p><strong>局限性</strong>：  </p><ul><li>无法直接处理非线性决策边界（需结合特征工程或核方法）；  </li><li>对特征多重共线性敏感。</li></ul></li></ul><h3 id="4-扩展应用"><a href="#4-扩展应用" class="headerlink" title="4.扩展应用"></a>4.扩展应用</h3><ul><li><strong>多分类问题</strong>：可通过 Softmax 函数推广到多分类（Multinomial Logistic Regression）【1†source】。  </li><li>在金融风控、医疗诊断、广告点击率预测等领域广泛应用。</li></ul><p>逻辑回归是机器学习入门的基础模型，理解其原理有助于深入学习更复杂的算法（如神经网络）。</p><h1 id="二、激活函数"><a href="#二、激活函数" class="headerlink" title="二、激活函数"></a>二、激活函数</h1><h3 id="1-神经网络为什么需要激活函数？"><a href="#1-神经网络为什么需要激活函数？" class="headerlink" title="1.神经网络为什么需要激活函数？"></a>1.神经网络为什么需要激活函数？</h3><blockquote><p>神经网络需要激活函数，核心原因是为网络引入非线性能力，否则无论网络有多少层，最终都只是线性变换，无法拟合现实世界中复杂的非线性问题。</p></blockquote><p>具体作用可分为三点：</p><ol><li><p>引入非线性：现实中的数据关系（如图像识别、语音处理中的特征映射）大多是非线性的。激活函数（如ReLU、Sigmoid）通过非线性变换，让神经网络能学习并表示这些复杂关系，例如区分不同形状的物体、识别不同语调的语音。</p></li><li><p>控制梯度流动：合理的激活函数（如ReLU）能缓解“梯度消失&#x2F;爆炸”问题。例如ReLU在正区间的梯度恒为1，可让深层网络的梯度有效传递，保证模型能正常训练；而早期的Sigmoid函数因两端梯度趋近于0，容易导致深层网络训练停滞。</p></li><li><p>调整输出特征分布：激活函数能将神经元的输出值映射到特定范围（如Sigmoid映射到[0,1]、Tanh映射到[-1,1]），使特征分布更稳定，避免因数值过大或过小导致网络训练波动，同时也能为后续层的计算提供更合适的输入特征。</p></li></ol><h3 id="2-激活函数：神经网络的非线性引擎"><a href="#2-激活函数：神经网络的非线性引擎" class="headerlink" title="2.激活函数：神经网络的非线性引擎"></a>2.激活函数：神经网络的非线性引擎</h3><p><strong>为什么必须使用非线性激活函数？</strong></p><blockquote><p>数学证明：线性网络的局限性<br>假设一个L层神经网络，每层使用线性激活函数：<br>𝑓(𝑥)&#x3D;𝑊𝐿(𝑊𝐿−1(⋯(𝑊2(𝑊1𝑥+𝑏1)+𝑏2)⋯)+𝑏𝐿−1)+𝑏𝐿<br>这可以简化为：<br>𝑓(𝑥)&#x3D;𝑊′𝑥+𝑏′</p></blockquote><p>结论：无论网络多深，线性激活函数都会使整个网络退化为单层线性模型，无法学习复杂非线性关系。</p><p><strong>非线性变换的重要性</strong></p><ul><li><p>   <strong>特征学习</strong>：通过非线性组合，网络可以学习数据的层次化特征表示</p></li><li><p>   <strong>万能近似定理</strong>：单隐藏层神经网络配合非线性激活函数可以近似任何连续函数</p></li><li><p>   <strong>决策边界</strong>：非线性激活函数使网络能够学习复杂的非线性决策边界</p></li></ul><h3 id="3-三个常见的激活函数"><a href="#3-三个常见的激活函数" class="headerlink" title="3.三个常见的激活函数"></a>3.三个常见的激活函数</h3><p><img src="/img/machine-learning/image-20251005192533841.png" alt="image-20251005192533841"></p><blockquote><p>线性激活函数不能用于隐藏层，因为它会将线性一直传递下去，导致输出层输出的还是线性函数<br>ReLu一般用于隐藏层，因为其大于0是，梯度恒为1，可保证模型正常训练 </p></blockquote><h1 id="三、梯度下降"><a href="#三、梯度下降" class="headerlink" title="三、梯度下降"></a>三、梯度下降</h1><p>梯度下降（Gradient Descent）是一种广泛应用于机器学习和深度学习中的优化算法，其核心目标是通过迭代调整模型参数，最小化损失函数（或目标函数）的值。以下从原理、算法步骤、关键组件、变种及优缺点等方面详细介绍。</p><h3 id="1-核心原理-1"><a href="#1-核心原理-1" class="headerlink" title="1.核心原理"></a>1.核心原理</h3><p>梯度下降基于函数的梯度（Gradient）方向进行优化。梯度是一个向量，表示函数在某一点对各参数偏导数的集合，指向函数值上升最快的方向。因此，<strong>负梯度方向</strong>即为函数值下降最快的方向。算法通过沿负梯度方向逐步更新参数，使损失函数值逐渐减小，最终收敛到局部最小值或全局最小值（若函数为凸函数）。</p><p>其数学原理可通过泰勒展开近似解释：<br><img src="/img/machine-learning/image-20251018120025377.png" alt="image-20251018120025377"></p><p><img src="/img/machine-learning/image-20251018120242101.png" alt="image-20251018120242101"></p><h3 id="2-算法步骤"><a href="#2-算法步骤" class="headerlink" title="2.算法步骤"></a>2.算法步骤</h3><ol><li><strong>初始化参数</strong>：<strong>随机</strong>选择初始参数值（如全零或随机值）。  </li><li><strong>计算梯度</strong>：计算当前参数下损失函数的梯度（即偏导数）。  </li><li><strong>更新参数</strong>：沿负梯度方向调整参数。  </li><li><strong>重复迭代</strong>：重复步骤2-3，直到梯度<strong>接近零或达到预设迭代次数</strong>。</li></ol><h3 id="3-关键组件"><a href="#3-关键组件" class="headerlink" title="3.关键组件"></a>3.关键组件</h3><ol><li><p><strong>学习率（Learning Rate, alpha）</strong>：  </p><ul><li>控制参数更新的步长。学习率<strong>过大会导致震荡或无法收敛</strong>；<strong>过小则收敛速度慢</strong>。  </li><li>需根据问题调整，也可采用自适应学习率策略（如Adam、RMSprop）。</li></ul></li><li><p><strong>损失函数（Loss Function）</strong>：  </p><ul><li>用于衡量模型预测值与真实值的误差，如均方误差（MSE）、交叉熵（Cross-Entropy）。  </li><li><strong>梯度下降的目标是最小化该函数。</strong></li></ul></li></ol><h3 id="4-主要变种算法"><a href="#4-主要变种算法" class="headerlink" title="4.主要变种算法"></a>4.主要变种算法</h3><p>梯度下降的经典变种包括：</p><ol><li><p><strong>批量梯度下降（BGD）</strong>：  </p><ul><li>每次迭代使用全部训练数据计算梯度，收敛稳定但计算开销大。</li></ul></li><li><p><strong>随机梯度下降（SGD）</strong>：  </p><ul><li>每次随机使用一个样本计算梯度，收敛快但波动大。</li></ul></li><li><p><strong>小批量梯度下降（MBGD）</strong>：  </p><ul><li>折中方案，使用小批量样本计算梯度，平衡效率与稳定性。</li></ul></li><li><p><strong>改进优化算法</strong>：  </p><ul><li><strong>Momentum</strong>：引入动量项加速收敛，减少震荡。  </li><li><strong>AdaGrad</strong>：自适应调整学习率，适合稀疏数据。  </li><li><strong>RMSprop</strong>：改进AdaGrad，避免学习率过早衰减。  </li><li><strong>Adam</strong>：结合Momentum和RMSprop的优点，自适应调整学习率和动量，广泛应用于深度学习。</li></ul></li></ol><h3 id="5-优缺点"><a href="#5-优缺点" class="headerlink" title="5.优缺点"></a>5.优缺点</h3><ul><li><p><strong>优点</strong>：  </p><ul><li>通用性强，适用于多种模型（如线性回归、神经网络）。  </li><li>实现简单，计算效率高（尤其变种算法如SGD、Adam）。</li></ul></li><li><p><strong>缺点</strong>：  </p><ul><li>可能收敛到局部最小值（非凸函数中）。  </li><li>学习率选择敏感，需调参。  </li><li>对高维或稀疏数据可能收敛慢。</li></ul></li></ul><h3 id="6-应用场景"><a href="#6-应用场景" class="headerlink" title="6.应用场景"></a>6.应用场景</h3><p>梯度下降是机器学习的核心优化工具，常用于：  </p><ul><li>神经网络训练（如CNN、RNN）。  </li><li>线性回归、逻辑回归的参数求解。  </li><li>深度学习框架（如PyTorch、TensorFlow）中的优化器实现。</li></ul><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>梯度下降通过迭代和梯度信息高效优化模型参数，其变种算法（如Adam）进一步提升了收敛速度和稳定性。理解其原理及组件（如学习率、损失函数）对机器学习实践至关重要。实际应用中需根据数据特性选择合适的变种算法，并合理调参以达到最佳效果。</p><h1 id="四、成本函数，损失函数及其正则化"><a href="#四、成本函数，损失函数及其正则化" class="headerlink" title="四、成本函数，损失函数及其正则化"></a>四、成本函数，损失函数及其正则化</h1><h2 id="1-区别与联系"><a href="#1-区别与联系" class="headerlink" title="1.区别与联系"></a>1.区别与联系</h2><p>首先，需要理解这两个密切相关但略有区别的概念。</p><ul><li><p><strong>损失函数 (Loss Function)，又称误差函数 (Error Function)</strong></p><ul><li><strong>定义</strong>：衡量<strong>单个训练样本</strong>的预测值 与真实值之间的差异或“损失”的函数。它计算的是单个点的误差。</li><li><strong>目的</strong>：指导模型如何根据一个样本调整其参数以减少误差。</li><li><strong>常见类型</strong>：<ul><li>**均方误差 (MSE - Mean Squared Error)**：主要用于回归问题。损失值为预测值与真实值之差的平方。</li><li>**交叉熵损失 (Cross-Entropy Loss)**：主要用于分类问题。衡量预测概率分布与真实概率分布之间的差异。</li></ul></li></ul></li><li><p><strong>成本函数 (Cost Function)，又称目标函数 (Objective Function)</strong></p><ul><li><p><strong>定义</strong>：衡量<strong>整个训练集</strong>上所有样本的预测值与真实值之间的平均差异。它是所有样本损失函数的平均值。</p></li><li><p><strong>目的</strong>：作为模型训练的整体优化目标。机器学习算法的核心就是找到一组参数 alpha，使得成本函数 J(alpha) 的值<strong>最小化</strong>。</p></li></ul></li></ul><blockquote><h3 id="成本函数是损失函数在整个数据集上的聚合。"><a href="#成本函数是损失函数在整个数据集上的聚合。" class="headerlink" title="成本函数是损失函数在整个数据集上的聚合。"></a>成本函数是损失函数在整个数据集上的聚合。</h3></blockquote><p><strong>简单比喻</strong>：</p><ul><li><strong>损失函数</strong>：就像你一次考试中做错一道题扣的分数。</li><li><strong>成本函数</strong>：就像你整个学期所有考试的平均分。你的目标是让这个平均分（成本函数）越高（对应误差越低）越好。</li></ul><h2 id="2-线性回归的平方误差成本函数"><a href="#2-线性回归的平方误差成本函数" class="headerlink" title="2.线性回归的平方误差成本函数"></a>2.线性回归的平方误差成本函数</h2><p><img src="/img/machine-learning/image-20251018140130269.png" alt="image-20251018140130269"><br>• 核心含义：用于线性回归任务（预测连续值，如房价、气温），衡量模型预测值与真实值的“平方误差”。</p><p>• 公式解释：<br>◦ m 是训练样本数量；</p><p>◦ 除以 2m 是为了后续求导时简化计算（导数中的系数会抵消）。</p><p>• 直观理解：左边的坐标系中，蓝色直线是模型预测的直线，红色“×”是真实数据点，误差是“真实点到直线的垂直距离的平方和”，损失函数的目标是最小化这个平方和，让直线尽可能拟合所有数据点。</p><h2 id="3-逻辑回归的交叉熵成本函数"><a href="#3-逻辑回归的交叉熵成本函数" class="headerlink" title="3.逻辑回归的交叉熵成本函数"></a>3.逻辑回归的交叉熵成本函数</h2><h3 id="（1）分段形式"><a href="#（1）分段形式" class="headerlink" title="（1）分段形式"></a>（1）分段形式</h3><p><img src="/img/machine-learning/image-20251018141737129.png" alt="image-20251018141737129"></p><h3 id="（2）简洁形式"><a href="#（2）简洁形式" class="headerlink" title="（2）简洁形式"></a>（2）简洁形式</h3><p><img src="/img/machine-learning/image-20251018141754608.png" alt="image-20251018141754608"></p><h2 id="3-正则化-Regularization"><a href="#3-正则化-Regularization" class="headerlink" title="3. 正则化 (Regularization)"></a>3. 正则化 (Regularization)</h2><h4 id="为什么要正则化？"><a href="#为什么要正则化？" class="headerlink" title="为什么要正则化？"></a>为什么要正则化？</h4><p>当模型过于复杂（参数过多或权重过大）时，它可能会过度拟合（Overfitting）训练数据。这意味着它在训练集上表现非常好，但遇到未见过的测试数据时表现很差，泛化能力弱。正则化是一种用于<strong>防止过拟合</strong>的技术。</p><h4 id="核心思想"><a href="#核心思想" class="headerlink" title="核心思想"></a>核心思想</h4><p>在成本函数中额外添加一个“惩罚项”（Penalty Term），这个惩罚项与模型参数的复杂度（通常是权重的大小）成正比。这样，在最小化成本函数的过程中，算法不仅要去拟合数据，还要尽量保持参数的值较小，从而约束模型的复杂度，使其变得更简单、更平滑。</p><h4 id="常见的正则化方法"><a href="#常见的正则化方法" class="headerlink" title="常见的正则化方法"></a>常见的正则化方法</h4><p>(1)<strong>L2 正则化 (L2 Regularization) &#x2F; 岭回归 (Ridge Regression)</strong></p><ul><li><strong>惩罚项</strong>：所有权重参数的<strong>平方和</strong>。</li><li><strong>效果</strong>：倾向于让权重参数趋向于0，但不会完全变为0（除非必要）。它使模型的所有特征都保留一点贡献，但削弱不重要特征的影响。这使得解更稳定。</li><li><strong>公式</strong>：正则化后的成本函数为：</li><li><img src="/img/machine-learning/image-20251018142037453.png" alt="image-20251018142037453"><ul><li>lambda：<strong>正则化参数</strong>，是一个超参数，控制惩罚的强度。$\lambda$ 越大，惩罚越重，模型越简单。</li><li>1&#x2F;2m：是为了方便求导后的计算，有时也会省略。</li></ul></li></ul><p>(2)<strong>L1 正则化 (L1 Regularization) &#x2F; 套索回归 (Lasso Regression)</strong></p><ul><li><p><strong>惩罚项</strong>：所有权重参数的<strong>绝对值之和</strong>。</p></li><li><p><strong>效果</strong>：倾向于让一部分不重要的特征的权重<strong>直接变为0</strong>。因此，L1正则化天然具备<strong>特征选择（Feature Selection）</strong> 的能力，可以产生稀疏的模型。</p></li><li><p><strong>公式</strong>：<br><img src="/img/machine-learning/image-20251018142207560.png" alt="image-20251018142207560"></p></li></ul><p>(3)<strong>弹性网络正则化 (Elastic Net)</strong><br>     <strong>惩罚项</strong>：L1正则化和L2正则化的结合。<br>     <strong>效果</strong>：结合了L1和L2的优点，既能进行特征选择，又能保持模型的稳定性。通常在特征高度相关时使用。</p><p><img src="/img/machine-learning/image-20251018142353373.png" alt="image-20251018142353373"></p><h4 id="正则化代码"><a href="#正则化代码" class="headerlink" title="正则化代码"></a>正则化代码</h4><p><img src="/img/machine-learning/image-20251018153216962.png" alt="image-20251018153216962"></p><h2 id="4-总结与关系"><a href="#4-总结与关系" class="headerlink" title="4.总结与关系"></a>4.总结与关系</h2><table><thead><tr><th align="left">概念</th><th align="left">作用层级</th><th align="left">目的</th><th align="left">示例</th></tr></thead><tbody><tr><td align="left"><strong>损失函数 (Loss)</strong></td><td align="left">单个样本</td><td align="left">计算单个预测的误差</td><td align="left">MSE, Cross-Entropy</td></tr><tr><td align="left"><strong>成本函数 (Cost)</strong></td><td align="left">整个数据集</td><td align="left">衡量模型整体误差，作为优化目标</td><td align="left">$J(\theta) &#x3D; \frac{1}{m}\sum L$</td></tr><tr><td align="left"><strong>正则化 (Regularization)</strong></td><td align="left">成本函数</td><td align="left">在成本函数中添加惩罚项，防止过拟合</td><td align="left">L1正则化, L2正则化</td></tr></tbody></table><p><strong>最终，一个完整的、带有正则化的机器学习模型的目标是：</strong></p><p><strong>最小化正则化后的成本函数：</strong><br><img src="/img/machine-learning/image-20251018142453758.png" alt="image-20251018142453758"></p><p>其中 R(theta)是正则化项（如 L1 或 L2 范数）。通过调整超参数 lambda，我们可以在“拟合训练数据”和“保持模型简单”之间找到最佳平衡。</p><p>Sigmoid函数和Softmax函数都是机器学习中常用的激活函数，主要用于将模型的原始输出转换为概率分布，但它们在应用场景和数学特性上存在显著差异。</p><h1 id="五、Sigmoid函数与-Softmax函数"><a href="#五、Sigmoid函数与-Softmax函数" class="headerlink" title="五、Sigmoid函数与 Softmax函数"></a>五、Sigmoid函数与 Softmax函数</h1><h2 id="1-Sigmoid函数"><a href="#1-Sigmoid函数" class="headerlink" title="1.Sigmoid函数"></a>1.Sigmoid函数</h2><p>Sigmoid函数（也称为逻辑函数）是一种S型曲线函数，其数学表达式为：</p><p><img src="/img/machine-learning/image-20251018143307539.png" alt="image-20251018143307539"></p><p>该函数将任意实数输入映射到(0,1)区间内，具有以下特性：</p><ul><li><p><strong>输出范围</strong>：(0,1)，适合表示概率。</p></li><li><p><strong>连续性</strong>：函数处处连续且光滑，便于求导。</p></li><li><p><strong>对称性</strong>：关于点(0,0.5)中心对称。</p></li><li><p><strong>导数特性</strong>：其导数可以用自身表示，即</p><p><img src="/img/machine-learning/image-20251018143330945.png" alt="image-20251018143330945"></p><p>，计算高效。</p></li></ul><p><strong>应用场景</strong>：</p><ul><li><strong>二分类问题</strong>：常用于逻辑回归或神经网络的输出层，将输出解释为样本属于正类的概率[[4]][[5]]。</li><li><strong>阈值函数</strong>：在神经网络中作为激活函数，引入非线性（但现代深度学习较少使用，因存在梯度消失问题）。</li><li><strong>其他领域</strong>：如心理学中的学习曲线建模、热传导问题及岩土工程中的土压力计算[[6]][[7]]。</li></ul><p><strong>优缺点</strong>：</p><ul><li>优点：输出概率意义明确、求导简单。</li><li>缺点：易导致梯度消失（输入值极大或极小时梯度接近零）、输出非零中心（可能影响训练效率）。</li></ul><h2 id="2-Softmax函数"><a href="#2-Softmax函数" class="headerlink" title="2.Softmax函数"></a>2.Softmax函数</h2><p>Softmax函数是Sigmoid函数在多分类问题中的推广，其数学表达式为：</p><p><img src="/img/machine-learning/image-20251018143408460.png" alt="image-20251018143408460"><br>其中 (z &#x3D; (z_1, z_2, …, z_K)) 是一个K维向量。该函数将任意实数向量转换为概率分布，具有以下特性：</p><ul><li><strong>输出性质</strong>：所有输出值在(0,1)范围内，且总和为1，符合概率公理。</li><li><strong>单调性</strong>：输入值 (z_i) 增大时，对应输出概率增加。</li><li><strong>数值稳定性</strong>：可通过减去输入向量中的最大值（即 (z_i - max(z))）避免计算指数时溢出。</li></ul><p><strong>应用场景</strong>：</p><ul><li><strong>多分类问题</strong>：广泛用于神经网络的输出层（如图像分类、自然语言处理），将原始输出转换为各类别的概率分布。</li><li><strong>与交叉熵损失结合</strong>：作为损失函数的一部分，优化模型预测与真实标签的差异。</li></ul><p><strong>优缺点</strong>：</p><ul><li>优点：输出为概率分布，直观且适用于多类别场景；与交叉熵损失配合时梯度计算高效。</li><li>缺点：计算复杂度较高（需计算所有元素的指数和）；可能因指数函数导致数值不稳定（需通过优化技巧避免）。</li></ul><h2 id="3-关键区别"><a href="#3-关键区别" class="headerlink" title="3.关键区别"></a>3.关键区别</h2><ol><li><strong>输出维度</strong>：<ul><li>Sigmoid：标量输出，适用于二分类。</li><li>Softmax：向量输出，适用于多分类（K≥2）。</li></ul></li><li><strong>概率和</strong>：<ul><li>Sigmoid：单个输出值表示概率，但多个Sigmoid输出的总和不一定为1。</li><li>Softmax：所有输出值之和严格为1，形成概率分布。</li></ul></li><li><strong>数学关系</strong>：当K&#x3D;2时，Softmax可退化为Sigmoid，但两者在实现和解释上通常区别处理[[14]][[15]]。</li></ol><h2 id="4-选择建议"><a href="#4-选择建议" class="headerlink" title="4.选择建议"></a>4.选择建议</h2><ul><li>使用<strong>Sigmoid</strong>：当问题为二分类或需对多个二分类任务独立输出概率时（如多标签分类）。</li><li>使用<strong>Softmax</strong>：当问题为互斥多分类（每个样本仅属一个类别）且需概率分布时。</li></ul><p>两者均为深度学习中的基础组件，实际选择需结合具体任务和模型结构。</p><p><img src="/img/machine-learning/image-20251018143714514.png" alt="image-20251018143714514"></p><p><img src="/img/machine-learning/image-20251018143723860.png" alt="image-20251018143723860"></p><h2 id="5-代码实现"><a href="#5-代码实现" class="headerlink" title="5.代码实现"></a>5.代码实现</h2><h3 id="基础形式"><a href="#基础形式" class="headerlink" title="基础形式"></a>基础形式</h3><p><img src="/img/machine-learning/image-20251018143813526.png" alt="image-20251018143813526"></p><h3 id="改进形式"><a href="#改进形式" class="headerlink" title="改进形式"></a>改进形式</h3><p><img src="/img/machine-learning/image-20251018143829547.png" alt="image-20251018143829547"></p><h3 id="改进原因"><a href="#改进原因" class="headerlink" title="改进原因"></a>改进原因</h3><p>这是在讲解数值精度优化，核心是通过调整模型的损失函数和激活函数实现更准确的计算，避免“数值舍入误差”。</p><p><strong>1. 逻辑回归的改进逻辑</strong></p><p>在逻辑回归中，原本的损失函数依赖sigmoid输出的概率  a （即  a &#x3D; 1&#x2F;（1 + e ^ (-z)））。但直接计算  a  时，若  z  过大或过小，容易出现数值溢出（比如  e^(-z)  趋近于0或无穷大）。</p><p>改进方法是绕过中间概率  a ，直接用原始得分  z  计算损失，</p><p><img src="/img/machine-learning/image-20251018144511221.png" alt="image-20251018144511221"></p><p>让框架直接基于原始得分  z  计算损失，避免中间步骤的精度丢失。</p><p><strong>2. Softmax回归（以MNIST为例）的改进逻辑</strong></p><p>在多分类任务中，原本的模型最后一层用softmax激活函数输出概率。但同样存在数值溢出问题（ e^(z_j)  可能因  z_j  过大而溢出）。</p><p>改进方法是最后一层用线性激活（linear）输出原始得分  z_j ，再在损失函数中集成Softmax计算。代码中通过设置 from_logits&#x3D;True，让损失函数（如SparseCategoricalCrossentropy）内部同时完成<strong>“Softmax概率转换 + 交叉熵损失计算”</strong>，这样可以利用框架的数值稳定优化（比如先对  z_j  做归一化再计算指数），避免直接计算softmax时的精度问题。</p><p>总结来说，这种改进的核心是跳过中间概率的显式计算，直接基于模型的原始得分（logits）计算损失，从而避免数值舍入或溢出带来的精度误差，让模型训练更稳定、结果更准确。</p><p><strong>3.关于原本的损失函数sigmoid化简后为什么不会出现下溢</strong></p><p><img src="/img/machine-learning/image-20251018145124316.png" alt="image-20251018145124316"></p><h1 id="六、训练集，验证集以及测试集"><a href="#六、训练集，验证集以及测试集" class="headerlink" title="六、训练集，验证集以及测试集"></a>六、训练集，验证集以及测试集</h1><p>在机器学习中，数据集通常被划分为训练集、验证集和测试集，三者分工明确，共同确保模型的可靠性和泛化能力。</p><h2 id="1-训练集（Training-Set）"><a href="#1-训练集（Training-Set）" class="headerlink" title="1.训练集（Training Set）"></a>1.训练集（Training Set）</h2><p>训练集是模型学习的基础数据，用于拟合模型参数（如神经网络中的权重和偏置）。通过反复迭代训练，模型从训练集中学习数据的内在规律和特征，逐步优化其预测能力。</p><h2 id="2-验证集（Validation-Set）"><a href="#2-验证集（Validation-Set）" class="headerlink" title="2.验证集（Validation Set）"></a>2.验证集（Validation Set）</h2><p>验证集用于模型调优和超参数选择（如学习率、网络层数等）。在训练过程中，通过验证集评估不同超参数组合下模型的性能，防止过拟合（即模型过度适应训练数据而丧失泛化能力），并辅助决策（如早停法）。其核心作用是提供“模拟考试”环境，避免测试集被间接泄露到训练环节。</p><h2 id="3-测试集（Test-Set）"><a href="#3-测试集（Test-Set）" class="headerlink" title="3.测试集（Test Set）"></a>3.测试集（Test Set）</h2><p>测试集是模型训练完成后的最终评估数据，用于无偏评估模型的泛化性能。它模拟真实场景中的未知数据，检验模型是否具备实际应用价值。测试集必须严格独立于训练和验证过程，且仅使用一次。</p><h2 id="4-为何需要验证集？"><a href="#4-为何需要验证集？" class="headerlink" title="4.为何需要验证集？"></a>4.为何需要验证集？</h2><p>引入验证集的主要原因包括：  </p><ol><li><strong>超参数优化</strong>：超参数（如正则化强度、迭代次数）无法通过训练集学习，需依赖验证集进行调优。  </li><li><strong>防止过拟合</strong>：仅依赖训练误差选择模型可能导致过拟合，验证集提供中间评估，帮助识别泛化能力最佳的模型。  </li><li><strong>保护测试集独立性</strong>：若直接使用测试集调参，会导致模型间接“窥见”测试数据，使最终评估结果失真。</li></ol><p><img src="/img/machine-learning/image-20251018145842927.png" alt="image-20251018145842927"></p><p>• 若直接用测试集评估不同次数的多项式模型（如d&#x3D;1,2,…,10），再选测试误差最小的d，会导致测试集误差过于乐观（因为模型在测试集上“作弊”了）。</p><p>• 解决方法：引入验证集（Validation Set），先用训练集训练不同模型，再用验证集选最优模型，最后用测试集评估泛化能力。</p><h2 id="5-三者的成本函数（只有训练集需要正则化）"><a href="#5-三者的成本函数（只有训练集需要正则化）" class="headerlink" title="5.三者的成本函数（只有训练集需要正则化）"></a>5.三者的成本函数（只有训练集需要正则化）</h2><p><img src="/img/machine-learning/image-20251018150206714.png" alt="image-20251018150206714"></p><h2 id="6-只有训练集需要正则化的原因"><a href="#6-只有训练集需要正则化的原因" class="headerlink" title="6.只有训练集需要正则化的原因"></a>6.只有训练集需要正则化的原因</h2><p>要理解“只有训练集需要正则化”，需从正则化的目的和模型评估的逻辑两方面分析：</p><p><strong>1. 正则化的核心目的</strong></p><p>正则化（如L2正则化）是为了约束模型复杂度，防止训练集上的过拟合。它通过给权重w添加惩罚项，迫使模型“不要过于依赖某几个特征”，从而提升泛化能力。</p><p>这个操作是训练阶段的“约束手段”，只有在训练时需要——因为训练的目标是“让模型在学到数据规律的同时，不要学太多噪声”。</p><p><strong>2. 测试&#x2F;验证阶段的逻辑</strong></p><p>当模型训练完成后，我们需要评估它在 unseen 数据（测试集&#x2F;验证集）上的泛化能力。此时：</p><p>• 测试&#x2F;验证集的作用是模拟真实场景，要反映模型“不加约束时的预测能力”。</p><p>• 若在测试阶段也加入正则化，相当于“人为给模型加了额外约束”，会导致测试误差不能真实反映模型的泛化能力（比如正则化可能让测试误差看起来更小，<strong>但这是虚假的</strong>）。</p><p>举个例子</p><blockquote><p>假设你训练了一个“带正则化的复杂模型”，在测试时如果继续加正则化，就像“考试时还在强制自己‘不能写太复杂的步骤’”——这不是真实的考试能力，而是额外约束下的表现。因此，只有训练集需要正则化，测试&#x2F;验证阶段只需评估模型的原始预测能力。</p></blockquote><h1 id="七、模型调试（方差与偏差）"><a href="#七、模型调试（方差与偏差）" class="headerlink" title="七、模型调试（方差与偏差）"></a>七、模型调试（方差与偏差）</h1><blockquote><p>在机器学习中，高偏差（High Bias）和高方差（High Variance）是模型泛化误差的两个核心来源，反映了模型在训练和预测过程中的不同问题。以下是详细分析：</p></blockquote><h2 id="1、高偏差（High-Bias）–-欠拟合"><a href="#1、高偏差（High-Bias）–-欠拟合" class="headerlink" title="1、高偏差（High Bias）–&gt; 欠拟合"></a>1、高偏差（High Bias）–&gt; 欠拟合</h2><p><strong>定义与表现</strong><br>高偏差指模型过于简单，无法捕捉数据中的复杂模式，导致预测结果系统性偏离真实值。典型表现为：</p><ul><li><strong>训练误差和测试误差均较高，且两者差距较小;</strong></li><li>模型欠拟合（Underfitting），即对训练数据的拟合不足。</li></ul><p><strong>常见原因</strong>  </p><ul><li>模型复杂度不足（如使用线性模型拟合非线性关系）；</li><li>特征工程不充分（缺少关键特征或未构造有效特征）。</li></ul><p><strong>解决策略</strong>  </p><ul><li>增加模型复杂度（如使用更高阶多项式、更深的神经网络）；</li><li>引入更多特征或构造新特征；</li><li>减少正则化强度（如<strong>降低正则化参数λ</strong>）。</li></ul><hr><h2 id="2、高方差（High-Variance）–-过拟合"><a href="#2、高方差（High-Variance）–-过拟合" class="headerlink" title="2、高方差（High Variance）–&gt; 过拟合"></a>2、高方差（High Variance）–&gt; 过拟合</h2><p><strong>定义与表现</strong><br>高方差指模型过度拟合训练数据中的噪声或细节，导致泛化能力差[[9]]。典型表现为：</p><ul><li><strong>训练误差低，但测试误差高，两者差距大；</strong></li><li>模型过拟合（Overfitting），对训练数据过度敏感。</li></ul><p><strong>常见原因</strong>  </p><ul><li>模型过于复杂（如高阶多项式、未正则化的深度网络）；</li><li>训练数据量不足或噪声过多[[14]]。</li></ul><p><strong>解决策略</strong>  </p><ul><li><p>增加训练数据量；</p></li><li><p>降低模型复杂度（如<strong>减少特征数量</strong>、简化网络结构）；</p></li><li><p>增强正则化（如<strong>增大L2正则化参数λ</strong>、使用Dropout）；</p></li><li><p>采用集成方法（如Bagging）降低方差。</p><p><img src="/img/machine-learning/image-20251018152200827.png" alt="image-20251018152200827"></p></li></ul><hr><h2 id="3、偏差-方差权衡（Bias-Variance-Tradeoff）"><a href="#3、偏差-方差权衡（Bias-Variance-Tradeoff）" class="headerlink" title="3、偏差-方差权衡（Bias-Variance Tradeoff）"></a>3、偏差-方差权衡（Bias-Variance Tradeoff）</h2><p>理想模型需平衡偏差和方差，以达到低总误差（误差 &#x3D; 偏差² + 方差 + 不可约误差）。  </p><ul><li><strong>低偏差低方差</strong>：模型既准确又稳定（如合理正则化的深度网络）；  </li><li><strong>高偏差低方差</strong>：预测稳定但系统性偏离（如欠拟合的线性模型）；  </li><li><strong>低偏差高方差</strong>：预测准确但不稳定（如过拟合的复杂模型）。</li></ul><hr><h2 id="4、实际判断与调整"><a href="#4、实际判断与调整" class="headerlink" title="4、实际判断与调整"></a>4、实际判断与调整</h2><p>通过<strong>学习曲线</strong>可直观诊断：  </p><ul><li>若训练误差和验证误差均高且接近 → 高偏差；  </li><li>若训练误差低但验证误差高 → 高方差。</li></ul><p><strong>调整方向</strong>：  </p><ul><li><p><strong>高偏差需提升模型能力，高方差需增强泛化性。</strong></p><p><img src="/img/machine-learning/image-20251018152929883.png" alt="image-20251018152929883"></p></li></ul><hr><h2 id="5、示例说明"><a href="#5、示例说明" class="headerlink" title="5、示例说明"></a>5、示例说明</h2><ul><li><strong>高偏差案例</strong>：用线性回归拟合二次函数数据，误差始终较大；  </li><li><strong>高方差案例</strong>：用高阶多项式回归拟合少量数据，训练误差近乎零但测试误差骤增。</li></ul><p>理解并平衡偏差与方差是优化模型性能的关键，需根据具体问题动态调整模型复杂度与正则化策略。</p><h1 id="八、迁移学习"><a href="#八、迁移学习" class="headerlink" title="八、迁移学习"></a>八、迁移学习</h1><h2 id="为什么要进行迁移学习？"><a href="#为什么要进行迁移学习？" class="headerlink" title="为什么要进行迁移学习？"></a>为什么要进行迁移学习？</h2><p>进行迁移学习主要源于以下几个关键原因：</p><p>（1） 解决数据稀缺问题</p><p>很多实际任务中，我们能获取的标注数据非常少（比如医疗影像、小众领域的图像&#x2F;文本分类）。而迁移学习可以利用在大型通用数据集（如ImageNet、维基百科文本）上预训练好的模型，将其学到的通用特征（如“边缘、纹理”“语法、语义”）迁移到新任务中，无需从零开始训练，大幅降低对自有数据量的依赖。</p><p>（2） 降低训练成本</p><p>训练一个深度神经网络（如ResNet、BERT）需要大量的计算资源和时间。迁移学习直接复用预训练模型的参数，只需“微调”少量层即可适配新任务，能节省算力、缩短训练周期（比如原本需要几天的训练，迁移学习可能几小时就能完成）。</p><p>（3） 提升模型泛化能力</p><p>预训练模型在大型数据集上学习到的是通用且鲁棒的特征表示（比如图像中的“形状抽象”、文本中的“语义逻辑”）。将这些特征迁移到新任务后，模型能更好地应对新数据中的变化（如不同的拍摄角度、不同的表达方式），从而提升泛化能力。</p><p>（4） 适配小众任务</p><p>对于一些小众或专业领域的任务（如“识别特定工业零件缺陷”“分析某领域学术论文情感”），很难收集到足够的标注数据来训练专属模型。迁移学习可以让模型先在通用领域“打好基础”，再快速适配这些小众场景。</p><p>举个例子：要做“识别不同品种的兰花”的图像分类任务，直接训练的话，自己的兰花数据可能只有几百张，模型容易过拟合。但如果先下载在ImageNet（含数百万张各类图像）上预训练好的模型，再用自己的兰花数据微调，模型就能快速学会“兰花的花瓣形状、纹理”等特征，效果远好于从零训练。</p><h2 id="如何进行迁移学习"><a href="#如何进行迁移学习" class="headerlink" title="如何进行迁移学习"></a>如何进行迁移学习</h2><p><strong>迁移学习（Transfer Learning）</strong>是一种让模型“借力已有知识”来解决新任务的机器学习方法，步骤如下：</p><p>• <strong>步骤1</strong>：下载（或自行训练）一个在大型数据集上预训练好的神经网络参数。这个数据集的输入类型要和你的应用场景一致（比如都是图像、音频或文本）。例如，如果你要做“猫&#x2F;狗分类”的图像任务，可以下载在ImageNet（大型图像数据集）上预训练好的ResNet、VGG等模型的参数。</p><p>• <strong>步骤2</strong>：用你自己的小数据集对这个预训练网络进行微调（fine tune），让它适配你的具体任务。</p><p>简单来说，迁移学习就是“先借别人在大数据上学到的通用知识，再用自己的小数据把知识掰成适合自己任务的形状”，能有效解决小数据场景下的模型训练问题。</p><p><img src="/img/machine-learning/image-20251018153721870.png" alt="image-20251018153721870"></p>]]>
      </content:encoded>
    </item>
    <item>
      <title>三天血泪史：我在RTX 4060上部署DeepSeek R1的完整指南</title>
      <link>https://swkajnbruceee.github.io/2025/08/20/%E4%B8%89%E5%A4%A9%E8%A1%80%E6%B3%AA%E5%8F%B2-%E6%88%91%E5%9C%A8RTX4060%E4%B8%8A%E9%83%A8%E7%BD%B2DeepSeek-R1%E7%9A%84%E5%AE%8C%E6%95%B4%E6%8C%87%E5%8D%97/</link>
      <description>详细记录在RTX 4060显卡上部署DeepSeek R1大语言模型的完整过程，包括踩坑经历和解决方案</description>
      <author>BruceLee</author>
      <category domain="https://swkajnbruceee.github.io/categories/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/">技术分享</category>
      <category domain="https://swkajnbruceee.github.io/tags/DeepSeek/">DeepSeek</category>
      <category domain="https://swkajnbruceee.github.io/tags/AI%E9%83%A8%E7%BD%B2/">AI部署</category>
      <category domain="https://swkajnbruceee.github.io/tags/Docker/">Docker</category>
      <category domain="https://swkajnbruceee.github.io/tags/Ubuntu/">Ubuntu</category>
      <category domain="https://swkajnbruceee.github.io/tags/GPU/">GPU</category>
      <category domain="https://swkajnbruceee.github.io/tags/%E6%9C%AC%E5%9C%B0AI/">本地AI</category>
      <category domain="https://swkajnbruceee.github.io/tags/Ollama/">Ollama</category>
      <pubDate>Wed, 20 Aug 2025 11:30:24 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h1 id="三天血泪史：我在RTX-4060上部署DeepSeek-R1的完整指南"><a href="#三天血泪史：我在RTX-4060上部署DeepSeek-R1的完整指南" class="headerlink" title="三天血泪史：我在RTX 4060上部署DeepSeek R1的完整指南"></a>三天血泪史：我在RTX 4060上部署DeepSeek R1的完整指南</h1><h2 id="前言：为什么我要选择本地部署？"><a href="#前言：为什么我要选择本地部署？" class="headerlink" title="前言：为什么我要选择本地部署？"></a>前言：为什么我要选择本地部署？</h2><p>最近大语言模型很火，但用API总有种”受制于人”的感觉，同时还常常遇到高峰期请求失败的情况。作为一名开发者，我决定在自己的电脑上搭建一个完全本地的AI助手。没想到，这个决定让我开始了72小时的”渡劫”之旅…</p><h2 id="硬件环境检查"><a href="#硬件环境检查" class="headerlink" title="硬件环境检查"></a>硬件环境检查</h2><p><strong>我的配置</strong>：</p><ul><li>💻 拯救者R9000P ARX8</li><li>🎮 NVIDIA RTX 4060 Laptop GPU (8GB VRAM)</li><li>🧠 16GB DDR5 RAM</li><li>🐧 Ubuntu 24.04 LTS</li></ul><p><strong>DeepSeek R1 模型参数与显卡需求</strong>（来自官方文档）</p><p><img src="/img/posts/deepseek-deployment/docker-deployment.png" alt="DeepSeek R1 模型参数表"></p><h2 id="第一阶段：正确的部署流程（避坑版）"><a href="#第一阶段：正确的部署流程（避坑版）" class="headerlink" title="第一阶段：正确的部署流程（避坑版）"></a>第一阶段：正确的部署流程（避坑版）</h2><h3 id="1-在-Ubuntu-上安装-Ollama"><a href="#1-在-Ubuntu-上安装-Ollama" class="headerlink" title="1.在 Ubuntu 上安装 Ollama"></a>1.在 Ubuntu 上安装 Ollama</h3><p><strong>官方方法（网络好时推荐）</strong>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 一键安装</span></span><br><span class="line">curl -fsSL https://ollama.com/install.sh | sh</span><br><span class="line"></span><br><span class="line"><span class="comment"># 验证安装</span></span><br><span class="line">ollama --version</span><br></pre></td></tr></table></figure><p><img src="/img/posts/deepseek-deployment/model-download.png" alt="Ollama安装成功截图"></p><p><strong>离线安装（我的实际方案）</strong>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#1. 下载离线包（在其他机器上）</span></span><br><span class="line"><span class="comment">#从 https://github.com/ollama/ollama/releases 下载 ollama-linux-amd64.tar.gz</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#2. 传输到Ubuntu（我用U盘，你也可以用scp）</span></span><br><span class="line"><span class="built_in">cp</span> /media/username/U盘/ollama-linux-amd64.tar.gz ~/</span><br><span class="line"></span><br><span class="line"><span class="comment">#3. 解压安装</span></span><br><span class="line">tar -xzvf ollama-linux-amd64.tar.gz</span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">mv</span> ollama /usr/local/bin/</span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">chmod</span> +x /usr/local/bin/ollama</span><br><span class="line"></span><br><span class="line"><span class="comment">#4. 创建数据目录</span></span><br><span class="line"><span class="built_in">mkdir</span> -p ~/.ollama</span><br><span class="line"></span><br><span class="line"><span class="comment">#5. 验证</span></span><br><span class="line">ollama --version</span><br><span class="line"><span class="comment">#应该输出：ollama version 0.5.7</span></span><br></pre></td></tr></table></figure><h3 id="2-下载DeepSeek-R1-模型"><a href="#2-下载DeepSeek-R1-模型" class="headerlink" title="2. 下载DeepSeek R1 模型"></a>2. 下载DeepSeek R1 模型</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#直接运行会自动下载</span></span><br><span class="line">ollama run deepseek-r1:1.5b</span><br><span class="line"></span><br><span class="line"><span class="comment">#如果下载中断，可以重复执行，支持断点续传</span></span><br><span class="line"><span class="comment">#下载完成后会自动进入对话模式</span></span><br><span class="line">&gt;&gt;&gt; 你好，我是DeepSeek-R1...</span><br></pre></td></tr></table></figure><p><img src="/img/posts/deepseek-deployment/model-requirements.png" alt="DeepSeek R1 模型下载过程"></p><h3 id="3-Docker部署（GPU版本）"><a href="#3-Docker部署（GPU版本）" class="headerlink" title="3. Docker部署（GPU版本）"></a>3. Docker部署（GPU版本）</h3><p><strong>步骤1：安装NVIDIA容器工具包</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 添加NVIDIA包仓库</span></span><br><span class="line">curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | <span class="built_in">sudo</span> gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg</span><br><span class="line">curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | sed <span class="string">&#x27;s#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g&#x27;</span> | <span class="built_in">sudo</span> <span class="built_in">tee</span> /etc/apt/sources.list.d/nvidia-container-toolkit.list</span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装工具包</span></span><br><span class="line"><span class="built_in">sudo</span> apt-get update</span><br><span class="line"><span class="built_in">sudo</span> apt-get install -y nvidia-container-toolkit</span><br><span class="line"><span class="built_in">sudo</span> nvidia-ctk runtime configure --runtime=docker</span><br><span class="line"><span class="built_in">sudo</span> systemctl restart docker</span><br><span class="line"></span><br><span class="line"><span class="comment"># 验证GPU支持</span></span><br><span class="line">docker run --<span class="built_in">rm</span> --gpus=all nvidia/cuda:11.8.0-base-ubuntu22.04 nvidia-smi</span><br></pre></td></tr></table></figure><p><strong>步骤2：拉取Ollama Docker镜像</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 使用可用镜像源</span></span><br><span class="line">docker pull docker.1ms.run/ollama/ollama</span><br><span class="line"></span><br><span class="line"><span class="comment"># 或者构建别名</span></span><br><span class="line">docker tag docker.1ms.run/ollama/ollama ollama/ollama</span><br></pre></td></tr></table></figure><p><strong>步骤3：运行ollama</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">docker run -d \</span><br><span class="line">  --gpus=all \</span><br><span class="line">  -v ollama-data:/root/.ollama \</span><br><span class="line">  -p 11434:11434 \</span><br><span class="line">  --name ollama \</span><br><span class="line">  ollama/ollama</span><br><span class="line"><span class="comment"># 参数解释：</span></span><br><span class="line">• -d：后台运⾏容器</span><br><span class="line">• --name ollama：容器命名为ollama</span><br><span class="line">• -p 11434:11434：映射Ollama的API端⼝</span><br><span class="line"></span><br><span class="line">**步骤4：启动模型服务**</span><br><span class="line"><span class="comment"># (1) 进⼊容器内</span></span><br><span class="line">docker <span class="built_in">exec</span> -it ollama /bin/bash</span><br><span class="line"><span class="comment"># (2) 启动模型服务</span></span><br><span class="line">ollama run deepseek-r1:1.5b</span><br></pre></td></tr></table></figure><p><img src="/img/posts/deepseek-deployment/ollama-install.png" alt="Docker部署成功截图"></p><h3 id="4-OpenWebUI部署与交互"><a href="#4-OpenWebUI部署与交互" class="headerlink" title="4. OpenWebUI部署与交互"></a>4. OpenWebUI部署与交互</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 参考链接：https://github.com/open-webui/open-webui.gi</span></span><br><span class="line"><span class="comment"># 1.拉取镜像</span></span><br><span class="line">docker pull ghcr.io/open-webui/open-webui:main</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2.启动并运行一个新的容器</span></span><br><span class="line">docker run -d \</span><br><span class="line">  -p 8805:8080 \</span><br><span class="line">  --add-host=host.docker.internal:host-gateway \</span><br><span class="line">  -v open-webui-data:/app/backend/data \</span><br><span class="line">  --name open-webui \</span><br><span class="line">  ghcr.io/open-webui/open-webui:main</span><br></pre></td></tr></table></figure><p><img src="/img/posts/deepseek-deployment/openwebui-interface.png" alt="运行容器"></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 3. 启动 ollama docker 模型服务</span></span><br><span class="line">docker restart ollama</span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. Web UI 交互</span></span><br><span class="line">localhost:8805</span><br></pre></td></tr></table></figure><p><img src="/img/posts/deepseek-deployment/openwebui-interaction.png" alt="OpenWebUI交互截图"></p><h2 id="第二阶段：我踩过的坑和解决方案"><a href="#第二阶段：我踩过的坑和解决方案" class="headerlink" title="第二阶段：我踩过的坑和解决方案"></a>第二阶段：我踩过的坑和解决方案</h2><h3 id="坑1：网络限制导致安装失败"><a href="#坑1：网络限制导致安装失败" class="headerlink" title="坑1：网络限制导致安装失败"></a>坑1：网络限制导致安装失败</h3><p><strong>问题现象</strong>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 一直卡住或者报错</span></span><br><span class="line">curl -fsSL https://ollama.com/install.sh | sh</span><br><span class="line"><span class="comment"># =&gt; curl: (7) Failed to connect to ollama.com port 443: Connection timed out</span></span><br></pre></td></tr></table></figure><p><strong>尝试的解决方案</strong>：</p><ol><li>❌ 换国内源（发现没有Ollama的源）</li><li>❌ 配置HTTP代理（公司网络限制）</li><li>❌ 手机热点（速度太慢且不稳定）</li><li>✅ <strong>离线安装</strong>：下载离线包+U盘传输</li></ol><h3 id="坑2：模型下载中途失败"><a href="#坑2：模型下载中途失败" class="headerlink" title="坑2：模型下载中途失败"></a>坑2：模型下载中途失败</h3><p><strong>问题现象</strong>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">ollama run deepseek-r1:1.5b</span><br><span class="line"><span class="comment"># =&gt; pulling manifest... 50% then disconnected</span></span><br></pre></td></tr></table></figure><p><strong>解决方案</strong>：</p><ul><li>发现Ollama支持断点续传，重复执行即可</li><li>选择网络较好的时段</li><li>耐心重试了3次才完成</li></ul><h3 id="坑3：Docker-GPU支持问题"><a href="#坑3：Docker-GPU支持问题" class="headerlink" title="坑3：Docker GPU支持问题"></a>坑3：Docker GPU支持问题</h3><p><strong>错误信息</strong>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker: Error response from daemon: could not <span class="keyword">select</span> device driver <span class="string">&quot;&quot;</span> with capabilities: [[gpu]].</span><br></pre></td></tr></table></figure><p><strong>根本原因</strong>：缺少NVIDIA容器工具包</p><p><strong>解决过程</strong>：</p><ol><li>先安装NVIDIA驱动：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt install nvidia-driver-550</span><br></pre></td></tr></table></figure></li><li>然后安装容器工具包（见前面正确步骤）</li><li>验证：<code>docker run --rm --gpus=all nvidia/cuda:11.8.0-base-ubuntu22.04 nvidia-smi</code></li></ol><h3 id="坑4：Docker镜像拉取失败"><a href="#坑4：Docker镜像拉取失败" class="headerlink" title="坑4：Docker镜像拉取失败"></a>坑4：Docker镜像拉取失败</h3><p><strong>问题</strong>：国内镜像源大面积停止服务</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">docker pull ollama/ollama</span><br><span class="line"><span class="comment"># =&gt; net/http: request canceled (Client.Timeout exceeded...)</span></span><br></pre></td></tr></table></figure><p><strong>解决方案</strong>：</p><ul><li>尝试阿里云、网易、中科大镜像，都失败</li><li>最终找到可用的私人镜像源：<code>docker.1ms.run/ollama/ollama</code></li></ul><h3 id="坑5：端口冲突"><a href="#坑5：端口冲突" class="headerlink" title="坑5：端口冲突"></a>坑5：端口冲突</h3><p><strong>问题</strong>：11434端口被占用</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker: Error response from daemon: Ports are not available: listen tcp 0.0.0.0:11434: <span class="built_in">bind</span>: address already <span class="keyword">in</span> use.</span><br></pre></td></tr></table></figure><p><strong>解决方案</strong>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查找占用进程</span></span><br><span class="line">lsof -i :11434</span><br><span class="line"><span class="comment"># 或者</span></span><br><span class="line">ss -tulpn | grep :11434</span><br><span class="line"></span><br><span class="line"><span class="comment"># 杀死进程</span></span><br><span class="line"><span class="built_in">kill</span> -9 &lt;PID&gt;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 或者改用其他端口</span></span><br><span class="line">docker run -p 11435:11434 ...</span><br></pre></td></tr></table></figure><h2 id="第三阶段：最终效果与性能测试"><a href="#第三阶段：最终效果与性能测试" class="headerlink" title="第三阶段：最终效果与性能测试"></a>第三阶段：最终效果与性能测试</h2><h3 id="成功部署后的界面"><a href="#成功部署后的界面" class="headerlink" title="成功部署后的界面"></a>成功部署后的界面</h3><p>访问 <code>http://localhost:8805</code> 可以看到：</p><ul><li>🎨 漂亮的聊天界面</li><li>⚡ 实时响应</li><li>📁 对话历史管理</li><li>⚙️ 模型切换功能</li></ul><h3 id="实际对话测试"><a href="#实际对话测试" class="headerlink" title="实际对话测试"></a>实际对话测试</h3><p><strong>我</strong>：请解释一下PPO强化学习算法<br><strong>DeepSeek-R1</strong>：PPO（Proximal Policy Optimization）是一种策略梯度算法，它通过限制策略更新的幅度来保证训练的稳定性。主要优点是实现简单、效果稳定、适用于连续和离散动作空间…</p><h2 id="经验总结与建议"><a href="#经验总结与建议" class="headerlink" title="经验总结与建议"></a>经验总结与建议</h2><h3 id="给新手的建议"><a href="#给新手的建议" class="headerlink" title="给新手的建议"></a>给新手的建议</h3><ol><li><strong>硬件选择</strong>：8GB显存老老实实用1.5B模型，别想7B</li><li><strong>网络准备</strong>：提前下载好离线安装包</li><li><strong>镜像源</strong>：多准备几个备选源</li><li><strong>耐心</strong>：模型下载很耗时，做好心理准备</li></ol><h3 id="必备检查清单"><a href="#必备检查清单" class="headerlink" title="必备检查清单"></a>必备检查清单</h3><ul><li><input disabled="" type="checkbox"> NVIDIA驱动已安装 (<code>nvidia-smi</code> 有输出)</li><li><input disabled="" type="checkbox"> Docker已安装 (<code>docker --version</code>)</li><li><input disabled="" type="checkbox"> NVIDIA容器工具包已安装</li><li><input disabled="" type="checkbox"> 11434端口未被占用</li><li><input disabled="" type="checkbox"> 磁盘空间充足 (至少10GB)</li></ul><h3 id="性能优化技巧"><a href="#性能优化技巧" class="headerlink" title="性能优化技巧"></a>性能优化技巧</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 调整Ollama的线程数</span></span><br><span class="line">OLLAMA_NUM_PARALLEL=4 ollama run deepseek-r1:1.5b</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用更高效的数据格式</span></span><br><span class="line">OLLAMA_QUANTIZE=q4_0 ollama run deepseek-r1:1.5b</span><br></pre></td></tr></table></figure><h2 id="未来计划"><a href="#未来计划" class="headerlink" title="未来计划"></a>未来计划</h2><ol><li><strong>接入本地文档</strong>：实现真正的RAG功能</li><li><strong>尝试微调</strong>：用自己数据训练专业模型</li><li><strong>优化部署</strong>：使用Docker Compose管理</li><li><strong>监控系统</strong>：添加性能监控和日志</li></ol><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>这72小时的部署之旅虽然曲折，但收获巨大。从一次次失败到最终成功，每个错误都让我对Linux、Docker、GPU驱动有了更深的理解。现在我的本地AI助手已经稳定运行，这种”一切尽在掌握”的感觉真的很棒！</p><p>如果你也想在本地部署大模型，希望我的经验能帮你少走弯路。记住：每个错误都是成长的机会，坚持下去一定能成功！</p><hr><p><strong>资源下载</strong>：</p><ul><li><a href="https://github.com/ollama/ollama/releases">Ollama官方发布页面</a></li><li><a href="https://huggingface.co/deepseek-ai">DeepSeek官方页面</a></li></ul><p>欢迎在评论区分享你的部署经历和问题！</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>曲折之路：在无法科学上网的Ubuntu上安装Clash Verge的实战记录</title>
      <link>https://swkajnbruceee.github.io/2025/08/20/%E6%9B%B2%E6%8A%98%E4%B9%8B%E8%B7%AF%EF%BC%9A%E5%9C%A8%E6%97%A0%E6%B3%95%E7%A7%91%E5%AD%A6%E4%B8%8A%E7%BD%91%E7%9A%84Ubuntu%E4%B8%8A%E5%AE%89%E8%A3%85Clash-Verge%E7%9A%84%E5%AE%9E%E6%88%98%E8%AE%B0%E5%BD%95/</link>
      <description>记录在无法科学上网的Ubuntu系统上安装Clash Verge的完整过程，分享&quot;曲线救国&quot;的解决方案。</description>
      <author>BruceLee</author>
      <category domain="https://swkajnbruceee.github.io/categories/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/">技术分享</category>
      <category domain="https://swkajnbruceee.github.io/tags/Linux/">Linux</category>
      <category domain="https://swkajnbruceee.github.io/tags/Ubuntu/">Ubuntu</category>
      <category domain="https://swkajnbruceee.github.io/tags/Clash-Verge/">Clash Verge</category>
      <category domain="https://swkajnbruceee.github.io/tags/%E7%A7%91%E5%AD%A6%E4%B8%8A%E7%BD%91/">科学上网</category>
      <category domain="https://swkajnbruceee.github.io/tags/%E7%BD%91%E7%BB%9C%E5%B7%A5%E5%85%B7/">网络工具</category>
      <pubDate>Wed, 20 Aug 2025 02:24:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h1 id="曲折之路：在无法科学上网的Ubuntu上安装Clash-Verge的实战记录"><a href="#曲折之路：在无法科学上网的Ubuntu上安装Clash-Verge的实战记录" class="headerlink" title="曲折之路：在无法科学上网的Ubuntu上安装Clash Verge的实战记录"></a>曲折之路：在无法科学上网的Ubuntu上安装Clash Verge的实战记录</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>作为一名双系统用户，我在Windows上一直用Clash Verge进行科学上网，体验非常顺畅。最近因为开发需要，要经常使用Ubuntu系统，自然就想着把Clash Verge也装过去，这样就能无缝使用我已有的订阅，省下一笔额外的开销。</p><p>想法很美好，现实却很骨感。我的Ubuntu系统是一个全新的环境，<strong>本身无法科学上网</strong>，而这恰恰是安装这类工具的最大悖论：<strong>你需要先科学上网，才能顺利下载科学上网的工具</strong>。这个过程耗费了我大量时间，搜寻了B站、油管上的无数教程，最终找到了一条”曲线救国”的路子。记录于此，希望能帮到有同样困境的朋友。</p><h2 id="一、-徒劳的尝试：在Ubuntu内直接操作"><a href="#一、-徒劳的尝试：在Ubuntu内直接操作" class="headerlink" title="一、 徒劳的尝试：在Ubuntu内直接操作"></a>一、 徒劳的尝试：在Ubuntu内直接操作</h2><p>和大多数人的第一反应一样，我首先选择在Ubuntu系统内解决问题。</p><p>我打开了终端，输入了各种教程里雷打不动的第一句命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">wget https://github.com/zzzgydi/clash-verge/releases/download/v1.6.6/clash-verge_1.6.6_amd64.deb</span><br></pre></td></tr></table></figure><p>结果不言而喻，速度只有几十KB&#x2F;s，最后必然是 <code>Connection timed out</code>。尝试了无数个版本号，换了好几个镜像站，结果都一样。在没有加速的情况下，从GitHub拉取大型文件实在是一种煎熬。</p><p><strong>误区：</strong> 很多教程还会让你安装一堆依赖，例如：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> dpkg -i libjavascriptcoregtk-4.0-18_2.43.3-1_amd64.deb</span><br><span class="line"><span class="built_in">sudo</span> dpkg -i libwebkit2gtk-4.0-37_2.43.3-1_amd64.deb</span><br></pre></td></tr></table></figure><p>我按照指示苦苦寻找这些依赖包，但在新版的Clash Verge发布包中，<strong>这些依赖包早已被合并或不再需要</strong>，盲目寻找只是徒增烦恼。在焦头烂额地折腾了几个小时后，我意识到此路不通。在”绝缘”的环境下，想通过常规手段安装，根本就是死循环。</p><h2 id="二、-破局思路：跳出Ubuntu的围墙"><a href="#二、-破局思路：跳出Ubuntu的围墙" class="headerlink" title="二、 破局思路：跳出Ubuntu的围墙"></a>二、 破局思路：跳出Ubuntu的围墙</h2><p>既然在Ubuntu内部无法下载，那就在能下载的地方下载好了。我的Windows系统是完全可以科学上网的。思路瞬间清晰：</p><ol><li><strong>在Windows上下载最新的Clash Verge安装包（.deb文件）。</strong></li><li>将下载好的文件拷贝到Ubuntu系统。</li><li>在Ubuntu中进行本地安装。</li></ol><p>这看似简单的一步，才是真正解决问题的关键。</p><h2 id="三、-实战步骤：手把手教你离线安装"><a href="#三、-实战步骤：手把手教你离线安装" class="headerlink" title="三、 实战步骤：手把手教你离线安装"></a>三、 实战步骤：手把手教你离线安装</h2><h3 id="步骤一：在Windows端获取资源"><a href="#步骤一：在Windows端获取资源" class="headerlink" title="步骤一：在Windows端获取资源"></a>步骤一：在Windows端获取资源</h3><ol><li>在<strong>Windows系统</strong>中，打开你的Clash Verge，确保处于科学上网环境。</li><li>打开浏览器，访问Clash Verge的官方GitHub Release页面：<br><code>https://github.com/zzzgydi/clash-verge/releases</code></li><li>找到最新版本（我当时用的是 <strong>v2.3.2</strong>）。在资源列表中，找到适用于Ubuntu的DEB包。它的名字应该是 <code>Clash.Verge_2.3.2_amd64.deb</code>。注意，<strong>只需要这一个文件就够了</strong>，新版已经包含了所有依赖。</li><li>点击下载，等待片刻即可完成。</li></ol><h3 id="步骤二：跨系统文件传输"><a href="#步骤二：跨系统文件传输" class="headerlink" title="步骤二：跨系统文件传输"></a>步骤二：跨系统文件传输</h3><p>将下载好的 <code>Clash.Verge_2.3.2_amd64.deb</code> 文件从Windows拷贝到Ubuntu。你有N种选择：</p><ul><li><strong>U盘拷贝</strong>：最直接物理的方式。</li><li><strong>使用飞鸽、LANDrop等跨平台传输工具</strong>：在同一个局域网内非常方便。</li><li><strong>借助云盘（如百度网盘）</strong>：在Windows上传，然后在Ubuntu里用官方客户端或网页版下载。虽然Ubuntu没加速，但下载国内云盘的速度还是可以的。</li></ul><p>我选择的是第一种，用U拷贝过去了。但其实后来我还发现了一种方法，虽然我单独为Ubuntu系统分了一个盘，但是它还是挂载了Windows的其他几个盘，这就导致了我下载在Windows中的东西，在Ubuntu中也能看到。</p><p><img src="/img/%E5%9B%BE%E7%89%871.png" alt="图片1"></p><p><img src="/img/%E5%9B%BE%E7%89%872.png" alt="图片2"></p><p>后续我也尝试了能否直接在Ubuntu中访问这些盘然后直接拷贝数据，答案是成功的。这就省略了U盘拷贝这一步骤，只需下载即可（不过这一步涉及跨系统移动数据，还不能确定它是否有风险，调研清楚后，我会再写一篇博客）</p><p>但请注意，这并不是真正意义上的”数据互通”，因为后期我在Ubuntu系统中尝试将文件放在”499G卷”中，发现在Windows下看不到，再打开Ubuntu后，发现这个文件也消失了，这说明，目前情况下，只能是从Ubuntu中获取Windows的文件</p><h3 id="步骤三：在Ubuntu中完成安装"><a href="#步骤三：在Ubuntu中完成安装" class="headerlink" title="步骤三：在Ubuntu中完成安装"></a>步骤三：在Ubuntu中完成安装</h3><ol><li><p>打开Ubuntu的终端，导航到你存放 <code>.deb</code> 文件的目录，通常是 <code>~/Downloads</code>。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> ~/Downloads</span><br></pre></td></tr></table></figure></li><li><p>执行安装命令。注意，文件名要和你下载的保持一致。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> dpkg -i Clash.Verge_2.3.2_amd64.deb</span><br></pre></td></tr></table></figure><p>如果运气好，会直接安装成功。但很多时候可能会报依赖错误。</p></li><li><p><strong>修复依赖问题（很大概率需要这一步）</strong>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt --fix-broken install -y</span><br></pre></td></tr></table></figure><p>这个命令会自动安装刚才缺失的依赖。完成后，Clash Verge就已经静静地躺在你的应用程序列表里了。</p></li></ol><h3 id="步骤四：导入订阅并享受"><a href="#步骤四：导入订阅并享受" class="headerlink" title="步骤四：导入订阅并享受"></a>步骤四：导入订阅并享受</h3><ol><li>在Ubuntu的应用菜单中搜索并启动 <strong>Clash Verge</strong>。</li><li>它的界面和Windows版一模一样。点击左侧的 <strong>Profiles</strong>。</li><li>点击新增按钮，选择 <strong>URL</strong>，然后将你在Windows版里使用的<strong>同一个订阅链接</strong>粘贴进去。</li><li>导入成功后，回到 <strong>Proxies</strong> 页面，选择节点，将系统代理（System Proxy）开关打开。</li></ol><p><em>大功告成！现在你的Ubuntu已经可以自由访问网络了。</em></p><h2 id="四、-总结与反思"><a href="#四、-总结与反思" class="headerlink" title="四、 总结与反思"></a>四、 总结与反思</h2><p>回顾整个过程，核心的教训就是：<strong>不要被问题困在发生问题的系统里思考解决方案</strong>。</p><ul><li><strong>坑点：</strong> 大多数教程都假设你的Ubuntu网络是通畅的，忽略了”需要先装梯子才能装梯子”这个经典悖论。</li><li><strong>解决方案：</strong> 利用其他可用的资源（另一台能科学上网的电脑）来获取关键文件，通过外部存储方式完成”输血”。</li><li><strong>关键：</strong> 只需要下载一个最新的 <code>.deb</code> 主程序包即可，无需再费心寻找任何单独的依赖包。</li></ul><p>希望这篇从实战中总结出的经验，能帮你节省那几个小时毫无意义的等待和搜索。如果你也遇到了同样的问题，不妨试试这个”曲线救国”的方法，或许能豁然开朗。</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>攻克 Linux 难题：全面解决 Cursor AppImage 沙箱权限与桌面快捷方式创建</title>
      <link>https://swkajnbruceee.github.io/2025/08/19/%E6%94%BB%E5%85%8BLinux%E9%9A%BE%E9%A2%98-%E5%85%A8%E9%9D%A2%E8%A7%A3%E5%86%B3Cursor-AppImage%E6%B2%99%E7%AE%B1%E6%9D%83%E9%99%90%E4%B8%8E%E6%A1%8C%E9%9D%A2%E5%BF%AB%E6%8D%B7%E6%96%B9%E5%BC%8F%E5%88%9B%E5%BB%BA/</link>
      <description>详细记录在Ubuntu系统上解决Cursor AppImage沙箱权限问题和桌面快捷方式创建问题的完整过程，包括多种解决方案和系统安全机制分析</description>
      <author>BruceLee</author>
      <category domain="https://swkajnbruceee.github.io/categories/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/">技术分享</category>
      <category domain="https://swkajnbruceee.github.io/tags/Linux/">Linux</category>
      <category domain="https://swkajnbruceee.github.io/tags/Ubuntu/">Ubuntu</category>
      <category domain="https://swkajnbruceee.github.io/tags/Cursor/">Cursor</category>
      <category domain="https://swkajnbruceee.github.io/tags/AppImage/">AppImage</category>
      <category domain="https://swkajnbruceee.github.io/tags/%E6%B2%99%E7%AE%B1/">沙箱</category>
      <category domain="https://swkajnbruceee.github.io/tags/%E6%A1%8C%E9%9D%A2%E5%BF%AB%E6%8D%B7%E6%96%B9%E5%BC%8F/">桌面快捷方式</category>
      <category domain="https://swkajnbruceee.github.io/tags/%E7%B3%BB%E7%BB%9F%E5%AE%89%E5%85%A8/">系统安全</category>
      <pubDate>Tue, 19 Aug 2025 14:32:06 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h1 id="攻克-Linux-难题：全面解决-Cursor-AppImage-沙箱权限与桌面快捷方式创建"><a href="#攻克-Linux-难题：全面解决-Cursor-AppImage-沙箱权限与桌面快捷方式创建" class="headerlink" title="攻克 Linux 难题：全面解决 Cursor AppImage 沙箱权限与桌面快捷方式创建"></a>攻克 Linux 难题：全面解决 Cursor AppImage 沙箱权限与桌面快捷方式创建</h1><h2 id="问题缘起：当优秀的工具遇上严格的系统"><a href="#问题缘起：当优秀的工具遇上严格的系统" class="headerlink" title="问题缘起：当优秀的工具遇上严格的系统"></a>问题缘起：当优秀的工具遇上严格的系统</h2><p>当我在Ubuntu系统上下载cursor时，我发现官方只提供了 AppImage 格式。虽然这种格式无需安装，解压即用，非常符合 Linux 的简洁哲学。但是，我在安装时却遇到了一个令人困扰的问题。直接执行 AppImage 文件时，终端抛出了一系列令人费解的错误：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">bruce@WP:~/下载$ ./Cursor-1.4.5-x86_64.AppImage</span><br><span class="line">The setuid sandbox is not running as root. Common causes:</span><br><span class="line">  * An unprivileged process using ptrace on it, like a debugger.</span><br><span class="line">  * A parent process <span class="built_in">set</span> prctl(PR_SET_NO_NEW_PRIVS, ...)</span><br><span class="line">Failed to move to new namespace: PID namespaces supported, Network namespace supported, but failed: errno = Operation not permitted</span><br><span class="line">[7730:0819/165401.243418:FATAL:zygote_host_impl_linux.cc(207)] Check failed: . : 无效的参数 (22)</span><br><span class="line">追踪或断点陷阱（核心已转储）</span><br></pre></td></tr></table></figure><p>更令人沮丧的是，当我尝试创建一个桌面快捷方式以便快速启动时，系统却提示”此.desktop文件是没有被信任的，它不能被执行”，而在文件属性的图形界面中，根本找不到所谓的”允许执行”选项。</p><p>这显然不是一次简单的故障，而是一次深入探索 Linux 安全机制的机会。下面记录了我解决这个问题的完整历程。</p><span id="more"></span><h2 id="深度诊断：理解问题的根源"><a href="#深度诊断：理解问题的根源" class="headerlink" title="深度诊断：理解问题的根源"></a>深度诊断：理解问题的根源</h2><h3 id="错误分析：沙箱安全机制的限制"><a href="#错误分析：沙箱安全机制的限制" class="headerlink" title="错误分析：沙箱安全机制的限制"></a>错误分析：沙箱安全机制的限制</h3><p>首先需要理解这些错误信息的含义。Cursor 基于 Electron 框架构建，而 Electron 又基于 Chromium。Chromium 使用了一套复杂的沙箱系统来隔离潜在的安全风险，保护主机系统。</p><p>关键错误信息表明：</p><ol><li><strong>setuid sandbox 未能以 root 权限运行</strong> - 这是 Chromium 的传统沙箱机制</li><li><strong>无法移动到新的命名空间</strong> - Linux 内核的命名空间隔离功能受限</li><li><strong>Operation not permitted</strong> - 当前用户权限不足</li></ol><p>这些问题的根本原因是：<strong>现代 Linux 发行版默认禁用了非特权用户命名空间（unprivileged user namespaces）</strong>，这是 Ubuntu 23.10+ 和其他基于安全考虑的发行版的默认设置。</p><h3 id="系统环境确认"><a href="#系统环境确认" class="headerlink" title="系统环境确认"></a>系统环境确认</h3><p>在开始修复前，我确认了系统环境：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 系统基本信息</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;系统版本: <span class="subst">$(lsb_release -d)</span>&quot;</span></span><br><span class="line"><span class="comment"># 输出: Description: Ubuntu 24.04.3 LTS</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;内核版本: <span class="subst">$(uname -r)</span>&quot;</span></span><br><span class="line"><span class="comment"># 输出: 5.15.0-86-generic</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;桌面环境: <span class="variable">$&#123;XDG_CURRENT_DESKTOP&#125;</span>&quot;</span></span><br><span class="line"><span class="comment"># 输出: ubuntu:GNOME</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 安全模块状态</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;AppArmor 状态: <span class="subst">$(aa-status | head -1)</span>&quot;</span></span><br><span class="line"><span class="comment"># 输出: apparmor module is loaded.</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 用户命名空间当前设置</span></span><br><span class="line"><span class="built_in">cat</span> /proc/sys/kernel/unprivileged_userns_clone</span><br><span class="line"><span class="comment"># 输出: 0 (表示禁用)</span></span><br></pre></td></tr></table></figure><p>这个”0”值确认了问题的核心——系统确实禁用了非特权用户命名空间。</p><h2 id="解决方案探索：多管齐下的修复策略"><a href="#解决方案探索：多管齐下的修复策略" class="headerlink" title="解决方案探索：多管齐下的修复策略"></a>解决方案探索：多管齐下的修复策略</h2><p>我尝试了多种解决方案，从根本修复到临时变通方法。</p><h3 id="方案一：启用用户命名空间（推荐）"><a href="#方案一：启用用户命名空间（推荐）" class="headerlink" title="方案一：启用用户命名空间（推荐）"></a>方案一：启用用户命名空间（推荐）</h3><p>这是最根本的解决方法，直接修复系统配置：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 临时启用（重启后失效）</span></span><br><span class="line"><span class="built_in">sudo</span> sysctl kernel.unprivileged_userns_clone=1</span><br><span class="line"></span><br><span class="line"><span class="comment"># 永久启用</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;kernel.unprivileged_userns_clone=1&quot;</span> | <span class="built_in">sudo</span> <span class="built_in">tee</span> /etc/sysctl.d/99-userns.conf</span><br><span class="line"><span class="built_in">sudo</span> sysctl -p /etc/sysctl.d/99-userns.conf</span><br><span class="line"></span><br><span class="line"><span class="comment"># 验证设置</span></span><br><span class="line">sysctl kernel.unprivileged_userns_clone</span><br><span class="line"><span class="comment"># 应该显示: kernel.unprivileged_userns_clone = 1</span></span><br></pre></td></tr></table></figure><p><strong>原理</strong>：这条指令告诉内核允许非特权用户创建用户命名空间，这是现代沙箱技术的基础。</p><h3 id="方案二：AppArmor-策略调整（针对-Ubuntu）"><a href="#方案二：AppArmor-策略调整（针对-Ubuntu）" class="headerlink" title="方案二：AppArmor 策略调整（针对 Ubuntu）"></a>方案二：AppArmor 策略调整（针对 Ubuntu）</h3><p>对于 Ubuntu 系统，有时还需要调整 AppArmor 策略：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建 AppArmor 覆盖规则</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;capability userns,&#x27;</span> | <span class="built_in">sudo</span> <span class="built_in">tee</span> /etc/apparmor.d/local/usr.bin.chromium-browser</span><br><span class="line"></span><br><span class="line"><span class="comment"># 重新加载配置</span></span><br><span class="line"><span class="built_in">sudo</span> systemctl reload apparmor</span><br></pre></td></tr></table></figure><p><strong>注意</strong>：这个方法可能因系统配置而异，在某些系统上可能找不到对应的 AppArmor 配置文件。</p><h3 id="方案三：禁用沙箱（临时方案）"><a href="#方案三：禁用沙箱（临时方案）" class="headerlink" title="方案三：禁用沙箱（临时方案）"></a>方案三：禁用沙箱（临时方案）</h3><p>如果上述方法不适用或急需使用，可以临时禁用沙箱：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./Cursor-1.4.5-x86_64.AppImage --no-sandbox --disable-gpu-sandbox</span><br></pre></td></tr></table></figure><p><strong>安全警告</strong>：这会显著降低安全性，仅在完全信任的环境中使用。不建议长期使用此方案。（但其实我的电脑只行的通此方案）</p><h2 id="桌面快捷方式难题的破解"><a href="#桌面快捷方式难题的破解" class="headerlink" title="桌面快捷方式难题的破解"></a>桌面快捷方式难题的破解</h2><p>解决了沙箱问题后，我遇到了第二个挑战：创建可靠的桌面快捷方式。</p><h3 id="问题分析：GNOME-的安全限制"><a href="#问题分析：GNOME-的安全限制" class="headerlink" title="问题分析：GNOME 的安全限制"></a>问题分析：GNOME 的安全限制</h3><p>现代 GNOME 桌面环境对 <code>.desktop</code> 文件实施了严格的安全策略：</p><ol><li>直接创建的 <code>.desktop</code> 文件被视为”不受信任”</li><li>图形界面中可能看不到”允许执行”选项</li><li>这是防止恶意代码通过下载文件自动执行的安全措施</li></ol><h3 id="解决方案：命令行信任法"><a href="#解决方案：命令行信任法" class="headerlink" title="解决方案：命令行信任法"></a>解决方案：命令行信任法</h3><p>经过多次尝试，我发现最可靠的方法是通过命令行直接设置信任标志（其他方法我的电脑均行不通）：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 确定桌面目录路径（适应多语言环境）</span></span><br><span class="line">DESKTOP_DIR=$(xdg-user-dir DESKTOP)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建桌面文件</span></span><br><span class="line"><span class="built_in">cat</span> &gt; <span class="string">&quot;<span class="variable">$DESKTOP_DIR</span>/Cursor.desktop&quot;</span> &lt;&lt; <span class="string">&#x27;EOF&#x27;</span></span><br><span class="line">[Desktop Entry]</span><br><span class="line">Version=1.0</span><br><span class="line">Name=Cursor</span><br><span class="line">Exec=/home/bruce/下载/Cursor-1.4.5-x86_64.AppImage --no-sandbox --disable-gpu-sandbox</span><br><span class="line">Icon=/usr/share/icons/hicolor/512x512/apps/cursor.png</span><br><span class="line">Terminal=<span class="literal">false</span></span><br><span class="line">Type=Application</span><br><span class="line">Categories=Development;</span><br><span class="line">StartupWMClass=cursor</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加执行权限</span></span><br><span class="line"><span class="built_in">chmod</span> +x <span class="string">&quot;<span class="variable">$DESKTOP_DIR</span>/Cursor.desktop&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 关键步骤：设置信任元数据</span></span><br><span class="line">gio <span class="built_in">set</span> <span class="string">&quot;<span class="variable">$DESKTOP_DIR</span>/Cursor.desktop&quot;</span> <span class="string">&quot;metadata::trusted&quot;</span> <span class="literal">true</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 验证设置</span></span><br><span class="line">gio info -a <span class="string">&quot;metadata::trusted&quot;</span> <span class="string">&quot;<span class="variable">$DESKTOP_DIR</span>/Cursor.desktop&quot;</span></span><br><span class="line"><span class="comment"># 应该显示: metadata::trusted: true</span></span><br></pre></td></tr></table></figure><p><strong>技术要点</strong>：</p><ol><li><code>gio set</code> 命令直接操作 GNOME 的元数据系统</li><li><code>metadata::trusted</code> 属性告诉系统此文件是用户明确信任的</li><li>需要同时设置执行权限(<code>chmod +x</code>)和信任标志</li></ol><h3 id="备选方案：启动脚本间接法"><a href="#备选方案：启动脚本间接法" class="headerlink" title="备选方案：启动脚本间接法"></a>备选方案：启动脚本间接法</h3><p>如果上述方法仍然不工作，可以创建间接启动脚本：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建启动脚本</span></span><br><span class="line"><span class="built_in">cat</span> &gt; ~/start-cursor.sh &lt;&lt; <span class="string">&#x27;EOF&#x27;</span></span><br><span class="line"><span class="comment">#!/bin/bash</span></span><br><span class="line"><span class="built_in">cd</span> ~/下载</span><br><span class="line">./Cursor-1.4.5-x86_64.AppImage --no-sandbox --disable-gpu-sandbox</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line"><span class="built_in">chmod</span> +x ~/start-cursor.sh</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建桌面文件指向启动脚本</span></span><br><span class="line"><span class="built_in">cat</span> &gt; <span class="string">&quot;<span class="variable">$DESKTOP_DIR</span>/Cursor.desktop&quot;</span> &lt;&lt; <span class="string">EOF</span></span><br><span class="line"><span class="string">[Desktop Entry]</span></span><br><span class="line"><span class="string">Version=1.0</span></span><br><span class="line"><span class="string">Name=Cursor</span></span><br><span class="line"><span class="string">Exec=$HOME/start-cursor.sh</span></span><br><span class="line"><span class="string">Icon=/usr/share/icons/hicolor/512x512/apps/cursor.png</span></span><br><span class="line"><span class="string">Terminal=false</span></span><br><span class="line"><span class="string">Type=Application</span></span><br><span class="line"><span class="string">Categories=Development;</span></span><br><span class="line"><span class="string">StartupWMClass=cursor</span></span><br><span class="line"><span class="string">EOF</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置信任</span></span><br><span class="line">gio <span class="built_in">set</span> <span class="string">&quot;<span class="variable">$DESKTOP_DIR</span>/Cursor.desktop&quot;</span> <span class="string">&quot;metadata::trusted&quot;</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><h2 id="系统级深度修复"><a href="#系统级深度修复" class="headerlink" title="系统级深度修复"></a>系统级深度修复</h2><p>对于坚持使用沙箱的用户，这里提供更深入的修复方法。</p><h3 id="内核参数调整"><a href="#内核参数调整" class="headerlink" title="内核参数调整"></a>内核参数调整</h3><p>检查并调整相关内核参数：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 检查当前用户命名空间限制</span></span><br><span class="line"><span class="built_in">cat</span> /proc/sys/user/max_user_namespaces</span><br><span class="line"><span class="comment"># 典型值: 10000</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 如果值为0，需要调整</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;user.max_user_namespaces=10000&quot;</span> | <span class="built_in">sudo</span> <span class="built_in">tee</span> -a /etc/sysctl.d/99-userns.conf</span><br><span class="line"><span class="built_in">sudo</span> sysctl -p /etc/sysctl.d/99-userns.conf</span><br></pre></td></tr></table></figure><h3 id="安全模块诊断"><a href="#安全模块诊断" class="headerlink" title="安全模块诊断"></a>安全模块诊断</h3><p>深入检查安全模块的状态：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看 AppArmor 状态详情</span></span><br><span class="line"><span class="built_in">sudo</span> aa-status</span><br><span class="line"></span><br><span class="line"><span class="comment"># 检查 SELinux 状态（如果使用）</span></span><br><span class="line">sestatus 2&gt;/dev/null || <span class="built_in">echo</span> <span class="string">&quot;SELinux not installed&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看系统日志中的沙箱相关错误</span></span><br><span class="line"><span class="built_in">sudo</span> dmesg | grep -i sandbox</span><br><span class="line">journalctl -b | grep -i sandbox</span><br></pre></td></tr></table></figure><h2 id="预防措施与最佳实践"><a href="#预防措施与最佳实践" class="headerlink" title="预防措施与最佳实践"></a>预防措施与最佳实践</h2><p>为了避免未来遇到类似问题，我总结了以下最佳实践：</p><ol><li><p><strong>定期更新系统</strong>：保持内核和系统工具最新</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt update &amp;&amp; <span class="built_in">sudo</span> apt upgrade</span><br></pre></td></tr></table></figure></li><li><p><strong>了解系统安全策略</strong>：定期检查安全设置</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 检查安全相关设置</span></span><br><span class="line">sysctl -a | grep user_namespace</span><br><span class="line">aa-status</span><br></pre></td></tr></table></figure></li><li><p><strong>文档化解决方案</strong>：记录成功解决的方法，便于未来参考</p></li></ol><h2 id="总结与反思"><a href="#总结与反思" class="headerlink" title="总结与反思"></a>总结与反思</h2><p>这次故障排除之旅揭示了现代 Linux 桌面环境中安全性与便利性之间的微妙平衡。通过解决 Cursor AppImage 的沙箱问题和桌面快捷方式信任问题，我们不仅恢复了一个重要工具的使用，更深入理解了：</p><ol><li><strong>Linux 内核安全机制</strong>：用户命名空间、沙箱技术和权限模型</li><li><strong>桌面环境安全策略</strong>：GNOME 对 .desktop 文件的信任管理</li><li><strong>多层解决方案</strong>：从根本修复到临时变通的完整应对策略</li></ol><p>最重要的是，这次经历提醒我们，在开源生态中，深入理解系统工作原理往往比寻找快速解决方案更为重要。每一个错误信息都是系统试图与我们沟通的方式，耐心解读这些信息是成为高效 Linux 用户的关键。</p><p>希望这篇详细的记录能够帮助遇到类似问题的开发者节省宝贵的时间，更深入地理解 Linux 系统的工作机制。</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>博客评论系统搭建指南：从零开始配置Waline与Giscus</title>
      <link>https://swkajnbruceee.github.io/2025/08/03/%E5%8D%9A%E5%AE%A2%E8%AF%84%E8%AE%BA%E7%B3%BB%E7%BB%9F%E6%90%AD%E5%BB%BA%E6%8C%87%E5%8D%97/</link>
      <description>
        <![CDATA[<h1 id="博客评论系统搭建指南：从零开始配置Waline与Giscus"><a href="#博客评论系统搭建指南：从零开始配置Waline与Giscus" class="headerlink" title="博客评论系统搭建指南：从零开始配置Waline与Giscus">]]>
      </description>
      <author>BruceLee</author>
      <category domain="https://swkajnbruceee.github.io/categories/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/">技术分享</category>
      <category domain="https://swkajnbruceee.github.io/tags/Hexo/">Hexo</category>
      <category domain="https://swkajnbruceee.github.io/tags/Waline/">Waline</category>
      <category domain="https://swkajnbruceee.github.io/tags/Giscus/">Giscus</category>
      <category domain="https://swkajnbruceee.github.io/tags/%E8%AF%84%E8%AE%BA%E7%B3%BB%E7%BB%9F/">评论系统</category>
      <pubDate>Sun, 03 Aug 2025 08:15:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h1 id="博客评论系统搭建指南：从零开始配置Waline与Giscus"><a href="#博客评论系统搭建指南：从零开始配置Waline与Giscus" class="headerlink" title="博客评论系统搭建指南：从零开始配置Waline与Giscus"></a>博客评论系统搭建指南：从零开始配置Waline与Giscus</h1><p>一个优秀的博客评论系统能够促进读者互动，建立社区氛围，同时也为博主提供宝贵的反馈。在经过多次尝试和对比后，我最终选择了Waline和Giscus作为我的博客评论系统。本文将详细记录这两个系统的部署过程、配置细节以及自定义样式的实现，希望能为同样在寻找合适评论系统的你提供参考。</p><h1 id="🤔-评论系统的选择考量"><a href="#🤔-评论系统的选择考量" class="headerlink" title="🤔 评论系统的选择考量"></a>🤔 评论系统的选择考量</h1><p>在选择评论系统时，我主要考虑了以下几个方面：</p><ul><li><strong>数据控制权</strong>：评论数据是否由自己掌控</li><li><strong>隐私保护</strong>：是否尊重用户隐私，不过度收集信息</li><li><strong>易用性</strong>：对访客是否友好，登录方式是否便捷</li><li><strong>功能完整性</strong>：是否支持回复、通知、表情等功能</li><li><strong>定制性</strong>：是否可以根据博客风格进行样式定制</li><li><strong>维护成本</strong>：部署和维护的复杂度和成本</li></ul><p>经过对比，我选择了Waline作为主力评论系统，同时配置了Giscus作为备选，两者可以灵活切换。</p><h1 id="🌟-Waline评论系统"><a href="#🌟-Waline评论系统" class="headerlink" title="🌟 Waline评论系统"></a>🌟 Waline评论系统</h1><h2 id="我的Waline部署历程"><a href="#我的Waline部署历程" class="headerlink" title="我的Waline部署历程"></a>我的Waline部署历程</h2><p>在部署Waline评论系统的过程中，我经历了一些波折，最终找到了最适合我的解决方案：</p><ol><li><p><strong>最初尝试MongoDB + Vercel</strong>：我最初计划使用MongoDB Atlas作为数据库，通过Vercel部署Waline。配置过程顺利，但在关键的管理员注册环节，我遇到了问题 - 无论如何尝试，都无法成功注册管理员账号，页面总是提示错误或无响应。</p></li><li><p><strong>考虑Railway部署</strong>：由于遇到的问题，我转向了Railway平台，它提供了更稳定的容器化环境。然而，Railway最近调整了定价策略，免费额度有限，长期使用需要付费，这不太符合我的个人博客需求。</p></li><li><p><strong>最终选择LeanCloud + Vercel</strong>：经过研究，我发现LeanCloud是Waline官方推荐的数据库解决方案，在国内访问稳定，配置也相对简单。结合Vercel的部署能力，这个组合既免费又高效，管理员注册问题也迎刃而解。</p></li></ol><p>这段经历让我明白，选择合适的技术栈不仅要考虑功能和性能，还要考虑稳定性、易用性和长期维护成本。</p><h2 id="为什么选择Vercel-LeanCloud部署Waline"><a href="#为什么选择Vercel-LeanCloud部署Waline" class="headerlink" title="为什么选择Vercel+LeanCloud部署Waline"></a>为什么选择Vercel+LeanCloud部署Waline</h2><p>Waline支持多种部署方式，如Vercel、Netlify、Railway等。经过实践，我发现Vercel+LeanCloud有以下优势：</p><ol><li><strong>免费且稳定</strong>：Vercel提供稳定的Serverless服务，免费额度对个人博客完全够用</li><li><strong>自动化部署</strong>：支持GitHub仓库关联，代码更新后自动部署</li><li><strong>全球CDN</strong>：内置Vercel Edge Network，访问速度快</li><li><strong>简单的部署流程</strong>：一键部署，配置简单</li><li><strong>LeanCloud集成</strong>：可以方便地使用LeanCloud作为数据库，国内访问稳定</li><li><strong>丰富的插件生态</strong>：支持各种扩展功能</li></ol><h2 id="LeanCloud准备工作"><a href="#LeanCloud准备工作" class="headerlink" title="LeanCloud准备工作"></a>LeanCloud准备工作</h2><p>在部署Waline前，我们需要先准备好LeanCloud数据库。LeanCloud提供免费的云数据库服务，非常适合个人博客使用。</p><h3 id="1-注册LeanCloud账号"><a href="#1-注册LeanCloud账号" class="headerlink" title="1. 注册LeanCloud账号"></a>1. 注册LeanCloud账号</h3><p>访问<a href="https://www.leancloud.cn/">LeanCloud官网</a>注册账号。</p><h3 id="2-创建应用"><a href="#2-创建应用" class="headerlink" title="2. 创建应用"></a>2. 创建应用</h3><ul><li>登录后点击「创建应用」</li><li>输入应用名称（如「博客评论系统」）</li><li>选择「开发版」（免费）</li><li>点击「创建」</li></ul><h3 id="3-创建数据库用户"><a href="#3-创建数据库用户" class="headerlink" title="3. 创建数据库用户"></a>3. 创建数据库用户</h3><ul><li>进入应用后，点击左侧菜单「设置」&gt;「应用凭证」</li><li>记录下「AppID」、「AppKey」和「服务器地址」（这些信息后续需要使用）</li></ul><h3 id="4-设置安全域名"><a href="#4-设置安全域名" class="headerlink" title="4. 设置安全域名"></a>4. 设置安全域名</h3><ul><li>点击左侧菜单「设置」&gt;「安全中心」</li><li>在「Web安全域名」中添加你的博客域名（如example.com）</li></ul><h3 id="5-常见问题与解决方案"><a href="#5-常见问题与解决方案" class="headerlink" title="5. 常见问题与解决方案"></a>5. 常见问题与解决方案</h3><p>在使用LeanCloud过程中，可能会遇到以下问题：</p><ul><li><strong>管理员注册失败</strong>：确保JWT_TOKEN已正确设置，并且在注册页面使用的是完整URL（包含&#x2F;ui&#x2F;register）</li><li><strong>评论提交失败</strong>：检查安全域名是否正确配置，确保包含了你的博客域名</li><li><strong>数据库连接错误</strong>：验证LEAN_ID、LEAN_KEY和LEAN_MASTER_KEY是否正确填写</li><li><strong>国内访问问题</strong>：如果使用国际版LeanCloud，可能需要设置代理或考虑使用国内版</li></ul><p>如果遇到持续的问题，可以尝试清空浏览器缓存，或查看浏览器控制台的错误信息进行排查。</p><h2 id="Vercel部署Waline"><a href="#Vercel部署Waline" class="headerlink" title="Vercel部署Waline"></a>Vercel部署Waline</h2><h3 id="1-注册Vercel账号"><a href="#1-注册Vercel账号" class="headerlink" title="1. 注册Vercel账号"></a>1. 注册Vercel账号</h3><p>访问<a href="https://vercel.com/">Vercel官网</a>注册账号。建议使用GitHub账号登录，这样可以直接关联你的GitHub仓库。</p><h3 id="2-一键部署Waline"><a href="#2-一键部署Waline" class="headerlink" title="2. 一键部署Waline"></a>2. 一键部署Waline</h3><ul><li>访问<a href="https://vercel.com/new/clone?repository-url=https://github.com/walinejs/waline/tree/main/example">Waline的Vercel部署模板</a></li><li>点击「Deploy」按钮</li><li>登录你的Vercel账号</li><li>填写项目名称（如「my-blog-comment」）</li><li>点击「Deploy」开始部署</li></ul><h3 id="3-配置环境变量"><a href="#3-配置环境变量" class="headerlink" title="3. 配置环境变量"></a>3. 配置环境变量</h3><p>部署完成后，需要配置以下环境变量：</p><ul><li>点击刚刚创建的项目</li><li>选择「Settings」&gt;「Environment Variables」</li><li>添加以下环境变量：<ul><li><code>LEAN_ID</code>：填入LeanCloud的AppID</li><li><code>LEAN_KEY</code>：填入LeanCloud的AppKey</li><li><code>LEAN_SERVER</code>：填入LeanCloud的服务器地址</li><li><code>JWT_TOKEN</code>：生成一个随机字符串作为JWT密钥（可以使用<a href="https://1password.com/password-generator/">随机字符串生成器</a>）</li></ul></li></ul><h3 id="4-获取Waline服务地址"><a href="#4-获取Waline服务地址" class="headerlink" title="4. 获取Waline服务地址"></a>4. 获取Waline服务地址</h3><ul><li>在项目页面，点击「Visit」按钮</li><li>复制生成的域名，如<code>https://my-blog-comment.vercel.app</code></li></ul><p>这个域名就是你的Waline服务地址，后续需要在博客中配置。</p><h2 id="Hexo-Butterfly主题配置Waline"><a href="#Hexo-Butterfly主题配置Waline" class="headerlink" title="Hexo Butterfly主题配置Waline"></a>Hexo Butterfly主题配置Waline</h2><p>有了Waline服务地址后，我们需要在Hexo的Butterfly主题中配置Waline评论系统：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Waline评论系统配置</span></span><br><span class="line"><span class="attr">waline:</span></span><br><span class="line">  <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line">  <span class="attr">serverURL:</span> <span class="string">https://your-waline-server.vercel.app</span> <span class="comment"># 替换为你的Waline服务地址</span></span><br><span class="line">  <span class="attr">bg:</span> <span class="string">/img/comment_bg.png</span> <span class="comment"># 评论框背景图</span></span><br><span class="line">  <span class="attr">pageview:</span> <span class="literal">true</span> <span class="comment"># 是否开启浏览量统计</span></span><br><span class="line">  <span class="attr">option:</span></span><br><span class="line">    <span class="comment"># 以下为自定义选项</span></span><br><span class="line">    <span class="attr">locale:</span></span><br><span class="line">      <span class="attr">placeholder:</span> <span class="string">欢迎留言交流...</span> <span class="comment"># 评论框占位提示文字</span></span><br><span class="line">    <span class="attr">emoji:</span> <span class="comment"># 表情包配置</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">https://cdn.jsdelivr.net/gh/walinejs/emojis@1.0.0/bilibili</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">https://cdn.jsdelivr.net/gh/walinejs/emojis@1.0.0/weibo</span></span><br><span class="line">    <span class="attr">meta:</span> [<span class="string">nick</span>, <span class="string">mail</span>, <span class="string">link</span>] <span class="comment"># 评论者信息显示项</span></span><br><span class="line">    <span class="attr">requiredMeta:</span> [<span class="string">nick</span>, <span class="string">mail</span>] <span class="comment"># 必填项</span></span><br><span class="line">    <span class="attr">wordLimit:</span> <span class="number">500</span> <span class="comment"># 评论字数限制</span></span><br><span class="line">    <span class="attr">pageSize:</span> <span class="number">10</span> <span class="comment"># 评论分页大小</span></span><br><span class="line">    <span class="attr">login:</span> <span class="string">enable</span> <span class="comment"># 是否启用登录</span></span><br><span class="line">    <span class="attr">avatar:</span> <span class="string">mp</span> <span class="comment"># 默认头像类型</span></span><br></pre></td></tr></table></figure><h2 id="Waline高级配置"><a href="#Waline高级配置" class="headerlink" title="Waline高级配置"></a>Waline高级配置</h2><h3 id="邮件通知配置"><a href="#邮件通知配置" class="headerlink" title="邮件通知配置"></a>邮件通知配置</h3><p>为了让博主及时收到评论通知，可以配置邮件通知功能：</p><ol><li>在Vercel项目的”Environment Variables”中添加以下环境变量：</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">SMTP_SERVICE=gmail  # 邮件服务提供商，如gmail、qq、163等</span><br><span class="line">SMTP_HOST=smtp.gmail.com  # SMTP服务器地址</span><br><span class="line">SMTP_PORT=465  # SMTP端口</span><br><span class="line">SMTP_USER=your-email@gmail.com  # 发送邮件的邮箱</span><br><span class="line">SMTP_PASS=your-password-or-token  # 邮箱密码或授权码</span><br><span class="line">SMTP_SECURE=true  # 是否使用SSL</span><br><span class="line">SITE_NAME=我的博客  # 网站名称</span><br><span class="line">SITE_URL=https://your-blog.com  # 网站地址</span><br><span class="line">ADMINISTRATOR_EMAIL=admin-email@gmail.com  # 管理员邮箱，用于接收评论通知</span><br></pre></td></tr></table></figure><ol start="2"><li>获取邮箱授权码的方法：<ul><li>QQ邮箱：设置 -&gt; 账户 -&gt; POP3&#x2F;IMAP&#x2F;SMTP&#x2F;Exchange&#x2F;CardDAV&#x2F;CalDAV服务 -&gt; 开启POP3&#x2F;SMTP服务 -&gt; 生成授权码</li><li>Gmail：账户设置 -&gt; 安全性 -&gt; 应用专用密码</li></ul></li></ol><h3 id="安全设置"><a href="#安全设置" class="headerlink" title="安全设置"></a>安全设置</h3><p>为了防止垃圾评论和恶意注册，可以配置以下安全设置：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">IPBLACKLIST=127.0.0.1,192.168.0.1  # 黑名单IP，多个IP用逗号分隔</span><br><span class="line">LIMIT_PER_MINUTE=10  # 每分钟评论次数限制</span><br><span class="line">LIMIT_PER_HOUR=50  # 每小时评论次数限制</span><br><span class="line">LIMIT_PER_DAY=100  # 每天评论次数限制</span><br><span class="line">FORBID_WORDS=敏感词1,敏感词2  # 禁止的关键词，多个关键词用逗号分隔</span><br><span class="line">DISALLOW_REGISTER=true  # 禁止注册，只允许管理员创建用户</span><br><span class="line">SECURE_DOMAINS=你的博客域名,二级域名等</span><br><span class="line">AKISMET_KEY=你的Akismet API密钥（用于垃圾评论过滤）</span><br></pre></td></tr></table></figure><h1 id="🔧-Giscus评论系统配置"><a href="#🔧-Giscus评论系统配置" class="headerlink" title="🔧 Giscus评论系统配置"></a>🔧 Giscus评论系统配置</h1><h2 id="为什么选择Giscus"><a href="#为什么选择Giscus" class="headerlink" title="为什么选择Giscus"></a>为什么选择Giscus</h2><p>Giscus是基于GitHub Discussions的评论系统，有以下优势：</p><ol><li><strong>零服务器成本</strong>：不需要自己部署服务器</li><li><strong>GitHub账号登录</strong>：对开发者友好</li><li><strong>Markdown支持</strong>：原生支持Markdown格式</li><li><strong>数据透明</strong>：所有评论都在GitHub Discussions中可见</li><li><strong>轻量级</strong>：加载速度快，不影响博客性能</li></ol><h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h2><h3 id="1-创建或选择GitHub仓库"><a href="#1-创建或选择GitHub仓库" class="headerlink" title="1. 创建或选择GitHub仓库"></a>1. 创建或选择GitHub仓库</h3><p>首先，你需要一个公开的GitHub仓库来存储评论。可以使用博客源码仓库，也可以创建一个专门的评论仓库。</p><h3 id="2-启用Discussions功能"><a href="#2-启用Discussions功能" class="headerlink" title="2. 启用Discussions功能"></a>2. 启用Discussions功能</h3><ul><li>进入仓库页面</li><li>点击”Settings”选项卡</li><li>在”Features”部分，勾选”Discussions”选项</li><li>点击”Save”保存设置</li></ul><h3 id="3-安装Giscus应用"><a href="#3-安装Giscus应用" class="headerlink" title="3. 安装Giscus应用"></a>3. 安装Giscus应用</h3><ul><li>访问<a href="https://github.com/apps/giscus">Giscus App</a></li><li>点击”Install”</li><li>选择要安装的仓库（刚才启用Discussions的仓库）</li><li>完成安装</li></ul><h2 id="获取Giscus配置信息"><a href="#获取Giscus配置信息" class="headerlink" title="获取Giscus配置信息"></a>获取Giscus配置信息</h2><p>访问<a href="https://giscus.app/">Giscus官网</a>，填写以下信息：</p><ol><li><strong>仓库</strong>：格式为<code>用户名/仓库名</code></li><li><strong>页面与Discussion映射关系</strong>：建议选择”Discussion的标题包含页面的pathname”</li><li><strong>Discussion分类</strong>：选择一个分类（如”Announcements”或”General”）</li><li><strong>主题</strong>：选择一个与你博客风格匹配的主题</li></ol><p>填写完成后，网站会生成一段配置代码，我们需要从中提取关键信息。</p><h2 id="获取正确的category-id"><a href="#获取正确的category-id" class="headerlink" title="获取正确的category_id"></a>获取正确的category_id</h2><p>有时候Giscus官网生成的<code>category_id</code>可能不正确，可以使用以下JavaScript脚本获取正确的ID：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在浏览器控制台中运行</span></span><br><span class="line"><span class="keyword">const</span> repo = <span class="string">&#x27;your-username/your-repo&#x27;</span>;</span><br><span class="line"><span class="keyword">const</span> category = <span class="string">&#x27;Announcements&#x27;</span>; <span class="comment">// 替换为你选择的分类名称</span></span><br><span class="line"></span><br><span class="line"><span class="title function_">fetch</span>(<span class="string">`https://api.github.com/repos/<span class="subst">$&#123;repo&#125;</span>/discussions/categories`</span>)</span><br><span class="line">  .<span class="title function_">then</span>(<span class="function"><span class="params">response</span> =&gt;</span> response.<span class="title function_">json</span>())</span><br><span class="line">  .<span class="title function_">then</span>(<span class="function"><span class="params">data</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> categoryInfo = data.<span class="title function_">find</span>(<span class="function"><span class="params">c</span> =&gt;</span> c.<span class="property">name</span> === category);</span><br><span class="line">    <span class="keyword">if</span> (categoryInfo) &#123;</span><br><span class="line">      <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;Category ID:&#x27;</span>, categoryInfo.<span class="property">id</span>);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;Category not found&#x27;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;);</span><br></pre></td></tr></table></figure><h2 id="Hexo-Butterfly主题配置Giscus"><a href="#Hexo-Butterfly主题配置Giscus" class="headerlink" title="Hexo Butterfly主题配置Giscus"></a>Hexo Butterfly主题配置Giscus</h2><p>在Butterfly主题的配置文件中添加Giscus配置：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Giscus评论系统配置</span></span><br><span class="line"><span class="attr">giscus:</span></span><br><span class="line">  <span class="attr">enable:</span> <span class="literal">false</span> <span class="comment"># 默认关闭，需要时开启</span></span><br><span class="line">  <span class="attr">repo:</span> <span class="string">your-username/your-repo</span> <span class="comment"># 替换为你的仓库</span></span><br><span class="line">  <span class="attr">repo_id:</span> <span class="string">R_kgDOxxxxx</span> <span class="comment"># 从Giscus生成的代码中获取</span></span><br><span class="line">  <span class="attr">category_id:</span> <span class="string">DIC_kwDOxxxxx</span> <span class="comment"># 使用上面脚本获取的正确ID</span></span><br><span class="line">  <span class="attr">theme:</span></span><br><span class="line">    <span class="attr">light:</span> <span class="string">light</span> <span class="comment"># 亮色主题</span></span><br><span class="line">    <span class="attr">dark:</span> <span class="string">dark</span> <span class="comment"># 暗色主题</span></span><br><span class="line">  <span class="attr">option:</span></span><br><span class="line">    <span class="attr">mapping:</span> <span class="string">pathname</span> <span class="comment"># 映射关系</span></span><br><span class="line">    <span class="attr">reactionsEnabled:</span> <span class="number">1</span> <span class="comment"># 启用反应</span></span><br><span class="line">    <span class="attr">emitMetadata:</span> <span class="number">0</span> <span class="comment"># 不发送元数据</span></span><br></pre></td></tr></table></figure><h1 id="🎨-评论系统样式定制"><a href="#🎨-评论系统样式定制" class="headerlink" title="🎨 评论系统样式定制"></a>🎨 评论系统样式定制</h1><h2 id="Waline样式定制"><a href="#Waline样式定制" class="headerlink" title="Waline样式定制"></a>Waline样式定制</h2><p>为了让Waline评论系统更好地融入博客风格，我对其进行了样式定制：</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Waline评论区容器 - 高级磨砂卡片效果 */</span></span><br><span class="line"><span class="selector-class">.waline-container</span> &#123;</span><br><span class="line">  <span class="attribute">background</span>: <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.95</span>);</span><br><span class="line">  <span class="attribute">backdrop-filter</span>: <span class="built_in">blur</span>(<span class="number">20px</span>);</span><br><span class="line">  <span class="attribute">border-radius</span>: <span class="number">16px</span>;</span><br><span class="line">  <span class="attribute">border</span>: <span class="number">1px</span> solid <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.3</span>);</span><br><span class="line">  <span class="attribute">box-shadow</span>: <span class="number">0</span> <span class="number">8px</span> <span class="number">32px</span> <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0.1</span>);</span><br><span class="line">  <span class="attribute">margin</span>: <span class="number">2rem</span> <span class="number">0</span>;</span><br><span class="line">  <span class="attribute">padding</span>: <span class="number">1.5rem</span>;</span><br><span class="line">  <span class="attribute">transition</span>: all <span class="number">0.3s</span> ease;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.waline-container</span><span class="selector-pseudo">:hover</span> &#123;</span><br><span class="line">  <span class="attribute">box-shadow</span>: <span class="number">0</span> <span class="number">12px</span> <span class="number">48px</span> <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0.15</span>);</span><br><span class="line">  <span class="attribute">transform</span>: <span class="built_in">translateY</span>(-<span class="number">2px</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 评论编辑器 */</span></span><br><span class="line"><span class="selector-class">.wl-editor</span> &#123;</span><br><span class="line">  <span class="attribute">border-radius</span>: <span class="number">12px</span> <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">border</span>: <span class="number">1px</span> solid <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0.1</span>) <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">transition</span>: all <span class="number">0.3s</span> ease <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">background</span>: <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.8</span>) <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">min-height</span>: <span class="number">120px</span> <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">font-family</span>: <span class="built_in">var</span>(--global-font), sans-serif <span class="meta">!important</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.wl-editor</span><span class="selector-pseudo">:focus</span> &#123;</span><br><span class="line">  <span class="attribute">border-color</span>: <span class="built_in">var</span>(--theme-color) <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">box-shadow</span>: <span class="number">0</span> <span class="number">0</span> <span class="number">0</span> <span class="number">2px</span> <span class="built_in">rgba</span>(<span class="built_in">var</span>(--theme-color-rgb), <span class="number">0.2</span>) <span class="meta">!important</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 评论按钮 */</span></span><br><span class="line"><span class="selector-class">.wl-btn</span> &#123;</span><br><span class="line">  <span class="attribute">border-radius</span>: <span class="number">8px</span> <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">border</span>: none <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">background</span>: <span class="built_in">var</span>(--theme-color) <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">color</span>: white <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">padding</span>: <span class="number">0.5rem</span> <span class="number">1.2rem</span> <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">font-weight</span>: <span class="number">600</span> <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">transition</span>: all <span class="number">0.3s</span> ease <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">font-size</span>: <span class="number">0.9rem</span> <span class="meta">!important</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.wl-btn</span><span class="selector-pseudo">:hover</span> &#123;</span><br><span class="line">  <span class="attribute">opacity</span>: <span class="number">0.9</span> <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">transform</span>: <span class="built_in">translateY</span>(-<span class="number">2px</span>) <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">box-shadow</span>: <span class="number">0</span> <span class="number">4px</span> <span class="number">12px</span> <span class="built_in">rgba</span>(<span class="built_in">var</span>(--theme-color-rgb), <span class="number">0.4</span>) <span class="meta">!important</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 评论卡片 */</span></span><br><span class="line"><span class="selector-class">.wl-card</span> &#123;</span><br><span class="line">  <span class="attribute">border-radius</span>: <span class="number">12px</span> <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">background</span>: <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.8</span>) <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">border</span>: <span class="number">1px</span> solid <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0.05</span>) <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">transition</span>: all <span class="number">0.3s</span> ease <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">margin-top</span>: <span class="number">1.5rem</span> <span class="meta">!important</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.wl-card</span><span class="selector-pseudo">:hover</span> &#123;</span><br><span class="line">  <span class="attribute">background</span>: <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.95</span>) <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">box-shadow</span>: <span class="number">0</span> <span class="number">4px</span> <span class="number">16px</span> <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0.08</span>) <span class="meta">!important</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 评论者头像 */</span></span><br><span class="line"><span class="selector-class">.wl-avatar</span> &#123;</span><br><span class="line">  <span class="attribute">border-radius</span>: <span class="number">50%</span> <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">border</span>: <span class="number">2px</span> solid <span class="built_in">rgba</span>(<span class="built_in">var</span>(--theme-color-rgb), <span class="number">0.2</span>) <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">transition</span>: all <span class="number">0.3s</span> ease <span class="meta">!important</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.wl-avatar</span><span class="selector-pseudo">:hover</span> &#123;</span><br><span class="line">  <span class="attribute">transform</span>: <span class="built_in">rotate</span>(<span class="number">360deg</span>) <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">border-color</span>: <span class="built_in">var</span>(--theme-color) <span class="meta">!important</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 评论者信息 */</span></span><br><span class="line"><span class="selector-class">.wl-meta</span> &#123;</span><br><span class="line">  <span class="attribute">font-family</span>: <span class="built_in">var</span>(--global-font), sans-serif <span class="meta">!important</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.wl-nick</span> &#123;</span><br><span class="line">  <span class="attribute">font-weight</span>: <span class="number">600</span> <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">color</span>: <span class="built_in">var</span>(--font-color) <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">font-size</span>: <span class="number">1rem</span> <span class="meta">!important</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 表情包选择器 */</span></span><br><span class="line"><span class="selector-class">.wl-emoji-popup</span> &#123;</span><br><span class="line">  <span class="attribute">border-radius</span>: <span class="number">12px</span> <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">border</span>: <span class="number">1px</span> solid <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0.1</span>) <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">box-shadow</span>: <span class="number">0</span> <span class="number">8px</span> <span class="number">24px</span> <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0.12</span>) <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">background</span>: <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.98</span>) <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">backdrop-filter</span>: <span class="built_in">blur</span>(<span class="number">20px</span>) <span class="meta">!important</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.wl-emoji</span> &#123;</span><br><span class="line">  <span class="attribute">transition</span>: all <span class="number">0.2s</span> ease <span class="meta">!important</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.wl-emoji</span><span class="selector-pseudo">:hover</span> &#123;</span><br><span class="line">  <span class="attribute">transform</span>: <span class="built_in">scale</span>(<span class="number">1.2</span>) <span class="meta">!important</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 移动端适配 */</span></span><br><span class="line"><span class="keyword">@media</span> (<span class="attribute">max-width</span>: <span class="number">768px</span>) &#123;</span><br><span class="line">  <span class="selector-class">.waline-container</span> &#123;</span><br><span class="line">    <span class="attribute">padding</span>: <span class="number">1rem</span> <span class="meta">!important</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="selector-class">.wl-avatar</span> &#123;</span><br><span class="line">    <span class="attribute">width</span>: <span class="number">28px</span> <span class="meta">!important</span>;</span><br><span class="line">    <span class="attribute">height</span>: <span class="number">28px</span> <span class="meta">!important</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="selector-class">.wl-head</span>,</span><br><span class="line">  <span class="selector-class">.wl-content</span>,</span><br><span class="line">  <span class="selector-class">.wl-meta</span> &#123;</span><br><span class="line">    <span class="attribute">padding</span>: <span class="number">0.5rem</span> <span class="meta">!important</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="Giscus样式定制"><a href="#Giscus样式定制" class="headerlink" title="Giscus样式定制"></a>Giscus样式定制</h2><p>为了保持评论区风格的一致性，我将Giscus的样式也定制为类似Waline的风格：</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Giscus容器 - 与Waline风格一致 */</span></span><br><span class="line"><span class="selector-class">.giscus</span> &#123;</span><br><span class="line">  <span class="attribute">background</span>: <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.95</span>);</span><br><span class="line">  <span class="attribute">backdrop-filter</span>: <span class="built_in">blur</span>(<span class="number">20px</span>);</span><br><span class="line">  <span class="attribute">border-radius</span>: <span class="number">16px</span>;</span><br><span class="line">  <span class="attribute">border</span>: <span class="number">1px</span> solid <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.3</span>);</span><br><span class="line">  <span class="attribute">box-shadow</span>: <span class="number">0</span> <span class="number">8px</span> <span class="number">32px</span> <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0.1</span>);</span><br><span class="line">  <span class="attribute">margin</span>: <span class="number">2rem</span> <span class="number">0</span>;</span><br><span class="line">  <span class="attribute">padding</span>: <span class="number">1.5rem</span>;</span><br><span class="line">  <span class="attribute">transition</span>: all <span class="number">0.3s</span> ease;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.giscus</span><span class="selector-pseudo">:hover</span> &#123;</span><br><span class="line">  <span class="attribute">box-shadow</span>: <span class="number">0</span> <span class="number">12px</span> <span class="number">48px</span> <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0.15</span>);</span><br><span class="line">  <span class="attribute">transform</span>: <span class="built_in">translateY</span>(-<span class="number">2px</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 评论框 */</span></span><br><span class="line"><span class="selector-class">.giscus</span> <span class="selector-class">.gsc-comment-box</span> &#123;</span><br><span class="line">  <span class="attribute">border-radius</span>: <span class="number">12px</span> <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">border</span>: <span class="number">1px</span> solid <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0.1</span>) <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">background</span>: <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.8</span>) <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">margin-bottom</span>: <span class="number">1.5rem</span> <span class="meta">!important</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.giscus</span> <span class="selector-class">.gsc-comment-box-textarea</span> &#123;</span><br><span class="line">  <span class="attribute">border-radius</span>: <span class="number">12px</span> <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">border</span>: none <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">background</span>: transparent <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">min-height</span>: <span class="number">120px</span> <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">font-family</span>: <span class="built_in">var</span>(--global-font), sans-serif <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">padding</span>: <span class="number">1rem</span> <span class="meta">!important</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 按钮 */</span></span><br><span class="line"><span class="selector-class">.giscus</span> <span class="selector-class">.gsc-comment-box-buttons</span> <span class="selector-tag">button</span>,</span><br><span class="line"><span class="selector-class">.giscus</span> <span class="selector-class">.gsc-pagination-button</span> &#123;</span><br><span class="line">  <span class="attribute">border-radius</span>: <span class="number">8px</span> <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">border</span>: none <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">background</span>: <span class="built_in">var</span>(--theme-color) <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">color</span>: white <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">padding</span>: <span class="number">0.5rem</span> <span class="number">1.2rem</span> <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">font-weight</span>: <span class="number">600</span> <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">transition</span>: all <span class="number">0.3s</span> ease <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">font-size</span>: <span class="number">0.9rem</span> <span class="meta">!important</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.giscus</span> <span class="selector-class">.gsc-comment-box-buttons</span> <span class="selector-tag">button</span><span class="selector-pseudo">:hover</span>,</span><br><span class="line"><span class="selector-class">.giscus</span> <span class="selector-class">.gsc-pagination-button</span><span class="selector-pseudo">:hover</span> &#123;</span><br><span class="line">  <span class="attribute">opacity</span>: <span class="number">0.9</span> <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">transform</span>: <span class="built_in">translateY</span>(-<span class="number">2px</span>) <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">box-shadow</span>: <span class="number">0</span> <span class="number">4px</span> <span class="number">12px</span> <span class="built_in">rgba</span>(<span class="built_in">var</span>(--theme-color-rgb), <span class="number">0.4</span>) <span class="meta">!important</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 评论项 */</span></span><br><span class="line"><span class="selector-class">.giscus</span> <span class="selector-class">.gsc-comment</span> &#123;</span><br><span class="line">  <span class="attribute">border-radius</span>: <span class="number">12px</span> <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">background</span>: <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.8</span>) <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">border</span>: <span class="number">1px</span> solid <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0.05</span>) <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">transition</span>: all <span class="number">0.3s</span> ease <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">margin-top</span>: <span class="number">1.5rem</span> <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">padding</span>: <span class="number">1rem</span> <span class="meta">!important</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.giscus</span> <span class="selector-class">.gsc-comment</span><span class="selector-pseudo">:hover</span> &#123;</span><br><span class="line">  <span class="attribute">background</span>: <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.95</span>) <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">box-shadow</span>: <span class="number">0</span> <span class="number">4px</span> <span class="number">16px</span> <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0.08</span>) <span class="meta">!important</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 头像 */</span></span><br><span class="line"><span class="selector-class">.giscus</span> <span class="selector-class">.gsc-comment-author-avatar</span> <span class="selector-tag">img</span> &#123;</span><br><span class="line">  <span class="attribute">border-radius</span>: <span class="number">50%</span> <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">border</span>: <span class="number">2px</span> solid <span class="built_in">rgba</span>(<span class="built_in">var</span>(--theme-color-rgb), <span class="number">0.2</span>) <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">transition</span>: all <span class="number">0.3s</span> ease <span class="meta">!important</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.giscus</span> <span class="selector-class">.gsc-comment-author-avatar</span> <span class="selector-tag">img</span><span class="selector-pseudo">:hover</span> &#123;</span><br><span class="line">  <span class="attribute">transform</span>: <span class="built_in">rotate</span>(<span class="number">360deg</span>) <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">border-color</span>: <span class="built_in">var</span>(--theme-color) <span class="meta">!important</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 暗色模式适配 */</span></span><br><span class="line"><span class="selector-attr">[data-theme=<span class="string">&quot;dark&quot;</span>]</span> <span class="selector-class">.giscus</span> &#123;</span><br><span class="line">  <span class="attribute">background</span>: <span class="built_in">rgba</span>(<span class="number">30</span>, <span class="number">30</span>, <span class="number">30</span>, <span class="number">0.95</span>);</span><br><span class="line">  <span class="attribute">border-color</span>: <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-attr">[data-theme=<span class="string">&quot;dark&quot;</span>]</span> <span class="selector-class">.giscus</span> <span class="selector-class">.gsc-comment-box</span>,</span><br><span class="line"><span class="selector-attr">[data-theme=<span class="string">&quot;dark&quot;</span>]</span> <span class="selector-class">.giscus</span> <span class="selector-class">.gsc-comment</span> &#123;</span><br><span class="line">  <span class="attribute">background</span>: <span class="built_in">rgba</span>(<span class="number">40</span>, <span class="number">40</span>, <span class="number">40</span>, <span class="number">0.8</span>) <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">border-color</span>: <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.1</span>) <span class="meta">!important</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="🔄-评论系统切换功能"><a href="#🔄-评论系统切换功能" class="headerlink" title="🔄 评论系统切换功能"></a>🔄 评论系统切换功能</h1><p>为了方便在Waline和Giscus之间切换，我编写了一个简单的切换脚本：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 评论系统切换脚本</span></span><br><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">&#x27;fs&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">&#x27;path&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> yaml = <span class="built_in">require</span>(<span class="string">&#x27;js-yaml&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 配置文件路径</span></span><br><span class="line"><span class="keyword">const</span> configPath = path.<span class="title function_">join</span>(__dirname, <span class="string">&#x27;_config.butterfly.yml&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 读取配置文件</span></span><br><span class="line"><span class="keyword">let</span> config;</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> fileContent = fs.<span class="title function_">readFileSync</span>(configPath, <span class="string">&#x27;utf8&#x27;</span>);</span><br><span class="line">  config = yaml.<span class="title function_">load</span>(fileContent);</span><br><span class="line">&#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">&#x27;读取配置文件失败:&#x27;</span>, e);</span><br><span class="line">  process.<span class="title function_">exit</span>(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取当前启用的评论系统</span></span><br><span class="line"><span class="keyword">let</span> currentSystem = <span class="string">&#x27;none&#x27;</span>;</span><br><span class="line"><span class="keyword">if</span> (config.<span class="property">waline</span> &amp;&amp; config.<span class="property">waline</span>.<span class="property">enable</span>) &#123;</span><br><span class="line">  currentSystem = <span class="string">&#x27;waline&#x27;</span>;</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (config.<span class="property">giscus</span> &amp;&amp; config.<span class="property">giscus</span>.<span class="property">enable</span>) &#123;</span><br><span class="line">  currentSystem = <span class="string">&#x27;giscus&#x27;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 切换评论系统</span></span><br><span class="line"><span class="keyword">const</span> targetSystem = process.<span class="property">argv</span>[<span class="number">2</span>];</span><br><span class="line"><span class="keyword">if</span> (!targetSystem || (targetSystem !== <span class="string">&#x27;waline&#x27;</span> &amp;&amp; targetSystem !== <span class="string">&#x27;giscus&#x27;</span>)) &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`当前评论系统: <span class="subst">$&#123;currentSystem&#125;</span>`</span>);</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;使用方法: node switch-comments.js [waline|giscus]&#x27;</span>);</span><br><span class="line">  process.<span class="title function_">exit</span>(<span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 禁用所有评论系统</span></span><br><span class="line"><span class="keyword">if</span> (config.<span class="property">waline</span>) config.<span class="property">waline</span>.<span class="property">enable</span> = <span class="literal">false</span>;</span><br><span class="line"><span class="keyword">if</span> (config.<span class="property">giscus</span>) config.<span class="property">giscus</span>.<span class="property">enable</span> = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 启用目标评论系统</span></span><br><span class="line"><span class="keyword">if</span> (targetSystem === <span class="string">&#x27;waline&#x27;</span> &amp;&amp; config.<span class="property">waline</span>) &#123;</span><br><span class="line">  config.<span class="property">waline</span>.<span class="property">enable</span> = <span class="literal">true</span>;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;已切换到Waline评论系统&#x27;</span>);</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;提醒: 确保你的Waline服务已正确部署&#x27;</span>);</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (targetSystem === <span class="string">&#x27;giscus&#x27;</span> &amp;&amp; config.<span class="property">giscus</span>) &#123;</span><br><span class="line">  config.<span class="property">giscus</span>.<span class="property">enable</span> = <span class="literal">true</span>;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;已切换到Giscus评论系统&#x27;</span>);</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;提醒: 确保你的GitHub仓库已启用Discussions功能&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 保存配置文件</span></span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> newContent = yaml.<span class="title function_">dump</span>(config);</span><br><span class="line">  fs.<span class="title function_">writeFileSync</span>(configPath, newContent, <span class="string">&#x27;utf8&#x27;</span>);</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;配置文件已更新，请重新生成博客以应用更改&#x27;</span>);</span><br><span class="line">&#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">&#x27;保存配置文件失败:&#x27;</span>, e);</span><br><span class="line">  process.<span class="title function_">exit</span>(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>使用方法很简单，只需在终端中运行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">node switch-comments.js waline  <span class="comment"># 切换到Waline</span></span><br><span class="line"><span class="comment"># 或</span></span><br><span class="line">node switch-comments.js giscus  <span class="comment"># 切换到Giscus</span></span><br></pre></td></tr></table></figure><h1 id="📊-评论系统管理"><a href="#📊-评论系统管理" class="headerlink" title="📊 评论系统管理"></a>📊 评论系统管理</h1><h2 id="Waline评论管理"><a href="#Waline评论管理" class="headerlink" title="Waline评论管理"></a>Waline评论管理</h2><p>Waline提供了一个管理后台，可以方便地管理评论：</p><ol><li>访问你的Waline服务地址，如<code>https://your-waline-server.vercel.app/ui/register</code></li><li>注册一个管理员账号（首次注册的用户自动成为管理员）</li><li>登录后，你可以：<ul><li>查看所有评论</li><li>审核评论（通过&#x2F;拒绝）</li><li>回复评论</li><li>删除评论</li><li>管理用户</li></ul></li></ol><h2 id="Giscus评论管理"><a href="#Giscus评论管理" class="headerlink" title="Giscus评论管理"></a>Giscus评论管理</h2><p>Giscus的评论管理直接在GitHub Discussions中进行：</p><ol><li>访问你的GitHub仓库</li><li>点击”Discussions”选项卡</li><li>你可以：<ul><li>查看所有评论</li><li>回复评论</li><li>删除评论</li><li>锁定讨论</li><li>转移讨论到其他分类</li></ul></li></ol><h1 id="🔍-评论系统性能优化"><a href="#🔍-评论系统性能优化" class="headerlink" title="🔍 评论系统性能优化"></a>🔍 评论系统性能优化</h1><h2 id="Waline性能优化"><a href="#Waline性能优化" class="headerlink" title="Waline性能优化"></a>Waline性能优化</h2><ol><li><strong>CDN加速</strong>：使用CDN加速Waline前端资源</li></ol><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">waline:</span></span><br><span class="line">  <span class="attr">option:</span></span><br><span class="line">    <span class="attr">cdn:</span> <span class="string">https://cdn.jsdelivr.net/npm/@waline/client@v2/dist/waline.js</span></span><br><span class="line">    <span class="attr">cssUrl:</span> <span class="string">https://cdn.jsdelivr.net/npm/@waline/client@v2/dist/waline.css</span></span><br></pre></td></tr></table></figure><ol start="2"><li><strong>延迟加载</strong>：只有当用户滚动到评论区时才加载Waline</li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 延迟加载Waline</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">lazyLoadWaline</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> walineContainer = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;waline&#x27;</span>);</span><br><span class="line">  <span class="keyword">if</span> (!walineContainer) <span class="keyword">return</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">const</span> observer = <span class="keyword">new</span> <span class="title class_">IntersectionObserver</span>(<span class="function">(<span class="params">entries</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (entries[<span class="number">0</span>].<span class="property">isIntersecting</span>) &#123;</span><br><span class="line">      <span class="comment">// 加载Waline</span></span><br><span class="line">      <span class="keyword">if</span> (<span class="keyword">typeof</span> <span class="title class_">Waline</span> === <span class="string">&#x27;undefined&#x27;</span>) &#123;</span><br><span class="line">        <span class="keyword">const</span> script = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&#x27;script&#x27;</span>);</span><br><span class="line">        script.<span class="property">src</span> = <span class="string">&#x27;https://cdn.jsdelivr.net/npm/@waline/client@v2/dist/waline.js&#x27;</span>;</span><br><span class="line">        script.<span class="property">onload</span> = initWaline;</span><br><span class="line">        <span class="variable language_">document</span>.<span class="property">head</span>.<span class="title function_">appendChild</span>(script);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="title function_">initWaline</span>();</span><br><span class="line">      &#125;</span><br><span class="line">      observer.<span class="title function_">disconnect</span>();</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;);</span><br><span class="line">  </span><br><span class="line">  observer.<span class="title function_">observe</span>(walineContainer);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 初始化页面时调用</span></span><br><span class="line"><span class="variable language_">document</span>.<span class="title function_">addEventListener</span>(<span class="string">&#x27;DOMContentLoaded&#x27;</span>, lazyLoadWaline);</span><br></pre></td></tr></table></figure><h2 id="Giscus性能优化"><a href="#Giscus性能优化" class="headerlink" title="Giscus性能优化"></a>Giscus性能优化</h2><ol><li><strong>延迟加载</strong>：同样，只有当用户滚动到评论区时才加载Giscus</li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 延迟加载Giscus</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">lazyLoadGiscus</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> giscusContainer = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;giscus-container&#x27;</span>);</span><br><span class="line">  <span class="keyword">if</span> (!giscusContainer) <span class="keyword">return</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">const</span> observer = <span class="keyword">new</span> <span class="title class_">IntersectionObserver</span>(<span class="function">(<span class="params">entries</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (entries[<span class="number">0</span>].<span class="property">isIntersecting</span>) &#123;</span><br><span class="line">      <span class="comment">// 加载Giscus</span></span><br><span class="line">      <span class="keyword">const</span> script = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&#x27;script&#x27;</span>);</span><br><span class="line">      script.<span class="property">src</span> = <span class="string">&#x27;https://giscus.app/client.js&#x27;</span>;</span><br><span class="line">      script.<span class="title function_">setAttribute</span>(<span class="string">&#x27;data-repo&#x27;</span>, <span class="string">&#x27;your-username/your-repo&#x27;</span>);</span><br><span class="line">      script.<span class="title function_">setAttribute</span>(<span class="string">&#x27;data-repo-id&#x27;</span>, <span class="string">&#x27;R_kgDOxxxxx&#x27;</span>);</span><br><span class="line">      script.<span class="title function_">setAttribute</span>(<span class="string">&#x27;data-category&#x27;</span>, <span class="string">&#x27;Announcements&#x27;</span>);</span><br><span class="line">      script.<span class="title function_">setAttribute</span>(<span class="string">&#x27;data-category-id&#x27;</span>, <span class="string">&#x27;DIC_kwDOxxxxx&#x27;</span>);</span><br><span class="line">      script.<span class="title function_">setAttribute</span>(<span class="string">&#x27;data-mapping&#x27;</span>, <span class="string">&#x27;pathname&#x27;</span>);</span><br><span class="line">      script.<span class="title function_">setAttribute</span>(<span class="string">&#x27;data-reactions-enabled&#x27;</span>, <span class="string">&#x27;1&#x27;</span>);</span><br><span class="line">      script.<span class="title function_">setAttribute</span>(<span class="string">&#x27;data-emit-metadata&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line">      script.<span class="title function_">setAttribute</span>(<span class="string">&#x27;data-theme&#x27;</span>, <span class="string">&#x27;light&#x27;</span>);</span><br><span class="line">      script.<span class="title function_">setAttribute</span>(<span class="string">&#x27;data-lang&#x27;</span>, <span class="string">&#x27;zh-CN&#x27;</span>);</span><br><span class="line">      script.<span class="property">crossOrigin</span> = <span class="string">&#x27;anonymous&#x27;</span>;</span><br><span class="line">      script.<span class="property">async</span> = <span class="literal">true</span>;</span><br><span class="line">      </span><br><span class="line">      giscusContainer.<span class="title function_">appendChild</span>(script);</span><br><span class="line">      observer.<span class="title function_">disconnect</span>();</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;);</span><br><span class="line">  </span><br><span class="line">  observer.<span class="title function_">observe</span>(giscusContainer);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 初始化页面时调用</span></span><br><span class="line"><span class="variable language_">document</span>.<span class="title function_">addEventListener</span>(<span class="string">&#x27;DOMContentLoaded&#x27;</span>, lazyLoadGiscus);</span><br></pre></td></tr></table></figure><h1 id="🔒-评论系统安全性考量"><a href="#🔒-评论系统安全性考量" class="headerlink" title="🔒 评论系统安全性考量"></a>🔒 评论系统安全性考量</h1><h2 id="Waline安全配置"><a href="#Waline安全配置" class="headerlink" title="Waline安全配置"></a>Waline安全配置</h2><ol><li><strong>限制评论频率</strong>：防止垃圾评论</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">LIMIT_PER_MINUTE=10  # 每分钟最多评论次数</span><br><span class="line">LIMIT_PER_HOUR=50    # 每小时最多评论次数</span><br><span class="line">LIMIT_PER_DAY=100    # 每天最多评论次数</span><br></pre></td></tr></table></figure><ol start="2"><li><strong>评论审核</strong>：开启评论审核功能</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">COMMENT_AUDIT=true  # 评论需要审核后才显示</span><br></pre></td></tr></table></figure><ol start="3"><li><strong>IP黑名单</strong>：屏蔽恶意IP</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">IPBLACKLIST=1.2.3.4,5.6.7.8  # 黑名单IP列表</span><br></pre></td></tr></table></figure><ol start="4"><li><strong>安全域名</strong>：限制评论来源</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SECURE_DOMAINS=your-blog.com,www.your-blog.com  # 允许的域名</span><br></pre></td></tr></table></figure><h2 id="Giscus安全考量"><a href="#Giscus安全考量" class="headerlink" title="Giscus安全考量"></a>Giscus安全考量</h2><ol><li><strong>仓库权限</strong>：确保Giscus应用只有必要的权限</li><li><strong>评论审核</strong>：可以在GitHub Discussions设置中开启”Limit who can comment”选项</li><li><strong>内容过滤</strong>：GitHub自带的内容过滤可以有效防止垃圾内容</li></ol><h1 id="🎁-总结与展望"><a href="#🎁-总结与展望" class="headerlink" title="🎁 总结与展望"></a>🎁 总结与展望</h1><p>通过本文的配置，我们成功搭建了两套功能完善、样式美观的评论系统。Waline提供了更丰富的功能和更高的定制性，而Giscus则更加轻量和简单。根据不同的需求，可以灵活选择使用哪一个。</p><h2 id="LeanCloud作为Waline数据库的优势"><a href="#LeanCloud作为Waline数据库的优势" class="headerlink" title="LeanCloud作为Waline数据库的优势"></a>LeanCloud作为Waline数据库的优势</h2><p>经过实际使用，我发现LeanCloud作为Waline的数据库有以下优势：</p><ol><li><strong>稳定可靠</strong>：服务稳定性高，几乎不会出现连接中断的情况</li><li><strong>配置简单</strong>：相比MongoDB，配置步骤更少，更容易上手</li><li><strong>国内访问友好</strong>：对于中国用户，访问速度更快，无需额外的代理设置</li><li><strong>免费额度充足</strong>：对于个人博客，免费额度完全够用</li><li><strong>官方推荐</strong>：作为Waline官方推荐的数据库方案，兼容性更好</li></ol><h2 id="维护建议"><a href="#维护建议" class="headerlink" title="维护建议"></a>维护建议</h2><p>为了确保评论系统长期稳定运行，建议定期进行以下维护工作：</p><ol><li><strong>数据备份</strong>：定期导出LeanCloud中的评论数据，防止意外丢失</li><li><strong>检查更新</strong>：关注Waline的版本更新，及时升级以获取新功能和安全修复</li><li><strong>监控使用量</strong>：关注LeanCloud的资源使用情况，避免超出免费额度</li><li><strong>安全审查</strong>：定期检查评论内容，删除垃圾评论和不良信息</li></ol><p>未来，我计划进一步优化评论系统：</p><ol><li><strong>集成更多登录方式</strong>：如微信、QQ等国内常用平台</li><li><strong>添加评论打分功能</strong>：让读者可以对文章进行评分</li><li><strong>开发评论数据分析工具</strong>：分析评论趋势和热点</li><li><strong>优化移动端体验</strong>：提供更好的移动端评论体验</li></ol><p>希望这篇文章能帮助你为自己的博客选择和配置合适的评论系统。如果你有任何问题或建议，欢迎在评论区留言交流！</p><hr><blockquote><p>本文首发于个人博客，转载请注明出处。<br>如果你喜欢这篇文章，欢迎分享给更多的人！</p></blockquote>]]>
      </content:encoded>
    </item>
    <item>
      <title>如何高效写博客</title>
      <link>https://swkajnbruceee.github.io/2025/07/24/%E5%A6%82%E4%BD%95%E9%AB%98%E6%95%88%E5%86%99%E5%8D%9A%E5%AE%A2/</link>
      <description>分享一些高效写博客的技巧和工具，让写作变得更轻松</description>
      <author>BruceLee</author>
      <category domain="https://swkajnbruceee.github.io/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/">技术笔记</category>
      <category domain="https://swkajnbruceee.github.io/tags/Hexo/">Hexo</category>
      <category domain="https://swkajnbruceee.github.io/tags/%E5%8D%9A%E5%AE%A2%E5%86%99%E4%BD%9C/">博客写作</category>
      <category domain="https://swkajnbruceee.github.io/tags/%E6%95%88%E7%8E%87%E6%8F%90%E5%8D%87/">效率提升</category>
      <pubDate>Thu, 24 Jul 2025 10:15:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<p>写博客是分享知识和记录思考的好方式，但如何高效地写出高质量的博客文章呢？</p><span id="more"></span><h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>在数字化时代，博客已经成为知识分享和个人品牌建设的重要工具。无论你是技术开发者、设计师还是其他领域的专家，掌握高效的博客写作技巧都能让你事半功倍。</p><h1 id="正文"><a href="#正文" class="headerlink" title="正文"></a>正文</h1><h2 id="1-选择合适的写作工具"><a href="#1-选择合适的写作工具" class="headerlink" title="1. 选择合适的写作工具"></a>1. 选择合适的写作工具</h2><h3 id="Markdown-编辑器推荐"><a href="#Markdown-编辑器推荐" class="headerlink" title="Markdown 编辑器推荐"></a>Markdown 编辑器推荐</h3><ul><li><strong>Typora</strong>: 所见即所得的 Markdown 编辑器</li><li><strong>VS Code</strong>: 配合 Markdown 插件，功能强大</li><li><strong>Obsidian</strong>: 适合构建知识网络</li><li><strong>Notion</strong>: 在线协作，功能丰富</li></ul><h3 id="图片处理工具"><a href="#图片处理工具" class="headerlink" title="图片处理工具"></a>图片处理工具</h3><ul><li><strong>PicGo</strong>: 图片上传工具，支持多种图床</li><li><strong>Unsplash</strong>: 高质量免费图片素材</li><li><strong>TinyPNG</strong>: 图片压缩工具</li></ul><h2 id="2-文章结构规划"><a href="#2-文章结构规划" class="headerlink" title="2. 文章结构规划"></a>2. 文章结构规划</h2><p>一篇好的技术博客通常包含：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">1. 吸引人的标题</span><br><span class="line">2. 简洁的前言（问题背景）</span><br><span class="line">3. 清晰的目录结构</span><br><span class="line">4. 详细的解决方案</span><br><span class="line">5. 代码示例和截图</span><br><span class="line">6. 总结和思考</span><br><span class="line">7. 参考资料</span><br></pre></td></tr></table></figure><h2 id="3-写作技巧"><a href="#3-写作技巧" class="headerlink" title="3. 写作技巧"></a>3. 写作技巧</h2><h3 id="标题优化"><a href="#标题优化" class="headerlink" title="标题优化"></a>标题优化</h3><ul><li>使用数字：《5个提高代码质量的技巧》</li><li>突出价值：《从0到1搭建个人博客》</li><li>制造悬念：《这个CSS技巧让我的网站性能提升50%》</li></ul><h3 id="内容组织"><a href="#内容组织" class="headerlink" title="内容组织"></a>内容组织</h3><ul><li>使用小标题分割内容</li><li>适当使用列表和表格</li><li>添加代码高亮和注释</li><li>插入相关图片和图表</li></ul><h2 id="4-Hexo-写作流程"><a href="#4-Hexo-写作流程" class="headerlink" title="4. Hexo 写作流程"></a>4. Hexo 写作流程</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1. 创建新文章</span></span><br><span class="line">hexo new <span class="string">&quot;文章标题&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 编写内容</span></span><br><span class="line"><span class="comment"># 在 source/_posts/ 目录下编辑 .md 文件</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 本地预览</span></span><br><span class="line">hexo server</span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 生成和部署</span></span><br><span class="line">hexo clean &amp;&amp; hexo generate &amp;&amp; hexo deploy</span><br></pre></td></tr></table></figure><h2 id="5-SEO-优化技巧"><a href="#5-SEO-优化技巧" class="headerlink" title="5. SEO 优化技巧"></a>5. SEO 优化技巧</h2><ul><li>合理使用关键词</li><li>优化文章描述</li><li>添加合适的标签和分类</li><li>使用语义化的标题结构</li><li>添加内链和外链</li></ul><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>高效写博客的关键在于：</p><ol><li><strong>工具选择</strong>：选择适合自己的编辑器和辅助工具</li><li><strong>结构规划</strong>：提前规划文章结构，逻辑清晰</li><li><strong>持续优化</strong>：不断改进写作流程和技巧</li><li><strong>定期发布</strong>：保持更新频率，建立读者习惯</li></ol><p>记住，好的博客不仅要有技术深度，更要有清晰的表达和良好的阅读体验。</p><hr><blockquote><p>本文首发于 <a href="https://swkajnbruceee.github.io/">Bruce’s Blog</a></p><p>转载请注明出处</p></blockquote>]]>
      </content:encoded>
    </item>
    <item>
      <title>博客UI优化之旅：打造个性化Hexo博客</title>
      <link>https://swkajnbruceee.github.io/2025/07/15/%E5%8D%9A%E5%AE%A2UI%E4%BC%98%E5%8C%96%E4%B9%8B%E6%97%85/</link>
      <description>
        <![CDATA[<h1 id="博客UI优化之旅：打造个性化Hexo博客"><a href="#博客UI优化之旅：打造个性化Hexo博客" class="headerlink" title="博客UI优化之旅：打造个性化Hexo博客"></a>博客UI优化之旅：打造个性化Hexo博客</h1><]]>
      </description>
      <author>BruceLee</author>
      <category domain="https://swkajnbruceee.github.io/categories/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/">技术分享</category>
      <category domain="https://swkajnbruceee.github.io/tags/Hexo/">Hexo</category>
      <category domain="https://swkajnbruceee.github.io/tags/UI%E4%BC%98%E5%8C%96/">UI优化</category>
      <category domain="https://swkajnbruceee.github.io/tags/%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91/">前端开发</category>
      <category domain="https://swkajnbruceee.github.io/tags/%E5%8D%9A%E5%AE%A2%E7%BE%8E%E5%8C%96/">博客美化</category>
      <pubDate>Tue, 15 Jul 2025 06:30:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h1 id="博客UI优化之旅：打造个性化Hexo博客"><a href="#博客UI优化之旅：打造个性化Hexo博客" class="headerlink" title="博客UI优化之旅：打造个性化Hexo博客"></a>博客UI优化之旅：打造个性化Hexo博客</h1><p>在搭建个人博客的过程中，除了内容的质量，博客的视觉体验同样重要。一个精心设计的UI不仅能提升用户体验，还能彰显个人品味。本文将分享我在优化Hexo博客UI过程中的经验和技巧，希望能为同样热爱折腾的你提供一些启发。</p><h1 id="🎨-整体设计理念"><a href="#🎨-整体设计理念" class="headerlink" title="🎨 整体设计理念"></a>🎨 整体设计理念</h1><p>在开始具体的优化工作前，我花了不少时间思考博客的整体设计理念。最终，我确定了以下几点：</p><ul><li><strong>简约而不简单</strong>：避免过多的视觉干扰，但保留足够的设计细节</li><li><strong>响应式设计</strong>：在各种设备上都能提供良好的浏览体验</li><li><strong>性能优先</strong>：确保加载速度快，交互流畅</li><li><strong>个性化表达</strong>：融入个人风格，与大众博客有所区别</li></ul><p>带着这些理念，我开始了一系列的优化工作。</p><h1 id="🚀-加载体验优化"><a href="#🚀-加载体验优化" class="headerlink" title="🚀 加载体验优化"></a>🚀 加载体验优化</h1><h2 id="自定义加载动画"><a href="#自定义加载动画" class="headerlink" title="自定义加载动画"></a>自定义加载动画</h2><p>网站加载时的第一印象非常重要。我设计了一个简洁的巫师加载动画，让等待变得有趣。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 修复巫师加载动画显示时间和隐藏问题</span></span><br><span class="line">(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="string">&#x27;use strict&#x27;</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;🧙‍♂️ 巫师加载动画修复脚本启动&#x27;</span>);</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">let</span> loadingStartTime = <span class="title class_">Date</span>.<span class="title function_">now</span>();</span><br><span class="line">  <span class="keyword">let</span> isLoadingHidden = <span class="literal">false</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 智能隐藏加载动画</span></span><br><span class="line">  <span class="keyword">function</span> <span class="title function_">smartHideLoading</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (isLoadingHidden) <span class="keyword">return</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">const</span> loadingBox = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;loading-box&#x27;</span>);</span><br><span class="line">    <span class="keyword">if</span> (!loadingBox) <span class="keyword">return</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">const</span> currentTime = <span class="title class_">Date</span>.<span class="title function_">now</span>();</span><br><span class="line">    <span class="keyword">const</span> elapsedTime = currentTime - loadingStartTime;</span><br><span class="line">    <span class="keyword">const</span> minDisplayTime = <span class="number">2500</span>; <span class="comment">// 最少显示2.5秒</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> (elapsedTime &lt; minDisplayTime) &#123;</span><br><span class="line">      <span class="comment">// 如果显示时间不足，延迟隐藏</span></span><br><span class="line">      <span class="keyword">const</span> remainingTime = minDisplayTime - elapsedTime;</span><br><span class="line">      <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">        <span class="title function_">hideLoadingAnimation</span>();</span><br><span class="line">      &#125;, remainingTime);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="comment">// 时间足够，立即隐藏</span></span><br><span class="line">      <span class="title function_">hideLoadingAnimation</span>();</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 隐藏加载动画</span></span><br><span class="line">  <span class="keyword">function</span> <span class="title function_">hideLoadingAnimation</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (isLoadingHidden) <span class="keyword">return</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">const</span> loadingBox = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;loading-box&#x27;</span>);</span><br><span class="line">    <span class="keyword">if</span> (!loadingBox) <span class="keyword">return</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 标记为已隐藏</span></span><br><span class="line">    isLoadingHidden = <span class="literal">true</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 添加loaded类，触发主题的隐藏动画</span></span><br><span class="line">    loadingBox.<span class="property">classList</span>.<span class="title function_">add</span>(<span class="string">&#x27;loaded&#x27;</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 确保页面可以正常滚动</span></span><br><span class="line">    <span class="variable language_">document</span>.<span class="property">body</span>.<span class="property">style</span>.<span class="property">overflow</span> = <span class="string">&#x27;auto&#x27;</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure><p>这段代码解决了加载动画显示时间过短或过长的问题，确保用户体验的一致性。同时，我还添加了超时保护机制，防止加载动画卡住。</p><h2 id="图片懒加载优化"><a href="#图片懒加载优化" class="headerlink" title="图片懒加载优化"></a>图片懒加载优化</h2><p>为了加快页面加载速度，我对图片懒加载进行了优化：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 图片懒加载</span></span><br><span class="line"><span class="attr">lazyload:</span></span><br><span class="line">  <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line">  <span class="attr">field:</span> <span class="string">site</span></span><br><span class="line">  <span class="attr">placeholder:</span> <span class="string">data:image/svg+xml;base64,...</span></span><br><span class="line">  <span class="attr">blur:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><p>这种配置不仅能延迟加载图片，还能在加载前显示模糊的占位图，提供更好的视觉过渡。</p><h1 id="🌙-暗色模式增强"><a href="#🌙-暗色模式增强" class="headerlink" title="🌙 暗色模式增强"></a>🌙 暗色模式增强</h1><h2 id="暗色模式切换修复"><a href="#暗色模式切换修复" class="headerlink" title="暗色模式切换修复"></a>暗色模式切换修复</h2><p>暗色模式是现代网站的标配，但Butterfly主题的暗色模式切换按钮有时会失效。我编写了修复脚本：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 修复夜间模式按钮点击问题</span></span><br><span class="line">(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="string">&#x27;use strict&#x27;</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">function</span> <span class="title function_">initDarkmodeFix</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> darkmodeButton = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;darkmode&#x27;</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> (!darkmodeButton) <span class="keyword">return</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 检查是否已有点击事件</span></span><br><span class="line">    <span class="keyword">const</span> hasClickEvent = darkmodeButton.<span class="property">onclick</span> || </span><br><span class="line">                         darkmodeButton.<span class="title function_">getAttribute</span>(<span class="string">&#x27;onclick&#x27;</span>) ||</span><br><span class="line">                         darkmodeButton.<span class="title function_">hasAttribute</span>(<span class="string">&#x27;data-click-bound&#x27;</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> (hasClickEvent) <span class="keyword">return</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 添加点击事件</span></span><br><span class="line">    darkmodeButton.<span class="title function_">addEventListener</span>(<span class="string">&#x27;click&#x27;</span>, <span class="keyword">function</span>(<span class="params">e</span>) &#123;</span><br><span class="line">      e.<span class="title function_">preventDefault</span>();</span><br><span class="line">      e.<span class="title function_">stopPropagation</span>();</span><br><span class="line">      </span><br><span class="line">      <span class="comment">// 方法1：使用主题的darkmode函数</span></span><br><span class="line">      <span class="keyword">if</span> (<span class="keyword">typeof</span> btf !== <span class="string">&#x27;undefined&#x27;</span> &amp;&amp; <span class="keyword">typeof</span> btf.<span class="property">darkmode</span> === <span class="string">&#x27;function&#x27;</span>) &#123;</span><br><span class="line">        btf.<span class="title function_">darkmode</span>();</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      </span><br><span class="line">      <span class="comment">// 方法2：手动切换主题</span></span><br><span class="line">      <span class="keyword">const</span> currentTheme = <span class="variable language_">document</span>.<span class="property">documentElement</span>.<span class="title function_">getAttribute</span>(<span class="string">&#x27;data-theme&#x27;</span>);</span><br><span class="line">      <span class="keyword">const</span> newTheme = currentTheme === <span class="string">&#x27;dark&#x27;</span> ? <span class="string">&#x27;light&#x27;</span> : <span class="string">&#x27;dark&#x27;</span>;</span><br><span class="line">      </span><br><span class="line">      <span class="comment">// 切换主题</span></span><br><span class="line">      <span class="variable language_">document</span>.<span class="property">documentElement</span>.<span class="title function_">setAttribute</span>(<span class="string">&#x27;data-theme&#x27;</span>, newTheme);</span><br><span class="line">      </span><br><span class="line">      <span class="comment">// 保存到本地存储</span></span><br><span class="line">      <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">typeof</span> btf !== <span class="string">&#x27;undefined&#x27;</span> &amp;&amp; btf.<span class="property">saveToLocal</span>) &#123;</span><br><span class="line">          btf.<span class="property">saveToLocal</span>.<span class="title function_">set</span>(<span class="string">&#x27;theme&#x27;</span>, newTheme, <span class="number">2</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">          <span class="variable language_">localStorage</span>.<span class="title function_">setItem</span>(<span class="string">&#x27;theme&#x27;</span>, newTheme);</span><br><span class="line">        &#125;</span><br><span class="line">      &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">warn</span>(<span class="string">&#x27;保存主题设置失败:&#x27;</span>, error);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 标记已绑定事件</span></span><br><span class="line">    darkmodeButton.<span class="title function_">setAttribute</span>(<span class="string">&#x27;data-click-bound&#x27;</span>, <span class="string">&#x27;true&#x27;</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 多种初始化时机</span></span><br><span class="line">  <span class="keyword">if</span> (<span class="variable language_">document</span>.<span class="property">readyState</span> === <span class="string">&#x27;loading&#x27;</span>) &#123;</span><br><span class="line">    <span class="variable language_">document</span>.<span class="title function_">addEventListener</span>(<span class="string">&#x27;DOMContentLoaded&#x27;</span>, initDarkmodeFix);</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="title function_">initDarkmodeFix</span>();</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 延迟初始化，确保主题脚本已加载</span></span><br><span class="line">  <span class="built_in">setTimeout</span>(initDarkmodeFix, <span class="number">500</span>);</span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure><h2 id="自定义暗色模式样式"><a href="#自定义暗色模式样式" class="headerlink" title="自定义暗色模式样式"></a>自定义暗色模式样式</h2><p>除了修复功能外，我还为暗色模式定制了专属样式：</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 暗色模式适配 */</span></span><br><span class="line"><span class="selector-attr">[data-theme=<span class="string">&quot;dark&quot;</span>]</span> <span class="selector-class">.nav-search-input</span> &#123;</span><br><span class="line">  <span class="attribute">border-color</span>: <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.2</span>);</span><br><span class="line">  <span class="attribute">background</span>: <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0.3</span>);</span><br><span class="line">  <span class="attribute">color</span>: <span class="number">#fff</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-attr">[data-theme=<span class="string">&quot;dark&quot;</span>]</span> <span class="selector-class">.nav-search-input</span><span class="selector-pseudo">:focus</span> &#123;</span><br><span class="line">  <span class="attribute">border-color</span>: <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.4</span>);</span><br><span class="line">  <span class="attribute">background</span>: <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0.5</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-attr">[data-theme=<span class="string">&quot;dark&quot;</span>]</span> <span class="selector-class">.nav-search-results</span> &#123;</span><br><span class="line">  <span class="attribute">background</span>: <span class="number">#2d3748</span>;</span><br><span class="line">  <span class="attribute">border</span>: <span class="number">1px</span> solid <span class="number">#4a5568</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-attr">[data-theme=<span class="string">&quot;dark&quot;</span>]</span> <span class="selector-class">.nav-search-item</span> &#123;</span><br><span class="line">  <span class="attribute">border-bottom-color</span>: <span class="number">#4a5568</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-attr">[data-theme=<span class="string">&quot;dark&quot;</span>]</span> <span class="selector-class">.nav-search-item</span><span class="selector-pseudo">:hover</span> &#123;</span><br><span class="line">  <span class="attribute">background-color</span>: <span class="number">#4a5568</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-attr">[data-theme=<span class="string">&quot;dark&quot;</span>]</span> <span class="selector-class">.nav-search-title</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: <span class="number">#e2e8f0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-attr">[data-theme=<span class="string">&quot;dark&quot;</span>]</span> <span class="selector-class">.nav-search-content</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: <span class="number">#a0aec0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这些样式确保了暗色模式下各元素的视觉协调性，提供了更舒适的夜间阅读体验。</p><h1 id="🔍-搜索功能增强"><a href="#🔍-搜索功能增强" class="headerlink" title="🔍 搜索功能增强"></a>🔍 搜索功能增强</h1><h2 id="导航栏搜索"><a href="#导航栏搜索" class="headerlink" title="导航栏搜索"></a>导航栏搜索</h2><p>为了方便用户快速查找内容，我在导航栏添加了搜索功能：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 轻量级导航栏搜索功能</span></span><br><span class="line">(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="string">&#x27;use strict&#x27;</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">let</span> searchData = [];</span><br><span class="line">  <span class="keyword">let</span> isDataLoaded = <span class="literal">false</span>;</span><br><span class="line">  <span class="keyword">let</span> searchTimeout = <span class="literal">null</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 初始化搜索功能</span></span><br><span class="line">  <span class="keyword">function</span> <span class="title function_">initSearch</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> searchInput = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;nav-search-input&#x27;</span>);</span><br><span class="line">    <span class="keyword">const</span> searchResults = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;nav-search-results&#x27;</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> (!searchInput || !searchResults) <span class="keyword">return</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 绑定事件</span></span><br><span class="line">    searchInput.<span class="title function_">addEventListener</span>(<span class="string">&#x27;input&#x27;</span>, handleInput);</span><br><span class="line">    searchInput.<span class="title function_">addEventListener</span>(<span class="string">&#x27;focus&#x27;</span>, handleFocus);</span><br><span class="line">    searchInput.<span class="title function_">addEventListener</span>(<span class="string">&#x27;keydown&#x27;</span>, handleKeydown);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 点击外部关闭结果</span></span><br><span class="line">    <span class="variable language_">document</span>.<span class="title function_">addEventListener</span>(<span class="string">&#x27;click&#x27;</span>, <span class="keyword">function</span>(<span class="params">e</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (!e.<span class="property">target</span>.<span class="title function_">closest</span>(<span class="string">&#x27;.nav-search-container&#x27;</span>)) &#123;</span><br><span class="line">        <span class="title function_">hideResults</span>();</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 加载搜索数据</span></span><br><span class="line">    <span class="title function_">loadSearchData</span>();</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 加载搜索数据</span></span><br><span class="line">  <span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">loadSearchData</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> response = <span class="keyword">await</span> <span class="title function_">fetch</span>(<span class="string">&#x27;/search.xml&#x27;</span>);</span><br><span class="line">      <span class="keyword">if</span> (!response.<span class="property">ok</span>) <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">&#x27;Failed to fetch search data&#x27;</span>);</span><br><span class="line">      </span><br><span class="line">      <span class="keyword">const</span> xmlText = <span class="keyword">await</span> response.<span class="title function_">text</span>();</span><br><span class="line">      <span class="keyword">const</span> parser = <span class="keyword">new</span> <span class="title class_">DOMParser</span>();</span><br><span class="line">      <span class="keyword">const</span> xmlDoc = parser.<span class="title function_">parseFromString</span>(xmlText, <span class="string">&#x27;text/xml&#x27;</span>);</span><br><span class="line">      </span><br><span class="line">      <span class="keyword">const</span> entries = xmlDoc.<span class="title function_">querySelectorAll</span>(<span class="string">&#x27;entry&#x27;</span>);</span><br><span class="line">      searchData = <span class="title class_">Array</span>.<span class="title function_">from</span>(entries).<span class="title function_">map</span>(<span class="function"><span class="params">entry</span> =&gt;</span> (&#123;</span><br><span class="line">        <span class="attr">title</span>: entry.<span class="title function_">querySelector</span>(<span class="string">&#x27;title&#x27;</span>)?.<span class="property">textContent</span> || <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">        <span class="attr">content</span>: entry.<span class="title function_">querySelector</span>(<span class="string">&#x27;content&#x27;</span>)?.<span class="property">textContent</span>?.<span class="title function_">replace</span>(<span class="regexp">/&lt;[^&gt;]+&gt;/g</span>, <span class="string">&#x27;&#x27;</span>) || <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">        <span class="attr">url</span>: entry.<span class="title function_">querySelector</span>(<span class="string">&#x27;url&#x27;</span>)?.<span class="property">textContent</span> || <span class="string">&#x27;&#x27;</span></span><br><span class="line">      &#125;));</span><br><span class="line">      </span><br><span class="line">      isDataLoaded = <span class="literal">true</span>;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">      <span class="variable language_">console</span>.<span class="title function_">warn</span>(<span class="string">&#x27;Search data loading failed:&#x27;</span>, error);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure><h2 id="搜索历史记录"><a href="#搜索历史记录" class="headerlink" title="搜索历史记录"></a>搜索历史记录</h2><p>为了提升用户体验，我还添加了搜索历史记录功能：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 搜索功能增强 - 性能优化版</span></span><br><span class="line">(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="string">&#x27;use strict&#x27;</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">let</span> searchHistory = [];</span><br><span class="line">  <span class="keyword">let</span> isInitialized = <span class="literal">false</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 延迟初始化，避免阻塞页面加载</span></span><br><span class="line">  <span class="built_in">setTimeout</span>(initSearchEnhance, <span class="number">1000</span>);</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">function</span> <span class="title function_">initSearchEnhance</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (isInitialized) <span class="keyword">return</span>;</span><br><span class="line">    isInitialized = <span class="literal">true</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 加载搜索历史</span></span><br><span class="line">    <span class="title function_">loadSearchHistory</span>();</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 添加键盘快捷键</span></span><br><span class="line">    <span class="title function_">addKeyboardShortcuts</span>();</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 监听搜索输入</span></span><br><span class="line">    <span class="title function_">enhanceSearchInput</span>();</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 加载搜索历史</span></span><br><span class="line">  <span class="keyword">function</span> <span class="title function_">loadSearchHistory</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> stored = <span class="variable language_">localStorage</span>.<span class="title function_">getItem</span>(<span class="string">&#x27;blog-search-history&#x27;</span>);</span><br><span class="line">      searchHistory = stored ? <span class="title class_">JSON</span>.<span class="title function_">parse</span>(stored) : [];</span><br><span class="line">    &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">      searchHistory = [];</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 保存搜索历史</span></span><br><span class="line">  <span class="keyword">function</span> <span class="title function_">saveSearchHistory</span>(<span class="params">query</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (!query || query.<span class="property">length</span> &lt; <span class="number">2</span>) <span class="keyword">return</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 移除重复项并添加到开头</span></span><br><span class="line">    searchHistory = searchHistory.<span class="title function_">filter</span>(<span class="function"><span class="params">item</span> =&gt;</span> item !== query);</span><br><span class="line">    searchHistory.<span class="title function_">unshift</span>(query);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 限制历史记录数量</span></span><br><span class="line">    searchHistory = searchHistory.<span class="title function_">slice</span>(<span class="number">0</span>, <span class="number">8</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="variable language_">localStorage</span>.<span class="title function_">setItem</span>(<span class="string">&#x27;blog-search-history&#x27;</span>, <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(searchHistory));</span><br><span class="line">    &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">      <span class="comment">// 忽略存储错误</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure><h1 id="🤖-AI摘要功能"><a href="#🤖-AI摘要功能" class="headerlink" title="🤖 AI摘要功能"></a>🤖 AI摘要功能</h1><p>为了让读者快速了解文章内容，我添加了AI摘要功能：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 轻量级AI摘要功能 - 性能优化版</span></span><br><span class="line">(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="string">&#x27;use strict&#x27;</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 只在文章页面显示AI摘要</span></span><br><span class="line">  <span class="keyword">if</span> (!<span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">&#x27;.post-content&#x27;</span>)) <span class="keyword">return</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 延迟初始化，避免阻塞页面加载</span></span><br><span class="line">  <span class="built_in">setTimeout</span>(initAISummary, <span class="number">500</span>);</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">function</span> <span class="title function_">initAISummary</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> aiSummaryHTML = <span class="string">`</span></span><br><span class="line"><span class="string">      &lt;div class=&quot;ai-summary&quot; id=&quot;ai-summary&quot;&gt;</span></span><br><span class="line"><span class="string">        &lt;div class=&quot;ai-summary-header&quot;&gt;</span></span><br><span class="line"><span class="string">          &lt;div class=&quot;ai-summary-left&quot;&gt;</span></span><br><span class="line"><span class="string">            &lt;div class=&quot;ai-summary-icon&quot;&gt;AI&lt;/div&gt;</span></span><br><span class="line"><span class="string">            &lt;h3 class=&quot;ai-summary-title&quot;&gt;AI摘要&lt;/h3&gt;</span></span><br><span class="line"><span class="string">          &lt;/div&gt;</span></span><br><span class="line"><span class="string">          &lt;div class=&quot;ai-summary-right&quot;&gt;</span></span><br><span class="line"><span class="string">            &lt;span class=&quot;ai-summary-badge&quot;&gt;智能生成&lt;/span&gt;</span></span><br><span class="line"><span class="string">            &lt;button class=&quot;ai-summary-refresh&quot; onclick=&quot;refreshAISummary()&quot;&gt;</span></span><br><span class="line"><span class="string">              &lt;i class=&quot;fas fa-sync-alt&quot;&gt;&lt;/i&gt;</span></span><br><span class="line"><span class="string">            &lt;/button&gt;</span></span><br><span class="line"><span class="string">          &lt;/div&gt;</span></span><br><span class="line"><span class="string">        &lt;/div&gt;</span></span><br><span class="line"><span class="string">        &lt;div class=&quot;ai-summary-content&quot;&gt;</span></span><br><span class="line"><span class="string">          &lt;div class=&quot;ai-summary-text&quot; id=&quot;ai-summary-text&quot;&gt;</span></span><br><span class="line"><span class="string">            &lt;div class=&quot;loading-dots&quot;&gt;</span></span><br><span class="line"><span class="string">              &lt;span&gt;&lt;/span&gt;</span></span><br><span class="line"><span class="string">              &lt;span&gt;&lt;/span&gt;</span></span><br><span class="line"><span class="string">              &lt;span&gt;&lt;/span&gt;</span></span><br><span class="line"><span class="string">            &lt;/div&gt;</span></span><br><span class="line"><span class="string">            正在生成摘要...</span></span><br><span class="line"><span class="string">          &lt;/div&gt;</span></span><br><span class="line"><span class="string">        &lt;/div&gt;</span></span><br><span class="line"><span class="string">        &lt;div class=&quot;ai-summary-footer&quot;&gt;</span></span><br><span class="line"><span class="string">          &lt;div class=&quot;ai-summary-info&quot;&gt;</span></span><br><span class="line"><span class="string">            &lt;i class=&quot;fas fa-robot&quot;&gt;&lt;/i&gt;</span></span><br><span class="line"><span class="string">            由AI智能生成</span></span><br><span class="line"><span class="string">          &lt;/div&gt;</span></span><br><span class="line"><span class="string">        &lt;/div&gt;</span></span><br><span class="line"><span class="string">      &lt;/div&gt;</span></span><br><span class="line"><span class="string">    `</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">const</span> postContent = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">&#x27;.post-content&#x27;</span>);</span><br><span class="line">    <span class="keyword">if</span> (postContent) &#123;</span><br><span class="line">      postContent.<span class="title function_">insertAdjacentHTML</span>(<span class="string">&#x27;beforebegin&#x27;</span>, aiSummaryHTML);</span><br><span class="line">      <span class="built_in">setTimeout</span>(generateAISummary, <span class="number">800</span>);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 打字机效果函数</span></span><br><span class="line">  <span class="keyword">function</span> <span class="title function_">typeWriter</span>(<span class="params">element, text, speed = <span class="number">40</span></span>) &#123;</span><br><span class="line">    element.<span class="property">innerHTML</span> = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">    <span class="keyword">let</span> i = <span class="number">0</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">function</span> <span class="title function_">type</span>(<span class="params"></span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (i &lt; text.<span class="property">length</span>) &#123;</span><br><span class="line">        element.<span class="property">innerHTML</span> += text.<span class="title function_">charAt</span>(i);</span><br><span class="line">        i++;</span><br><span class="line">        <span class="built_in">setTimeout</span>(type, speed);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 打字完成后添加光标闪烁效果</span></span><br><span class="line">        element.<span class="property">innerHTML</span> += <span class="string">&#x27;&lt;span class=&quot;typing-cursor&quot;&gt;&lt;/span&gt;&#x27;</span>;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="title function_">type</span>();</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure><p>这个功能通过分析文章内容，自动生成摘要，并以打字机效果呈现，增加了交互的趣味性。</p><h1 id="📊-性能监控"><a href="#📊-性能监控" class="headerlink" title="📊 性能监控"></a>📊 性能监控</h1><p>为了持续优化博客性能，我添加了轻量级性能监控：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 轻量级性能监控</span></span><br><span class="line">(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="string">&#x27;use strict&#x27;</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 只在开发环境或需要时启用</span></span><br><span class="line">  <span class="keyword">const</span> <span class="variable constant_">ENABLE_MONITORING</span> = <span class="literal">false</span>; <span class="comment">// 设置为 true 启用监控</span></span><br><span class="line">  </span><br><span class="line">  <span class="keyword">if</span> (!<span class="variable constant_">ENABLE_MONITORING</span>) <span class="keyword">return</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">let</span> performanceData = &#123;</span><br><span class="line">    <span class="attr">pageLoadTime</span>: <span class="number">0</span>,</span><br><span class="line">    <span class="attr">domContentLoadedTime</span>: <span class="number">0</span>,</span><br><span class="line">    <span class="attr">firstPaintTime</span>: <span class="number">0</span>,</span><br><span class="line">    <span class="attr">resourceLoadTime</span>: <span class="number">0</span>,</span><br><span class="line">    <span class="attr">jsExecutionTime</span>: <span class="number">0</span></span><br><span class="line">  &#125;;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 监控页面加载性能</span></span><br><span class="line">  <span class="keyword">function</span> <span class="title function_">monitorPageLoad</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">&#x27;load&#x27;</span>, <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">      <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">const</span> navigation = performance.<span class="title function_">getEntriesByType</span>(<span class="string">&#x27;navigation&#x27;</span>)[<span class="number">0</span>];</span><br><span class="line">        <span class="keyword">const</span> paint = performance.<span class="title function_">getEntriesByType</span>(<span class="string">&#x27;paint&#x27;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> (navigation) &#123;</span><br><span class="line">          performanceData.<span class="property">pageLoadTime</span> = <span class="title class_">Math</span>.<span class="title function_">round</span>(navigation.<span class="property">loadEventEnd</span> - navigation.<span class="property">fetchStart</span>);</span><br><span class="line">          performanceData.<span class="property">domContentLoadedTime</span> = <span class="title class_">Math</span>.<span class="title function_">round</span>(navigation.<span class="property">domContentLoadedEventEnd</span> - navigation.<span class="property">fetchStart</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> (paint.<span class="property">length</span> &gt; <span class="number">0</span>) &#123;</span><br><span class="line">          <span class="keyword">const</span> firstPaint = paint.<span class="title function_">find</span>(<span class="function"><span class="params">entry</span> =&gt;</span> entry.<span class="property">name</span> === <span class="string">&#x27;first-paint&#x27;</span>);</span><br><span class="line">          <span class="keyword">if</span> (firstPaint) &#123;</span><br><span class="line">            performanceData.<span class="property">firstPaintTime</span> = <span class="title class_">Math</span>.<span class="title function_">round</span>(firstPaint.<span class="property">startTime</span>);</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 输出性能数据</span></span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">group</span>(<span class="string">&#x27;🚀 页面性能监控&#x27;</span>);</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;页面加载时间:&#x27;</span>, performanceData.<span class="property">pageLoadTime</span> + <span class="string">&#x27;ms&#x27;</span>);</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;DOM加载时间:&#x27;</span>, performanceData.<span class="property">domContentLoadedTime</span> + <span class="string">&#x27;ms&#x27;</span>);</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;首次绘制时间:&#x27;</span>, performanceData.<span class="property">firstPaintTime</span> + <span class="string">&#x27;ms&#x27;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 性能评级</span></span><br><span class="line">        <span class="keyword">const</span> rating = <span class="title function_">getPerformanceRating</span>(performanceData.<span class="property">pageLoadTime</span>);</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;性能评级:&#x27;</span>, rating.<span class="property">text</span>, rating.<span class="property">emoji</span>);</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">groupEnd</span>();</span><br><span class="line">      &#125;, <span class="number">100</span>);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure><h1 id="🎨-自定义CSS样式"><a href="#🎨-自定义CSS样式" class="headerlink" title="🎨 自定义CSS样式"></a>🎨 自定义CSS样式</h1><p>除了功能性优化，我还添加了大量自定义CSS样式，使博客更具个性：</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 评论区容器 - 高级磨砂卡片效果 */</span></span><br><span class="line"><span class="selector-class">.waline-container</span> &#123;</span><br><span class="line">  <span class="attribute">background</span>: <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.95</span>);</span><br><span class="line">  <span class="attribute">backdrop-filter</span>: <span class="built_in">blur</span>(<span class="number">20px</span>);</span><br><span class="line">  <span class="attribute">border-radius</span>: <span class="number">16px</span>;</span><br><span class="line">  <span class="attribute">border</span>: <span class="number">1px</span> solid <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.3</span>);</span><br><span class="line">  <span class="attribute">box-shadow</span>: <span class="number">0</span> <span class="number">8px</span> <span class="number">32px</span> <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0.1</span>);</span><br><span class="line">  <span class="attribute">margin</span>: <span class="number">2rem</span> <span class="number">0</span>;</span><br><span class="line">  <span class="attribute">padding</span>: <span class="number">1.5rem</span>;</span><br><span class="line">  <span class="attribute">transition</span>: all <span class="number">0.3s</span> ease;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.waline-container</span><span class="selector-pseudo">:hover</span> &#123;</span><br><span class="line">  <span class="attribute">box-shadow</span>: <span class="number">0</span> <span class="number">12px</span> <span class="number">48px</span> <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0.15</span>);</span><br><span class="line">  <span class="attribute">transform</span>: <span class="built_in">translateY</span>(-<span class="number">2px</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这些样式为博客添加了现代感和立体感，提升了整体视觉效果。</p><h1 id="📱-响应式设计优化"><a href="#📱-响应式设计优化" class="headerlink" title="📱 响应式设计优化"></a>📱 响应式设计优化</h1><p>为了确保在各种设备上的良好体验，我对响应式设计进行了优化：</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 响应式设计 */</span></span><br><span class="line"><span class="keyword">@media</span> (<span class="attribute">max-width</span>: <span class="number">768px</span>) &#123;</span><br><span class="line">  <span class="selector-class">.nav-search-container</span> &#123;</span><br><span class="line">    <span class="attribute">display</span>: none;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="selector-class">.waline-container</span> &#123;</span><br><span class="line">    <span class="attribute">padding</span>: <span class="number">1rem</span> <span class="meta">!important</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="selector-class">.wl-avatar</span> &#123;</span><br><span class="line">    <span class="attribute">width</span>: <span class="number">28px</span> <span class="meta">!important</span>;</span><br><span class="line">    <span class="attribute">height</span>: <span class="number">28px</span> <span class="meta">!important</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="selector-class">.wl-head</span>,</span><br><span class="line">  <span class="selector-class">.wl-content</span>,</span><br><span class="line">  <span class="selector-class">.wl-meta</span> &#123;</span><br><span class="line">    <span class="attribute">padding</span>: <span class="number">0.5rem</span> <span class="meta">!important</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这些样式确保了在移动设备上的良好显示效果，提供了更好的移动端体验。</p><h1 id="🔧-配置优化"><a href="#🔧-配置优化" class="headerlink" title="🔧 配置优化"></a>🔧 配置优化</h1><p>最后，我对Butterfly主题的配置进行了优化：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 代码块设置</span></span><br><span class="line"><span class="attr">code_blocks:</span></span><br><span class="line">  <span class="attr">theme:</span> <span class="string">light</span></span><br><span class="line">  <span class="attr">macStyle:</span> <span class="literal">false</span></span><br><span class="line">  <span class="attr">height_limit:</span> <span class="literal">false</span></span><br><span class="line">  <span class="attr">word_wrap:</span> <span class="literal">false</span></span><br><span class="line">  <span class="attr">copy:</span> <span class="literal">true</span></span><br><span class="line">  <span class="attr">language:</span> <span class="literal">true</span></span><br><span class="line">  <span class="attr">shrink:</span> <span class="literal">false</span></span><br><span class="line">  <span class="attr">fullpage:</span> <span class="literal">false</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 社交链接</span></span><br><span class="line"><span class="attr">social:</span></span><br><span class="line">  <span class="attr">fab fa-github:</span> <span class="string">https://github.com/swkajnBruceee</span> <span class="string">||</span> <span class="string">Github</span> <span class="string">||</span> <span class="string">&#x27;#24292e&#x27;</span></span><br><span class="line">  <span class="attr">fas fa-envelope:</span> <span class="string">mailto:your-email@example.com</span> <span class="string">||</span> <span class="string">Email</span> <span class="string">||</span> <span class="string">&#x27;#4a7dbe&#x27;</span></span><br><span class="line">  <span class="attr">fab fa-twitter:</span> <span class="string">https://twitter.com/</span> <span class="string">||</span> <span class="string">Twitter</span> <span class="string">||</span> <span class="string">&#x27;#00acee&#x27;</span></span><br><span class="line">  <span class="attr">fab fa-telegram:</span> <span class="string">https://t.me/</span> <span class="string">||</span> <span class="string">Telegram</span> <span class="string">||</span> <span class="string">&#x27;#0088cc&#x27;</span></span><br></pre></td></tr></table></figure><p>这些配置使博客的功能更加丰富，同时保持了视觉的一致性。</p><h1 id="🎁-总结与展望"><a href="#🎁-总结与展望" class="headerlink" title="🎁 总结与展望"></a>🎁 总结与展望</h1><p>通过这一系列的UI优化，我的Hexo博客不仅在视觉上更加吸引人，功能上也更加完善。当然，博客优化是一个持续的过程，未来我计划：</p><ol><li><strong>进一步优化加载速度</strong>：使用更先进的资源压缩和缓存策略</li><li><strong>添加更多交互元素</strong>：如阅读进度条、目录自动展开等</li><li><strong>优化SEO</strong>：提升博客在搜索引擎中的排名</li><li><strong>增强无障碍功能</strong>：让博客对所有用户都友好</li></ol><p>希望这篇文章能给你的博客优化之旅带来一些启发。如果你有任何问题或建议，欢迎在评论区留言交流！</p><hr><blockquote><p>本文首发于个人博客，转载请注明出处。<br>如果你喜欢这篇文章，欢迎分享给更多的人！</p></blockquote>]]>
      </content:encoded>
    </item>
    <item>
      <title>Hexo博客搭建踩坑记</title>
      <link>https://swkajnbruceee.github.io/2025/06/26/Hexo%E5%8D%9A%E5%AE%A2%E6%90%AD%E5%BB%BA%E8%B8%A9%E5%9D%91%E8%AE%B0/</link>
      <description>
        <![CDATA[<h1 id="🚀-Hexo博客搭建踩坑记"><a href="#🚀-Hexo博客搭建踩坑记" class="headerlink" title="🚀 Hexo博客搭建踩坑记"></a>🚀 Hexo博客搭建踩坑记</h1><h1 id="🧩-问题一：YAML-配置的”变]]>
      </description>
      <author>BruceLee</author>
      <category domain="https://swkajnbruceee.github.io/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/">技术笔记</category>
      <category domain="https://swkajnbruceee.github.io/tags/Hexo/">Hexo</category>
      <category domain="https://swkajnbruceee.github.io/tags/GitHub-Pages/">GitHub Pages</category>
      <category domain="https://swkajnbruceee.github.io/tags/%E5%8D%9A%E5%AE%A2%E6%90%AD%E5%BB%BA/">博客搭建</category>
      <pubDate>Thu, 26 Jun 2025 05:42:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h1 id="🚀-Hexo博客搭建踩坑记"><a href="#🚀-Hexo博客搭建踩坑记" class="headerlink" title="🚀 Hexo博客搭建踩坑记"></a>🚀 Hexo博客搭建踩坑记</h1><h1 id="🧩-问题一：YAML-配置的”变形记”"><a href="#🧩-问题一：YAML-配置的”变形记”" class="headerlink" title="🧩 问题一：YAML 配置的”变形记”"></a>🧩 问题一：YAML 配置的”变形记”</h1><p><strong>问题</strong><br>配置 <code>_config.yml</code> 时，发现实际语法与文档不一致，特别是缩进规则。</p><p><strong>解决方案</strong>  </p><ol><li>确认 YAML 语法规则：<ul><li>使用 <strong>空格缩进</strong> 而非制表符</li><li>键值对格式 <code>key: value</code></li></ul></li></ol><h1 id="🦋-问题二：Butterfly-主题的”华丽转身”"><a href="#🦋-问题二：Butterfly-主题的”华丽转身”" class="headerlink" title="🦋 问题二：Butterfly 主题的”华丽转身”"></a>🦋 问题二：Butterfly 主题的”华丽转身”</h1><p><strong>问题</strong><br>主题仓库结构变更，导致配置失效。</p><p><strong>解决步骤</strong>：</p><ol><li>发现配置文件迁移到 <code>_config.butterfly.yml</code></li><li>使用 AI 生成兼容模板：<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">theme:</span> <span class="string">butterfly</span></span><br><span class="line"><span class="attr">butterfly:</span></span><br><span class="line">  <span class="attr">menu:</span></span><br><span class="line">    <span class="attr">Home:</span> <span class="string">/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-home</span></span><br></pre></td></tr></table></figure></li></ol><h1 id="🔌-问题三：4000-端口的”神秘占用”"><a href="#🔌-问题三：4000-端口的”神秘占用”" class="headerlink" title="🔌 问题三：4000 端口的”神秘占用”"></a>🔌 问题三：4000 端口的”神秘占用”</h1><p><strong>问题</strong><br>启动时报错：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">FATAL Port 4000 has been used.</span><br></pre></td></tr></table></figure><p><strong>解决方案</strong>：</p><ol><li>查找占用进程：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">lsof -i :4000</span><br></pre></td></tr></table></figure></li><li>指定新端口启动：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo server -p 4001</span><br></pre></td></tr></table></figure></li><li>永久修改配置：<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">4001</span></span><br></pre></td></tr></table></figure></li></ol><h1 id="💡-实用技巧"><a href="#💡-实用技巧" class="headerlink" title="💡 实用技巧"></a>💡 实用技巧</h1><ol><li><strong>分支管理</strong>：<blockquote><p>由于我之前已经搭建过一个博客，而且部署在master分支里，恰巧GitHub在调用.io域名时，自动调用master分支的内容，而我新搭建的博客是在main分支，所以我需要更改github的配置要求，才能让我的新博客正常显示</p><ol><li>登录GitHub仓库，点击”Settings”。</li><li>在左侧导航栏，选择”Pages”。</li><li>在”Build and deployment”部分，将”Branch”改为”main”。</li></ol></blockquote></li></ol>]]>
      </content:encoded>
    </item>
    <item>
      <title>Question of N Queen</title>
      <link>https://swkajnbruceee.github.io/2025/06/26/Question-of-N-Queen/</link>
      <description>
        <![CDATA[<h1 id="N皇后问题"><a href="#N皇后问题" class="headerlink" title="N皇后问题"></a>N皇后问题</h1><blockquote>
<p>N皇后问题是指在N*N的棋盘上要摆N个皇后，要求任何两个皇后不同行、不同列，也不在同一条斜]]>
      </description>
      <author>BruceLee</author>
      <category domain="https://swkajnbruceee.github.io/tags/algorithm/">algorithm</category>
      <category domain="https://swkajnbruceee.github.io/tags/backtracking/">backtracking</category>
      <pubDate>Thu, 26 Jun 2025 03:59:32 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h1 id="N皇后问题"><a href="#N皇后问题" class="headerlink" title="N皇后问题"></a>N皇后问题</h1><blockquote><p>N皇后问题是指在N*N的棋盘上要摆N个皇后，要求任何两个皇后不同行、不同列，也不在同一条斜线上。给定一个整数n，返回n皇后的摆法有多少种。</p></blockquote><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">num</span><span class="params">(<span class="type">int</span> n)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(n &lt; <span class="number">1</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    <span class="comment">// record[i]=j 表示i行的皇后放在了第j列(这样可以省去了用二维数组来表示)</span></span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">record</span><span class="params">(n)</span></span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">process</span>(<span class="number">0</span>, record, n);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 目前来到了第i行(总共有n行)</span></span><br><span class="line"><span class="comment">// record[0...i-1]表示之前的行放了皇后(潜台词：前i-1行任何两个皇后都不共行、列、斜线)</span></span><br><span class="line"><span class="comment">// 返回值代表百万所有的皇后，合法的摆法共有多少种</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">process</span><span class="params">(<span class="type">int</span> i, vector&lt;<span class="type">int</span>&gt; record, <span class="type">int</span> n)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(i == n) <span class="keyword">return</span> <span class="number">1</span>;  <span class="comment">// 来到终止行</span></span><br><span class="line">    <span class="type">int</span> res = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> j = <span class="number">0</span>; j &lt; n; j++) <span class="comment">// 当前行在i行，但要尝试i行所有的列 -&gt; j</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 当前i行的皇后，放在j列，会不会和之前(0...i-1)的皇后共行共列共斜线</span></span><br><span class="line">        <span class="comment">// 如果是，认为无效；如果不是，认为有效</span></span><br><span class="line">        <span class="keyword">if</span>(<span class="built_in">isValid</span>(record, i, j))</span><br><span class="line">        &#123;</span><br><span class="line">            record[i] = j;</span><br><span class="line">            res += <span class="built_in">process</span>(i + <span class="number">1</span>, record, n);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">isValid</span><span class="params">(vector&lt;<span class="type">int</span>&gt; record, <span class="type">int</span> i, <span class="type">int</span> j)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> k = <span class="number">0</span>; k &lt; i; k++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>( j == record[k] || <span class="built_in">abs</span>(record[k]-j) == i-k) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        <span class="comment">//    ^ 共列 ^                 ^ 共斜线 ^</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>]]>
      </content:encoded>
    </item>
  </channel>
</rss>
