<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Liang&apos;s Blog</title>
    <link>https://in-x.cc</link>
    <description>写代码、折腾工具、偶尔拍照。记录那些让我觉得「有意思」的东西。</description>
    <language>zh-CN</language>
    <atom:link href="https://in-x.cc/rss.xml" rel="self" type="application/rss+xml" />
    <lastBuildDate>Thu, 14 May 2026 08:20:56 GMT</lastBuildDate>

    <item>
      <title>论 “保安执法权”</title>
      <link>https://in-x.cc/blog/lun-bao-an-zhi-fa-quan</link>
      <guid>https://in-x.cc/blog/lun-bao-an-zhi-fa-quan</guid>
      <pubDate>Sat, 25 Apr 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[<blockquote>
<p><a href="https://www.zhihu.com/question/2030333131283226860/answer/2030765574314308996">如何评价福建三明一女子脚踹保安后继续上手打对方耳光却被保安正当防卫反扇耳光？</a></p>
</blockquote>
<p>近些年，每次出现诸如保安与某某发生冲突的新闻时，社会舆论中总会涌现一些关于 “保安执法权” 的讨论。</p>
<p>这个议题乍一听似乎很有价值，但问题在于 —— <strong>保安是否在执法？</strong></p>
<p>当女司机违停，保安劝阻，此时并没有涉及到所谓执法权的问题，任何公民都可以对他人的破坏规则的行为进行劝阻。</p>
<p>而后，当双方发生口角并产生肢体接触的瞬间，首先动手的人已经超出了一名合法公民对另一名合法公民可以动用的合法手段。</p>
<p>事态已经从对于规则的维护升级为某一方的违法行为，更不存在所谓的执法权问题。</p>
<p>那么我们到底在讨论什么？或者说我们到底在回避什么根本的问题？</p>
<blockquote>
<p>这两年频繁冲上热搜的保安争议，本质是当前社会的规则和共识在破灭</p>
<p>保安只是这个规则的具象化体现而已</p>
<p>……</p>
<p>但在我看来这是一种回避，回避了最尖锐的问题 —— <strong>那就是有人破坏社会规则时，该怎么办。</strong></p>
<p>—— <a href="https://www.zhihu.com/question/2030333131283226860/answer/2030765574314308996">卢诗翰</a></p>
</blockquote>
<p>我认为，中国的很多规矩就是建立在多方互相妥协的社会共识之下。</p>
<p>你越线，我制止，你退回去，这件事就算完了，社会成本极低。</p>
<p>但是现在社会上有一部分人，精致利己，当他们破坏社会软规则，如果有人劝阻，他们则咬定对方没有权利管他，必须上升到公权力的硬规则介入才肯罢休。</p>
<p>这部分人就是抓住了大部分人其实并不希望付出太多的成本来解决这件小事的心理，获得了 “打破规则的红利”。</p>
<p>当不遵守规则的人获得奖励，那其他遵守规则的人等同于受到了 “规则的惩罚”。</p>
<p>“凭什么他随便停就没事儿，我还要费时间去找车位？”</p>
<p>于是越来越多的人开始破坏规则，并且都学会了那句话：“你管得着吗？叫警察来。”</p>
<p>当足够多的人拒绝为社会默契 “充值”，而只愿意为硬规则 “买单” 时，整个社会的运行成本就会急剧上升，最终导致所有人都被锁死在一个低效率、高对抗的系统中。</p>
]]></description>
    </item>,
    <item>
      <title>寓言故事一则</title>
      <link>https://in-x.cc/blog/fable</link>
      <guid>https://in-x.cc/blog/fable</guid>
      <pubDate>Wed, 08 Apr 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[<p>知云和尚去参访石头禅师，两人聊得高兴，走到江边。船夫把沙滩上的渡船用力推入江中，船底压死了一堆虾螺蟹。</p>
<p>知云和尚看了心生怜悯，问石头禅师：“这是乘客的过错，还是船夫的过错？”</p>
<p>石头禅师直接回：“既不是乘客的过，也不是船夫的过。”</p>
<p>知云追问：“那到底是谁的过？”</p>
<p>石头厉声答：“是你的过错！”</p>
<p>知云懵了。</p>
<p>石头禅师解释：船夫推船是为谋生，乘客坐船是为过江，虾螺躲在沙里也是求生，大家都是自然行为，无心造业。罪业由心生，无心怎能算罪？倒是你，好端端的事，非要强行分别是非、贴上“杀生罪过”的标签，无中生有、自造分别心，这才是真正的过错。</p>
]]></description>
    </item>,
    <item>
      <title>yt-dlp 下载 YouTube/Bilibili 视频</title>
      <link>https://in-x.cc/blog/yt-dlp-download</link>
      <guid>https://in-x.cc/blog/yt-dlp-download</guid>
      <pubDate>Thu, 26 Mar 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[<p>安装 <a href="https://github.com/yt-dlp/yt-dlp">yt-dlp</a></p>
<pre><code class="language-shell">scoop install yt-dlp  # windows
brew install yt-dlp   # macOS
</code></pre>
<p>下载视频</p>
<pre><code class="language-shell">yt-dlp [video_url]
</code></pre>
<p>使用浏览器 Cookies 下载视频</p>
<pre><code class="language-shell">yt-dlp --cookies-from-browser [browser] [video_url]
</code></pre>
<blockquote>
<p>支持的浏览器：brave、chrome、chromium、edge、firefox、opera、safari、vivaldi、whale。</p>
</blockquote>
<p>查看可下载的文件格式，不下载</p>
<pre><code class="language-shell">yt-dlp --list-formats [video_url]

# 输出
ID     EXT RESOLUTION FPS │   FILESIZE   TBR PROTO │ VCODEC          VBR ACODEC      ABR
────────────────────────────────────────────────────────────────────────────────────────
30280  m4a audio only     │ ≈ 35.30MiB  207k https │ audio only          mp4a.40.2  207k
100026 mp4 1920x1080   30 │ ≈143.41MiB  839k https │ av01.0.00M.10  839k video only
</code></pre>
<p>下载指定的文件格式</p>
<pre><code class="language-shell">yt-dlp --format [format_id] [video_url]

# 示例
yt-dlp --format 30280 [video_url]         # 下载音频
yt-dlp --format 100026 [video_url]        # 下载视频
yt-dlp --format 30280+100026 [video_url]  # 下载音频+视频，合并成视频
</code></pre>
<p>查看更多支持下载的视频网站：<a href="https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md">Supported sites</a></p>
]]></description>
    </item>,
    <item>
      <title>macOS Launchd 入门</title>
      <link>https://in-x.cc/blog/mac-launchd</link>
      <guid>https://in-x.cc/blog/mac-launchd</guid>
      <pubDate>Wed, 04 Mar 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[<p>Launchd 是 macOS 的系统服务管理工具（相当于 Windows 服务和 Linux systemd），它允许用户创建、启动、停止和配置 macOS 系统服务。</p>
<h2>Launchd 的基本概念</h2>
<p>launchd 分为两个部分：<code>launchd</code> 与 <code>launchctl</code>。</p>
<p><code>launchd</code> 是常驻后台的守护进程，负责读取各种 <code>*.plist</code> 配置进行启动、停止、重启服务等操作，是真正干活的力工。</p>
<p><code>launchctl</code> 是用来管理 <code>launchd</code> 的操作台，负责向 <code>launchd</code> 发送指令，属于下发命令的管理层。</p>
<h2>Launchd 配置文件</h2>
<p>Launchd 配置是一个 XML 格式的文本文件：</p>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;

&lt;plist version=&quot;1.0&quot;&gt;
&lt;dict&gt;
    &lt;!-- 必填：全局唯一 ID，通常反域名 --&gt;
    &lt;key&gt;Label&lt;/key&gt;
    &lt;string&gt;com.example.hello&lt;/string&gt;

    &lt;!-- 必填：启动命令与参数 --&gt;
    &lt;key&gt;ProgramArguments&lt;/key&gt;
    &lt;array&gt;
        &lt;string&gt;/bin/bash&lt;/string&gt;
        &lt;string&gt;/Users/you/scripts/hello.sh&lt;/string&gt;
    &lt;/array&gt;

    &lt;!-- 可选：指定启动命令程序的工作目录 --&gt;
    &lt;key&gt;WorkingDirectory&lt;/key&gt;
    &lt;string&gt;/Users/you&lt;/string&gt;

    &lt;!-- 可选：开机/登录时立即运行一次 --&gt;
    &lt;key&gt;RunAtLoad&lt;/key&gt;
    &lt;true/&gt;

    &lt;!-- 可选：每 300 秒执行一次 --&gt;
    &lt;key&gt;StartInterval&lt;/key&gt;
    &lt;integer&gt;300&lt;/integer&gt;

    &lt;!-- 可选：每天 09:30 执行一次 --&gt;
    &lt;key&gt;StartCalendarInterval&lt;/key&gt;
    &lt;dict&gt;
        &lt;key&gt;Hour&lt;/key&gt;
        &lt;integer&gt;9&lt;/integer&gt;
        &lt;key&gt;Minute&lt;/key&gt;
        &lt;integer&gt;30&lt;/integer&gt;
    &lt;/dict&gt;

    &lt;!-- 可选：保持存活；true 时退出后会被拉起 --&gt;
    &lt;key&gt;KeepAlive&lt;/key&gt;
    &lt;true/&gt;

    &lt;!-- 可选：常规日志输出 --&gt;
    &lt;key&gt;StandardOutPath&lt;/key&gt;
    &lt;string&gt;/tmp/com.example.hello.out.log&lt;/string&gt;

    &lt;!-- 可选：异常日志输出 --&gt;
    &lt;key&gt;StandardErrorPath&lt;/key&gt;
    &lt;string&gt;/tmp/com.example.hello.err.log&lt;/string&gt;
&lt;/dict&gt;
&lt;/plist&gt;
</code></pre>
<p>Launchd 配置文件必须以 <code>.plist</code> 作为后缀名，文件名通常与配置中的 <code>Label</code> 一致。</p>
<p>Launchd 配置文件通常放在以下目录中，放置目录会决定自动加载行为、运行权限和启动时机：</p>
<table>
<thead>
<tr>
<th>目录</th>
<th>运行权限</th>
<th>启动时机</th>
<th>适用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><code>/Library/LaunchDaemons/</code></strong></td>
<td><code>root</code></td>
<td>系统启动时</td>
<td>需要高权限的系统级服务，如网络守护进程、硬件监控、系统备份等</td>
</tr>
<tr>
<td><strong><code>/Library/LaunchAgents/</code></strong></td>
<td>登录用户</td>
<td>所有用户登录时</td>
<td>对所有用户可用的服务，如全局快捷键、多用户应用助手等</td>
</tr>
<tr>
<td><strong><code>~/Library/LaunchAgents/</code></strong></td>
<td>当前用户</td>
<td>当前用户登录时</td>
<td><strong>最常用</strong>，仅当前用户需要的服务，如个人定时任务、应用通知、同步工具等</td>
</tr>
</tbody>
</table>
<p>以上目录会被系统在启动/登录流程中自动扫描并加载。</p>
<p>除了等待登录时自动加载外，也可以手动 <code>bootstrap</code> 指定任意可访问路径的 <code>plist</code>。</p>
<p>使用 <code>bootstrap</code> 启动服务只是临时加载到目标域，当域的生命周期结束后丢失，下次系统启动/登录时只会自动加载配置目录中未被 <code>disable</code> 的 plist 配置。</p>
<h2>常用管理命令</h2>
<h3>配置校验</h3>
<pre><code class="language-bash"># 校验 plist 语法是否正确（推荐先执行）
plutil -lint ~/Library/LaunchAgents/com.example.hello.plist

# 查看 plist 内容（格式化输出）
plutil -p ~/Library/LaunchAgents/com.example.hello.plist
</code></pre>
<h3>服务加载与卸载</h3>
<pre><code class="language-bash"># 加载并激活服务
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.example.hello.plist

# 移除服务（与 bootstrap 相对）
launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.example.hello.plist
</code></pre>
<p><strong>参数说明：</strong></p>
<ul>
<li><code>$(id -u)</code>：获取当前用户 ID</li>
</ul>
<h3>服务状态查看</h3>
<pre><code class="language-bash"># 列出所有已加载的服务
launchctl list

# 查找特定服务
launchctl list | grep com.example.hello

# 查看详细状态（推荐）
launchctl print gui/$(id -u)/com.example.hello

# 查看服务是否正在运行
ps aux | grep -i &quot;hello.sh&quot;
</code></pre>
<p><strong>状态字段说明：</strong></p>
<pre><code>PID    Status   Label
56789  0        com.example.hello
#      ↑        ↑
#      │        └─ 服务标识符
#      └─ 退出代码（0 表示正常）
</code></pre>
<h3>服务控制</h3>
<pre><code class="language-bash"># 重启服务（一键操作）
launchctl kickstart gui/$(id -u)/com.example.hello

# 强制停止服务（不卸载）
launchctl kill SIGTERM gui/$(id -u)/com.example.hello

# 立即触发一次运行（即使不在定时时间）
launchctl kickstart --immediate gui/$(id -u)/com.example.hello
</code></pre>
<p><strong>常用信号：</strong></p>
<table>
<thead>
<tr>
<th>信号</th>
<th>说明</th>
<th>使用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>SIGTERM</code></td>
<td>正常终止</td>
<td>优先尝试</td>
</tr>
<tr>
<td><code>SIGKILL</code></td>
<td>强制杀死</td>
<td>无响应时使用</td>
</tr>
<tr>
<td><code>SIGHUP</code></td>
<td>重新加载配置</td>
<td>支持热重载的服务</td>
</tr>
</tbody>
</table>
<h3>系统级服务（需要 sudo）</h3>
<pre><code class="language-bash"># 加载系统级服务
sudo launchctl bootstrap system /Library/LaunchDaemons/com.example.daemon.plist

# 卸载系统级服务
sudo launchctl bootout system /Library/LaunchDaemons/com.example.daemon.plist

# 查看系统服务状态
sudo launchctl print system/com.example.daemon
</code></pre>
]]></description>
    </item>,
    <item>
      <title>珂芝（Kzzi）K75 Lite 说明书</title>
      <link>https://in-x.cc/blog/kzzi-k75-lite</link>
      <guid>https://in-x.cc/blog/kzzi-k75-lite</guid>
      <pubDate>Fri, 14 Nov 2025 00:00:00 GMT</pubDate>
      <description><![CDATA[<p><a href="https://en.kzzi.com/media/files/20240705/20240705164922_9738.pdf">下载官方说明书</a></p>
<h2>电量指示灯</h2>
<p>按下 <code>Fn + Enter</code> 查看电量，通过数字区 <code>1~0</code> 按键背光显示剩余电量。</p>
<table>
<thead>
<tr>
<th>指示灯状态</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>红灯闪烁</td>
<td>电量低</td>
</tr>
<tr>
<td>常亮（接入电源）</td>
<td>充电中</td>
</tr>
<tr>
<td>熄灭（接入电源）</td>
<td>已充满</td>
</tr>
</tbody>
</table>
<h2>蓝牙连接</h2>
<p>将右上角开关拨动到中间位置，表示蓝牙连接模式。</p>
<p>短按 <code>Fn + Q / W / E</code> —— 切换蓝牙频道</p>
<p>长按 <code>Fn + Q / W / E</code> —— 进入配对模式</p>
<h2>键盘背光</h2>
<table>
<thead>
<tr>
<th>组合键</th>
<th>说明</th>
<th>组合键</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>Fn + \</td>
<td>切换背光效果</td>
<td>Fn + ↑</td>
<td>提高背光亮度</td>
</tr>
<tr>
<td>Fn + ,</td>
<td>切换背光颜色</td>
<td>Fn + ↓</td>
<td>降低背光亮度</td>
</tr>
</tbody>
</table>
<h2>切换修饰键</h2>
<p>按下 <code>Fn + A</code> —— 切换至 Win 系统</p>
<p>按下 <code>Fn + S</code> —— 切换至 Mac 系统</p>
<h2>功能键</h2>
<table>
<thead>
<tr>
<th>组合键</th>
<th>说明</th>
<th>组合键</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Fn + HOME</code></td>
<td>Pause</td>
<td><code>Fn + END</code></td>
<td>Insert</td>
</tr>
<tr>
<td><code>Fn + DEL</code></td>
<td>ScrLK</td>
<td><code>Fn + PGUP</code></td>
<td>PrtSc</td>
</tr>
</tbody>
</table>
]]></description>
    </item>,
    <item>
      <title>NSSM 使用说明</title>
      <link>https://in-x.cc/blog/nssm</link>
      <guid>https://in-x.cc/blog/nssm</guid>
      <pubDate>Tue, 04 Nov 2025 00:00:00 GMT</pubDate>
      <description><![CDATA[<p>NSSM（Non‑Sucking Service Manager）是一个轻量、稳定的 Windows 工具，用于将普通可执行程序注册为系统服务，使其能够随系统启动、在后台持续运行，并支持自动重启、进程优先级设置和日志管理，无需修改程序源码，广泛应用于后台任务、网络代理内核和长期运行的自定义工具管理。</p>
<h2>安装 NSSM</h2>
<p>前往官网 <a href="https://nssm.cc/download">下载安装</a>，或使用 WinGet 安装：</p>
<pre><code class="language-bash">winget install NSSM.NSSM
</code></pre>
<h2>NSSM 常用命令</h2>
<blockquote>
<p><code>[]</code> 表示可选参数，<code>&lt;&gt;</code> 表示必填参数。</p>
</blockquote>
<pre><code class="language-bash">nssm install [servicename]  # 新增服务
nssm remove [servicename]   # 移除服务
nssm edit &lt;servicename&gt;     # 编辑服务
</code></pre>
<pre><code class="language-bash">nssm start &lt;servicename&gt;    # 启动服务
nssm stop &lt;servicename&gt;     # 停止服务
nssm restart &lt;servicename&gt;  # 重启服务
nssm pause &lt;servicename&gt;    # 暂停服务
nssm continue &lt;servicename&gt; # 恢复服务
nssm status &lt;servicename&gt;   # 查询服务状态
</code></pre>
<p>更多命令请 <a href="https://nssm.cc/commands">查看官方文档</a>。</p>
]]></description>
    </item>,
    <item>
      <title>远程主机不满足运行 VS Code 服务器的先决条件</title>
      <link>https://in-x.cc/blog/vscode-remote-connection-failed</link>
      <guid>https://in-x.cc/blog/vscode-remote-connection-failed</guid>
      <pubDate>Mon, 07 Apr 2025 00:00:00 GMT</pubDate>
      <description><![CDATA[<h2>问题描述</h2>
<p><a href="https://code.visualstudio.com/docs/remote/faq#_can-i-run-vs-code-server-on-older-linux-distributions">我可以在较旧的 Linux 发行版上运行 VS Code Server 吗？</a></p>
<blockquote>
<p>从 VS Code 版本 1.99（2025 年 3 月）开始，由 VS Code 分发的预构建服务器仅与基于 glibc 2.28 或更高版本的 Linux 发行版兼容。例如，这些包括 Debian 10、RHEL 8 或 Ubuntu 20.04。</p>
</blockquote>
<h2>临时解决方案</h2>
<p>这个可作为最简单的临时解决方案，先使用最后支持的旧版本连接远程服务器，后面再研究如何彻底解决问题。</p>
<p>下载 VS Code 最后可用的版本 <code>1.98.2</code> 重装即可正常连接远程服务器。</p>
<p>覆盖安装前先到设置中 <a href="vscode://settings/update.mode">关闭 VS Code 的自动更新</a>，某则重装后会自动更新。</p>
<p><a href="https://update.code.visualstudio.com/1.98.2/darwin/stable">下载 macOS Intel 芯片</a></p>
<p><a href="https://update.code.visualstudio.com/1.98.2/darwin-arm64/stable">下载 macOS Apple 芯片</a></p>
<p><a href="https://update.code.visualstudio.com/1.98.2/win32-x64/stable">下载 Windows x64</a></p>
<p><a href="https://update.code.visualstudio.com/1.98.2/win32-arm64/stable">下载 Windows Arm64</a></p>
<p>如果需要其他版本请到这里 <a href="https://code.visualstudio.com/docs/supporting/faq#_previous-release-versions">获取旧版本 VS Code 安装包</a>。</p>
]]></description>
    </item>,
    <item>
      <title>快速查询 Mac 磁盘信息</title>
      <link>https://in-x.cc/blog/mac-query-disk-info</link>
      <guid>https://in-x.cc/blog/mac-query-disk-info</guid>
      <pubDate>Tue, 11 Mar 2025 00:00:00 GMT</pubDate>
      <description><![CDATA[<blockquote>
<p>参考：<a href="https://zhuanlan.zhihu.com/p/354385629">MacBook M1 查询硬盘写入教程 | 知乎</a></p>
</blockquote>
<h2>安装磁盘工具</h2>
<p><a href="https://www.smartmontools.org/">smartmontools</a> 是一款用于监控和分析硬盘健康状态的命令行工具。</p>
<p>通过 <a href="./homebrew-guide">Homebrew</a> 安装：</p>
<pre><code class="language-bash">brew install smartmontools
</code></pre>
<h2>查询磁盘信息</h2>
<p>安装后，即可查询磁盘信息：</p>
<pre><code class="language-bash">smartctl -a disk0
</code></pre>
<p>::: details 输出磁盘信息参考</p>
<pre><code class="language-bash">smartctl 7.4 2023-08-01 r5530 [Darwin 24.3.0 arm64] (local build)
Copyright (C) 2002-23, Bruce Allen, Christian Franke, www.smartmontools.org

=== START OF INFORMATION SECTION ===
Model Number:                       APPLE SSD AP2048Q
Serial Number:                      03a486784034c426
Firmware Version:                   499.0.9
PCI Vendor/Subsystem ID:            0x106b
IEEE OUI Identifier:                0x000000
Controller ID:                      0
NVMe Version:                       &lt;1.2
Number of Namespaces:               3
Local Time is:                      Tue Mar 11 19:05:25 2025 CST
Firmware Updates (0x02):            1 Slot
Optional Admin Commands (0x0004):   Frmw_DL
Optional NVM Commands (0x0004):     DS_Mngmt
Maximum Data Transfer Size:         256 Pages

Supported Power States
St Op     Max   Active     Idle   RL RT WL WT  Ent_Lat  Ex_Lat
 0 +     0.00W       -        -    0  0  0  0        0       0

=== START OF SMART DATA SECTION ===
SMART overall-health self-assessment test result: PASSED

SMART/Health Information (NVMe Log 0x02)
Critical Warning:                   0x00
Temperature:                        46 Celsius
Available Spare:                    100%
Available Spare Threshold:          99%
Percentage Used:                    0%
Data Units Read:                    2,122,071 [1.08 TB]
Data Units Written:                 1,233,010 [631 GB]
Host Read Commands:                 35,854,349
Host Write Commands:                14,027,979
Controller Busy Time:               0
Power Cycles:                       12
Power On Hours:                     11
Unsafe Shutdowns:                   0
Media and Data Integrity Errors:    0
Error Information Log Entries:      0

Read 1 entries from Error Information Log failed: GetLogPage failed: system=0x38, sub=0x0, code=745
</code></pre>
<p>:::</p>
<p>主要信息查看的信息就这些：</p>
<ul>
<li>Temperature：硬盘温度</li>
<li>Data Units Read：磁盘累计读取量</li>
<li>Data Units Written：磁盘累计写入量</li>
<li>Power On Hours：通电小时数</li>
</ul>
<p>懒得看的朋友，也可以直接复制所有信息丢给 AI 分析。</p>
]]></description>
    </item>,
    <item>
      <title>Commitlint 使用指南</title>
      <link>https://in-x.cc/blog/commitlint</link>
      <guid>https://in-x.cc/blog/commitlint</guid>
      <pubDate>Tue, 19 Dec 2023 00:00:00 GMT</pubDate>
      <description><![CDATA[<h2>简介</h2>
<p>Commitlint 是一个用于检查 Git 提交信息的工具，通过检查提交信息是否符合规范，以提高代码库的可读性和维护性。</p>
<ul>
<li>提供了一套规范化的检查规则，且支持自定义规则</li>
<li>与 Git 钩子集成，在创建提交时自动检查提交信息是否符合规范</li>
</ul>
<h2>安装</h2>
<h3>1. 安装并创建配置文件</h3>
<pre><code class="language-bash"># 安装 commitlint 和 commitlint 配置
npm install -D commitlint-cli commitlint-config-conventional
# 创建配置文件
echo &quot;import '@commitlint/config-conventional'

export default {
  extends: ['@commitlint/config-conventional'],
}&quot; &gt; commitlint.config.js
</code></pre>
<p>::: details 检查最新提交</p>
<p>检查当前当前仓库最后一次提交记录是否符合规范，如果检查未通过，会提示错误信息。</p>
<pre><code class="language-bash">npx commitlint --from HEAD~1 --to HEAD --verbose
# ✔   found 0 problems, 0 warnings
</code></pre>
<p>:::</p>
<h3>2. 配置 git hooks</h3>
<pre><code class="language-bash"># 安装 husky
npm install -D husky
# 激活 git hooks
npx husky install
# 添加 commit-msg 钩子
npx husky add .husky/commit-msg  'npx --no -- commitlint --edit ${1}'
</code></pre>
<p>::: details 测试 Git Hooks</p>
<p>可以通过简单地提交来测试该钩子，如果配置正常，您应该会看到提交失败的提示。</p>
<pre><code class="language-bash">git commit -m &quot;foo: 这条提交将会失败&quot;
</code></pre>
<p>:::</p>
<h3>3. 配置 script prepare</h3>
<p>在 package.json 中增加 prepare 脚本，用于在安装依赖时自动安装 husky。</p>
<pre><code class="language-json">{
  &quot;scripts&quot;: {
    &quot;prepare&quot;: &quot;husky install&quot;
    // other...
  }
}
</code></pre>
<h2>使用</h2>
<h3>提交格式</h3>
<blockquote>
<p>注：<code>?</code> 表示可选，不输入 <code>scope</code> 时可省略圆括号。</p>
</blockquote>
<pre><code>type(scope?): subject
body?
footer?
</code></pre>
<h3>type</h3>
<p>用于说明 commit 的类别，只允许使用下列值：</p>
<table>
<thead>
<tr>
<th>type</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>build</td>
<td>对项目构建系统或外部依赖项的更改。例如，修改构建脚本、配置文件等。</td>
</tr>
<tr>
<td>chore</td>
<td>非业务性的任务和更改。通常用于修改构建过程、辅助工具的代码，或者进行一些不涉及用户功能的工作。</td>
</tr>
<tr>
<td>ci</td>
<td>对持续集成 (Continuous Integration) 配置文件和脚本的更改。</td>
</tr>
<tr>
<td>docs</td>
<td>文档变更，包括但不限于 README 文件、文档生成器或注释的变更。</td>
</tr>
<tr>
<td>feat</td>
<td>新功能的添加。通常伴随着用户功能或其他明显的变更。</td>
</tr>
<tr>
<td>fix</td>
<td>修复 bug。</td>
</tr>
<tr>
<td>perf</td>
<td>改进性能的代码更改。</td>
</tr>
<tr>
<td>refactor</td>
<td>代码重构，不是修复 bug 也不是添加新功能的代码更改。</td>
</tr>
<tr>
<td>revert</td>
<td>撤销先前的提交。</td>
</tr>
<tr>
<td>style</td>
<td>与代码风格相关的更改，例如空格、格式化等，而不涉及功能性代码更改。</td>
</tr>
<tr>
<td>test</td>
<td>添加或修改测试。</td>
</tr>
</tbody>
</table>
<h3>body &amp; footer</h3>
<p><code>body</code> 用于提供更详细的说明，可以分成多行。</p>
<p><code>footer</code> 用于提供一些与提交信息不直接相关的信息，可以分成多行。</p>
<pre><code>feat: 防止请求竞争

引入一个请求ID和对最新请求的引用。忽略除最新请求外的所有传入响应。

移除用于解决竞争问题但现在已过时的超时机制。

参考：#123
</code></pre>
<h3>重大修改</h3>
<p>当仓库产生比较重大的修改时，例如删除了某个 API，或者改变了大量的 API，可以通过一下两种方式来表示：</p>
<h4>type!</h4>
<p>在提交信息的 <code>type</code> 后面添加 <code>!</code> 符号，表示该提交是重大更改。</p>
<pre><code>feat!: 新增了新的 API，移除了旧的 API
</code></pre>
<p><code>type</code> 可以是可选枚举值的任意一个。</p>
<h4>BREAKING CHANGE</h4>
<p>在提交信息的 <code>body</code> 中添加 <code>BREAKING CHANGE</code>，表示该提交是重大更改。</p>
<pre><code>feat: 新增了新的 API

BREAKING CHANGE: 移除了旧的 API
</code></pre>
]]></description>
    </item>,
    <item>
      <title>JSON Web Token 鉴权</title>
      <link>https://in-x.cc/blog/json-web-token</link>
      <guid>https://in-x.cc/blog/json-web-token</guid>
      <pubDate>Mon, 04 Dec 2023 00:00:00 GMT</pubDate>
      <description><![CDATA[<p>::: tip Nuxt 3 项目实例
这里提供了一个使用 Nuxt 3 开发的项目实例，你可以结合文章和项目实例来学习如何相对安全地存储用户密码。</p>
<p><a href="https://github.com/lianginx/jwt">查看项目实例 —— lianginx/jwt</a>
:::</p>
<p>JSON Web Token 是一种基于 JSON 的开放标准（RFC 7519），用于在各个层面上向用户提供身份验证。</p>
<blockquote>
<p>以下内容中将简称为 JWT 或 Token</p>
</blockquote>
<p>JWT 由三部分组成：</p>
<ul>
<li>Header</li>
<li>Payload</li>
<li>Signature</li>
</ul>
<p>其中，Header 和 Payload 都是 JSON 对象，Signature 是 Header 和 Payload 的签名，用于验证 Token 的完整性。</p>
<h2>Header</h2>
<p>Header 部分包含了 Token 签名所用的算法。</p>
<pre><code class="language-json">{
  &quot;alg&quot;: &quot;HS256&quot;,
  &quot;typ&quot;: &quot;JWT&quot;
}
</code></pre>
<pre><code class="language-ts">export interface JWTHeader {
  alg: string
  typ: string
}
</code></pre>
<h2>Payload</h2>
<p>Payload 部分包含了用户的身份信息，例如用户 ID、用户名、权限等。</p>
<pre><code class="language-json">{
  &quot;userId&quot;: &quot;1234567890&quot;,
  &quot;username&quot;: &quot;admin&quot;,
  &quot;write&quot;: true,
  &quot;exp&quot;: 1516239022
}
</code></pre>
<pre><code class="language-ts">export interface JWTPayload {
  userId: string // 用户ID
  username: string // 用户名
  write?: boolean // 管理界面权限
  exp?: number // 过期时间（暂未用到）
}
</code></pre>
<h2>Signature</h2>
<p>Signature 部分是 Header 和 Payload 的签名，用于验证 Token 的完整性。</p>
<p>首先将 Header 和 Payload 进行 Base64url 编码，然后使用 Header 中的签名算法进行签名，得出 Signature，最后将 Header、Payload 和 Signature 这三个 Base64url 字符串使用 <code>.</code> 进行拼接，就得到了完整的 Token。</p>
<pre><code class="language-ts">import { Buffer } from 'node:buffer'

/** Object 转 Base64url 字符串 */
function objectToBase64url(obj: object): string {
  return Buffer.from(JSON.stringify(obj)).toString('base64url')
}

const header = { alg: 'HS256', typ: 'JWT' }
const headerString = objectToBase64url(header)
const payloadString = objectToBase64url(payload)

const signatureString = crypto
  .createHmac('sha256', secret)
  .update(headerString)
  .update(payloadString)
  .digest('base64url')

return `${headerString}.${payloadString}.${signatureString}`
</code></pre>
<h3>验证 Token</h3>
<p>服务端验证 Token 是否有效，需要先解析 Header 获取到加密所用的算法，然后使用该算法对 Header、Payload 重新进行签名，然后比对新的签名是否与第三段签名一致，不一致则表示签名已被篡改。</p>
<pre><code class="language-ts">const [headerString, payloadString, oldSignatureString] = token.split('.')

const newSignatureString = crypto
  .createHmac('sha256', secret)
  .update(headerString)
  .update(payloadString)
  .digest('base64url')

if (newSignatureString !== oldSignatureString)
  throw new Error('无效签名')
</code></pre>
<h2>认证流程</h2>
<h3>1. 用户登录/注册</h3>
<p>用户在登录/注册页面输入用户名和密码，点击登录/注册按钮，服务端将用户名和密码在数据库中进行验证/创建，验证通过后使用用户身份信息生成一个 Token，并将 Token 返回给客户端。</p>
<h3>2. 客户端保存并使用 Token</h3>
<p>客户端将 Token 保存在 Cookie 中。</p>
<p>客户端每次请求都会自动带上 Cookie，服务器可以通过验证 Cookie 中 Token 的有效性来判断用户的身份。</p>
<h3>3. 服务端验证 Token</h3>
<p>服务端首先检查请求的 Cookie 中是否有 Token，如果没有则返回错误信息。</p>
<p>如果有 Token，则通过解析 Token 验证它的有效性，没有通过验证则返回错误信息。</p>
<p>验证 Token 的有效性包括但不限于：</p>
<ul>
<li>Token 是否被篡改</li>
<li>Token 是否过期</li>
</ul>
<p>如果验证通过，则继续返回用户需要的资源。</p>
<h2>权限控制</h2>
<p>目前实现比较简单，通过解析 JWT Payload 对象，获取并校验 <code>write</code> 字段来判断用户是否有权限访问管理界面。</p>
<pre><code class="language-ts">if (!payload.write) {
  // 处理没有权限的情况
}
</code></pre>
<h2>常见问题</h2>
<h3>1. 如何防止 Token 被篡改？</h3>
<p>在生成 JSON Web Token 时，服务器会使用一个只存在于服务端的密钥 <code>secret</code> 来生成每个 Token 的第三段的签名。</p>
<p>当客户端使用 Token 时，服务端会使用相同的密钥根据 Token 的第一段和第二段重新生成 Token 的签名，如果两个签名不一致，则说明 Token 被篡改了。</p>
<p><strong>所以存在服务端的密钥 <code>secret</code> 很关键，一定不要泄漏它！</strong></p>
]]></description>
    </item>,
    <item>
      <title>加密储存用户密码</title>
      <link>https://in-x.cc/blog/save-password</link>
      <guid>https://in-x.cc/blog/save-password</guid>
      <pubDate>Mon, 04 Dec 2023 00:00:00 GMT</pubDate>
      <description><![CDATA[<p>::: tip Nuxt 3 项目实例
这里提供了一个使用 Nuxt 3 开发的项目实例，你可以结合文章和项目实例来学习如何相对安全地存储用户密码。</p>
<p><a href="https://github.com/lianginx/password">查看项目实例 —— lianginx/password</a>
:::</p>
<p>在设计登录注册系统时，存储用户密码的方式非常重要，因为这关系到用户数据的安全。以下是一些关于如何安全存储用户密码的建议：</p>
<ol>
<li>
<p><strong>不要明文存储密码</strong>：你绝对不应该在数据库中明文存储用户的密码。这是因为如果你的数据库被黑客攻击，那么用户的密码就会被直接暴露。</p>
</li>
<li>
<p><strong>使用哈希函数</strong>：你应该使用哈希函数来存储用户密码的哈希值，而不是密码本身。哈希函数可以将任意长度的输入转化为固定长度的输出，且具有单向性，即不能从哈希值反推出原始输入。</p>
</li>
<li>
<p><strong>使用盐值</strong>：为了防止彩虹表攻击，你应该为每个用户的密码添加一个随机的盐值，然后将密码和盐值一起进行哈希。盐值应该是足够长且随机的，以确保每个用户的盐值都是唯一的。</p>
</li>
<li>
<p><strong>使用强哈希函数</strong>：你应该使用被广泛认可的强哈希函数，如SHA-256，SHA-3，bcrypt，scrypt，Argon2 等。</p>
</li>
<li>
<p><strong>密码更新</strong>：如果用户更改了密码，你应该生成一个新的盐值，并使用新的盐值和新密码计算新的哈希值。</p>
</li>
<li>
<p><strong>使用多重哈希</strong>：为了增加黑客破解密码的难度，你可以对密码进行多次哈希。</p>
</li>
</ol>
<p>请注意，这些只是一些基本的建议，实际的密码存储策略可能需要根据你的具体需求和环境进行调整。在处理用户密码时，你应该始终遵循最佳的安全实践，并在可能的情况下，寻求专业的安全建议。</p>
<h2>密码储存、验证</h2>
<p>当用户注册时，你会生成一个随机的盐值，然后将这个盐值和用户的密码一起进行哈希，得到哈希值。你需要在数据库中存储这个哈希值和对应的盐值。</p>
<p>当用户登录时，他们会提供他们的用户名和密码。你首先需要从数据库中获取与该用户名对应的盐值和哈希值。然后，你将这个盐值和用户提供的密码一起进行同样的哈希操作，得到一个新的哈希值。如果这个新的哈希值和数据库中存储的哈希值相同，那么密码就是正确的。</p>
<p>这是因为哈希函数是单向的，你不能从哈希值反推出原始的密码。但是，如果你有原始的密码和盐值，你可以通过同样的哈希操作得到同样的哈希值。所以，你需要保存盐值，以便你可以验证用户提供的密码是否正确。但请注意，你应该保护好盐值，不要让它们泄露。</p>
<h2>盐值的作用</h2>
<p>如果数据库被泄露，盐值也会被泄露。但是，即使在这种情况下，使用盐值仍然可以提高安全性。</p>
<p>盐值的主要目的是防止彩虹表攻击。彩虹表是一种预先计算好的哈希值到原始密码的映射表。如果没有使用盐值，攻击者可以使用彩虹表来查找哈希值对应的原始密码。但是，如果使用了盐值，即使两个用户的密码相同，由于盐值的随机性，他们的哈希值也会不同。这就意味着攻击者不能使用同一份彩虹表来攻击所有用户，他们必须为每个盐值单独生成一份彩虹表，这在计算上是不可行的。</p>
<p>此外，即使数据库被泄露，攻击者也无法直接获取到用户的密码。他们必须通过尝试大量的可能的密码，找到一个和盐值一起哈希后能够得到同样哈希值的密码，这通常需要大量的时间和计算资源。</p>
<p>总的来说，虽然使用盐值不能完全防止密码被破解，但它可以大大增加破解的难度，从而提高系统的安全性。当然，最好的做法是采取多种措施来保护用户数据，包括使用强密码，定期更换密码，使用二次验证等。</p>
<h2>Node.js 生成盐值</h2>
<p>在 Node.js 中，你可以使用 <code>crypto</code> 库来生成一个安全的随机盐值。以下是一个例子：</p>
<pre><code class="language-javascript">const crypto = require('node:crypto')

// 生成一个16字节的盐值
const salt = crypto.randomBytes(16).toString('hex')

console.log(salt)
</code></pre>
<p>这段代码会生成一个 32 个字符长的十六进制字符串作为盐值。这个盐值是随机的，并且长度足够长，以确保每个用户的盐值都是唯一的。你可以将这个盐值和用户的密码一起进行哈希，然后将哈希值和盐值一起存储在数据库中。</p>
<p>当需要验证密码时，你可以使用同样的盐值和哈希函数来计算哈希值，然后将计算出的哈希值与存储在数据库中的哈希值进行比较。如果两个哈希值相同，那么密码就是正确的。</p>
<p>这样，即使数据库被泄露，攻击者也无法直接获取到用户的密码，因为他们需要知道原始的密码和盐值才能计算出正确的哈希值。这大大增加了攻击的难度，提高了系统的安全性。</p>
]]></description>
    </item>,
    <item>
      <title>二分查找</title>
      <link>https://in-x.cc/blog/binary-search</link>
      <guid>https://in-x.cc/blog/binary-search</guid>
      <pubDate>Sat, 01 Jul 2023 00:00:00 GMT</pubDate>
      <description><![CDATA[<h2>简介</h2>
<p>二分查找（Binary Search）是一种高效的搜索算法，用于在 <strong>有序数组（或有序列表）</strong> 中查找特定元素的位置。它将目标值与数组的中间元素进行比较，并根据比较结果缩小搜索范围，直到找到目标值或确定目标值不存在。</p>
<p><strong>二分查找的关键点是每次迭代都能将搜索范围缩小一半</strong>，因此其时间复杂度为 <code>O(log n)</code> ，其中 n 是数组的长度。这使得二分查找成为处理大规模有序数据集的有效算法。</p>
<h2>代码演示</h2>
<p>二分查找的基本思想：</p>
<ol>
<li>首先，确定数组的左边界 <code>left</code> 和右边界 <code>right</code> ，通常初始时 <code>left = 0</code> ， <code>right = 数组长度 - 1</code> 。</li>
<li>在每一次迭代中，计算中间元素的索引 <code>mid</code> ，使用 <code>floor</code> 函数将中间值可能的小数部分向下向下取整。</li>
<li>将目标值与中间元素 <code>nums[mid]</code> 进行比较：
<ul>
<li>如果 <code>nums[mid]</code> 等于目标值，表示找到了目标值，返回 <code>mid</code> 。</li>
<li>如果 <code>nums[mid]</code> 大于目标值，表示目标值可能在左侧，更新查询边界 <code>right = mid - 1</code> 。</li>
<li>如果 <code>nums[mid]</code> 小于目标值，表示目标值可能在右侧，更新查询边界 <code>left = mid + 1</code> 。</li>
</ul>
</li>
<li>重复执行步骤 2 和步骤 3，直到找到目标值或搜索范围为空（ <code>left &gt; right</code> ），此时目标值不存在，返回 -1。</li>
</ol>
<p>以下是 JavaScript 代码演示：</p>
<pre><code class="language-js">function binarySearch(nums, target) {
  let left = 0
  let right = nums.length - 1

  while (left &lt;= right) {
    const mid = Math.floor((left + right) / 2)

    if (nums[mid] === target)
      return mid
    else if (nums[mid] &gt; target)
      right = mid - 1
    else
      left = mid + 1
  }

  return -1
}
</code></pre>
<h2>进阶练习</h2>
<p><a href="https://leetcode.cn/problems/search-insert-position/">LeetCode - 搜索插入位置</a></p>
]]></description>
    </item>,
    <item>
      <title>macOS 允许安装任何来源软件</title>
      <link>https://in-x.cc/blog/mac-app-corruption-issue</link>
      <guid>https://in-x.cc/blog/mac-app-corruption-issue</guid>
      <pubDate>Tue, 27 Jun 2023 00:00:00 GMT</pubDate>
      <description><![CDATA[<h2>遇到的问题</h2>
<ul>
<li>“XXX”已损坏，无法打开。你应该将它移动到废纸篓。</li>
<li>已阻止使用“XXX”，因为来自身份不明的开发者。</li>
</ul>
<h2>解决方案</h2>
<blockquote>
<p>设置入口：系统设置 &gt; 安全性与隐私 &gt; 安全性 &gt; 允许从以下位置下载的应用程序</p>
</blockquote>
<h3>临时处理</h3>
<p>当遇到以上问题时，打开设置会看到应用被拦截的提示：</p>
<blockquote>
<p>已组织使用“XXX”，因为来自身份不明的开发者。</p>
</blockquote>
<p>并给予「<strong>仍要打开</strong>」的按钮，点击打开之后便可以正常使用。</p>
<h3>关闭拦截</h3>
<p>将「允许从以下位置下载的应用程序」修改为「<strong>任何来源</strong>」。</p>
<p>如果没有此选项，打开终端执行命令后重启系统设置将显示此选项：</p>
<pre><code class="language-sh">sudo spctl --master-disable
</code></pre>
]]></description>
    </item>,
    <item>
      <title>Homebrew 安装与使用</title>
      <link>https://in-x.cc/blog/homebrew-guide</link>
      <guid>https://in-x.cc/blog/homebrew-guide</guid>
      <pubDate>Sat, 17 Jun 2023 00:00:00 GMT</pubDate>
      <description><![CDATA[<p>Homebrew 是一个软件包管理器，支持 macOS 和 Linux，它可以让你一个命令快速安装软件，而不需要再去下载之后用鼠标把 APP 拖到应用程序文件夹中。</p>
<h2>如何安装</h2>
<p>安装脚本来自：<a href="https://brew.idayer.com/">Homebrew 国内镜像</a>，安装成功后需要重启终端才能使用。</p>
<pre><code class="language-sh">/bin/bash -c &quot;$(curl -fsSL https://gitee.com/ineo6/homebrew-install/raw/master/install.sh)&quot;
</code></pre>
<blockquote>
<p>默认使用中科大镜像源，如果需要换源参考 <a href="https://brew.idayer.com/guide/change-source/">换源</a>。</p>
</blockquote>
<h2>常用命令</h2>
<table>
<thead>
<tr>
<th>命令</th>
<th>说明</th>
<th>其他</th>
</tr>
</thead>
<tbody>
<tr>
<td>brew install [–cask] [name]</td>
<td>安装软件</td>
<td>cask 表示 GUI 软件</td>
</tr>
<tr>
<td>brew uninstall [name]</td>
<td>卸载软件</td>
<td></td>
</tr>
<tr>
<td>brew search [name]</td>
<td>搜索软件</td>
<td><a href="https://formulae.brew.sh/">通过网页搜索</a></td>
</tr>
<tr>
<td>brew info [name]</td>
<td>查看软件信息</td>
<td></td>
</tr>
<tr>
<td>brew list</td>
<td>查看已安装软件</td>
<td></td>
</tr>
<tr>
<td>brew update</td>
<td>更新 Homebrew</td>
<td></td>
</tr>
<tr>
<td>brew upgrade [name]</td>
<td>更新全部（某个）软件</td>
<td></td>
</tr>
<tr>
<td>brew cleanup [name]</td>
<td>清理全部（某个）软件的历史版本</td>
<td></td>
</tr>
<tr>
<td>brew tap [user/repo]</td>
<td>添加 tap</td>
<td></td>
</tr>
<tr>
<td>brew tap</td>
<td>查看已添加的 tap</td>
<td></td>
</tr>
<tr>
<td>brew untap [user/repo]</td>
<td>删除 tap</td>
<td></td>
</tr>
</tbody>
</table>
<h2>常用软件包</h2>
<h3>命令行工具</h3>
<pre><code class="language-bash">brew install eza        # 现代化 ls（带图标和颜色）
brew install bat        # 现代化 cat（语法高亮）
brew install fzf        # 模糊搜索（Ctrl+R 历史命令）
brew install zoxide     # 快速跳转目录（cd 替代品）
brew install ripgrep    # 现代化 grep（速度更快）
brew install fd         # 快速查找文件
brew install gh         # GitHub CLI
brew install starship   # 终端美化提示符
brew install yazi       # 终端文件管理器
brew install yt-dlp     # 视频下载
brew install pyenv      # Python 版本管理
brew install jq         # JSON 处理
</code></pre>
<h3>GUI 应用</h3>
<pre><code class="language-bash">brew install --cask ghostty                 # 终端模拟器
brew install --cask visual-studio-code      # vscode
brew install --cask google-chrome           # Chrome 浏览器
brew install --cask wechat                  # 微信
brew install --cask telegram                # Telegram
brew install --cask obsidian                # markdown 编辑器
brew install --cask iina                    # 视频播放器
brew install --cask orbstack                # Docker 虚拟机（比 Docker Desktop 轻量）
brew install --cask karabiner-elements      # 键盘映射
brew install --cask qlmarkdown              # 空格预览 Markdown
brew install --cask chatgpt                 # ChatGPT 客户端
brew install --cask apifox                  # API 调试工具
brew install --cask netnewswire             # RSS 阅读器
brew install --cask font-maple-mono-nf-cn   # 编程字体
</code></pre>
]]></description>
    </item>,
    <item>
      <title>Mac Dock Bar 易用性调整</title>
      <link>https://in-x.cc/blog/mac-dock-bar</link>
      <guid>https://in-x.cc/blog/mac-dock-bar</guid>
      <pubDate>Sat, 17 Jun 2023 00:00:00 GMT</pubDate>
      <description><![CDATA[<blockquote>
<p>参考：<a href="https://sspai.com/post/33366">通过终端命令调整 Dock 栏的隐藏速度｜一日一技 · Mac</a></p>
</blockquote>
<h2>前置条件</h2>
<p>右键点击 Dock 栏，选择 <code>启用隐藏</code> 后，再进行以下配置。</p>
<h2>移除显示延迟</h2>
<pre><code class="language-sh"># 移除延迟
defaults write com.apple.dock autohide-delay -int 0; killall Dock;
# 恢复默认
defaults delete com.apple.dock autohide-delay; killall Dock;
</code></pre>
<h2>调整动画速度</h2>
<pre><code class="language-sh"># 无动画
defaults write com.apple.dock autohide-time-modifier -float 0; killall Dock;
# 动画 0.5s
defaults write com.apple.dock autohide-time-modifier -float 0.5; killall Dock;
# 恢复默认
defaults delete com.apple.dock autohide-time-modifier; killall Dock;
</code></pre>
<h2>恢复出厂设置</h2>
<pre><code class="language-sh">defaults delete com.apple.dock; killall Dock;
</code></pre>
<p>此命令会把 Dock 栏恢复成出厂时的默认状态（包括图标）。</p>
]]></description>
    </item>,
    <item>
      <title>Nuxt 3 如何使用 ECharts 图表</title>
      <link>https://in-x.cc/blog/nuxt3-use-echarts</link>
      <guid>https://in-x.cc/blog/nuxt3-use-echarts</guid>
      <pubDate>Wed, 19 Apr 2023 00:00:00 GMT</pubDate>
      <description><![CDATA[<blockquote>
<p>参考： <a href="https://stackblitz.com/edit/github-unqktr">Vue Echarts Nuxt3</a></p>
</blockquote>
<h2>安装依赖</h2>
<pre><code class="language-bash">yarn add echarts vue-echarts
</code></pre>
<h2>通用/服务端渲染</h2>
<p>如果项目使用通用/服务端 <a href="https://nuxt.com.cn/docs/guide/concepts/rendering">渲染模式</a>（Nuxt 3 默认渲染模式），修改 <code>nuxt.config.ts</code> 配置文件，使用 <a href="https://www.babeljs.cn/docs/">Babel</a> 转译 <code>echarts</code> 依赖项，未使用服务端渲染不用转译。</p>
<pre><code class="language-ts">export default defineNuxtConfig({
  // ……
  build: {
    transpile: [/echarts/],
  },
})
</code></pre>
<p>另外，在使用图表组件时也须将组件放在 <a href="https://nuxt.com.cn/docs/api/components/client-only#clientonly"><code>&lt;ClientOnly&gt;</code></a> 标签中使用：</p>
<pre><code class="language-html">&lt;ClientOnly&gt;
  &lt;VChart :option=&quot;option&quot;/&gt;
&lt;/ClientOnly&gt;
</code></pre>
<h2>开始使用</h2>
<p>以下是在 Nuxt 3 服务端渲染项目中，封装的一个饼图组件示例：</p>
<pre><code class="language-vue">&lt;script setup&gt;
// 引入饼图
import { PieChart } from 'echarts/charts'

// 按需引入组件
import {
  LegendComponent,
  TitleComponent,
  TooltipComponent,
} from 'echarts/components'

// 引入 echarts 核心模块，核心模块提供了 echarts 使用必须要的接口。
import { use } from 'echarts/core'
// 引入 CanvasRenderer 或者 SVGRenderer 是，这必须的一步
import { CanvasRenderer } from 'echarts/renderers'
import { ref } from 'vue'
// 引入 vue-echarts 组件
import VChart, { THEME_KEY } from 'vue-echarts'

// 注册引入的组件
use([
  CanvasRenderer,
  PieChart,
  TitleComponent,
  TooltipComponent,
  LegendComponent,
])

// 设置黑暗模式主题
provide(THEME_KEY, 'dark')

// 图表数据
const option = ref({
  title: {
    text: 'Traffic Sources',
    left: 'center',
  },
  tooltip: {
    trigger: 'item',
    formatter: '{a} &lt;br/&gt;{b} : {c} ({d}%)',
  },
  legend: {
    orient: 'vertical',
    left: 'left',
    data: ['Direct', 'Email', 'Ad Networks', 'Video Ads', 'Search Engines'],
  },
  series: [
    {
      name: 'Traffic Sources',
      type: 'pie',
      radius: '55%',
      center: ['50%', '60%'],
      data: [
        { value: 335, name: 'Direct' },
        { value: 310, name: 'Email' },
        { value: 234, name: 'Ad Networks' },
        { value: 135, name: 'Video Ads' },
        { value: 1548, name: 'Search Engines' },
      ],
      emphasis: {
        itemStyle: {
          shadowBlur: 10,
          shadowOffsetX: 0,
          shadowColor: 'rgba(0, 0, 0, 0.5)',
        },
      },
    },
  ],
})
&lt;/script&gt;

&lt;template&gt;
  &lt;div&gt;
    &lt;!-- 客户端组件 --&gt;
    &lt;ClientOnly fallback-tag=&quot;div&quot; fallback=&quot;Loading comments...&quot;&gt;
      &lt;VChart class=&quot;chart&quot; :option=&quot;option&quot; /&gt;
    &lt;/ClientOnly&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;style scoped&gt;
.chart {
  height: 100vh;
}
&lt;/style&gt;
</code></pre>
<h2>相关链接</h2>
<h3>ECharts</h3>
<ul>
<li><a href="https://echarts.apache.org/zh/option.html">API 文档</a></li>
<li><a href="https://echarts.apache.org/examples/zh/index">全部图表示例</a></li>
<li><a href="https://echarts.apache.org/zh/option.html">配置项手册</a></li>
<li><a href="https://echarts.apache.org/zh/download-extension.html">扩展下载</a></li>
<li><a href="https://echarts.apache.org/zh/download-theme.html">主题下载</a></li>
</ul>
<h3>Vue-ECharts</h3>
<ul>
<li><a href="https://github.com/ecomfe/vue-echarts">使用文档</a></li>
<li><a href="https://vue-echarts.dev/">全部示例</a></li>
</ul>
]]></description>
    </item>,
    <item>
      <title>Dexie.js 入门教程</title>
      <link>https://in-x.cc/blog/dexie-js-guide</link>
      <guid>https://in-x.cc/blog/dexie-js-guide</guid>
      <pubDate>Sat, 01 Apr 2023 00:00:00 GMT</pubDate>
      <description><![CDATA[<h2>简介</h2>
<p>Dexie.js 是一款流行的客户端 JavaScript 库，它提供了一个简单而优雅的 API，用于操作 indexedDB，一种强大的基于浏览器的数据库系统。使用 Dexie.js，您可以轻松存储和检索数据，而无需直接处理 indexedDB 的复杂性。</p>
<p>本教程将介绍使用 Dexie.js 的基础知识，包括安装、创建数据库实例、定义对象存储区和索引、向数据库添加数据、更新数据、查询数据和删除数据。</p>
<p>如果你想先了解 indexedDB 基础可以学习 <a href="https://www.tangshuang.net/3735.html">IndexedDB 中文教程</a>，这篇文章详细介绍了 IndexedDB 的基础知识和原生 API 使用方法，非常推荐！</p>
<h2>安装</h2>
<p>您可以使用 npm 或 yarn 安装 Dexie.js：</p>
<pre><code class="language-bash">npm install dexie
</code></pre>
<pre><code class="language-bash">yarn add dexie
</code></pre>
<h2>快速开始</h2>
<h3>JavaScript</h3>
<pre><code class="language-js">// 导入
const Dexie = require('dexie')

// 创建并连接数据库实例
const db = new Dexie('myDatabase')

// 指定版本，创建对象仓库和索引
db.version(1).stores({
  friends: 'name, age', // 第一个索引将被作为主键
})

// 增加
db.friends.add({
  name: 'Liang',
  age: 18,
})

// 更新
db.friends.put({
  name: 'Liang', // 当主键存在时，将更新数据，不存在则创建
  age: 24,
})

// 查询
db.friends
  .get('Liang')
  .then((item) =&gt; {
    console.log(item?.age)
  })

// 删除
db.friends.delete('Liang')
</code></pre>
<h3>TypeScript</h3>
<blockquote>
<p>此方法来自 <a href="https://dexie.org/docs/Typescript">官方文档 &gt; TypeScript</a></p>
</blockquote>
<p>在 TypeScript 中，当你定义了 <code>friends</code> 对象仓库后，并不能直接像在 JavaScrip 中一样直接操作对象仓库进行增删改查：</p>
<pre><code class="language-js">db.friends.add({ // 报错：类型 “Dexie” 上不存在属性 “friends”
  name: 'Liang',
  age: 18
})
</code></pre>
<p>因为 TypeScript 无法检测到隐式定义的对象，此时可以使用 <code>Dexie.table()</code> 方法解决：</p>
<pre><code class="language-ts">db.table('friends').add({
  name: 'Liang',
  age: 18
})
</code></pre>
<p>但是，这不仅麻烦，而且丢失了 TypeScript 特性：智能代码补全和类型安全校验。</p>
<p>此时，可以通过扩展一个 <code>Dexie</code> 子类，并在子类中定义其运行时才存在 <code>Table</code> 属性：</p>
<pre><code class="language-ts">// 导入
import Dexie from 'dexie'

// 定义 friends 类型接口
interface IFriends {
  name: string
  age: number
}

class HappyDB extends Dexie {
  // 声明 friends 对象并指定其类型接口
  friends!: Dexie.Table&lt;IFriends&gt;

  constructor(databaseName: string) {
    super(databaseName)
    this.version(1).stores({
      friends: 'name, age',
    })
  }
}
</code></pre>
<blockquote>
<p><code>!</code> 是 TypeScript 中的非空断言操作符，用于告诉编译器当前变量肯定是非空的，从而避免出现空值异常。</p>
</blockquote>
<blockquote>
<p><code>super()</code> 是在子类构造函数中调用其父类构造函数的关键字，用于实现继承。当子类调用 <code>super()</code> 时，它会执行父类的构造函数，以便对父类的属性和方法进行初始化。</p>
</blockquote>
<p>现在你只需要像上面 JavaScrip 中一样使用它：</p>
<pre><code class="language-ts">const db = new HappyDB('HappyDatabase')

db.version(1).stores({
  friends: 'name, age',
})

// ……
</code></pre>
<h3>Node.js</h3>
<p>当你在 Node.js 环境中使用 Dexie.js 的时候，会提示缺少 indexedDB API：</p>
<blockquote>
<p>DexieError [DatabaseClosedError]: MissingAPIError 缺少 IndexedDB API。请访问 <a href="https://tinyurl.com/y2uuvskb">https://tinyurl.com/y2uuvskb</a> 。</p>
</blockquote>
<p>这是因为 indexedDB 是前端浏览器中的本地数据库，挂载在 window 上的全局对象，在 Node.js 中是没有的这个数据库和 API 的。</p>
<p>在 Dexie.js 报错信息提示的官方文档中，提供了解决方案：</p>
<ul>
<li><a href="https://github.com/dumbmatter/fakeIndexedDB#with-dexie-and-other-indexeddb-api-wrappers">将 Dexie 与 fakeIndexedDB 一起使用</a></li>
<li><a href="https://github.com/indexeddbshim/IndexedDBShim#node-set-up">将 Dexie 与 IndexedDBShim 一起使用</a></li>
</ul>
<p><code>fakeIndexedDB</code> 是通过纯 JS 内存实现的 IndexedDB API，它的主要用途是在 Node.js 中测试依赖 IndexedDB 的代码。</p>
<p><code>IndexedDBShim</code> 是一个可以在所有桌面和移动浏览器以及 Node.js 中使用的 indexedDB API。</p>
]]></description>
    </item>,
    <item>
      <title>本地存储：localStorage 和 sessionStorage 的差异</title>
      <link>https://in-x.cc/blog/js-local-storage</link>
      <guid>https://in-x.cc/blog/js-local-storage</guid>
      <pubDate>Sun, 05 Mar 2023 00:00:00 GMT</pubDate>
      <description><![CDATA[<h2>简介</h2>
<p>本地存储是一种在浏览器端存储数据的技术，它允许在不需要服务器支持的情况下，将数据保存在用户的本地浏览器中。</p>
<p>localStorage 和 sessionStorage 是 HTML5 引入的两个新API，它们都提供了在客户端存储数据的方式。他们都是基于键值对存储，可以用于在客户端存储字符串、数字、对象等类型的数据，与 Cookie 不同，它们不会被上传到服务端。</p>
<p>然而，两者之间还存在一些差异，主要在以下几个方面：</p>
<ol>
<li>
<p>存储时效性：localStorage 存储的数据没有过期时间，除非用户手动清除浏览器缓存或者使用代码删除；而 sessionStorage 存储的数据在当前会话结束后就会被清除。</p>
</li>
<li>
<p>存储大小限制：两者都有存储大小限制，一般为5MB左右，但具体大小限制可能因浏览器而异。</p>
</li>
<li>
<p>数据共享性：localStorage 保存在本地浏览器中当前域名的作用域下，同域名不同的页面或标签页都可以访问到，而 sessionStorage 只在当前页面或标签页中有效。</p>
</li>
</ol>
<p><img src="/images/localstorage-sessionstorage-scope.excalidraw.png" alt="localstorage-sessionstorage-scope.excalidraw"></p>
<h2>如何使用</h2>
<p>使用 localStorage 和 sessionStorage 非常简单且一致，只需要通过 JavaScript 的 localStorage 和 sessionStorage 对象调用相应的方法即可。</p>
<h3>存储数据</h3>
<pre><code class="language-js">// 存储数据到 localStorage
localStorage.setItem('key', 'value')

// 存储数据到 sessionStorage
sessionStorage.setItem('key', 'value')
</code></pre>
<h3>获取数据</h3>
<pre><code class="language-js">// 从 localStorage 中获取数据
const valueFromLocalStorage = localStorage.getItem('key')

// 从 sessionStorage 中获取数据
const valueFromSessionStorage = sessionStorage.getItem('key')
</code></pre>
<h3>删除数据</h3>
<pre><code class="language-js">// 从 localStorage 中删除指定的键值对
localStorage.removeItem('key')

// 从 sessionStorage 中删除指定的键值对
sessionStorage.removeItem('key')
</code></pre>
<h2>应用场景</h2>
<h3>1. 记住用户登录状态</h3>
<p>在用户登录成功后，可以使用 localStorage 或 sessionStorage 存储 token 或其他标识符，以便在用户下次访问时自动登录。</p>
<p>使用 localStorage 可以长期存储用户信息，即使浏览器关闭也不会丢失。而 sessionStorage 存储的数据只在当前会话中有效，当浏览器关闭后就会被清除。</p>
<h3>2. 缓存数据</h3>
<p>对于一些经常需要获取的数据，可以使用 localStorage 进行缓存，以便加快网站或应用程序的加载速度。例如，在一个电商网站中，可以使用 localStorage 存储用户的购物车信息、收货地址等常用数据，从而避免每次重新加载页面时都需要从服务器获取这些数据，提高用户体验。</p>
<h3>3. 记住用户偏好设置</h3>
<p>在用户进行一些个性化设置后，可以使用 localStorage 存储这些偏好设置，以便在下次访问时自动应用。例如，在一个新闻网站中，用户可以选择自己感兴趣的新闻分类，并将其存储到 localStorage 中，从而下次访问时直接显示用户所感兴趣的内容。</p>
<h3>4. 存储表单数据</h3>
<p>当用户填写表单时，可以使用 sessionStorage 存储表单数据，以便在用户重新加载页面或回退时恢复表单数据。而在需要长期存储数据的情况下，可以使用 localStorage。</p>
<p>localStorage 和 sessionStorage 都是 HTML5 中新增的本地存储方案，它们都可以在客户端保存键值对数据，并且可以被同源页面共享访问。但是它们之间还是有一些差异的。</p>
<h2>总结</h2>
<p>本文主要介绍了本地存储方案中的 localStorage 和 sessionStorage，它们的差异和使用场景。</p>
<p>两者相似又具有差异性的特性，是现代web开发中不可或缺的一部分，使得我们可以在业务中灵活运用它们来高效存取用户数据，提升用户体验。</p>
]]></description>
    </item>,
    <item>
      <title>🍅 番茄炖牛腩</title>
      <link>https://in-x.cc/blog/stewed-beef-brisket-with-tomato</link>
      <guid>https://in-x.cc/blog/stewed-beef-brisket-with-tomato</guid>
      <pubDate>Sun, 05 Mar 2023 00:00:00 GMT</pubDate>
      <description><![CDATA[<h2>食材准备</h2>
<ul>
<li>牛腩</li>
<li>番茄、番茄酱</li>
<li>葱、姜、蒜、八角</li>
<li>料酒、生抽、蚝油</li>
<li>土豆、洋葱</li>
<li>开水</li>
</ul>
<h2>预处理</h2>
<ul>
<li>辅料 <em>（焯水和炒肉时都需要，可以多切一些）</em>
<ul>
<li>葱切大段。</li>
<li>姜切大片。</li>
<li>蒜拍松且不碎。</li>
</ul>
</li>
<li>配菜
<ul>
<li>番茄：番茄提前冷冻好，使用前顶部划十字热水烫去皮、去蒂，一半切大块，一半切丁。</li>
<li>土豆：滚刀小块，最后下锅的，不要切太大。</li>
<li>洋葱：切小块。</li>
</ul>
</li>
<li>牛腩
<ul>
<li>提前浸泡 30-60 分钟去除血水。</li>
<li>凉水下锅焯水，加姜片、葱段、料酒，水开后再煮 5 分钟煮出血沫。</li>
<li>捞出肉用热水冲洗干净，控干水分。</li>
</ul>
</li>
</ul>
<h2>烹饪</h2>
<ol>
<li>开中小火，冷锅下油，锅热后将焯水后的牛肉下锅煸炒出微微焦褐状。</li>
<li>下入姜片、蒜片、洋葱，炒香后下入番茄丁继续煸炒。</li>
<li>番茄炒出果胶后，加入适量番茄酱、蚝油翻炒均匀后，倒入没过牛肉的开水。</li>
<li>加入一个八角，大火煮沸后转小火炖 90 分钟。</li>
<li>时间到后，开盖挑出八角，下入番茄块和土豆块，加入一勺糖、一勺盐、少量生抽。</li>
<li>开大火煮 10 分钟收汁，即可出锅！</li>
</ol>
<h2>注意事项</h2>
<ol>
<li>牛肉一定泡水或冷水下锅焯水去腥。</li>
<li>炖牛肉时，水只多不少，必须一次性加够，中途不能添水。</li>
<li>盐不够可以少量多次加，不要一次加太多。</li>
<li>不加番茄酱味道是不够的，尽量用番茄酱（产地新疆）而不是番茄沙司，没有酱用沙司也无所谓。</li>
<li>番茄炖牛腩需要炖的时间较长，一定要耐心等待，不要急于开盖查看。</li>
<li>最后大火收汁阶段要多搅动，防止糊锅！</li>
<li>烹饪完成后可以撒上一些香菜叶或者青葱末增加美感。</li>
</ol>
]]></description>
    </item>,
    <item>
      <title>高尿酸血症与痛风指南</title>
      <link>https://in-x.cc/blog/gout-hua-guideline</link>
      <guid>https://in-x.cc/blog/gout-hua-guideline</guid>
      <pubDate>Tue, 20 Dec 2022 00:00:00 GMT</pubDate>
      <description><![CDATA[<p>::: warning 阅前提醒</p>
<p>本文章内容均整理自参考文献，仅做为学习资料，请不要作为病情诊断依据，如感觉身体不适请立即前往医院请专业医生判断病情，并遵循医嘱进行相应治疗。</p>
<p>:::</p>
<p>痛风（gout）是嘌呤代谢紊乱和（或）尿酸排泄减少所引起的一种晶体性关节炎。</p>
<p>临床表现为高尿酸血症（HUA）和尿酸盐结晶沉积所致的特征性急性关节炎、痛风石形成、痛风石性慢性关节炎，并可发生尿酸盐肾病、尿酸性尿路结石等，严重者可出现关节致残、肾功能不全等。</p>
<h2>痛风的诱发因素</h2>
<p>有关痛风的定义和分类特别强调关注 <strong>无症状高尿酸血症</strong> 和 <strong>合并疾病</strong>：</p>
<h3>无症状高尿酸血症</h3>
<p>::: tip 为什么会出现高尿酸血症</p>
<p>在几万年的进化过程中，人类产生基因突变失去了尿酸酶的功能，仅靠肾脏的排泄和机体尿酸池的动态平衡维持血尿酸的稳定。</p>
<p>现今随着生活方式的改变，机体产生的尿酸增多，在肾功能正常的人体，推测机体可能通过增加尿尿酸的排泄和增加尿酸池的容量（组织沉积）来处理因为尿酸酶缺失而导致的尿酸增多。</p>
<p>但是长期代偿导致肾功能损伤后，这种代偿能力下降，尿尿酸的排泄量减少，组织的沉积进一步增多。有关尿尿酸的变化和患者临床特点的关系有待于未来的深入研究。</p>
<p>:::</p>
<p>对于无症状 HUA 是否需要治疗一直存在争议，因为在血尿酸水平持续增高人群中，仅有 10% 左右罹患痛风性关节炎，大多为无症状性（即无痛风的相关症状）HUA，所以多数指南提出不需要治疗，大致理由包括以下 3 点：</p>
<ol>
<li>只有少数 HUA 会发展成痛风；</li>
<li>降尿酸治疗对于预防痛风发作缺乏足够的临床证据；</li>
<li>降尿酸治疗存在不良反应。</li>
</ol>
<p>但如今越来越多的证据表明，慢性 HUA 也会造成体内尿酸盐沉积，除了是痛风发生的最重要的生化基础以外，也是高血压、代谢综合征、CKD 及心血管疾病的独立危险因素，而对无症状 HUA 的降尿酸治疗可改善患者心血管及肾脏不良终点事件发生。</p>
<p>首先，要充分认识到痛风的自然病程包括 3 个阶段，即：无症状高尿酸血症、急性痛风性关节炎、慢性痛风。</p>
<pre><code class="language-plantuml">(*)-&gt;无症状高尿酸血症
-&gt;急性痛风性关节炎
-&gt;慢性痛风
</code></pre>
<p>这一点对于进一步为不同阶段的患者合理制定个体化治疗决策至关重要。</p>
<h3>痛风常见的合并疾病</h3>
<p>通常强调痛风分为 <strong>原发性</strong> 和 <strong>继发性</strong> 两大类。</p>
<p>原发性痛风有一定的家族遗传性， 10%~20% 的患者有阳性家族史。以前认为除 1% 左右的原发性痛风由先天性酶缺陷引起外，绝大多数发病原因不明，应该寻找其是否存在引起尿酸升高的继发因素，如肾脏病、血液病，或由于服用某些药物、肿瘤放化疗等多种原因。</p>
<p>但近年的研究更加强调痛风或高尿酸血症常常与中心性肥胖、高脂血症、糖尿病、高血压以及心脑血管病伴发，长期的高尿酸血症已被证实为这些合并疾病的独立危险因素。而积极治疗这些合并疾病将有利于高尿酸血症的控制。</p>
<p>因此，对于痛风或高尿酸血症患者应该积极寻找继发因素或确定是否存在合并疾病，如中心性肥胖、高脂血症、糖尿病、高血压以及心脑血管病的危险因素等。合并疾病的治疗同样非常重要在治疗的同时，要积极治疗伴发的高脂血症、糖尿病、高血压、冠心病和脑血管疾病等，这一点在新近的指南中特别强调。</p>
<h2>药物治疗时机与控制目标</h2>
<h3>无症状 HUA</h3>
<p>无症状 HUA 患者出现下列情况时可开始降尿酸药物治疗：</p>
<ul>
<li>血尿酸水平 ≥ 540 μmol/L</li>
<li>血尿酸水平 ≥ 480 μmol/L 且有下列合并症之一：高血压、脂代谢异常、糖尿病、肥胖、脑卒中、冠心病、心功能不全、尿酸性肾石病、肾功能损害（CKD 2 期及以上分期）。</li>
</ul>
<p>建议无合并症患者血尿酸水平控制在 ＜ 420 μmol/L；伴合并症时建议控制在 ＜ 360 μmol/L。</p>
<h3>伴痛风发作的 HUA</h3>
<p>一旦痛风急性发作，应尽早开始抗炎镇痛治疗，EULAR 推荐治疗时间为痛风发作 12h 内，中华医学会风湿病学分会（CRA）推荐治疗时间为痛风发作 24h 内。</p>
<p>对于伴痛风发作的 HUA 治疗，建议当血尿酸 ≥ 480 μmol/L 时开始降尿酸药物治疗，控制目标为血尿酸 ＜ 360 μmol/L；但如患者伴其他心血管疾病高危因素或痛风性关节炎、慢性肾脏疾 病及痛风发作频繁，血尿酸 ≥ 420 μmol/L 时即需积极治疗，控制目标为血尿酸 ＜ 300 μmol/L。</p>
<p>同时本指南强调降尿酸治疗应在痛风急性发作完全缓解后 2~4 周开始；正在进行降尿酸治疗的急性发作患者不建议停药。</p>
<p>当然，血尿酸水平的控制也存在下限，有研究指出低尿酸血症是健康人发生肾功能异常的危险因素，不建议将血尿酸水平长期控制在 ＜ 180 μmol/L。</p>
<h2>非药物辅助治疗</h2>
<p>首先，应避免热疗，进行冰敷、病变关节休息等对于急性痛风关节炎具有辅助镇痛作用。</p>
<p>然后是饮食方面，应采用低热能膳食，避免高嘌呤食物，<strong>保持理想体重是非常有益且低成本的治疗手段，特别是对于早期痛风或高尿酸血症患者</strong>。低嘌呤饮食、低脂饮食特别强调避免红肉、海产品。新近的研究特别指出要避免含糖的软饮料和避免酒精摄入，为减少肾结石的形成，每日饮水应在 2000mL 以上。</p>
<p>循证医学证据表明，通过饮食和生活方式的改变可以有效改善糖尿病、高血压和冠心病患者的预后，特别是对于早期的患者。因此，对于早期发现的无症状高尿酸血症者和早期急性发作性痛风性关节炎患者，相信通过生活方式的改变、危险因素的去除以及合理规范的药物治疗，同样有望改变其痛风的</p>
<h2>病情的长期管理</h2>
<h3>保持健康的生活方式</h3>
<p>健康的生活方式包括：</p>
<ol>
<li>控制体重、规律运动；</li>
<li>限制酒精及高嘌呤、高果糖饮食的摄入；</li>
<li>鼓励奶制品和新鲜蔬菜的摄入及适量饮水；</li>
<li>不推荐也不限制豆制品（如豆腐）的摄入。</li>
</ol>
<p>::: tip 豆制品对血尿酸水平的影响</p>
<p>其中对于豆制品的摄入未作明确推荐或限制，2016 年 CRA 制定的中国痛风诊疗指南提倡食用植物蛋白，但 2012 年 ACR 及 2016 年 EULAR 制定的最新指南并未提及，此外有研究表明豆制品的嘌呤含量因加工方式而有所差异，因此本指南不推荐也不限制豆制品的摄入。</p>
<p>:::</p>
<h3>长期关注并控制血尿酸水平</h3>
<p>所有患者需知晓：要终生将血尿酸水平控制在 <strong>理想范围（240~420 μmol/L）</strong>，并为此可能需要长期甚至终身服用降尿酸药物，摒弃 <strong>「痛风只是急性关节炎发作，控制疼痛即可」</strong> 的观念，要树立长期治疗的意识。</p>
<p>这与 2020 年 5 月最新版《美国风湿病学会痛风管理指南》建议痛风患者「有条件推荐无限期使用降尿酸药物」一致。</p>
<h3>了解 HUA 和痛风的危害</h3>
<blockquote>
<p>希望大家都能了解到 HUA 和痛风的危害，这也是我整理这篇文章的意义所在。</p>
</blockquote>
<p>了解 HUA 和痛风的危害，筛查及监测相关并发症，控制合并症：HUA 和痛风是慢性代谢性疾病，可损害多个靶器官，应及早监测，从而达到早期诊断及治疗的目的。</p>
<p>此外，痛风与多种疾病互为因果，如 CKD、心血管疾病及糖尿病等，控制好相关合并症对痛风的预防与治疗也至关重要。</p>
<h2>参考文献</h2>
<ol>
<li><a href="https://kns.cnki.net/kcms/detail/detail.aspx?dbcode=CJFD&amp;dbname=CJFD2012&amp;filename=SYNK201206019&amp;uniplatform=NZKPT&amp;v=TyV3_xqX4kORdje8sX5fuOuIsGG5MUlfHCthwSKAN3b5BCFNflNchpq0iaaMOUGh">曾学军。《2010 年中国痛风临床诊治指南》解读 [J]. 中国实用内科杂志，2012,32(06):438-441.</a></li>
<li><a href="https://kns.cnki.net/kcms/detail/detail.aspx?dbcode=CJFD&amp;dbname=CJFDLAST2022&amp;filename=LCLZ202007023&amp;uniplatform=NZKPT&amp;v=1vX2R4b9wGG-uIZRiWAcoSdpAiFy-9mky6qL7DOD0VsWLWHuRxZgvxRb8AbZoCFw">冯文文,崔岱,杨涛.《中国高尿酸血症与痛风诊疗指南(2019)》要点解读[J].临床内科杂志,2020,37(07):528-531.</a></li>
</ol>
]]></description>
    </item>
  </channel>
</rss>