<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://vvsxmja.github.io</id>
    <title>VVsxmja's Blog Blog</title>
    <updated>2024-08-26T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://vvsxmja.github.io"/>
    <subtitle>VVsxmja's Blog Blog</subtitle>
    <icon>https://vvsxmja.github.io/img/favicon.ico</icon>
    <entry>
        <title type="html"><![CDATA[用 Giscus 给 Docusaurus 博客增加评论区]]></title>
        <id>https://vvsxmja.github.io/2024/08/26/giscus-as-blog-comment-system</id>
        <link href="https://vvsxmja.github.io/2024/08/26/giscus-as-blog-comment-system"/>
        <updated>2024-08-26T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[使用 Giscus 为你在 GitHub 上的博客添加一个评论区，同时尽量少地引入 GitHub 以外的第三方。]]></summary>
        <content type="html"><![CDATA[<p>使用 Giscus 为你在 GitHub 上的博客添加一个评论区，同时尽量少地引入 GitHub 以外的第三方。</p>
<p>顺便介绍了通过 Docusaurus 的 API ，在切换博客的亮/暗模式时，同步切换 Giscus 主题的方式。</p>
<hr>
<p>我想给我的博客增加一个评论区了。</p>
<p>既然我的博客文章全部存储 GitHub 上，我希望它的评论系统也能尽量基于 GitHub ，尽量不要引入 GitHub 之外的第三方。</p>
<p>选择 Giscus 的原因是 Giscus 以 GitHub Discussion 作为唯一数据源，而且评论者身份和 GitHub 账号绑定，不像其他评论系统可能需要另外部署数据库或者使用第三方 Auth 。</p>
<p>如果能做到纯客户端实现，只在用户侧调用 GitHub API 来实现所有功能，就更好了。可惜 Giscus 仍然需要在第三方 Host 一个服务来做这件事，但是算了，无所谓。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="如果你没有听说过-giscus">如果你没有听说过 Giscus<a href="https://vvsxmja.github.io/2024/08/26/giscus-as-blog-comment-system#%E5%A6%82%E6%9E%9C%E4%BD%A0%E6%B2%A1%E6%9C%89%E5%90%AC%E8%AF%B4%E8%BF%87-giscus" class="hash-link" aria-label="如果你没有听说过 Giscus的直接链接" title="如果你没有听说过 Giscus的直接链接">​</a></h2>
<p><a href="https://github.com/giscus/giscus" target="_blank" rel="noopener noreferrer">Giscus</a> 是一个基于 GitHub Discussion 的评论系统。而 <a href="https://giscus.app/" target="_blank" rel="noopener noreferrer"><code>giscus.app</code></a> 是 Giscus 的文档兼脚本生成器。</p>
<p>跟随 <a href="https://giscus.app/" target="_blank" rel="noopener noreferrer"><code>giscus.app</code></a> 上的指引，我们最终会得到一个 HTML 元素，将这个元素插入到网页源代码中，Giscus 就出现啦。</p>
<p>如果你不喜欢直接写 HTML 元素，或者正在使用某个前端框架，你也可以使用 <a href="https://github.com/giscus/giscus-component" target="_blank" rel="noopener noreferrer">Giscus Component</a> 。比如你现在正在看的这个博客使用的框架是 <a href="https://docusaurus.io/" target="_blank" rel="noopener noreferrer">Docusaurus</a> ，而这个框架是基于 <a href="https://react.dev/" target="_blank" rel="noopener noreferrer">React</a> 的，所以我使用了 <a href="https://github.com/giscus/giscus-component/tree/main/react" target="_blank" rel="noopener noreferrer"><code>@giscus/react</code></a> 。</p>
<p>所以我们总共需要做三件事：</p>
<ol>
<li>在 GitHub 上的，我们的博客仓库中，<a href="https://docs.github.com/en/github/administering-a-repository/managing-repository-settings/enabling-or-disabling-github-discussions-for-a-repository" target="_blank" rel="noopener noreferrer">启用 Discussions</a> ，并且（可选）创建一个 Giscus 专属的 Discussion Category 来将 Giscus 发布的内容与其他内容区分开。这个 Category 最好是 Announcement 类型的（其中的内容只能由仓库维护者和 Giscus 创建）。</li>
<li>使用 <a href="https://giscus.app/" target="_blank" rel="noopener noreferrer"><code>giscus.app</code></a> 来为我们在 GitHub 上的博客仓库生成一个对应的评论区 HTML 元素代码；</li>
<li>把这个 HTML 代码翻译成对应的 <a href="https://github.com/giscus/giscus-component" target="_blank" rel="noopener noreferrer">Giscus Component</a> ，嵌入我们的博客中。</li>
</ol>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="giscus-的配置该怎么选">Giscus 的配置该怎么选<a href="https://vvsxmja.github.io/2024/08/26/giscus-as-blog-comment-system#giscus-%E7%9A%84%E9%85%8D%E7%BD%AE%E8%AF%A5%E6%80%8E%E4%B9%88%E9%80%89" class="hash-link" aria-label="Giscus 的配置该怎么选的直接链接" title="Giscus 的配置该怎么选的直接链接">​</a></h2>
<p><a href="https://giscus.app/" target="_blank" rel="noopener noreferrer"><code>giscus.app</code></a> 上对于每个配置项都有详细的描述，这里就不全部展开了，挑重点说。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="页面和-discussion-的对应关系">页面和 Discussion 的对应关系<a href="https://vvsxmja.github.io/2024/08/26/giscus-as-blog-comment-system#%E9%A1%B5%E9%9D%A2%E5%92%8C-discussion-%E7%9A%84%E5%AF%B9%E5%BA%94%E5%85%B3%E7%B3%BB" class="hash-link" aria-label="页面和 Discussion 的对应关系的直接链接" title="页面和 Discussion 的对应关系的直接链接">​</a></h3>
<p>我选择的是 URL ，原因是我的博客可能有多份部署（比如 PR Preview 部署在 Vercel 上），而我希望将不同部署之间的评论区互相隔离。</p>
<p>在 Giscus 提供的选项中，只有 URL 恰好支持了这个需求。不过代价是 GitHub Discussion 的标题会是 Blog Post 的 URL 而不是标题，在 GitHub 里面看起来比较丑（不过又有谁会在 GitHub 中查看博客的评论区，而不是直接在博客中查看呢）。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="主题">主题<a href="https://vvsxmja.github.io/2024/08/26/giscus-as-blog-comment-system#%E4%B8%BB%E9%A2%98" class="hash-link" aria-label="主题的直接链接" title="主题的直接链接">​</a></h3>
<p>这个选项改动之后，在页面下方可以看到实时预览。</p>
<p>我们的博客目前有明亮和暗黑两种颜色模式，我们希望读者切换 Docusaurus 的日夜模式后，Giscus 也能切换到对应的主题。</p>
<p>实现这个功能的方法下文会描述，这里我们先尝试下所有选项，分别为明亮和暗黑模式找到对应的合适的主题，然后记住下面代码中的 <code>theme</code> ，等下要用。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="在-docusaurus-中嵌入我们的评论区组件">在 Docusaurus 中嵌入我们的评论区组件<a href="https://vvsxmja.github.io/2024/08/26/giscus-as-blog-comment-system#%E5%9C%A8-docusaurus-%E4%B8%AD%E5%B5%8C%E5%85%A5%E6%88%91%E4%BB%AC%E7%9A%84%E8%AF%84%E8%AE%BA%E5%8C%BA%E7%BB%84%E4%BB%B6" class="hash-link" aria-label="在 Docusaurus 中嵌入我们的评论区组件的直接链接" title="在 Docusaurus 中嵌入我们的评论区组件的直接链接">​</a></h2>
<p><a href="https://github.com/giscus/giscus-component#documentation" target="_blank" rel="noopener noreferrer">Giscus Component 的文档</a>详细描述了将 HTML 元素翻译成对应组件的方法。</p>
<p>对照着文档和上面生成的 HTML 元素代码填空之后，我们得到了一个 Giscus React 组件：</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">Giscus</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@giscus/react'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token maybe-class-name">GiscusContainer</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">Giscus</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token comment" style="color:#999988;font-style:italic">/* ...... */</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    mapping</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"url"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    theme</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token comment" style="color:#999988;font-style:italic">/* light or dark_dimmed */</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token operator" style="color:#393A34">/</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>接下来要做的事情就是将这个组件插入到 Docusaurus 中。如果你也在使用 Docusaurus ，并且对于魔改 Docusaurus 组件的方法并不熟悉，你可以参考 <a href="https://docusaurus.io/docs/swizzling" target="_blank" rel="noopener noreferrer">Docusaurus 官方魔改教程</a>。</p>
<p>Docusaurus 提供了两种魔改的方式：Ejecting 和 Wrapping 。简单来说就是：</p>
<ul>
<li>Ejecting ：将 Docusaurus 某个组件的代码复制一份，开发者在其上进行修改。这是真正的魔改，自由度最高，但缺点是大量的组件代码被复制出来，会造成代码仓库体积的膨胀。</li>
<li>Wrapping ：在 Docusaurus 某个组件的外层创建一个 Wrapper ，比较优雅。大部分时候 Wrapping 就够用了。</li>
</ul>
<p>这里，我们希望在博客页面的下方添加我们的评论区组件，所以我们可以 Wrap 展示「博客内容」的组件，在博客内容的下方加上 Giscus 的代码。</p>
<p>我们使用的是 Docusaurus 默认的博客插件，需要 Wrap <code>@docusaurus/theme-classic</code> 这个主题的 <code>BlogPostItem</code> 组件。</p>
<div class="language-sh codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-sh codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># 以 npm 为例</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">npm run -- docusaurus swizzle @docusaurus/theme-classic BlogPostItem --wrap</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>项目根目录中会出现这些文件：</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">src</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">└── theme</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    └── BlogPostItem</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        └── index.tsx</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>打开 <code>src/theme/BlogPostItem/index.tsx</code> ，在 <code>BlogPostItem</code> 之后添加我们的 Giscus React 组件，打开我们的 Docusaurus 本地预览，我们就能看到博文下方的评论区啦！</p>
<p>然而预览之后会发现，博文下方确实出现了评论区，但是首页（博文目录）的每一个摘要下方也出现了评论区…… 🤡</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="限制-giscus-只在具体博文下方显示">限制 Giscus 只在具体博文下方显示<a href="https://vvsxmja.github.io/2024/08/26/giscus-as-blog-comment-system#%E9%99%90%E5%88%B6-giscus-%E5%8F%AA%E5%9C%A8%E5%85%B7%E4%BD%93%E5%8D%9A%E6%96%87%E4%B8%8B%E6%96%B9%E6%98%BE%E7%A4%BA" class="hash-link" aria-label="限制 Giscus 只在具体博文下方显示的直接链接" title="限制 Giscus 只在具体博文下方显示的直接链接">​</a></h3>
<p>Docusaurus 默认主题提供了一个 Internal API 叫做 <code>useBlogPost()</code> ，可以用来查询当前页面属于博文目录还是具体的博文页面。</p>
<p>因为是 Internal API ，所以文档里面没有关于这个 API 的描述 🤡 ，我在 Docusaurus 的 GitHub Repo 里面搜罗了一圈才找到相关的内容：</p>
<ul>
<li><a href="https://github.com/facebook/docusaurus/discussions/8140" target="_blank" rel="noopener noreferrer">https://github.com/facebook/docusaurus/discussions/8140</a></li>
<li><a href="https://github.com/facebook/docusaurus/pull/8088/files" target="_blank" rel="noopener noreferrer">https://github.com/facebook/docusaurus/pull/8088/files</a></li>
<li><a href="https://github.com/facebook/docusaurus/pull/10316" target="_blank" rel="noopener noreferrer">https://github.com/facebook/docusaurus/pull/10316</a></li>
</ul>
<p>我们通过这个 API 来拿到当前页面所属的类别，并在此基础上控制 Giscus 的显示：</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// other imports......</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> useBlogPost </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@docusaurus/plugin-content-blog/client'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">BlogPostItemWrapper</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">props</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">Props</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">JSX</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access maybe-class-name">Element</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> isBlogPostPage </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useBlogPost</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token maybe-class-name">GiscusContainer</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">Giscus</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">{</span><span class="token comment" style="color:#999988;font-style:italic">/* ...... */</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">/</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">BlogPostItem</span><span class="token tag" style="color:#00009f"> </span><span class="token tag spread punctuation" style="color:#393A34">{</span><span class="token tag spread operator" style="color:#393A34">...</span><span class="token tag spread" style="color:#00009f">props</span><span class="token tag spread punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token punctuation" style="color:#393A34">{</span><span class="token comment" style="color:#999988;font-style:italic">/* Prevent giscus to show up on blog post menu */</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">isBlogPostPage </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token maybe-class-name">GiscusContainer</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>再次打开预览，此时的 Giscus 应该仅在博文页面显示。我们也可以尝试写一些评论，会发现 GitHub 中出现了标题为 <code>http://localhost:3000/......</code> 的 Discussions ，符合预期。接下来只需要加上我们的日夜模式切换功能，就大功告成啦。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="根据当前颜色模式切换-giscus-theme">根据当前颜色模式切换 Giscus Theme<a href="https://vvsxmja.github.io/2024/08/26/giscus-as-blog-comment-system#%E6%A0%B9%E6%8D%AE%E5%BD%93%E5%89%8D%E9%A2%9C%E8%89%B2%E6%A8%A1%E5%BC%8F%E5%88%87%E6%8D%A2-giscus-theme" class="hash-link" aria-label="根据当前颜色模式切换 Giscus Theme的直接链接" title="根据当前颜色模式切换 Giscus Theme的直接链接">​</a></h3>
<blockquote>
<p>这个方法比较省力。这个博客（20240826）使用的也是这个方法。下文还会介绍一种可能更加优雅的方法。</p>
</blockquote>
<p>Docusaurus 提供了 <code>useColorMode()</code> 来获取当前读者选择的颜色模式。</p>
<p><a href="https://docusaurus.io/docs/api/themes/configuration#use-color-mode" target="_blank" rel="noopener noreferrer">这个 API 的文档</a>藏在<strong>默认主题</strong>的文档里，而不是<strong>默认主题的博客插件</strong>的文档里，导致我读的时候直接略过了……最后又是到 Docusaurus 的 GitHub Repo 里找了一通，才找到了相关内容：</p>
<ul>
<li><a href="https://github.com/facebook/docusaurus/discussions/9196" target="_blank" rel="noopener noreferrer">https://github.com/facebook/docusaurus/discussions/9196</a></li>
</ul>
<p>我们先定义好 Docusaurus ColorMode和 Giscus Theme 之间的映射，再通过这个 API 来拿到当前读者选择的 ColorMode ，并让 Giscus 使用与之对应 Theme ：</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// other imports......</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> useColorMode</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> </span><span class="token imports maybe-class-name">ColorMode</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@docusaurus/theme-common'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">Giscus</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">Theme</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@giscus/react'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token maybe-class-name">DocusaurusColorModeToGiscusTheme</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">Record</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">ColorMode</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token maybe-class-name">Theme</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  light</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'light'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  dark</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'dark_dimmed'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">BlogPostItemWrapper</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">props</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">Props</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">JSX</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access maybe-class-name">Element</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ......</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> colorMode </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useColorMode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> giscusTheme </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token maybe-class-name">DocusaurusColorModeToGiscusTheme</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">colorMode</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token maybe-class-name">GiscusContainer</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">Giscus</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">{</span><span class="token comment" style="color:#999988;font-style:italic">/* ...... */</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      theme</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">giscusTheme</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">/</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ......</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>再次打开预览测试，此时 Giscus Theme 应该会随着 Docusaurus ColorMode 的切换而切换。唯一的一点小瑕疵是 Giscus 组件的颜色变换是瞬间完成的，不像 Docusaurus 内部其他组件一样有过渡动画。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="另一种让-giscus-变色的方式">另一种让 Giscus 变色的方式<a href="https://vvsxmja.github.io/2024/08/26/giscus-as-blog-comment-system#%E5%8F%A6%E4%B8%80%E7%A7%8D%E8%AE%A9-giscus-%E5%8F%98%E8%89%B2%E7%9A%84%E6%96%B9%E5%BC%8F" class="hash-link" aria-label="另一种让 Giscus 变色的方式的直接链接" title="另一种让 Giscus 变色的方式的直接链接">​</a></h3>
<p>这个方法是我写这篇文章的时候脑补出来的，还没实现过，留作习题供读者尝试（doge）。</p>
<p>简单来说就是 Giscus 的主题可以是自定义的 CSS 。而我们知道在 CSS 中实现日夜切换的常规操作是通过 CSS 变量，让组件在不同模式下继承不同的变量得到不同的前景色和背景色。</p>
<p>那么这里理论上更优雅的实现方式，是我们为 Giscus 编写一套 Docusaurus Theme ，将 Giscus 所有颜色交由 Docusaurus 管理，这样可以让 Giscus 和 Docusaurus 更加融合统一。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="结语">结语<a href="https://vvsxmja.github.io/2024/08/26/giscus-as-blog-comment-system#%E7%BB%93%E8%AF%AD" class="hash-link" aria-label="结语的直接链接" title="结语的直接链接">​</a></h2>
<p>我们使用 Docusaurus Swizzling 将 Giscus 嵌入了 Docusaurus 中，并添加了日夜颜色切换的功能，接下来就可以通过这个评论区愉快地和小伙伴们聊天啦！（现实是根本没有小伙伴 QAQ）</p>]]></content>
        <author>
            <name>Yikai Cai</name>
            <uri>https://github.com/VVsxmja</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[使用 Netdata 来监控开发机的系统指标]]></title>
        <id>https://vvsxmja.github.io/2024/08/08/use-netdata-to-monitor-your-dev-machine</id>
        <link href="https://vvsxmja.github.io/2024/08/08/use-netdata-to-monitor-your-dev-machine"/>
        <updated>2024-08-08T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Netdata 本来是对标 Prometheus 的监控方案，但日常开发中我们也可以用 Netdata 替代 htop 和 dstat ，除了具备二者几乎所有能力之外，还提供图形化界面，支持回放，并且开箱即用、易于上手。]]></summary>
        <content type="html"><![CDATA[<p>Netdata 本来是对标 Prometheus 的监控方案，但日常开发中我们也可以用 Netdata 替代 <code>htop</code> 和 <code>dstat</code> ，除了具备二者几乎所有能力之外，还提供图形化界面，支持回放，并且开箱即用、易于上手。</p>
<hr>
<p>不知道大伙在 Linux 系统上进行开发的时候，有没有过这样的体验：</p>
<ul>
<li>我跑的某个工具卡住了，但我不知道它是在计算，还是在进行网络传输，还是挂了。</li>
<li>我的某个程序跑的很慢，但我不知道它 Bound 在 IO 还是 CPU 还是别的地方。</li>
</ul>
<p>如果你之前只接触过 Windows 系统，那么这时你应该会下意识地寻找「任务管理器」这种东西。你可能会找到 <code>htop</code> ，或者（如果你正在使用图形界面）某个桌面环境的任务管理器。</p>
<p>这些程序提供了最基本的系统指标，例如 CPU 利用率、内存利用率等。但当我们遇到一些比较复杂/玄学的问题时，我们可能会想：如果能看到更详细的系统指标（比如 CPU 中断发生的频率），说不定能找到什么解决问题的线索。</p>
<p>比较有经验的 Linux 用户此时会转而使用 <code>dstat</code> 等工具，这些工具能够看到更加细节、一般的「任务管理器」不会展示的指标。</p>
<p>但这些工具仍然存在「不够开箱即用」，「难以回放历史记录」等问题，例如初次使用 <code>dstat</code> 的用户可能会遇到下面的困难：</p>
<ul>
<li><code>dstat</code> 的默认输出中是不包含时间信息的，需要通过加上 <code>-t</code> 来在每行输出中附加时间信息。</li>
<li><code>dstat</code> 默认输出到 <code>stdout</code> ，能够回看的长度受限于终端缓冲区大小，需要通过加上 <code>-o ...</code> 来在打印命令行输出的同时输出到 CSV 文件。</li>
<li><code>dstat</code> 不支持在运行时更改采样间隔，只能对着打出来的 CSV 去按不同频率采样/聚合。</li>
</ul>
<p>另外，相信大伙在项目中工作时，往往会发出「线上环境的基建，如果能在我的开发机上也布一套就好了」的感慨，在 CI 或是在生产部署上可以访问到的详细的监控，和我们开发机上可怜的寥寥几项指标相比，就像是奥迪和奥迪双钻的区别。</p>
<p>那么有没有一套开箱即用的监控服务，能很方便地在我的开发机上布起来，能够解决上面的所有痛点，并且也具备接入其他应用的能力？</p>
<p>答案是有的，请看 <a href="https://www.netdata.cloud/" target="_blank" rel="noopener noreferrer">Netdata</a> 。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="如果你没有听说过-netdata">如果你没有听说过 Netdata<a href="https://vvsxmja.github.io/2024/08/08/use-netdata-to-monitor-your-dev-machine#%E5%A6%82%E6%9E%9C%E4%BD%A0%E6%B2%A1%E6%9C%89%E5%90%AC%E8%AF%B4%E8%BF%87-netdata" class="hash-link" aria-label="如果你没有听说过 Netdata的直接链接" title="如果你没有听说过 Netdata的直接链接">​</a></h2>
<p><a href="https://www.netdata.cloud/" target="_blank" rel="noopener noreferrer">Netdata</a> 是一款很强大的<a href="https://github.com/netdata/netdata" target="_blank" rel="noopener noreferrer">开源</a>监控工具。它开箱即用，除了具备 <code>dstat</code> 的各种能力之外，还提供了非常友好的 Web Dashboard ，<strong>对于每一项指标都有很详细的描述</strong>，就算是第一次接触的用户，也能快速上手。</p>
<p><img decoding="async" loading="lazy" src="https://github.com/netdata/netdata/assets/2662304/af4caa23-19be-46ef-9779-8fdad8d99d2a" alt="Netdata Agent" class="img_ev3q"></p>
<p>例如默认配置下，Overview 中第一个 Metric - CPU 利用率的图表上方，就有如下的介绍：</p>
<blockquote>
<p>Total CPU utilization (all cores).</p>
<p>100% here means there is no CPU idle time at all. You can get per core usage at the CPUs section and per application usage at the Applications Monitoring section.</p>
<p>Keep an eye on iowait. If it is constantly high, your disks are a bottleneck and they slow your system down.</p>
<p>An important metric worth monitoring, is softirq. A constantly high percentage of softirq may indicate network driver issues. The individual metrics can be found in the kernel documentation.</p>
</blockquote>
<p>Netdata 就像是<strong>生怕用户看不懂这些 Metrics</strong> 一样，在每一个图表的上方都撰写了详细的描述。结合 Netdata 对于系统指标的强大的抓取能力，不管是新手还是老手，都能在 Netdata 中发现查看节点运行状况的一个新的角度。</p>
<p>百闻不如一见，Netdata 官方在世界各地都布了 Netdata Demo ，大伙可以直接点进去看看 Netdata 长什么样。</p>
<table><thead><tr><th><a href="https://frankfurt.netdata.rocks/" target="_blank" rel="noopener noreferrer">FRANKFURT</a></th><th><a href="https://newyork.netdata.rocks/" target="_blank" rel="noopener noreferrer">NEWYORK</a></th><th><a href="https://atlanta.netdata.rocks/" target="_blank" rel="noopener noreferrer">ATLANTA</a></th><th><a href="https://sanfrancisco.netdata.rocks/" target="_blank" rel="noopener noreferrer">SANFRANCISCO</a></th><th><a href="https://toronto.netdata.rocks/" target="_blank" rel="noopener noreferrer">TORONTO</a></th><th><a href="https://singapore.netdata.rocks/" target="_blank" rel="noopener noreferrer">SINGAPORE</a></th><th><a href="https://bangalore.netdata.rocks/" target="_blank" rel="noopener noreferrer">BANGALORE</a></th></tr></thead></table>
<p>Netdata 除了自身具有观测系统指标的能力之外，同时也支持接入其他应用（例如 OpenMetrics 和 StatsD）以拓展它的能力。比如，安装了<a href="https://github.com/netdata/netdata/tree/master/src/collectors/systemd-journal.plugin" target="_blank" rel="noopener noreferrer">这个插件</a>，你就可以在 Netdata 中查看 <code>systemd</code> 的 Journal Logs 了！</p>
<p>相信你此时已经按耐不住自己的心情，想要自己部署一套 Netdata 实例了！</p>
<p>（如果你还是 Get 不到 Netdata 好用在哪里，你也可以跟随下面的指引，一键启动一个 Netdata 来玩一玩，看看它是不是你的菜）</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="使用-docker-启动-netdata-实例">使用 Docker 启动 Netdata 实例<a href="https://vvsxmja.github.io/2024/08/08/use-netdata-to-monitor-your-dev-machine#%E4%BD%BF%E7%94%A8-docker-%E5%90%AF%E5%8A%A8-netdata-%E5%AE%9E%E4%BE%8B" class="hash-link" aria-label="使用 Docker 启动 Netdata 实例的直接链接" title="使用 Docker 启动 Netdata 实例的直接链接">​</a></h2>
<p>以下内容基于 <a href="https://learn.netdata.cloud/docs/netdata-agent/installation/docker" target="_blank" rel="noopener noreferrer">Netdata 官方文档</a>编写。</p>
<p>Netdata 推荐的安装/部署方式是通过容器启动的。只需要一条命令 / 一个配置文件就能快速部署 Netdata ：</p>
<p>（当然在此之前，你需要确认你的网络条件可以让 Docker Engine 访问到 DockerHub ）</p>
<!-- -->
<div class="tabs-container tabList__CuJ"><ul role="tablist" aria-orientation="horizontal" class="tabs"><li role="tab" tabindex="0" aria-selected="true" class="tabs__item tabItem_LNqP tabs__item--active">Docker CLI</li><li role="tab" tabindex="-1" aria-selected="false" class="tabs__item tabItem_LNqP">Docker Compose</li></ul><div class="margin-top--md"><div role="tabpanel" class="tabItem_Ymn6"><div class="language-sh codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-sh codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">docker run -d --name=netdata \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --pid=host \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --network=host \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  -v netdataconfig:/etc/netdata \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  -v netdatalib:/var/lib/netdata \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  -v netdatacache:/var/cache/netdata \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  -v /:/host/root:ro,rslave \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  -v /etc/passwd:/host/etc/passwd:ro \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  -v /etc/group:/host/etc/group:ro \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  -v /etc/localtime:/etc/localtime:ro \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  -v /proc:/host/proc:ro \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  -v /sys:/host/sys:ro \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  -v /etc/os-release:/host/etc/os-release:ro \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  -v /var/log:/host/var/log:ro \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  -v /var/run/docker.sock:/var/run/docker.sock:ro \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --restart unless-stopped \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --cap-add SYS_PTRACE \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --cap-add SYS_ADMIN \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --security-opt apparmor=unconfined \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  netdata/netdata</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></div><div role="tabpanel" class="tabItem_Ymn6" hidden=""><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">services</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">netdata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> netdata/netdata</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">container_name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> netdata</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">pid</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> host</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">network_mode</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> host</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">restart</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> unless</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">stopped</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">cap_add</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> SYS_PTRACE</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> SYS_ADMIN</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">security_opt</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> apparmor</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">unconfined</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> netdataconfig</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/etc/netdata</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> netdatalib</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/var/lib/netdata</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> netdatacache</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/var/cache/netdata</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> /</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/host/root</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">ro</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">rslave</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> /etc/passwd</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/host/etc/passwd</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">ro</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> /etc/group</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/host/etc/group</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">ro</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> /etc/localtime</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/etc/localtime</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">ro</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> /proc</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/host/proc</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">ro</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> /sys</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/host/sys</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">ro</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> /etc/os</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">release</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/host/etc/os</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">release</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">ro</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> /var/log</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/host/var/log</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">ro</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> /var/run/docker.sock</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/var/run/docker.sock</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">ro</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">netdataconfig</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">netdatalib</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  netdatacache</span><span class="token punctuation" style="color:#393A34">:</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></div></div></div>
<p>这里的配置是最简单的开箱即用配置，关于更高级的配置，以及除了 Docker 以外的部署方法，请参考<a href="https://learn.netdata.cloud/docs/netdata-agent/installation" target="_blank" rel="noopener noreferrer">官方文档</a>。</p>
<p>Netdata 会在 <code>19999</code> 端口跑一个 Web Dashboard ，结合 <code>--network=host</code> ，也就是跑在 Host 的 <code>19999</code> 端口上。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="通过-ssh-port-forwarding-访问开发机上的-web-dashboard">通过 SSH Port Forwarding 访问开发机上的 Web Dashboard<a href="https://vvsxmja.github.io/2024/08/08/use-netdata-to-monitor-your-dev-machine#%E9%80%9A%E8%BF%87-ssh-port-forwarding-%E8%AE%BF%E9%97%AE%E5%BC%80%E5%8F%91%E6%9C%BA%E4%B8%8A%E7%9A%84-web-dashboard" class="hash-link" aria-label="通过 SSH Port Forwarding 访问开发机上的 Web Dashboard的直接链接" title="通过 SSH Port Forwarding 访问开发机上的 Web Dashboard的直接链接">​</a></h2>
<p>我们可以通过 SSH Tunnel 等方式，将这个端口转发到我们访问开发机的电脑上，然后就可以用浏览器访问啦！</p>
<p>最简单的转发方式，就是开一个 VS Code ，通过 <a href="https://code.visualstudio.com/docs/remote/ssh" target="_blank" rel="noopener noreferrer">SSH Remote</a> 连接到我们的开发机，<a href="https://code.visualstudio.com/docs/remote/ssh#_forwarding-a-port-creating-ssh-tunnel" target="_blank" rel="noopener noreferrer">在 Panel 里的「端口」标签页中增加一个 <code>19999</code> 端口的转发</a>，然后用浏览器打开 <code>http://localhost:19999</code> 就能访问了。</p>
<p>如果我们希望自动进行这样的操作，可以更改我们的 SSH Config ，使得每次通过 SSH 连接到开发机的时候，自动转发 19999 端口到本地。在上面的 VS Code 文档链接中<a href="https://code.visualstudio.com/docs/remote/ssh#_always-forwarding-a-port" target="_blank" rel="noopener noreferrer">也有提到</a>。</p>
<p>下面提供我的配置文件作为参考：</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">Host dev</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    # 基础配置</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    HostName 开发机的 IP 地址</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    User 开发机登陆用户名</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    IdentityFile SSH 密钥地址</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    # 转发 19999 端口到本地</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    LocalForward 19999 127.0.0.1:19999</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    # 可选，VS Code 会自动保活，不需要加这个</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ServerAliveInterval 15</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ServerAliveCountMax 240</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="结语">结语<a href="https://vvsxmja.github.io/2024/08/08/use-netdata-to-monitor-your-dev-machine#%E7%BB%93%E8%AF%AD" class="hash-link" aria-label="结语的直接链接" title="结语的直接链接">​</a></h2>
<p>Netdata 提供了开箱即用的使用体验，详细、实时的系统指标，以及友好的 Web Dashboard ，值得大家进行尝试。</p>
<p>在具体的开发场景下，结合 Port Forwarding ，可以很方便地随时查看开发机的各项指标。</p>
<p>希望这篇文章有帮到你。</p>]]></content>
        <author>
            <name>Yikai Cai</name>
            <uri>https://github.com/VVsxmja</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Docusaurus 初体验！]]></title>
        <id>https://vvsxmja.github.io/2024/05/26/first-blog-in-docusaurus</id>
        <link href="https://vvsxmja.github.io/2024/05/26/first-blog-in-docusaurus"/>
        <updated>2024-05-26T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Docusaurus 是一个基于 React 的静态站点框架，在开箱即用的同时，提供了优雅的魔改方法。]]></summary>
        <content type="html"><![CDATA[<p>Docusaurus 是一个基于 React 的静态站点框架，在开箱即用的同时，提供了优雅的魔改方法。</p>
<p><img decoding="async" loading="lazy" alt="Docusaurus Introducing" src="https://vvsxmja.github.io/assets/images/slash-introducing-411a16dd05086935b8e9ddae38ae9b45.svg" width="760" height="160" class="img_ev3q"></p>
<hr>
<p>很早就有写博客的想法，但是一直没找到一个很对我胃口的框架。因为除了写博客这件事之外，我还希望能够大力魔改我用的博客框架。</p>
<p>——要开箱即用，又要高可玩性，还要详细的文档，如果能方便地用 JavaScript/TypeScript 添加自己想要的功能，用 CSS 改成自己喜欢的样式，就最好了。</p>
<p>之前用过一段时间的 <a href="https://www.mkdocs.org/" target="_blank" rel="noopener noreferrer">Mkdocs</a> 和 <a href="https://hexo.io/" target="_blank" rel="noopener noreferrer">Hexo</a> ，它们都是很出色的框架，使用体验也很好，但就是不太能满足我的定制化需求：它们的插件系统要不就是用起来很麻烦，要不就是缺少文档。</p>
<p>后来用了一段时间的 <a href="https://vitepress.dev/" target="_blank" rel="noopener noreferrer">VitePress</a> ，当时以为自己找到了<strong>命中注定的那个框架</strong>：基于 Vue 和 Vite ，极高的可定制性；框架本身也并不复杂，代码不多，很快就能看完。我甚至写了<a href="https://github.com/VVsxmja/vitepress-theme-blog-pure" target="_blank" rel="noopener noreferrer">一个 VitePress 的主题</a>。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="vitepress-之旅">VitePress 之旅<a href="https://vvsxmja.github.io/2024/05/26/first-blog-in-docusaurus#vitepress-%E4%B9%8B%E6%97%85" class="hash-link" aria-label="VitePress 之旅的直接链接" title="VitePress 之旅的直接链接">​</a></h2>
<p>VitePress 的可定制性太强了！强的原因是 VitePress 本身的功能并不多。</p>
<p>它只做这么几件事：把 Markdown 用 <a href="https://github.com/markdown-it/markdown-it" target="_blank" rel="noopener noreferrer">markdown-it</a> 和 <a href="https://github.com/mdit-vue/mdit-vue" target="_blank" rel="noopener noreferrer">mdit-vue</a> 翻译成 HTML ，处理一下各个文件之间的链接，然后<a href="https://vitepress.dev/reference/runtime-api#content" target="_blank" rel="noopener noreferrer">嵌入到默认/自定义 Layout 中的 <code>&lt;Content /&gt;</code>元素里</a>，基本上就大功告成了。</p>
<p>而自定义 Layout 更是 100% Hackable 的逆天存在，<a href="https://vitepress.dev/guide/custom-theme#building-a-layout" target="_blank" rel="noopener noreferrer">它就是一个 Vue Component</a>（顺便一提，<a href="https://vitepress.dev/guide/using-vue" target="_blank" rel="noopener noreferrer">VitePress 的 Markdown 中也支持插入 Vue Component</a>），可以在运行时做自己想做的任何的事情。</p>
<p>在构建期间也可以通过自定义的 <a href="https://vitepress.dev/guide/data-loading" target="_blank" rel="noopener noreferrer">Build-Time Data Loader</a> 构建出任意的自定义数据，例如博客框架里常用的<a href="https://github.com/VVsxmja/vitepress-theme-blog-pure/blob/main/.vitepress/theme/posts.data.ts" target="_blank" rel="noopener noreferrer">博文列表</a>（一般会加上分页），<a href="https://github.com/VVsxmja/vitepress-theme-blog-pure/blob/main/.vitepress/theme/timestamp.data.ts" target="_blank" rel="noopener noreferrer">各篇文章的编辑日期和修改日期</a>，以及各种其他的好玩的东西……</p>
<p>后来放弃 VitePress 的原因有两个，一个是 VitePress 的文档太弱了，另一个是 VitePress 和 Vite 强绑定，但是 Vite 太难懂了（而且文档也比较弱）。为了把我的自定义主题导出成 npm 包，费了九牛二虎之力都没有成功，最后只好作罢。</p>
<blockquote>
<p>其实我大概知道问题在哪：因为我用了 <a href="https://unocss.dev/" target="_blank" rel="noopener noreferrer">UnoCSS</a> 的 <a href="https://unocss.dev/integrations/vite" target="_blank" rel="noopener noreferrer">Vite 集成</a>，所以除了用 npm 包分发代码之外，用户还需要额外进行 <a href="https://vitepress.dev/reference/site-config#vite" target="_blank" rel="noopener noreferrer">VitePress 的 Vite 配置</a>，但我自己用新的环境尝试了几次，遇到了各种难以描述的奇怪的错误……总之失败了几次之后彻底失去耐心了.</p>
</blockquote>
<p>我很热衷于自己造轮子，事实上造这个 VitePress 主题的过程中我已经造出了一个博客框架大部分必要的组成部分了。但是碰到奇怪的问题又找不到文档时，那种挫败感真的令人难受。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="图标是一个小恐龙的静态站点框架">图标是一个小恐龙的静态站点框架<a href="https://vvsxmja.github.io/2024/05/26/first-blog-in-docusaurus#%E5%9B%BE%E6%A0%87%E6%98%AF%E4%B8%80%E4%B8%AA%E5%B0%8F%E6%81%90%E9%BE%99%E7%9A%84%E9%9D%99%E6%80%81%E7%AB%99%E7%82%B9%E6%A1%86%E6%9E%B6" class="hash-link" aria-label="图标是一个小恐龙的静态站点框架的直接链接" title="图标是一个小恐龙的静态站点框架的直接链接">​</a></h2>
<p>放弃 VitePress 之后决定寻找一个新的框架，网上冲浪了一段时间之后发现了 <a href="https://docusaurus.io/" target="_blank" rel="noopener noreferrer">Docusaurus</a> 这个好东西。基于 React ，文档非常详细（Docusaurus 的官网本身就是使用 Docusaurus 写的文档博客二合一网站），有<a href="https://docusaurus.io/docs/blog" target="_blank" rel="noopener noreferrer">开箱即用的博客框架</a>，并且也支持定制化，而且<a href="https://docusaurus.io/docs/swizzling" target="_blank" rel="noopener noreferrer">这个定制的方式还很神奇</a>……总之被圈粉了。</p>
<p>这个小恐龙的图标真的很魔性，越看越喜欢。</p>
<p><img decoding="async" loading="lazy" alt="Docusaurus" src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCIgdmlld0JveD0iMCAwIDIwMCAyMDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48cGF0aCBmaWxsPSIjRkZGIiBkPSJNOTkgNTJoODR2MzRIOTl6Ii8+PHBhdGggZD0iTTIzIDE2M2MtNy4zOTggMC0xMy44NDMtNC4wMjctMTcuMzAzLTEwQTE5Ljg4NiAxOS44ODYgMCAwIDAgMyAxNjNjMCAxMS4wNDYgOC45NTQgMjAgMjAgMjBoMjB2LTIwSDIzeiIgZmlsbD0iIzNFQ0M1RiIvPjxwYXRoIGQ9Ik0xMTIuOTggNTcuMzc2TDE4MyA1M1Y0M2MwLTExLjA0Ni04Ljk1NC0yMC0yMC0yMEg3M2wtMi41LTQuMzNjLTEuMTEyLTEuOTI1LTMuODg5LTEuOTI1LTUgMEw2MyAyM2wtMi41LTQuMzNjLTEuMTExLTEuOTI1LTMuODg5LTEuOTI1LTUgMEw1MyAyM2wtMi41LTQuMzNjLTEuMTExLTEuOTI1LTMuODg5LTEuOTI1LTUgMEw0MyAyM2MtLjAyMiAwLS4wNDIuMDAzLS4wNjUuMDAzbC00LjE0Mi00LjE0MWMtMS41Ny0xLjU3MS00LjI1Mi0uODUzLTQuODI4IDEuMjk0bC0xLjM2OSA1LjEwNC01LjE5Mi0xLjM5MmMtMi4xNDgtLjU3NS00LjExMSAxLjM4OS0zLjUzNSAzLjUzNmwxLjM5IDUuMTkzLTUuMTAyIDEuMzY3Yy0yLjE0OC41NzYtMi44NjcgMy4yNTktMS4yOTYgNC44M2w0LjE0MiA0LjE0MmMwIC4wMjEtLjAwMy4wNDItLjAwMy4wNjRsLTQuMzMgMi41Yy0xLjkyNSAxLjExMS0xLjkyNSAzLjg4OSAwIDVMMjMgNTNsLTQuMzMgMi41Yy0xLjkyNSAxLjExMS0xLjkyNSAzLjg4OSAwIDVMMjMgNjNsLTQuMzMgMi41Yy0xLjkyNSAxLjExMS0xLjkyNSAzLjg4OSAwIDVMMjMgNzNsLTQuMzMgMi41Yy0xLjkyNSAxLjExMS0xLjkyNSAzLjg4OSAwIDVMMjMgODNsLTQuMzMgMi41Yy0xLjkyNSAxLjExMS0xLjkyNSAzLjg4OSAwIDVMMjMgOTNsLTQuMzMgMi41Yy0xLjkyNSAxLjExMS0xLjkyNSAzLjg4OSAwIDVMMjMgMTAzbC00LjMzIDIuNWMtMS45MjUgMS4xMTEtMS45MjUgMy44ODkgMCA1TDIzIDExM2wtNC4zMyAyLjVjLTEuOTI1IDEuMTExLTEuOTI1IDMuODg5IDAgNUwyMyAxMjNsLTQuMzMgMi41Yy0xLjkyNSAxLjExMS0xLjkyNSAzLjg4OSAwIDVMMjMgMTMzbC00LjMzIDIuNWMtMS45MjUgMS4xMTEtMS45MjUgMy44ODkgMCA1TDIzIDE0M2wtNC4zMyAyLjVjLTEuOTI1IDEuMTExLTEuOTI1IDMuODg5IDAgNUwyMyAxNTNsLTQuMzMgMi41Yy0xLjkyNSAxLjExMS0xLjkyNSAzLjg4OSAwIDVMMjMgMTYzYzAgMTEuMDQ2IDguOTU0IDIwIDIwIDIwaDEyMGMxMS4wNDYgMCAyMC04Ljk1NCAyMC0yMFY4M2wtNzAuMDItNC4zNzZBMTAuNjQ1IDEwLjY0NSAwIDAgMSAxMDMgNjhjMC01LjYyMSA0LjM3LTEwLjI3MyA5Ljk4LTEwLjYyNCIgZmlsbD0iIzNFQ0M1RiIvPjxwYXRoIGZpbGw9IiMzRUNDNUYiIGQ9Ik0xNDMgMTgzaDMwdi00MGgtMzB6Ii8+PHBhdGggZD0iTTE5MyAxNThjLS4yMTkgMC0uNDI4LjAzNy0uNjM5LjA2NC0uMDM4LS4xNS0uMDc0LS4zMDEtLjExNi0uNDUxQTUgNSAwIDAgMCAxOTAuMzIgMTQ4YTQuOTYgNC45NiAwIDAgMC0zLjAxNiAxLjAzNiAyNi41MzEgMjYuNTMxIDAgMCAwLS4zMzUtLjMzNiA0Ljk1NSA0Ljk1NSAwIDAgMCAxLjAxMS0yLjk4NyA1IDUgMCAwIDAtOS41OTktMS45NTljLS4xNDgtLjA0Mi0uMjk3LS4wNzctLjQ0NS0uMTE1LjAyNy0uMjExLjA2NC0uNDIuMDY0LS42MzlhNSA1IDAgMCAwLTUtNSA1IDUgMCAwIDAtNSA1YzAgLjIxOS4wMzcuNDI4LjA2NC42MzktLjE0OC4wMzgtLjI5Ny4wNzMtLjQ0NS4xMTVhNC45OTggNC45OTggMCAwIDAtOS41OTkgMS45NTljMCAxLjEyNS4zODQgMi4xNTEgMS4wMTEgMi45ODctMy43MTcgMy42MzItNi4wMzEgOC42OTMtNi4wMzEgMTQuMyAwIDExLjA0NiA4Ljk1NCAyMCAyMCAyMCA5LjMzOSAwIDE3LjE2LTYuNDEgMTkuMzYxLTE1LjA2NC4yMTEuMDI3LjQyLjA2NC42MzkuMDY0YTUgNSAwIDAgMCA1LTUgNSA1IDAgMCAwLTUtNSIgZmlsbD0iIzQ0RDg2MCIvPjxwYXRoIGZpbGw9IiMzRUNDNUYiIGQ9Ik0xNTMgMTIzaDMwdi0yMGgtMzB6Ii8+PHBhdGggZD0iTTE5MyAxMTUuNWEyLjUgMi41IDAgMSAwIDAtNWMtLjEwOSAwLS4yMTQuMDE5LS4zMTkuMDMyLS4wMi0uMDc1LS4wMzctLjE1LS4wNTgtLjIyNWEyLjUwMSAyLjUwMSAwIDAgMC0uOTYzLTQuODA3Yy0uNTY5IDAtMS4wODguMTk3LTEuNTA4LjUxOGE2LjY1MyA2LjY1MyAwIDAgMC0uMTY4LS4xNjhjLjMxNC0uNDE3LjUwNi0uOTMxLjUwNi0xLjQ5NGEyLjUgMi41IDAgMCAwLTQuOC0uOTc5QTkuOTg3IDkuOTg3IDAgMCAwIDE4MyAxMDNjLTUuNTIyIDAtMTAgNC40NzgtMTAgMTBzNC40NzggMTAgMTAgMTBjLjkzNCAwIDEuODMzLS4xMzggMi42OS0uMzc3YTIuNSAyLjUgMCAwIDAgNC44LS45NzljMC0uNTYzLS4xOTItMS4wNzctLjUwNi0xLjQ5NC4wNTctLjA1NS4xMTMtLjExMS4xNjgtLjE2OC40Mi4zMjEuOTM5LjUxOCAxLjUwOC41MThhMi41IDIuNSAwIDAgMCAuOTYzLTQuODA3Yy4wMjEtLjA3NC4wMzgtLjE1LjA1OC0uMjI1LjEwNS4wMTMuMjEuMDMyLjMxOS4wMzIiIGZpbGw9IiM0NEQ4NjAiLz48cGF0aCBkPSJNNjMgNTUuNWEyLjUgMi41IDAgMCAxLTIuNS0yLjVjMC00LjEzNi0zLjM2NC03LjUtNy41LTcuNXMtNy41IDMuMzY0LTcuNSA3LjVhMi41IDIuNSAwIDEgMS01IDBjMC02Ljg5MyA1LjYwNy0xMi41IDEyLjUtMTIuNVM2NS41IDQ2LjEwNyA2NS41IDUzYTIuNSAyLjUgMCAwIDEtMi41IDIuNSIgZmlsbD0iIzAwMCIvPjxwYXRoIGQ9Ik0xMDMgMTgzaDYwYzExLjA0NiAwIDIwLTguOTU0IDIwLTIwVjkzaC02MGMtMTEuMDQ2IDAtMjAgOC45NTQtMjAgMjB2NzB6IiBmaWxsPSIjRkZGRjUwIi8+PHBhdGggZD0iTTE2OC4wMiAxMjRoLTUwLjA0YTEgMSAwIDEgMSAwLTJoNTAuMDRhMSAxIDAgMSAxIDAgMm0wIDIwaC01MC4wNGExIDEgMCAxIDEgMC0yaDUwLjA0YTEgMSAwIDEgMSAwIDJtMCAyMGgtNTAuMDRhMSAxIDAgMSAxIDAtMmg1MC4wNGExIDEgMCAxIDEgMCAybTAtNDkuODE0aC01MC4wNGExIDEgMCAxIDEgMC0yaDUwLjA0YTEgMSAwIDEgMSAwIDJtMCAxOS44MTRoLTUwLjA0YTEgMSAwIDEgMSAwLTJoNTAuMDRhMSAxIDAgMSAxIDAgMm0wIDIwaC01MC4wNGExIDEgMCAxIDEgMC0yaDUwLjA0YTEgMSAwIDEgMSAwIDJNMTgzIDYxLjYxMWMtLjAxMiAwLS4wMjItLjAwNi0uMDM0LS4wMDUtMy4wOS4xMDUtNC41NTIgMy4xOTYtNS44NDIgNS45MjMtMS4zNDYgMi44NS0yLjM4NyA0LjcwMy00LjA5MyA0LjY0Ny0xLjg4OS0uMDY4LTIuOTY5LTIuMjAyLTQuMTEzLTQuNDYtMS4zMTQtMi41OTQtMi44MTQtNS41MzYtNS45NjMtNS40MjYtMy4wNDYuMTA0LTQuNTEzIDIuNzk0LTUuODA3IDUuMTY3LTEuMzc3IDIuNTI4LTIuMzE0IDQuMDY1LTQuMTIxIDMuOTk0LTEuOTI3LS4wNy0yLjk1MS0xLjgwNS00LjEzNi0zLjgxMy0xLjMyMS0yLjIzNi0yLjg0OC00Ljc1LTUuOTM2LTQuNjY0LTIuOTk0LjEwMy00LjQ2NSAyLjM4NS01Ljc2MyA0LjQtMS4zNzMgMi4xMy0yLjMzNSAzLjQyOC00LjE2NSAzLjM1MS0xLjk3My0uMDctMi45OTItMS41MS00LjE3MS0zLjE3Ny0xLjMyNC0xLjg3My0yLjgxNi0zLjk5My01Ljg5NS0zLjg5LTIuOTI4LjEtNC4zOTkgMS45Ny01LjY5NiAzLjYxOC0xLjIzMiAxLjU2NC0yLjE5NCAyLjgwMi00LjIyOSAyLjcyNGExIDEgMCAwIDAtLjA3MiAyYzMuMDE3LjEwMSA0LjU0NS0xLjggNS44NzItMy40ODcgMS4xNzctMS40OTYgMi4xOTMtMi43ODcgNC4xOTMtMi44NTUgMS45MjYtLjA4MiAyLjgyOSAxLjExNSA0LjE5NSAzLjA0NSAxLjI5NyAxLjgzNCAyLjc2OSAzLjkxNCA1LjczMSA0LjAyMSAzLjEwMy4xMDQgNC41OTYtMi4yMTUgNS45MTgtNC4yNjcgMS4xODItMS44MzQgMi4yMDItMy40MTcgNC4xNS0zLjQ4NCAxLjc5My0uMDY3IDIuNzY5IDEuMzUgNC4xNDUgMy42ODEgMS4yOTcgMi4xOTcgMi43NjYgNC42ODYgNS43ODcgNC43OTYgMy4xMjUuMTA4IDQuNjM0LTIuNjIgNS45NDktNS4wMzUgMS4xMzktMi4wODggMi4yMTQtNC4wNiA0LjExOS00LjEyNiAxLjc5My0uMDQyIDIuNzI4IDEuNTk1IDQuMTExIDQuMzMgMS4yOTIgMi41NTMgMi43NTcgNS40NDUgNS44MjUgNS41NTZsLjE2OS4wMDNjMy4wNjQgMCA0LjUxOC0zLjA3NSA1LjgwNS01Ljc5NCAxLjEzOS0yLjQxIDIuMjE3LTQuNjggNC4wNjctNC43NzN2LTJ6IiBmaWxsPSIjMDAwIi8+PHBhdGggZmlsbD0iIzNFQ0M1RiIgZD0iTTgzIDE4M2g0MHYtNDBIODN6Ii8+PHBhdGggZD0iTTE0MyAxNThjLS4yMTkgMC0uNDI4LjAzNy0uNjM5LjA2NC0uMDM4LS4xNS0uMDc0LS4zMDEtLjExNi0uNDUxQTUgNSAwIDAgMCAxNDAuMzIgMTQ4YTQuOTYgNC45NiAwIDAgMC0zLjAxNiAxLjAzNiAyNi41MzEgMjYuNTMxIDAgMCAwLS4zMzUtLjMzNiA0Ljk1NSA0Ljk1NSAwIDAgMCAxLjAxMS0yLjk4NyA1IDUgMCAwIDAtOS41OTktMS45NTljLS4xNDgtLjA0Mi0uMjk3LS4wNzctLjQ0NS0uMTE1LjAyNy0uMjExLjA2NC0uNDIuMDY0LS42MzlhNSA1IDAgMCAwLTUtNSA1IDUgMCAwIDAtNSA1YzAgLjIxOS4wMzcuNDI4LjA2NC42MzktLjE0OC4wMzgtLjI5Ny4wNzMtLjQ0NS4xMTVhNC45OTggNC45OTggMCAwIDAtOS41OTkgMS45NTljMCAxLjEyNS4zODQgMi4xNTEgMS4wMTEgMi45ODctMy43MTcgMy42MzItNi4wMzEgOC42OTMtNi4wMzEgMTQuMyAwIDExLjA0NiA4Ljk1NCAyMCAyMCAyMCA5LjMzOSAwIDE3LjE2LTYuNDEgMTkuMzYxLTE1LjA2NC4yMTEuMDI3LjQyLjA2NC42MzkuMDY0YTUgNSAwIDAgMCA1LTUgNSA1IDAgMCAwLTUtNSIgZmlsbD0iIzQ0RDg2MCIvPjxwYXRoIGZpbGw9IiMzRUNDNUYiIGQ9Ik04MyAxMjNoNDB2LTIwSDgzeiIvPjxwYXRoIGQ9Ik0xMzMgMTE1LjVhMi41IDIuNSAwIDEgMCAwLTVjLS4xMDkgMC0uMjE0LjAxOS0uMzE5LjAzMi0uMDItLjA3NS0uMDM3LS4xNS0uMDU4LS4yMjVhMi41MDEgMi41MDEgMCAwIDAtLjk2My00LjgwN2MtLjU2OSAwLTEuMDg4LjE5Ny0xLjUwOC41MThhNi42NTMgNi42NTMgMCAwIDAtLjE2OC0uMTY4Yy4zMTQtLjQxNy41MDYtLjkzMS41MDYtMS40OTRhMi41IDIuNSAwIDAgMC00LjgtLjk3OUE5Ljk4NyA5Ljk4NyAwIDAgMCAxMjMgMTAzYy01LjUyMiAwLTEwIDQuNDc4LTEwIDEwczQuNDc4IDEwIDEwIDEwYy45MzQgMCAxLjgzMy0uMTM4IDIuNjktLjM3N2EyLjUgMi41IDAgMCAwIDQuOC0uOTc5YzAtLjU2My0uMTkyLTEuMDc3LS41MDYtMS40OTQuMDU3LS4wNTUuMTEzLS4xMTEuMTY4LS4xNjguNDIuMzIxLjkzOS41MTggMS41MDguNTE4YTIuNSAyLjUgMCAwIDAgLjk2My00LjgwN2MuMDIxLS4wNzQuMDM4LS4xNS4wNTgtLjIyNS4xMDUuMDEzLjIxLjAzMi4zMTkuMDMyIiBmaWxsPSIjNDREODYwIi8+PHBhdGggZD0iTTE0MyA0MS43NWMtLjE2IDAtLjMzLS4wMi0uNDktLjA1YTIuNTIgMi41MiAwIDAgMS0uNDctLjE0Yy0uMTUtLjA2LS4yOS0uMTQtLjQzMS0uMjMtLjEzLS4wOS0uMjU5LS4yLS4zOC0uMzEtLjEwOS0uMTItLjIxOS0uMjQtLjMwOS0uMzhzLS4xNy0uMjgtLjIzMS0uNDNhMi42MTkgMi42MTkgMCAwIDEtLjE4OS0uOTZjMC0uMTYuMDItLjMzLjA1LS40OS4wMy0uMTYuMDgtLjMxLjEzOS0uNDcuMDYxLS4xNS4xNDEtLjI5LjIzMS0uNDMuMDktLjEzLjItLjI2LjMwOS0uMzguMTIxLS4xMS4yNS0uMjIuMzgtLjMxLjE0MS0uMDkuMjgxLS4xNy40MzEtLjIzLjE0OS0uMDYuMzEtLjExLjQ3LS4xNC4zMi0uMDcuNjUtLjA3Ljk4IDAgLjE1OS4wMy4zMi4wOC40Ny4xNC4xNDkuMDYuMjkuMTQuNDMuMjMuMTMuMDkuMjU5LjIuMzguMzEuMTEuMTIuMjIuMjUuMzEuMzguMDkuMTQuMTcuMjguMjMuNDMuMDYuMTYuMTEuMzEuMTQuNDcuMDI5LjE2LjA1LjMzLjA1LjQ5IDAgLjY2LS4yNzEgMS4zMS0uNzMgMS43Ny0uMTIxLjExLS4yNS4yMi0uMzguMzEtLjE0LjA5LS4yODEuMTctLjQzLjIzYTIuNTY1IDIuNTY1IDAgMCAxLS45Ni4xOW0yMC0xLjI1Yy0uNjYgMC0xLjMtLjI3LTEuNzcxLS43M2EzLjgwMiAzLjgwMiAwIDAgMS0uMzA5LS4zOGMtLjA5LS4xNC0uMTctLjI4LS4yMzEtLjQzYTIuNjE5IDIuNjE5IDAgMCAxLS4xODktLjk2YzAtLjY2LjI3LTEuMy43MjktMS43Ny4xMjEtLjExLjI1LS4yMi4zOC0uMzEuMTQxLS4wOS4yODEtLjE3LjQzMS0uMjMuMTQ5LS4wNi4zMS0uMTEuNDctLjE0LjMyLS4wNy42Ni0uMDcuOTggMCAuMTU5LjAzLjMyLjA4LjQ3LjE0LjE0OS4wNi4yOS4xNC40My4yMy4xMy4wOS4yNTkuMi4zOC4zMS40NTkuNDcuNzMgMS4xMS43MyAxLjc3IDAgLjE2LS4wMjEuMzMtLjA1LjQ5LS4wMy4xNi0uMDguMzItLjE0LjQ3LS4wNy4xNS0uMTQuMjktLjIzLjQzLS4wOS4xMy0uMi4yNi0uMzEuMzgtLjEyMS4xMS0uMjUuMjItLjM4LjMxLS4xNC4wOS0uMjgxLjE3LS40My4yM2EyLjU2NSAyLjU2NSAwIDAgMS0uOTYuMTkiIGZpbGw9IiMwMDAiLz48L2c+PC9zdmc+" width="200" height="200" class="img_ev3q">
<img decoding="async" loading="lazy" alt="Docusaurus Keytar" src="https://vvsxmja.github.io/assets/images/docusaurus_keytar-0cb85c4468d358b2028c7ab2a38aa3ff.svg" width="200" height="200" class="img_ev3q">
<img decoding="async" loading="lazy" alt="Docusaurus Speed" src="https://vvsxmja.github.io/assets/images/docusaurus_speed-3cf95ee960d17fd7f1d2714e0d7525b2.svg" width="250" height="200" class="img_ev3q"></p>
<p>简单上手了一下，使用体验非常好：文档很丰富，甚至<a href="https://docusaurus.io/docs/markdown-features#front-matter" target="_blank" rel="noopener noreferrer">连 Frontmatter 都有文档</a>。</p>
<p>大悦，遂撰文记之。于是就有了你现在正在看的这篇文章以及这个网站。</p>]]></content>
        <author>
            <name>Yikai Cai</name>
            <uri>https://github.com/VVsxmja</uri>
        </author>
    </entry>
</feed>