<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
  xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>YFun&#39;s Blog</title>
    <link>https://blog.yfun.top/</link>
    
    <image>
      <url>https://blog.yfun.top/icon.png</url>
      <title>YFun&#39;s Blog</title>
      <link>https://blog.yfun.top/</link>
    </image>
    
    <atom:link href="https://blog.yfun.top/rss.xml" rel="self" type="application/rss+xml"/>
    
    <description>YFun&#39;s Blog, YFun 的学习、编程博客。探寻一切有趣的事物！</description>
    <pubDate>Tue, 23 Jan 2024 00:12:19 GMT</pubDate>
    <generator>http://hexo.io/</generator>
    
    <item>
      <title>YFun&#39;s Blog 将迁移至 CKY.IM</title>
      <link>https://blog.yfun.top/posts/move-to-cky-im/</link>
      <guid>https://blog.yfun.top/posts/move-to-cky-im/</guid>
      <pubDate>Sun, 21 Jan 2024 16:00:00 GMT</pubDate>
      
      <description>经过一年对 CKY.IM 的完善优化，我将完成对 YFun&#39;s Blog 的完全迁移。在博客三周年之际，开始用 CKY.IM 书写新的篇章。</description>
      
      
      
      <content:encoded><![CDATA[<p>上一次更新这个博客还是在去年的 1 月 19 日，转眼间一年已经过去。</p><p>其实，去年的此时，我已经开始开发 <a href="https://cky.im/zh">CKY.IM</a> 这个博客，新的博客不再使用 Hexo 作为内容生成器，而是转而使用 Nuxt。选择 Nuxt 的原因不仅是因为部署方便，也是因为开发简单，功能强大。</p><p>我写了两篇文章：*<a href="https://cky.im/zh/d/the-design-of-the-new-blog">重新开始：新博客的设计</a>* 和 <em><a href="https://cky.im/zh/d/optimize-the-design-of-the-blog">继续：对博客设计的再优化</a></em> 说明我基本重构和迁移的思路。</p><p>此外，原内容依然可用，你可以点击导航栏中的 <a href="https://cky.im/zh/archived">归档</a> 来访问他们。</p><p>未来，域名 <code>yfun.top</code> 将在 2024 年 11 月底到期，我也将尽快将所有 <code>yfun.top</code> 服务和端点迁移至新的域名。</p><p><a href="https://cky.im/zh">感谢过去三年的陪伴，现在我们一起书写新的篇章：）</a></p>]]></content:encoded>
      
      
      <category domain="https://blog.yfun.top/categories/%E5%8D%9A%E5%AE%A2/">博客</category>
      
      
      
      <comments>https://blog.yfun.top/posts/move-to-cky-im/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>JSEW —— 类似于 jsDelivr 的静态资源加速</title>
      <link>https://blog.yfun.top/posts/2023/jsew/</link>
      <guid>https://blog.yfun.top/posts/2023/jsew/</guid>
      <pubDate>Sun, 29 Jan 2023 07:30:00 GMT</pubDate>
      
      <description>网络上大部分公益项目都为 jsDelivr 提供镜像加速，此时用户的资源文件必须从源服务器经 jsDelivr 再绕过公益服务才能回到用户手上。本身 jsDelivr 也回源 GitHub 和 NPM，那么为什么不从源拉起？</description>
      
      
      
      <content:encoded><![CDATA[<p>现在网络上大部分公益项目都为 jsDelivr 提供镜像加速，此时用户的资源文件必须从源服务器经 jsDelivr 再绕过公益服务才能回到用户手上。本身 jsDelivr 也回源 GitHub 和 NPM，那么为什么不从源拉起？</p><p>那好，那就从源拉起。</p><h2 id="域名"><a href="#域名" class="headerlink" title="域名"></a>域名</h2><p>目前，JSEW 的域名已经开启中国大陆境内和境外双 CDN 加速，为用户提供快速的服务。</p><p>使用 <code>s.cky.qystu.cc</code> 替换 <code>fastly.jsdelivr.net</code>, <code>cdn.jsdelivr.net</code> 作为加速域名，遵循中国大陆相关地区的法律法规。</p><h2 id="端点"><a href="#端点" class="headerlink" title="端点"></a>端点</h2><blockquote><p>目前仅支持使用 <code>/gh</code> 和 <code>npm</code>，并且自动开启 HTTPS 访问与 Gzip 压缩。</p></blockquote><h3 id="GitHub"><a href="#GitHub" class="headerlink" title="GitHub"></a>GitHub</h3><p>GitHub 文件来源于 <code>raw.githubusercontent.com</code>，将自动转换路径，API 如下：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">/gh/[USER_NAME]/[REPO_NAME]@[BRANCH_NAME]/[FILE]</span><br></pre></td></tr></table></figure><p>请求链接：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">https://s.cky.qystu.cc/gh/detalkjs/client@master/dist/detalk.js</span><br></pre></td></tr></table></figure><p>需要注意的是，部分 CDN 的缓存时间会比较长，如果是仓库 <code>master</code> 等变动较快的分支不建议使用。如果你需要实时获取最新的 Commit，那么请在请求 URL 后追加 <code>!latest</code>，这样 JSEW 会自动 307 跳转至最新的 Commit Tag.</p><p>同时，如果没有指定分支名，则会自动默认为 <code>master</code>。</p><h3 id="NPM"><a href="#NPM" class="headerlink" title="NPM"></a>NPM</h3><p>NPM 文件来源于 <code>www.npmjs.com</code>，将自动转换路径并获取文件 Hex，API 如下：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">/npm/[PACKAGE_NAME]@[VERSION]/[FILE]</span><br></pre></td></tr></table></figure><p>请求链接例如：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">https://s.cky.qystu.cc/npm/@detalk/static@1.3.2/dist/detalk.js</span><br></pre></td></tr></table></figure><p>如果当版本号为 <code>@latest</code> 时，为了避免缓存问题，将会自动 307 跳转至最新的版本号。</p><p>如果版本号为 <code>@1</code> 等只包含 <code>major</code> 或 <code>minor</code> 的形式时，将会自动 307 跳转至最新的以指定版本号为限制条件的完整版本号。</p><p>例如：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">https://s.cky.qystu.cc/npm/@detalk/static@1/dist/detalk.js</span><br><span class="line">Redirect to =&gt;</span><br><span class="line">https://s.cky.qystu.cc/npm/@detalk/static@1.3.2/dist/detalk.js</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">https://s.cky.qystu.cc/npm/@detalk/static@1.2/dist/detalk.js</span><br><span class="line">Redirect to =&gt;</span><br><span class="line">https://s.cky.qystu.cc/npm/@detalk/static@1.2.1/dist/detalk.js</span><br></pre></td></tr></table></figure><p>为了避免潜在问题，<strong>即使不指定版本号，也必须指定 <code>@latest</code>，否则无法访问。</strong></p><h2 id="自动压缩"><a href="#自动压缩" class="headerlink" title="自动压缩"></a>自动压缩</h2><p>除 Gzip 压缩外，目前 CKY#JSEW 还兼容部分文件的自动压缩功能。</p><p>例如，请求 <code>main.min.js</code> 文件，而目录下无对应文件，只有 <code>main.js</code>，那么则会自动获取 <code>main.js</code> 并进行压缩返回。</p><p>此功能目前适用于 CSS 与 JavaScript，只有在请求文件为 <code>.min.js</code> 或 <code>.min.css</code> 且对应文件不存在时可用。</p><p>感谢您对公益服务的支持！</p>]]></content:encoded>
      
      
      <category domain="https://blog.yfun.top/categories/%E6%8A%98%E8%85%BE/">折腾</category>
      
      
      <category domain="https://blog.yfun.top/tags/%E5%85%AC%E7%9B%8A/">公益</category>
      
      <category domain="https://blog.yfun.top/tags/%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90/">静态资源</category>
      
      <category domain="https://blog.yfun.top/tags/jsDelivr/">jsDelivr</category>
      
      
      <comments>https://blog.yfun.top/posts/2023/jsew/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Detalk.js —— 具有高自定义性、简洁、轻量的开源评论系统</title>
      <link>https://blog.yfun.top/posts/2023/detalkjs/</link>
      <guid>https://blog.yfun.top/posts/2023/detalkjs/</guid>
      <pubDate>Mon, 09 Jan 2023 16:00:00 GMT</pubDate>
      
      <description>我一直在寻找开源、可以免费部署的评论系统，最好还是在我的技术栈内，所以博客尝试过 Valine，Waline 和 Twikoo。但这些评论系统都没有让我完全满意，在 2022 年末，我开了一个新坑 —— Detalk.js</description>
      
      
      
      <content:encoded><![CDATA[<h2 id="为什么开发-Detalk-js"><a href="#为什么开发-Detalk-js" class="headerlink" title="为什么开发 Detalk.js?"></a>为什么开发 Detalk.js?</h2><p>我一直在寻找开源、可以免费部署的评论系统，在 Valine 发现隐私问题和 XSS 漏洞后，我就放弃了 Valine 和「无后端」解决方案。</p><p>Waline 是「有后端的 Valine」，保留了 Valine 的很多特性，但还是没有让我满意。</p><p>后来，<a href="https://blog.yfun.top/posts/1987652759/#%E8%AF%84%E8%AE%BA">我转到了 Twikoo</a>，这也是我使用最久的评论系统。它有非常丰富的特性，但我最后还是弃用了它。</p><p>Detalk.js 的部署平台从名字就可以看出，基于 Deta Bases 和 Deta Micros. 这些平台对个人开发者免费使用，非常的友好。</p><h2 id="服务端开发"><a href="#服务端开发" class="headerlink" title="服务端开发"></a>服务端开发</h2><p>Detalk.js 一开始就选择优先开发服务端，而前端则可以随意构建，有完整的服务端文档。</p><p>所以，我将主代码放到了 <a href="https://github.com/detalkjs/server">detalkjs/server</a> 中，得益于 Deta 平台，现在可以一键部署，稍等片刻就可以直接使用。</p><p>服务端我直接从官方的 Demo 开始写，使用官方推荐的 Express 框架，代码语言选择 JavaScript.</p><p>在开发过程中，我发现 Detalk.js 的响应时长至少在 1s+，我推测是与 Deta 平台的运行方案和冷启动有关，但对于评论系统来说，这无伤大雅。</p><h3 id="数据库"><a href="#数据库" class="headerlink" title="数据库"></a>数据库</h3><p>Deta 平台上，最方便的数据库就是 Deta Bases. 这又是一款 Key-Value 数据库，有着简单易于理解的 SDK 文档。</p><p>导入方法也很简单：</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> &#123; <span class="title class_">Deta</span> &#125; = <span class="built_in">require</span>(<span class="string">&#x27;deta&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> deta = <span class="title class_">Deta</span>();</span><br><span class="line"><span class="comment">// 为了方便用户部署多个 Detalk，用户可自定义 Base Name.</span></span><br><span class="line"><span class="keyword">const</span> db = deta.<span class="title class_">Base</span>(process.<span class="property">env</span>.<span class="property">BASE_NAME</span> || <span class="string">&#x27;detalk&#x27;</span>);</span><br></pre></td></tr></table></figure><p>使用方法整体类似于 <a href="https://github.com/yfun-lab/gh-worker-kv">yfun-lab/gh-worker-kv</a>，对于部分代码的上手也是很简单的。</p><p>前往 Web 面板截图，可以看到，Detalk 所创建的数据库基本格式如下：</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.19/rawimg/ck31673269985711.png"></p><p>以 <code>CMT_</code> 开头的存放评论，<code>FUNCTION_</code> 开头的存放事件函数，还有一些关于站点的配置。</p><h3 id="API"><a href="#API" class="headerlink" title="API"></a>API</h3><p>目前，所有后端的 API 都在 <a href="https://detalk.js.org/server/api.html">API | Detalk.js</a> 可以查看。</p><p>部分 API 需要鉴权登录操作，部分为公共 API，只需要后端地址即可调用。</p><h2 id="前端开发"><a href="#前端开发" class="headerlink" title="前端开发"></a>前端开发</h2><p>我对前端 <a href="https://github.com/detalkjs/client">detalkjs/client</a> 的简介是：</p><blockquote><p>⚡ The fastest way to add Detalk (Based on Deta) to your website. | 将 Detalk (Based on Deta) 加入你的网站。</p></blockquote><p>（这再次说明官方提供的前端只是一种方案，完全可以自己开发</p><p>也正因如此，前端的 NPM 包名是 <code>@detalk/static</code> （逃</p><h3 id="打包"><a href="#打包" class="headerlink" title="打包"></a>打包</h3><p>前端打包是 Webpack 5 方案，我多加了一个配置文件，可以打包出 JS, CSS 分离的版本。这样或许可以方便部分用户的引入需要。</p><p>但是从各方面来看，还是建议引入单 JS 的版本，大小约为 43kb，Gzip 压缩后约 12kb.</p><h3 id="前端配置？"><a href="#前端配置？" class="headerlink" title="前端配置？"></a>前端配置？</h3><p>设计前端时有两种读取配置文件的方案，一种是类似于 Twikoo，从服务端读取，另一种是直接在 <code>detalk.init</code> 时配置。</p><p>明显，前者需要多发送 1 - 2 次 HTTP 请求，加载速度也会变慢，所以我选择了后者。</p><h2 id="控制面板"><a href="#控制面板" class="headerlink" title="控制面板"></a>控制面板</h2><p>控制面板也可以使用完全 API 自构建，但是，我们提供了「依托答辩」的官方后端：<a href="https://detalk-dash.netlify.app/">https://detalk-dash.netlify.app/</a> </p><p><del>除可维护角度以外，都挺好的（</del></p><h2 id="一些问题"><a href="#一些问题" class="headerlink" title="一些问题"></a>一些问题</h2><h3 id="Markdown-解析"><a href="#Markdown-解析" class="headerlink" title="Markdown 解析"></a>Markdown 解析</h3><p>如果你没有额外的配置，那么点击「预览」的时候。你肯定会发现需要等待一段时间。</p><p>我之前想把 Marked.js（号称轻量）的 Markdown 解析器加入 Detalk.js，结果发现包体积直接翻了一倍，于是放弃。</p><p>如果你需要更换解析器，不用担心，<a href="https://detalk.js.org/client/advanced.html#%E8%87%AA%E5%AE%9A%E4%B9%89-markdown-%E9%A2%84%E8%A7%88">这里</a>给出了方法。</p><h3 id="URL-载入配置"><a href="#URL-载入配置" class="headerlink" title="URL 载入配置"></a>URL 载入配置</h3><p>前文说到，Detalk 并没有采取后端配置的方法，但是，我们还是支持了从 URL 载入配置。</p><p>关于这一特性，可<a href="https://detalk.js.org/client/advanced.html#beta-%E4%BB%8E-url-%E8%BD%BD%E5%85%A5%E9%85%8D%E7%BD%AE">见此处</a>。</p><h3 id="多语言的实现"><a href="#多语言的实现" class="headerlink" title="多语言的实现"></a>多语言的实现</h3><p>Detalk.js 并没有默认提供多语言功能，这主要是因为不同站点的使用者受众不同，于是我决定这项功能可以由用户实现。</p><p>这依赖 <code>window.DETALK_I18N</code> 参数，如果没有此项，则会在 <code>detalk.init</code> 时由程序默认提供简体中文项。</p><p>实现代码可以见如下：</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">if</span> (navigator.<span class="property">language</span> == <span class="string">&#x27;zh-CN&#x27;</span>) &#123;</span><br><span class="line">  <span class="variable language_">window</span>.<span class="property">DETALK_I18N</span> = &#123;</span><br><span class="line">      <span class="attr">loadMore</span>: <span class="string">&#x27;加载更多&#x27;</span>,</span><br><span class="line">      <span class="attr">notAllowedInput</span>: <span class="string">&#x27;输入内容不符合要求！&#x27;</span>,</span><br><span class="line">      <span class="attr">send</span>: <span class="string">&#x27;发送&#x27;</span>,</span><br><span class="line">      <span class="attr">preview</span>: <span class="string">&#x27;预览&#x27;</span>,</span><br><span class="line">      <span class="attr">reply</span>: <span class="string">&#x27;回复&#x27;</span>,</span><br><span class="line">      <span class="attr">replyTo</span>: <span class="string">&#x27;回复&#x27;</span>,</span><br><span class="line">      <span class="attr">cancel</span>: <span class="string">&#x27;取消&#x27;</span>,</span><br><span class="line">      <span class="attr">loadingLoginFrame</span>: <span class="string">&#x27;登录窗口加载中...&#x27;</span>,</span><br><span class="line">      <span class="attr">gotoLoginFrame</span>: <span class="string">&#x27;请在登录窗口中继续&#x27;</span>,</span><br><span class="line">      <span class="attr">waitingInfo</span>: <span class="string">&#x27;登录成功，正在获取用户信息...&#x27;</span>,</span><br><span class="line">      <span class="attr">loginSuccess</span>: <span class="string">&#x27;登录成功&#x27;</span>,</span><br><span class="line">      <span class="attr">failedLoadingInfo</span>: <span class="string">&#x27;获取用户信息失败&#x27;</span>,</span><br><span class="line">      <span class="attr">deleteConfirm</span>: <span class="string">&#x27;即将删除 ID:[#ID] 评论，是否继续？&#x27;</span>,</span><br><span class="line">      <span class="attr">total</span>: <span class="string">&#x27;共 [#TOTAL] 条评论&#x27;</span>,</span><br><span class="line">      <span class="attr">noComment</span>: <span class="string">&#x27;暂无评论&#x27;</span>,</span><br><span class="line">      <span class="attr">up</span>: <span class="string">&#x27;正序&#x27;</span>,</span><br><span class="line">      <span class="attr">down</span>: <span class="string">&#x27;倒序&#x27;</span>,</span><br><span class="line">      <span class="attr">delete</span>: <span class="string">&#x27;删除&#x27;</span>,</span><br><span class="line">      <span class="attr">top</span>: <span class="string">&#x27;置顶&#x27;</span>,</span><br><span class="line">      <span class="attr">login</span>: <span class="string">&#x27;登录&#x27;</span>,</span><br><span class="line">      <span class="attr">required</span>: <span class="string">&#x27;必填&#x27;</span>,</span><br><span class="line">      <span class="attr">optional</span>: <span class="string">&#x27;选填&#x27;</span>,</span><br><span class="line">      <span class="attr">nickname</span>: <span class="string">&#x27;昵称&#x27;</span>,</span><br><span class="line">      <span class="attr">email</span>: <span class="string">&#x27;邮箱&#x27;</span>,</span><br><span class="line">      <span class="attr">link</span>: <span class="string">&#x27;网址&#x27;</span>,</span><br><span class="line">      <span class="attr">day</span>: &#123;</span><br><span class="line">      <span class="attr">justNow</span>: <span class="string">&#x27;刚刚&#x27;</span>,</span><br><span class="line">      <span class="attr">minute</span>: <span class="string">&#x27;[#TIME] 分钟前&#x27;</span>,</span><br><span class="line">      <span class="attr">hour</span>: <span class="string">&#x27;[#TIME] 小时前&#x27;</span>,</span><br><span class="line">      <span class="attr">day</span>: <span class="string">&#x27;[#TIME] 天前&#x27;</span>,</span><br><span class="line">  &#125;</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (navigator.<span class="property">language</span> == <span class="string">&#x27;zh-TW&#x27;</span>) &#123;</span><br><span class="line">    <span class="variable language_">window</span>.<span class="property">DETALK_I18N</span> = &#123;</span><br><span class="line">      <span class="attr">loadMore</span>: <span class="string">&#x27;加載更多&#x27;</span>,</span><br><span class="line">      <span class="attr">notAllowedInput</span>: <span class="string">&#x27;輸入內容不符合要求！&#x27;</span>,</span><br><span class="line">      <span class="attr">send</span>: <span class="string">&#x27;發送&#x27;</span>,</span><br><span class="line">      <span class="attr">preview</span>: <span class="string">&#x27;預覽&#x27;</span>,</span><br><span class="line">      <span class="attr">reply</span>: <span class="string">&#x27;回复&#x27;</span>,</span><br><span class="line">      <span class="attr">replyTo</span>: <span class="string">&#x27;回复&#x27;</span>,</span><br><span class="line">      <span class="attr">cancel</span>: <span class="string">&#x27;取消&#x27;</span>,</span><br><span class="line">      <span class="attr">loadingLoginFrame</span>: <span class="string">&#x27;登錄窗口加載中...&#x27;</span>,</span><br><span class="line">      <span class="attr">gotoLoginFrame</span>: <span class="string">&#x27;請在登錄窗口中繼續&#x27;</span>,</span><br><span class="line">      <span class="attr">waitingInfo</span>: <span class="string">&#x27;登錄成功，正在獲取用戶信息...&#x27;</span>,</span><br><span class="line">      <span class="attr">loginSuccess</span>: <span class="string">&#x27;登錄成功&#x27;</span>,</span><br><span class="line">      <span class="attr">failedLoadingInfo</span>: <span class="string">&#x27;獲取用戶信息失敗&#x27;</span>,</span><br><span class="line">      <span class="attr">deleteConfirm</span>: <span class="string">&#x27;即將刪除 ID:[#ID] 評論，是否繼續？&#x27;</span>,</span><br><span class="line">      <span class="attr">total</span>: <span class="string">&#x27;共 [#TOTAL] 條評論&#x27;</span>,</span><br><span class="line">      <span class="attr">noComment</span>: <span class="string">&#x27;暫無評論&#x27;</span>,</span><br><span class="line">      <span class="attr">up</span>: <span class="string">&#x27;正序&#x27;</span>,</span><br><span class="line">      <span class="attr">down</span>: <span class="string">&#x27;倒序&#x27;</span>,</span><br><span class="line">      <span class="attr">delete</span>: <span class="string">&#x27;刪除&#x27;</span>,</span><br><span class="line">      <span class="attr">top</span>: <span class="string">&#x27;置頂&#x27;</span>,</span><br><span class="line">      <span class="attr">login</span>: <span class="string">&#x27;登錄&#x27;</span>,</span><br><span class="line">      <span class="attr">required</span>: <span class="string">&#x27;必填&#x27;</span>,</span><br><span class="line">      <span class="attr">optional</span>: <span class="string">&#x27;選填&#x27;</span>,</span><br><span class="line">      <span class="attr">nickname</span>: <span class="string">&#x27;暱稱&#x27;</span>,</span><br><span class="line">      <span class="attr">email</span>: <span class="string">&#x27;郵箱&#x27;</span>,</span><br><span class="line">      <span class="attr">link</span>: <span class="string">&#x27;網址&#x27;</span>,</span><br><span class="line">      <span class="attr">day</span>: &#123;</span><br><span class="line">      <span class="attr">justNow</span>: <span class="string">&#x27;剛剛&#x27;</span>,</span><br><span class="line">      <span class="attr">minute</span>: <span class="string">&#x27;[#TIME] 分鐘前&#x27;</span>,</span><br><span class="line">      <span class="attr">hour</span>: <span class="string">&#x27;[#TIME] 小時前&#x27;</span>,</span><br><span class="line">      <span class="attr">day</span>: <span class="string">&#x27;[#TIME] 天前&#x27;</span>,</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p>Detalk.js 未来会与 ESHexoN 融合，在 ESHexoN 中提供管理面板。</p><p>同时，YFun&#39;s Blog 也会在后续更新有关 Detalk.js 的更多文章。</p><p>目前，此博客已经更换至 Detalk.js 评论系统，欢迎测试与使用！</p>]]></content:encoded>
      
      
      <category domain="https://blog.yfun.top/categories/%E5%BC%80%E6%BA%90/">开源</category>
      
      
      <category domain="https://blog.yfun.top/tags/%E8%AF%84%E8%AE%BA/">评论</category>
      
      <category domain="https://blog.yfun.top/tags/%E5%BC%80%E6%BA%90/">开源</category>
      
      <category domain="https://blog.yfun.top/tags/Detalk/">Detalk</category>
      
      
      <comments>https://blog.yfun.top/posts/2023/detalkjs/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>2023: 新年快乐 &amp; 博客总结</title>
      <link>https://blog.yfun.top/posts/2023/</link>
      <guid>https://blog.yfun.top/posts/2023/</guid>
      <pubDate>Thu, 29 Dec 2022 16:00:00 GMT</pubDate>
      
      <description>是的，2022 年已经过去，新的一年全新开始。很高兴认识你，这里是 YFun&#39;s Blog.</description>
      
      
      
      <content:encoded><![CDATA[<p>这一年对于我来说过的非常快，同时，也是很不平凡的一年。同样，今年也是 Blog 的第三年了。</p><h2 id="友链"><a href="#友链" class="headerlink" title="友链"></a>友链</h2><p>在这一年中，YFun&#39;s Blog 友链数量（不包括本站站点）为 9 个，其中很多都是大佬 (\=!!</p><p>其中，<a href="https://dusays.com/">杜老师说</a> 最活跃力！</p><h2 id="文章"><a href="#文章" class="headerlink" title="文章"></a>文章</h2><p>在 2022 年中，YFun&#39;s Blog 发布 6 篇文章。</p><p>其中，在 1 月 14 日到 8 月 4 日之间<strong>整整咕了七个月</strong>（</p><p>今年水了两篇文章，其余的自认为质量还行。</p><h2 id="博客"><a href="#博客" class="headerlink" title="博客"></a>博客</h2><p>与去年相同，YFun&#39;s Blog 依然在使用自己开发的 <a href="https://github.com/oCoke/hexo-theme-miracle">Theme Miracle</a>.</p><p>今年 Miracle 已经迭代到 <code>v2.2.1</code> 版本，主要在修 Bug 和提升体验<del>（比如高斯模糊又加回来了</del></p><p>目前，我正在使用 ESHexoN 编辑，如果想了解更多信息，可以看这篇文章：<a href="https://blog.yfun.top/posts/2022/eshexon/">抛弃静态博客的缺点，用 ESHexoN 在线编辑！</a></p><p>此外，还有一个项目可能会在 2023 年初加入到 YFun&#39;s Blog 上，那就是 <a href="https://detalk.js.org/">Detalk.js</a>。我尝试用它实现自定义性更强的评论系统。</p><p>现在，Detalk 已具备基础功能，感兴趣的同学可以试用一下（（</p><h2 id="统计"><a href="#统计" class="headerlink" title="统计"></a>统计</h2><p>与去年一样，今年的数据是：<code>N/A</code>。</p><p>虽然 Cloudflare Web Analytics 在 2021 年 4 月份启用，但后来又将统计代码去除。所以关于本站的统计信息一直是个谜。</p><p>关于这一点，我有计划加入开源统计系统。</p><blockquote><p>我希望是一款<strong>不会侵犯用户隐私</strong>、<strong>不收集用户指纹信息</strong>的统计系统，目前考虑 <a href="https://umami.is/">umami</a>.</p></blockquote><h2 id="未来"><a href="#未来" class="headerlink" title="未来"></a>未来</h2><p>我希望有时间可以重构这个 Blog，计划使用 <code>Vue 3 + Nuxt.js + TypeScript</code>。当然，这只是个计划。</p><h2 id="更多"><a href="#更多" class="headerlink" title="更多"></a>更多</h2><p>这个世界变化太快，我们生活中的每一秒都是在见证历史。</p><p>2022 已经过去，欢迎 2023 的到来，希望新的一年中，一切会变得更好。</p>]]></content:encoded>
      
      
      <category domain="https://blog.yfun.top/categories/%E5%8D%9A%E5%AE%A2/">博客</category>
      
      
      <category domain="https://blog.yfun.top/tags/%E6%96%B0%E5%B9%B4/">新年</category>
      
      <category domain="https://blog.yfun.top/tags/2023/">2023</category>
      
      
      <comments>https://blog.yfun.top/posts/2023/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>什么年代还在用传统 Pjax? —— 自定义 Pjax 提升页面加载速度</title>
      <link>https://blog.yfun.top/posts/2022/pjax/</link>
      <guid>https://blog.yfun.top/posts/2022/pjax/</guid>
      <pubDate>Thu, 15 Dec 2022 04:30:00 GMT</pubDate>
      
      <description>Hexo 属于静态博客，很多同学给自己的博客加上 Pjax 是为了音乐播放器等不中断。经过一番分析后我觉得，这不仅引入了一个巨大的文件，反而优化效果不明显。近期，随着一个脑海中的思路，我重新思考这个问题。</description>
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>Hexo 属于静态博客，很多同学给自己的博客加上 Pjax 是为了音乐播放器等功能不中断。</p><p>之前我也想过对博客和主题加入 Pjax 支持，但经过一番分析后觉得，这不仅引入了一个巨大的 <code>jquery.pjax.js</code>，反而优化效果不明显。</p><h2 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h2><p>其实，Pjax 的原理并不复杂。或许说，README 一开始就告诉你了：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">pjax = pushState + ajax</span><br></pre></td></tr></table></figure><p>其中 <code>ajax</code> 用于页面的新内容，<code>pushState</code> 改变浏览器状态。</p><p>很简单吧。</p><p>事实上，<code>pjax</code> 并不应该应用于整个页面当中。而应该只是局部更改。</p><p>这样，Blog 当中的导航栏、样式文件等就不需要重复下载与预览。</p><h2 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h2><p>以我使用 Miracle 为主题的博客为例，进入首页，按 <code>F12</code> 查看页面 Elements.</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.18/rawimg/2022-12-15_12-32-37.png"></p><p>可以发现，页面主要更改的也就是 <code>#page-main</code> 部分，只需要实现动态刷新这部分的内容就可以了。</p><p>那怎么实现呢？</p><h2 id="最小化的数据接口"><a href="#最小化的数据接口" class="headerlink" title="最小化的数据接口"></a>最小化的数据接口</h2><p>现在生成的页面当中，有 <code>&lt;head&gt;</code> 部分声明大量样式与元信息，<code>&lt;body&gt;</code> 之下重复的页脚、导航栏，还有每个页面下方都有的一些 <code>&lt;script&gt;</code>。</p><p>很明显，我们不需要这些。我们只要 <code>#page-main</code> 中的主要内容。</p><p>最重要的是，Hexo 是静态博客，这一点只能在生成文件时进行。</p><h3 id="载入-HTML"><a href="#载入-HTML" class="headerlink" title="载入 HTML"></a>载入 HTML</h3><p>我是用 Cheerio 模块帮我完成这一工作。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> cheerio = <span class="built_in">require</span>(<span class="string">&#x27;cheerio&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">&quot;fs&quot;</span>);</span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">&quot;path&quot;</span>);</span><br><span class="line"><span class="keyword">const</span> filePath = path.<span class="title function_">resolve</span>(<span class="string">&#x27;public/&#x27;</span>);</span><br></pre></td></tr></table></figure><p>定义一个 <code>parse function</code>，打开文件并解析相关信息，顺便把不是 HTML 的文件排除掉。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">parse</span> = (<span class="params">filename, fullpath</span>) =&gt; &#123;</span><br><span class="line">    <span class="comment">// 不是 .html 我不要</span></span><br><span class="line">    <span class="keyword">if</span> (!filename.<span class="title function_">endsWith</span>(<span class="string">&quot;.html&quot;</span>)) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后通过 Cheerio 解析 HTML:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">&#123;...</span><br><span class="line">  <span class="comment">// 组合新文件名</span></span><br><span class="line">  <span class="keyword">let</span> filepath = fullpath+<span class="string">&quot;.page.json&quot;</span>;</span><br><span class="line">  <span class="comment">// 读取文件内容</span></span><br><span class="line">  <span class="keyword">let</span> pageContent = fs.<span class="title function_">readFileSync</span>(fullpath).<span class="title function_">toString</span>();</span><br><span class="line">  <span class="comment">// 解析页面内容</span></span><br><span class="line">  <span class="keyword">let</span> $pg = cheerio.<span class="title function_">load</span>(pageContent);</span><br><span class="line">  <span class="keyword">let</span> rtData = &#123;&#125;;</span><br><span class="line">...&#125;</span><br></pre></td></tr></table></figure><p>然后获取页面的标题和 <code>#page-main</code> 下的 HTML.</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">&#123;...</span><br><span class="line"><span class="comment">// 页面标题</span></span><br><span class="line">    rtData.<span class="property">title</span> = $pg(<span class="string">&quot;title&quot;</span>).<span class="title function_">text</span>();</span><br><span class="line">    <span class="comment">// OR $pg(&quot;#page-main&quot;).html()</span></span><br><span class="line">    <span class="comment">// 我这么写是因为主题 #page-main 下还有 script 无法执行</span></span><br><span class="line">    rtData.<span class="property">page</span> = <span class="string">`</span></span><br><span class="line"><span class="string">    &lt;div class=&quot;mg-top&quot;&gt;</span></span><br><span class="line"><span class="string">        <span class="subst">$&#123;$pg(<span class="string">&quot;.mg-top&quot;</span>).html() || <span class="string">&quot;&quot;</span>&#125;</span></span></span><br><span class="line"><span class="string">    &lt;/div&gt;</span></span><br><span class="line"><span class="string">    &lt;footer class=&quot;text-center&quot;&gt;</span></span><br><span class="line"><span class="string">        <span class="subst">$&#123;$pg(<span class="string">&quot;footer&quot;</span>).html() || <span class="string">&quot;&quot;</span>&#125;</span></span></span><br><span class="line"><span class="string">    &lt;/footer&gt;</span></span><br><span class="line"><span class="string">    &lt;div class=&quot;p-btn&quot;&gt;</span></span><br><span class="line"><span class="string">        <span class="subst">$&#123;$pg(<span class="string">&quot;.p-btn&quot;</span>).html() || <span class="string">&quot;&quot;</span>&#125;</span></span></span><br><span class="line"><span class="string">    &lt;/div&gt;</span></span><br><span class="line"><span class="string">    `</span>;</span><br><span class="line">    rtData.<span class="property">path</span> = filename;</span><br><span class="line">...&#125;</span><br></pre></td></tr></table></figure><p>页面中还有一些 <code>script</code>，比如阅读进度、懒加载等。所以需要一个 <code>extraJS</code> 放置额外的 Script.</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">&#123;...</span><br><span class="line">    rtData.<span class="property">extraJS</span> = []</span><br><span class="line">    <span class="comment">// 只解析 #page-main 下的 script</span></span><br><span class="line">    <span class="keyword">let</span> $pageMain = cheerio.<span class="title function_">load</span>($pg(<span class="string">&quot;#page-main&quot;</span>).<span class="title function_">html</span>());</span><br><span class="line">    $pageMain(<span class="string">&#x27;script&#x27;</span>).<span class="title function_">map</span>(<span class="keyword">function</span>(<span class="params">i, el</span>) &#123;</span><br><span class="line">        <span class="comment">// 尝试往 extraJS 中 push 相关代码</span></span><br><span class="line">        <span class="keyword">try</span> &#123;rtData.<span class="property">extraJS</span>.<span class="title function_">push</span>($pageMain(<span class="variable language_">this</span>)[<span class="number">0</span>].<span class="property">children</span>[<span class="number">0</span>].<span class="property">data</span>);&#125; <span class="keyword">catch</span>(e) &#123;&#125;</span><br><span class="line">        $pageMain(<span class="variable language_">this</span>).<span class="title function_">remove</span>();</span><br><span class="line">    &#125;);</span><br><span class="line">...&#125;</span><br></pre></td></tr></table></figure><p>最后，将 JSON 写入文件中。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">&#123;...</span><br><span class="line">    fs.<span class="title function_">writeFileSync</span>(filepath, <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(rtData));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="文件递归"><a href="#文件递归" class="headerlink" title="文件递归"></a>文件递归</h3><p>我们还需要一个函数递归 <code>public</code> 目录下的所有文件，这个不用多说。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">fileDisplay</span>(<span class="params">filePath</span>) &#123;</span><br><span class="line">    <span class="comment">// 根据文件路径读取文件，返回文件列表</span></span><br><span class="line">    fs.<span class="title function_">readdir</span>(filePath, <span class="keyword">function</span>(<span class="params">err, files</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (err) &#123;</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">warn</span>(err, <span class="string">&quot;读取文件夹错误！&quot;</span>)</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 遍历读取到的文件列表</span></span><br><span class="line">            files.<span class="title function_">forEach</span>(<span class="keyword">function</span>(<span class="params">filename</span>) &#123;</span><br><span class="line">                <span class="comment">// 获取当前文件的绝对路径</span></span><br><span class="line">                <span class="keyword">var</span> filedir = path.<span class="title function_">join</span>(filePath, filename);</span><br><span class="line">                <span class="keyword">var</span> fullname = filedir.<span class="title function_">split</span>(<span class="string">&quot;public&quot;</span>)[<span class="number">1</span>];</span><br><span class="line">                fs.<span class="title function_">stat</span>(filedir, <span class="keyword">function</span>(<span class="params">eror, stats</span>) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (eror) &#123;</span><br><span class="line">                        <span class="variable language_">console</span>.<span class="title function_">warn</span>(<span class="string">&#x27;获取文件 Stats 失败!&#x27;</span>);</span><br><span class="line">                    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                        <span class="keyword">var</span> isFile = stats.<span class="title function_">isFile</span>(); <span class="comment">// 是文件</span></span><br><span class="line">                        <span class="keyword">var</span> isDir = stats.<span class="title function_">isDirectory</span>(); <span class="comment">// 是文件夹</span></span><br><span class="line">                        <span class="keyword">if</span> (isFile) &#123;</span><br><span class="line">                            <span class="title function_">parse</span>(fullname, filedir);</span><br><span class="line">                        &#125;</span><br><span class="line">                        <span class="keyword">if</span> (isDir) &#123;</span><br><span class="line">                            <span class="title function_">fileDisplay</span>(filedir); <span class="comment">// 递归，如果是文件夹，就继续遍历该文件夹下面的文件</span></span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;);</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br><span class="line"><span class="title function_">fileDisplay</span>(filePath);</span><br></pre></td></tr></table></figure><p>最后运行这个 Node.js 文件，就可以看到 <code>public/</code> 目录下多出很多 <code>***.page.json</code> 文件。</p><h3 id="基本结构"><a href="#基本结构" class="headerlink" title="基本结构"></a>基本结构</h3><p>这些文件内容也很简单，基本如下：</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="comment">// 页面的标题</span></span><br><span class="line">    <span class="attr">&quot;title&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Hello World&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="comment">// 内容</span></span><br><span class="line">    <span class="attr">&quot;page&quot;</span><span class="punctuation">:</span> <span class="string">&quot;...&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="comment">// 路径</span></span><br><span class="line">    <span class="attr">&quot;path&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/foo/bar&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="comment">// JS</span></span><br><span class="line">    <span class="attr">&quot;extraJS&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span>&#x27;alert(<span class="string">&quot;Hello World&quot;</span>);&#x27;<span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h2 id="前端-pjax-js"><a href="#前端-pjax-js" class="headerlink" title="前端 pjax.js"></a>前端 <code>pjax.js</code></h2><p>新建一个 <code>pjax.js</code>。</p><h3 id="替换链接"><a href="#替换链接" class="headerlink" title="替换链接"></a>替换链接</h3><p>我们需要先将页面当中所有本站链接转为 Pjax 的 Jump 函数。</p><p>判断条件是：有链接，不带 hash，且为本站链接</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 转换页面中的链接为 Pjax 链接</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">$pjax_convertAllLinks</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line"><span class="comment">// 所有的 a 标签</span></span><br><span class="line">    <span class="keyword">const</span> linkElements = <span class="variable language_">document</span>.<span class="title function_">querySelectorAll</span>(<span class="string">&quot;a&quot;</span>);</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i <span class="keyword">of</span> linkElements) &#123;</span><br><span class="line">        <span class="comment">// 有链接，不带 hash，且为本站链接</span></span><br><span class="line">        <span class="keyword">if</span> (i.<span class="property">href</span> &amp;&amp; !i.<span class="property">href</span>.<span class="title function_">includes</span>(<span class="string">&quot;/#&quot;</span>) &amp;&amp; (i.<span class="property">href</span>.<span class="title function_">startsWith</span>(<span class="string">&quot;/&quot;</span>) || i.<span class="property">href</span>.<span class="title function_">match</span>(<span class="keyword">new</span> <span class="title class_">RegExp</span>(<span class="variable language_">window</span>.<span class="property">location</span>.<span class="property">hostname</span>)))) &#123;</span><br><span class="line">            <span class="keyword">let</span> thisLink = <span class="keyword">new</span> <span class="title function_">URL</span>(i.<span class="property">href</span>).<span class="property">pathname</span>+<span class="keyword">new</span> <span class="title function_">URL</span>(i.<span class="property">href</span>).<span class="property">hash</span>;</span><br><span class="line">            i.<span class="property">href</span> = <span class="string">`javascript:$pjax_jump(&#x27;<span class="subst">$&#123;thisLink&#125;</span>&#x27;);`</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>另外，要转化页面链接为全路径。</p><p>这里参考了下 ChenYFan 的 Service Worker 函数，需要根据实际情况做出调整。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 转换路径为全路径</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">$pjax_fullpath</span> = (<span class="params">path</span>) =&gt; &#123;</span><br><span class="line">    path = path.<span class="title function_">split</span>(<span class="string">&#x27;?&#x27;</span>)[<span class="number">0</span>].<span class="title function_">split</span>(<span class="string">&#x27;#&#x27;</span>)[<span class="number">0</span>]</span><br><span class="line">    <span class="keyword">if</span> (path.<span class="title function_">match</span>(<span class="regexp">/\/$/</span>)) &#123;</span><br><span class="line">        path += <span class="string">&#x27;index.html&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (!path.<span class="title function_">match</span>(<span class="regexp">/\.[a-zA-Z]+$/</span>)) &#123;</span><br><span class="line">        path += <span class="string">&#x27;/index.html&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> path;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// $pjax_fullpath(&#x27;/&#x27;) =&gt; /index.html</span></span><br></pre></td></tr></table></figure><h3 id="跳转"><a href="#跳转" class="headerlink" title="跳转"></a>跳转</h3><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 跳转页面</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">$pjax_jump</span> = <span class="keyword">async</span> (<span class="params">path</span>) =&gt; &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 是 # 就别跳转了</span></span><br><span class="line">        <span class="keyword">if</span> (path.<span class="title function_">startsWith</span>(<span class="string">&quot;#&quot;</span>)) &#123;</span><br><span class="line">            <span class="variable language_">window</span>.<span class="property">hash</span> = path;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 加载动画</span></span><br><span class="line">        <span class="keyword">let</span> loading = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&#x27;div&#x27;</span>);</span><br><span class="line">        loading.<span class="property">innerHTML</span> = <span class="string">`&lt;div style=&quot;position: fixed;top:0;left:0;z-index:99999;display: block;width: 100%;height: 4px;overflow: hidden;background-color: rgba(63,81,181,.2);border-radius: 2px;&quot;&gt;&lt;div class=&quot;progress-indeterminate&quot; style=&quot;background-color: #3f51b5;&quot;&gt;&lt;/div&gt;&lt;style&gt;#page-main&#123;transition:0.2s;&#125;.progress-indeterminate::before&#123;position:absolute;top:0;bottom:0;left:0;background-color:inherit;-webkit-animation:mdui-progress-indeterminate 2s linear infinite;animation:mdui-progress-indeterminate 2s linear infinite;content:&#x27; &#x27;;will-change:left,width;&#125;.progress-indeterminate::after&#123;position:absolute;top:0;bottom:0;left:0;background-color:inherit;-webkit-animation:mdui-progress-indeterminate-short 2s linear infinite;animation:mdui-progress-indeterminate-short 2s linear infinite;content:&#x27; &#x27;;will-change:left,width;&#125;@keyframes mdui-progress-indeterminate&#123;0%&#123;left:0;width:0;&#125;50%&#123;left:30%;width:70%;&#125;75%&#123;left:100%;width:0;&#125;&#125;@keyframes mdui-progress-indeterminate-short&#123;0%&#123;left:0;width:0;&#125;50%&#123;left:0;width:0;&#125;75%&#123;left:0;width:25%;&#125;100%&#123;left:100%;width:0;&#125;&#125;&lt;/style&gt;&lt;/div&gt;`</span>;</span><br><span class="line">        <span class="comment">// 在 body 后加入 &lt;div&gt;</span></span><br><span class="line">        <span class="variable language_">document</span>.<span class="property">body</span>.<span class="title function_">appendChild</span>(loading);</span><br><span class="line">        <span class="comment">// 如果页面中没有 page.css 或 search.css，为防止样式错乱，需要在加载过程中隐藏页面内容</span></span><br><span class="line">        <span class="keyword">if</span> (!<span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&quot;page_css&quot;</span>) || !<span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&quot;search_css&quot;</span>)) <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&quot;page-main&quot;</span>).<span class="property">style</span>.<span class="property">opacity</span> = <span class="number">0</span>;</span><br><span class="line">        <span class="comment">// 获取页面数据</span></span><br><span class="line">        <span class="keyword">let</span> pageData;</span><br><span class="line">        <span class="comment">// 看看 SessionStorage 里有没有缓存</span></span><br><span class="line">        <span class="comment">// 依赖后文的 prefetch</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="variable language_">sessionStorage</span>.<span class="title function_">getItem</span>(<span class="string">`<span class="subst">$&#123;location.protocol&#125;</span>//<span class="subst">$&#123;location.hostname&#125;</span><span class="subst">$&#123;location.port ? <span class="string">&quot;:&quot;</span>+location.port:location.port&#125;</span><span class="subst">$&#123;$pjax_fullpath(path)&#125;</span>`</span>)) &#123;</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;FROM SESSIONSTORAGE&quot;</span>);</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                pageData = <span class="title class_">JSON</span>.<span class="title function_">parse</span>(<span class="variable language_">sessionStorage</span>.<span class="title function_">getItem</span>(<span class="string">`<span class="subst">$&#123;location.protocol&#125;</span>//<span class="subst">$&#123;location.hostname&#125;</span><span class="subst">$&#123;location.port ? <span class="string">&quot;:&quot;</span>+location.port:location.port&#125;</span><span class="subst">$&#123;$pjax_fullpath(path)&#125;</span>`</span>));</span><br><span class="line">            &#125; <span class="keyword">catch</span>(e) &#123;</span><br><span class="line">                <span class="comment">// 还是出错就从服务器获取</span></span><br><span class="line">                <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;FROM SERVER&quot;</span>);</span><br><span class="line">                pageData = <span class="keyword">await</span> <span class="title function_">fetch</span>($pjax_fullpath(path) + <span class="string">&quot;.page.json&quot;</span>).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> res.<span class="title function_">json</span>());</span><br><span class="line">                <span class="comment">// 写到 SessionStorage 中</span></span><br><span class="line">                <span class="variable language_">sessionStorage</span>.<span class="title function_">setItem</span>(<span class="string">`<span class="subst">$&#123;location.protocol&#125;</span>//<span class="subst">$&#123;location.hostname&#125;</span><span class="subst">$&#123;location.port ? <span class="string">&quot;:&quot;</span>+location.port:location.port&#125;</span><span class="subst">$&#123;$pjax_fullpath(path)&#125;</span>`</span>, <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(pageData));</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;FROM SERVER&quot;</span>);</span><br><span class="line">            <span class="comment">// fetch JSON</span></span><br><span class="line">            pageData = <span class="keyword">await</span> <span class="title function_">fetch</span>($pjax_fullpath(path) + <span class="string">&quot;.page.json&quot;</span>).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> res.<span class="title function_">json</span>());</span><br><span class="line">            <span class="variable language_">sessionStorage</span>.<span class="title function_">setItem</span>(<span class="string">`<span class="subst">$&#123;location.protocol&#125;</span>//<span class="subst">$&#123;location.hostname&#125;</span><span class="subst">$&#123;location.port ? <span class="string">&quot;:&quot;</span>+location.port:location.port&#125;</span><span class="subst">$&#123;$pjax_fullpath(path)&#125;</span>`</span>, <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(pageData));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 补齐页面 CSS</span></span><br><span class="line">        <span class="keyword">if</span> (!<span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&quot;search_css&quot;</span>)) &#123;</span><br><span class="line">            <span class="title function_">fetch</span>(<span class="string">&quot;/css/search.css&quot;</span>).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> res.<span class="title function_">text</span>()).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">                <span class="keyword">let</span> ele = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&quot;style&quot;</span>);</span><br><span class="line">                ele.<span class="property">innerHTML</span> = res;</span><br><span class="line">                ele.<span class="property">id</span> = <span class="string">&quot;search_css&quot;</span>;</span><br><span class="line">                <span class="variable language_">document</span>.<span class="property">body</span>.<span class="title function_">appendChild</span>(ele);</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (!<span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&quot;page_css&quot;</span>)) &#123;</span><br><span class="line">            <span class="title function_">fetch</span>(<span class="string">&quot;/css/page.css&quot;</span>).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> res.<span class="title function_">text</span>()).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">                <span class="keyword">let</span> ele = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&quot;style&quot;</span>);</span><br><span class="line">                ele.<span class="property">innerHTML</span> = res;</span><br><span class="line">                ele.<span class="property">id</span> = <span class="string">&quot;page_css&quot;</span>;</span><br><span class="line">                <span class="variable language_">document</span>.<span class="property">body</span>.<span class="title function_">appendChild</span>(ele);</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (!pageData) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        <span class="comment">// 组合 state</span></span><br><span class="line">        <span class="keyword">var</span> state = &#123; <span class="attr">title</span>: <span class="string">&#x27;&#x27;</span>, <span class="attr">url</span>: <span class="variable language_">window</span>.<span class="property">location</span>.<span class="property">href</span>.<span class="title function_">split</span>(<span class="string">&quot;?&quot;</span>)[<span class="number">0</span>] &#125;;</span><br><span class="line">        <span class="comment">// 利用 history.pushState() 修改地址栏而不跳转</span></span><br><span class="line">        history.<span class="title function_">pushState</span>(state, <span class="string">&#x27;&#x27;</span>, path);</span><br><span class="line">        <span class="comment">// 修改页面标题</span></span><br><span class="line">        <span class="variable language_">document</span>.<span class="property">title</span> = pageData.<span class="property">title</span>;</span><br><span class="line">        <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">            <span class="comment">// 滚动到页面顶部</span></span><br><span class="line">            <span class="variable language_">window</span>.<span class="title function_">scrollTo</span>(&#123;<span class="attr">top</span>: <span class="number">0</span>, <span class="attr">behavior</span>: <span class="string">&quot;smooth&quot;</span>&#125;);</span><br><span class="line">            <span class="comment">// 写入 HTML</span></span><br><span class="line">            <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&quot;page-main&quot;</span>).<span class="property">innerHTML</span> = pageData.<span class="property">page</span>;</span><br><span class="line">            <span class="variable language_">window</span>.<span class="property">onscroll</span> = <span class="literal">null</span>;</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">let</span> i <span class="keyword">in</span> pageData.<span class="property">extraJS</span>) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="comment">// eval() 执行 JS</span></span><br><span class="line">                    <span class="built_in">eval</span>(pageData.<span class="property">extraJS</span>[i]);</span><br><span class="line">                &#125; <span class="keyword">catch</span>(e) &#123;&#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">try</span>&#123;$pjax_prefetch();&#125;<span class="keyword">catch</span>(e)&#123;&#125;</span><br><span class="line">            <span class="comment">// 再次转换所有链接</span></span><br><span class="line">            $pjax_convertAllLinks();</span><br><span class="line">        &#125;, <span class="number">200</span>);</span><br><span class="line">        <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">            <span class="comment">// 重新显示页面</span></span><br><span class="line">            <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&quot;page-main&quot;</span>).<span class="property">style</span>.<span class="property">opacity</span> = <span class="number">1</span>;</span><br><span class="line">            loading.<span class="title function_">remove</span>();</span><br><span class="line">        &#125;, <span class="number">1000</span>);</span><br><span class="line">    &#125; <span class="keyword">catch</span>(e) &#123;</span><br><span class="line">        <span class="comment">// 有报错 直接跳转</span></span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">warn</span>(e);</span><br><span class="line">        <span class="variable language_">window</span>.<span class="property">location</span>.<span class="property">href</span> = path;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果使用 <code>window.location.href</code> 修改，那么页面就会刷新。<br>为了实现无刷新跳转，必须要使用 <code>pushState()</code> 更改。</p><p>执行 JavaScript 方面使用 <code>eval()</code> 函数。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 组合 state</span></span><br><span class="line"><span class="keyword">var</span> state = &#123; <span class="attr">title</span>: <span class="string">&#x27;&#x27;</span>, <span class="attr">url</span>: <span class="variable language_">window</span>.<span class="property">location</span>.<span class="property">href</span>.<span class="title function_">split</span>(<span class="string">&quot;?&quot;</span>)[<span class="number">0</span>] &#125;;</span><br><span class="line"><span class="comment">// 利用 history.pushState() 修改地址栏而不跳转</span></span><br><span class="line">history.<span class="title function_">pushState</span>(state, <span class="string">&#x27;&#x27;</span>, path);</span><br><span class="line"><span class="comment">// 修改页面标题</span></span><br><span class="line"><span class="variable language_">document</span>.<span class="property">title</span> = pageData.<span class="property">title</span>;</span><br><span class="line"><span class="comment">// 滚动到页面顶部</span></span><br><span class="line"><span class="variable language_">window</span>.<span class="title function_">scrollTo</span>(&#123;<span class="attr">top</span>: <span class="number">0</span>, <span class="attr">behavior</span>: <span class="string">&quot;smooth&quot;</span>&#125;);</span><br><span class="line"><span class="comment">// 写入 HTML</span></span><br><span class="line"><span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&quot;page-main&quot;</span>).<span class="property">innerHTML</span> = pageData.<span class="property">page</span>;</span><br><span class="line"><span class="variable language_">window</span>.<span class="property">onscroll</span> = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> i <span class="keyword">in</span> pageData.<span class="property">extraJS</span>) &#123;</span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="comment">// eval() 执行 JS</span></span><br><span class="line">    <span class="built_in">eval</span>(pageData.<span class="property">extraJS</span>[i]);</span><br><span class="line">  &#125; <span class="keyword">catch</span>(e) &#123;&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Prefetch-amp-Refetch"><a href="#Prefetch-amp-Refetch" class="headerlink" title="Prefetch &amp; Refetch"></a>Prefetch &amp; Refetch</h3><p>此处借鉴乐特关于 Prefetch Page 的源码，当用户打开节流模式或为低速网络时就不要 Prefetch.</p><p>Prefetch 可以提前缓存部分数据。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">$pjax_prefetch</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line">    <span class="comment">// 节流和低速网络不要 Prefetch</span></span><br><span class="line">    <span class="keyword">const</span> nav = navigator;</span><br><span class="line">    <span class="keyword">const</span> &#123; saveData, effectiveType &#125; = nav.<span class="property">connection</span> || nav.<span class="property">mozConnection</span> || nav.<span class="property">webkitConnection</span> || &#123;&#125;;</span><br><span class="line">    <span class="keyword">if</span> (saveData || <span class="regexp">/2g/</span>.<span class="title function_">test</span>(effectiveType)) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">  </span><br><span class="line">    <span class="comment">// 此处是 Blog 的一些常见链接</span></span><br><span class="line">    <span class="keyword">let</span> posts_list = <span class="variable language_">document</span>.<span class="title function_">querySelectorAll</span>(<span class="string">&quot;.index-header a&quot;</span>);</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i <span class="keyword">in</span> posts_list) &#123;</span><br><span class="line">        <span class="comment">// 全路径</span></span><br><span class="line">        <span class="keyword">let</span> thisLink = $pjax_fullpath(posts_list[i].<span class="property">href</span>);</span><br><span class="line">        <span class="comment">// Session Storage 没有才 Fetch</span></span><br><span class="line">        <span class="keyword">if</span> (!<span class="variable language_">sessionStorage</span>.<span class="title function_">getItem</span>(thisLink)) &#123;</span><br><span class="line">            <span class="title function_">fetch</span>(thisLink + <span class="string">&quot;.page.json&quot;</span>).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> res.<span class="title function_">text</span>()).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">                <span class="variable language_">sessionStorage</span>.<span class="title function_">setItem</span>(thisLink,res);</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Refetch 用于刷新已有的缓存（虽然 <code>SessionStorage</code> 关闭页面就没了）</p><p>其原理也很简单，<code>SessionStorage</code> 中所有的 Pjax 缓存重新获取就完事了。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">$pjax_refetch</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line">    <span class="keyword">let</span> sst = <span class="variable language_">sessionStorage</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i <span class="keyword">in</span> sst) &#123;</span><br><span class="line">        <span class="keyword">if</span> (i.<span class="title function_">startsWith</span>(<span class="string">&quot;http://&quot;</span>) || i.<span class="title function_">startsWith</span>(<span class="string">&quot;https://&quot;</span>)) &#123;</span><br><span class="line">            <span class="title function_">fetch</span>(i + <span class="string">&quot;.page.json&quot;</span>).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> res.<span class="title function_">text</span>()).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">                <span class="variable language_">sessionStorage</span>.<span class="title function_">setItem</span>(i, res);</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="一些优化"><a href="#一些优化" class="headerlink" title="一些优化"></a>一些优化</h3><h4 id="Prefetch-CSS-文件"><a href="#Prefetch-CSS-文件" class="headerlink" title="Prefetch CSS 文件"></a>Prefetch CSS 文件</h4><p>既然 CSS 文件需要补齐，那么打开页面 5s 后自动 Prefetch 可以提升速度。</p><blockquote><p>5s 后再获取是为了防止阻塞页面。</p></blockquote><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// Prefetch CSS 文件</span></span><br><span class="line">    <span class="keyword">if</span> (!<span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&quot;search_css&quot;</span>)) &#123;</span><br><span class="line">        <span class="title function_">fetch</span>(<span class="string">&quot;/css/search.css&quot;</span>).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> res.<span class="title function_">text</span>()).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">            <span class="keyword">let</span> ele = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&quot;style&quot;</span>)</span><br><span class="line">            ele.<span class="property">innerHTML</span> = res;</span><br><span class="line">            ele.<span class="property">id</span> = <span class="string">&quot;search_css&quot;</span>;</span><br><span class="line">            <span class="variable language_">document</span>.<span class="property">body</span>.<span class="title function_">appendChild</span>(ele);</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (!<span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&quot;page_css&quot;</span>)) &#123;</span><br><span class="line">        <span class="title function_">fetch</span>(<span class="string">&quot;/css/page.css&quot;</span>).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> res.<span class="title function_">text</span>()).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">            <span class="keyword">let</span> ele = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&quot;style&quot;</span>)</span><br><span class="line">            ele.<span class="property">innerHTML</span> = res;</span><br><span class="line">            ele.<span class="property">id</span> = <span class="string">&quot;page_css&quot;</span>;</span><br><span class="line">            <span class="variable language_">document</span>.<span class="property">body</span>.<span class="title function_">appendChild</span>(ele);</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;    </span><br><span class="line">&#125;, <span class="number">5000</span>);</span><br></pre></td></tr></table></figure><h4 id="关于-Robots"><a href="#关于-Robots" class="headerlink" title="关于 Robots"></a>关于 Robots</h4><p>当你运行 <code>$pjax_convertAllLinks();</code> 后，你肯定会发现所有的链接都变成了 <code>javascript:$pjax_jump(&#39;/xxx&#39;);</code>。这对机器人来说很不友好。</p><p>所以，我们需要排除这些机器人。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> runningOnBrowser = <span class="keyword">typeof</span> <span class="variable language_">window</span> !== <span class="string">&quot;undefined&quot;</span>;</span><br><span class="line"><span class="keyword">var</span> isBot = runningOnBrowser &amp;&amp; !(<span class="string">&quot;onscroll&quot;</span> <span class="keyword">in</span> <span class="variable language_">window</span>) || <span class="keyword">typeof</span> navigator !== <span class="string">&quot;undefined&quot;</span> &amp;&amp; <span class="regexp">/(gle|ing|ro|msn)bot|crawl|spider|yand|duckgo/i</span>.<span class="title function_">test</span>(navigator.<span class="property">userAgent</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (runningOnBrowser &amp;&amp; !isBot) &#123;</span><br><span class="line">    <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">try</span>&#123;$pjax_prefetch();&#125;<span class="keyword">catch</span>(e)&#123;&#125;</span><br><span class="line">        $pjax_convertAllLinks();</span><br><span class="line">    &#125;, <span class="number">100</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p>在启用 Pjax 后，YFun&#39;s Blog 传输大小理论上最高缩小 3/4，性能速度均有提升。</p><p>如果你也在使用 Pjax，不妨试试看。</p><h2 id="还有一些错误"><a href="#还有一些错误" class="headerlink" title="还有一些错误"></a>还有一些错误</h2><p>如果你定义了 <code>onload</code> 等事件，页面没有刷新即代表没有变化，你需要在 <code>$pjax_jump()</code> 中简单清除一下这些信息。</p><h2 id="广告时间"><a href="#广告时间" class="headerlink" title="广告时间"></a>广告时间</h2><p>我的博客即将同步至腾讯云开发者社区，邀请大家一同入驻：<a href="https://cloud.tencent.com/developer/support-plan?invite_code=16qkaef2qdvzm">https://cloud.tencent.com/developer/support-plan?invite_code=16qkaef2qdvzm</a></p>]]></content:encoded>
      
      
      <category domain="https://blog.yfun.top/categories/%E5%8D%9A%E5%AE%A2/">博客</category>
      
      
      <category domain="https://blog.yfun.top/tags/%E5%8D%9A%E5%AE%A2/">博客</category>
      
      <category domain="https://blog.yfun.top/tags/JavaScript/">JavaScript</category>
      
      <category domain="https://blog.yfun.top/tags/Pjax/">Pjax</category>
      
      <category domain="https://blog.yfun.top/tags/%E4%BC%98%E5%8C%96/">优化</category>
      
      
      <comments>https://blog.yfun.top/posts/2022/pjax/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>抛弃静态博客的缺点，用 ESHexoN 在线编辑！</title>
      <link>https://blog.yfun.top/posts/2022/eshexon/</link>
      <guid>https://blog.yfun.top/posts/2022/eshexon/</guid>
      <pubDate>Sun, 04 Dec 2022 16:00:00 GMT</pubDate>
      
      <description>ESHexoN 是一款基于 JavaScript 的 Hexo 在线编辑器。本文介绍 ESHexoN 实现的基本原理与背后故事。</description>
      
      
      
      <content:encoded><![CDATA[<h2 id="什么是-ESHexoN"><a href="#什么是-ESHexoN" class="headerlink" title="什么是 ESHexoN?"></a>什么是 ESHexoN?</h2><blockquote><p>ESHexoN 是一款简洁、强大的 Hexo 在线编辑器。</p></blockquote><p>其灵感来自于 <a href="https://github.com/HexoPlusPlus/HexoPlusPlus">HexoPlusPlus/HexoPlusPlus</a> (基于 Cloudflare Workers 的无服务器 Hexo 后端)</p><p>但是，遗憾的是 HPP 突然停止维护，只留下一个 <a href="https://github.com/Wexagonal/Wexagonal">Wexagonal</a>。</p><blockquote><p>However, 这更新速度也太慢了啊喂~</p></blockquote><p><a href="https://github.com/Qexo/Qexo">Qexo</a> 也是一个很不错的项目，但是它基于 Python + MongoDB，不属于我擅长的技术栈，有点劝退。</p><p>于是，ESHexoN 诞生了。它致力于使用 JavaScript Edge Function 构建 Hexo「后端」，实现普通静态博客所不具备的功能，解决大部分静态博客的痛点。</p><h2 id="如何使用-ESHexoN"><a href="#如何使用-ESHexoN" class="headerlink" title="如何使用 ESHexoN?"></a>如何使用 ESHexoN?</h2><blockquote><p>ESHexoN 的使用说明可以参见文档 --&gt; <a href="https://eshexon-docs.netlify.app/">文档链接</a></p></blockquote><h2 id="ESHexoN-代码-amp-原理"><a href="#ESHexoN-代码-amp-原理" class="headerlink" title="ESHexoN 代码 &amp; 原理"></a>ESHexoN 代码 &amp; 原理</h2><h3 id="代码目录"><a href="#代码目录" class="headerlink" title="代码目录"></a>代码目录</h3><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.17/rawimg/20221205115647.png"></p><p><code>dist</code> 目录存放构建的文件，<code>src</code> 目录为源码，<code>tools</code> 是一些工具，<code>index.js</code> 则为入口文件。</p><h3 id="后端的主要文件"><a href="#后端的主要文件" class="headerlink" title="后端的主要文件"></a>后端的主要文件</h3><h4 id="src-res-js"><a href="#src-res-js" class="headerlink" title="src/res.js"></a><code>src/res.js</code></h4><p><code>res.js</code> 主要负责 HTTP 响应输出，统一输出格式。</p><p>故 ESHexoN API 主要返回格式为:</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;statusCode&quot;</span><span class="punctuation">:</span> <span class="string">&quot;...&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;statusInfo&quot;</span><span class="punctuation">:</span> <span class="string">&quot;...&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p><a href="https://github.com/ESHexoN/ESHexoN/blob/master/src/res.js">[Source Code]</a></p><h4 id="src-check-token-js"><a href="#src-check-token-js" class="headerlink" title="src/check_token.js"></a><code>src/check_token.js</code></h4><p><code>check_token.js</code> 主要负责 API 请求的鉴权，所有的鉴权 API 都需要携带 <code>token</code> 发送。</p><p>生成代码：</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> token = <span class="title function_">md5</span>(userdata[<span class="number">0</span>].<span class="property">username</span> + userdata[<span class="number">0</span>].<span class="property">password</span> + <span class="string">`<span class="subst">$&#123;<span class="keyword">new</span> <span class="built_in">Date</span>(<span class="built_in">Date</span>.now()).getFullYear()&#125;</span><span class="subst">$&#123;<span class="keyword">new</span> <span class="built_in">Date</span>(<span class="built_in">Date</span>.now()).getMonth()+<span class="number">1</span>&#125;</span>`</span>);</span><br></pre></td></tr></table></figure><p>故 ESHexoN Token 格式为：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">md5(USERNAME+PASSWORD+YEAR+MONTH);</span><br></pre></td></tr></table></figure><p>过期时间为一个月。</p><h4 id="src-env-js"><a href="#src-env-js" class="headerlink" title="src/env.js"></a><code>src/env.js</code></h4><p>ESHexoN 除了依靠 <a href="https://github.com/yfun-lab/gh-worker-kv">yfun-lab/gh-worker-kv</a> 存储主要数据库文件外，还使用环境变量存储基本的 Token、仓库等。</p><p>ESHexoN 自设计之初就预备支持 Cloudflare Workers, Deno 等平台。</p><p>事实上，这两个平台获取环境变量的方式根本不一样。为了在一个代码下同时支持两个平台，<code>env.js</code> 简单的写了个判断。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.17/rawimg/20221205120944.png"></p><h4 id="是不是目录？文件遍历"><a href="#是不是目录？文件遍历" class="headerlink" title="是不是目录？文件遍历"></a>是不是目录？文件遍历</h4><p>GitHub API 会返回当前目录下的文件和文件夹，但有人的 <code>source/_posts</code> 目录下还有文件夹。根据观察发现，文件夹的 <code>type</code> 为 <code>dir</code>，而普通文件为 <code>file</code>，那么只需要一个简单的判断，就可以返回单个目录下所有文件。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.17/rawimg/20221205121416.png"></p><h4 id="写文件"><a href="#写文件" class="headerlink" title="写文件"></a>写文件</h4><p>在写文件之前，我们需要先获取该文件的 <code>sha</code> 值。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.17/rawimg/20221205121728.png"></p><blockquote><p>这是因为 GitHub 规定修改文件必须在 <code>body</code> 中携带源文件 <code>sha</code>.</p></blockquote><p>同时，文件还必须接受 Base64 编码。这个问题很好解决，ESHexoN 的仓库依赖中已经包含。当然，我更倾向于在前端编码文本，在后端直接 PUT。</p><h3 id="那前端呢？"><a href="#那前端呢？" class="headerlink" title="那前端呢？"></a>那前端呢？</h3><p>以上是后端的主要代码。因为 ESHexoN 是前后端分离项目，并且开放跨域，所以我们构建了一个官方前端。只需要输入你的用户名、密码和后端地址就可以开始使用。</p><p>前端的代码也是开源的，基于 Vue + Vuetify 构建，<a href="https://github.com/ESHexoN/eshexon-frontend">在 GitHub 上查看它</a></p><h4 id="一些小细节"><a href="#一些小细节" class="headerlink" title="一些小细节"></a>一些小细节</h4><p>早在 HexoPlusPlus <code>dev</code> 阶段的时候，ChenYFan 就在群内讨论如何解决 Markdown 编辑器的问题。在当时，EditorMD 的兼容性很差。于是，HPP 上采取的解决方案是：<code>textarea</code> + <code>marked.js</code>。</p><p>然后，HPP 编辑器就是这样的：</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.17/rawimg/20221205122539.png"></p><p>这并不是一个很好的主意。</p><p>于是在 ESHexoN 上，我们为 Hexo 特性定制了一款简单的编辑器。</p><p>首先，它是由 <a href="https://yfun.top/lab/markdown/">https://yfun.top/lab/markdown/</a> 修改而来的，在部分特性上与其类似。</p><p>但是受限于页面尺寸和小屏幕优化，ESHexoN 的编辑器在大屏上是这样显示的：</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.17/rawimg/20221205122828.png"></p><p>双栏布局，实时预览。</p><p>为了保证在小屏幕的体验，在小屏幕上将自动收起预览，改为全屏模式。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.17/rawimg/20221205122955.png"></p><p>如果需要预览，可以点击「预览」按键。</p><p>同时 ESHexoN 的编辑器有自动保存功能，即使写到一半刷新页面或是浏览器崩溃也不用怕，页面内容已经存储到 <code>localStorage</code> 内了。（我也不知道会不会撑炸</p><p>一些基本的搜索功能都得以保留（与 HPP 是十分甚至九分的类似</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.17/rawimg/20221205123316.png"></p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.17/rawimg/20221205123356.png"></p><p>不仅仅局限于文章，配置文件、代码都可以修改：</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.17/rawimg/20221205123427.png"></p><h2 id="在未来"><a href="#在未来" class="headerlink" title="在未来"></a>在未来</h2><p>目前 ESHexoN 还处于 Beta 阶段，部分功能还没有开发完成。</p><p>关于说说、评论等也都在等待开发（最近可能不会有）</p><p>如果在使用中出现了任何的问题，欢迎提 Issue 反馈。</p>]]></content:encoded>
      
      
      <category domain="https://blog.yfun.top/categories/%E5%8D%9A%E5%AE%A2/">博客</category>
      
      
      <category domain="https://blog.yfun.top/tags/%E5%8D%9A%E5%AE%A2/">博客</category>
      
      <category domain="https://blog.yfun.top/tags/ESHexoN/">ESHexoN</category>
      
      <category domain="https://blog.yfun.top/tags/%E9%9B%86%E6%88%90%E9%83%A8%E7%BD%B2/">集成部署</category>
      
      
      <comments>https://blog.yfun.top/posts/2022/eshexon/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>让阅读无缝衔接 —— JS 获取用户阅读进度</title>
      <link>https://blog.yfun.top/posts/2022/js-get-users-reading-progress/</link>
      <guid>https://blog.yfun.top/posts/2022/js-get-users-reading-progress/</guid>
      <pubDate>Fri, 11 Nov 2022 16:40:00 GMT</pubDate>
      
      <description>很多博客中最常见的问题就是：文章很长，但是读者很忙。下次阅读的时候，可能要花一些时间才能恢复到先前的阅读位置。如果可以设备间，识别二维码或是一个链接就可以让阅读无缝衔接，直接跳转到相应位置，那么阅读体验就会变得更加优秀。那么，开始吧！</description>
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>很多博客中最常见的问题就是：文章很长，但是读者很忙。下次阅读的时候，可能要花一些时间才能恢复到先前的阅读位置。</p><p>如果可以设备间，识别二维码或是一个链接就可以让阅读无缝衔接，直接跳转到相应位置，那么阅读体验就会变得更加优秀。</p><p>那么，开始吧！</p><h2 id="实践"><a href="#实践" class="headerlink" title="实践"></a>实践</h2><p>要知道阅读位置，那么就要知道当前页面的坐标。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">getScrollPosition</span> = (<span class="params">el = <span class="variable language_">window</span></span>) =&gt; (&#123;</span><br><span class="line">    <span class="attr">x</span>: el.<span class="property">pageXOffset</span> !== <span class="literal">undefined</span> ? el.<span class="property">pageXOffset</span> : el.<span class="property">scrollLeft</span>,</span><br><span class="line">    <span class="attr">y</span>: el.<span class="property">pageYOffset</span> !== <span class="literal">undefined</span> ? el.<span class="property">pageYOffset</span> : el.<span class="property">scrollTop</span></span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">// getScollPosition().x =&gt; 页面横坐标; getScrollPosition().y =&gt; 页面纵坐标;</span></span><br></pre></td></tr></table></figure><p>大部分情况下，我们只用关注纵坐标。横坐标大概率为 <code>0</code>。</p><p>我们还需要一个页面滚动的事件，用于记录当前坐标，并储存在临时存储中。</p><p>至于为什么是 <code>sessionStorage</code> 而不是 <code>localStorage</code>，则是因为 <code>localStorage</code> 除手动清除外，不会自动过期。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 此处的 750 是「页面元素的最大宽度」</span></span><br><span class="line"><span class="keyword">var</span> wx = <span class="variable language_">window</span>.<span class="property">innerWidth</span> &gt;= <span class="number">750</span> ? <span class="number">750</span> : <span class="variable language_">window</span>.<span class="property">innerWidth</span>;</span><br><span class="line"><span class="keyword">var</span> wy = <span class="variable language_">window</span>.<span class="property">innerHeight</span>;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">windowScroll</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="comment">// 反复修改 确保页面尺寸不改变</span></span><br><span class="line">    wx = <span class="variable language_">window</span>.<span class="property">innerWidth</span> &gt;= <span class="number">750</span> ? <span class="number">750</span> : <span class="variable language_">window</span>.<span class="property">innerWidth</span>;</span><br><span class="line">    wy = <span class="variable language_">window</span>.<span class="property">innerHeight</span>;</span><br><span class="line">    <span class="keyword">let</span> y = <span class="title class_">Math</span>.<span class="title function_">round</span>(<span class="title function_">getScrollPosition</span>().<span class="property">y</span>);</span><br><span class="line">    <span class="comment">// 组合字符串，同时记录页面坐标，页面宽度和高度</span></span><br><span class="line">    <span class="keyword">let</span> p = <span class="string">`<span class="subst">$&#123;y&#125;</span>:<span class="subst">$&#123;wx&#125;</span>:<span class="subst">$&#123;wy&#125;</span>`</span>;</span><br><span class="line">    <span class="comment">// 写入到 sessionStorage 中</span></span><br><span class="line">    <span class="variable language_">sessionStorage</span>.<span class="title function_">setItem</span>(<span class="string">&quot;read_y&quot;</span>, p);</span><br><span class="line">&#125;</span><br><span class="line"><span class="variable language_">window</span>.<span class="property">onscroll</span> = windowScroll;</span><br></pre></td></tr></table></figure><p>你可能发现了，此处的变量 <code>p</code>，并不仅仅是「页面纵坐标」，而是「页面高度」与「纵坐标」的组合字符串。</p><p>事实上，如果单纯是纵坐标判断位置，那么在不同高度，不同宽度的设备上，就会出现错位的情况。而同时记录三个信息，就可以还原真实坐标。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// URL 中是否包含传递的坐标信息</span></span><br><span class="line"><span class="keyword">if</span> (location.<span class="property">hash</span>.<span class="title function_">split</span>(<span class="string">&quot;#read=&quot;</span>).<span class="property">length</span> &gt; <span class="number">1</span>) &#123;</span><br><span class="line">    <span class="comment">// 分离字符串</span></span><br><span class="line">    <span class="keyword">let</span> read_y = location.<span class="property">hash</span>.<span class="title function_">split</span>(<span class="string">&quot;#read=&quot;</span>)[<span class="number">1</span>];</span><br><span class="line">    read_y = read_y.<span class="title function_">split</span>(<span class="string">&quot;:&quot;</span>);</span><br><span class="line">    <span class="comment">// 组合乘积，顺滑移动至坐标</span></span><br><span class="line">    <span class="variable language_">window</span>.<span class="title function_">scrollTo</span>(&#123;<span class="attr">top</span>: <span class="title class_">Math</span>.<span class="title function_">round</span>(<span class="title class_">Number</span>(read_y[<span class="number">0</span>]) * <span class="title class_">Number</span>(read_y[<span class="number">1</span>] * <span class="title class_">Number</span>(read_y[<span class="number">2</span>] / wx / wy))), <span class="attr">behavior</span>: <span class="string">&quot;smooth&quot;</span>&#125;);</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="comment">// 从 sessionStorage 中获取</span></span><br><span class="line">    <span class="keyword">let</span> read_y = <span class="variable language_">sessionStorage</span>.<span class="title function_">getItem</span>(<span class="string">&quot;read_y&quot;</span>) || <span class="string">&quot;0:0:0&quot;</span>;</span><br><span class="line">    read_y = read_y.<span class="title function_">split</span>(<span class="string">&quot;:&quot;</span>);</span><br><span class="line">    <span class="variable language_">window</span>.<span class="title function_">scrollTo</span>(&#123;<span class="attr">top</span>: <span class="title class_">Math</span>.<span class="title function_">round</span>(<span class="title class_">Number</span>(read_y[<span class="number">0</span>]) * <span class="title class_">Number</span>(read_y[<span class="number">1</span>] * <span class="title class_">Number</span>(read_y[<span class="number">2</span>] / wx / wy))), <span class="attr">behavior</span>: <span class="string">&quot;smooth&quot;</span>&#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>到现在，我们已经完成了 URL 的解析和基本生成。</p><p>那么，URL 即为:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="string">`<span class="subst">$&#123;location.protocol&#125;</span>//<span class="subst">$&#123;location.hostname&#125;</span><span class="subst">$&#123;location.port ? <span class="string">&quot;:&quot;</span>+location.port:location.port&#125;</span><span class="subst">$&#123;location.pathname&#125;</span>#read=<span class="subst">$&#123;<span class="variable language_">sessionStorage</span>.getItem(<span class="string">&quot;read_y&quot;</span>)&#125;</span>`</span>;</span><br></pre></td></tr></table></figure><h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p>搭配生成二维码等插件效果更佳。</p><blockquote><p>Miracle 主题将在下个版本中更新该功能。</p></blockquote>]]></content:encoded>
      
      
      <category domain="https://blog.yfun.top/categories/%E5%8D%9A%E5%AE%A2/">博客</category>
      
      
      <category domain="https://blog.yfun.top/tags/%E5%8D%9A%E5%AE%A2/">博客</category>
      
      <category domain="https://blog.yfun.top/tags/JavaScript/">JavaScript</category>
      
      <category domain="https://blog.yfun.top/tags/Miracle/">Miracle</category>
      
      
      <comments>https://blog.yfun.top/posts/2022/js-get-users-reading-progress/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>NPM 自动更新版本号</title>
      <link>https://blog.yfun.top/posts/2022/auto-update-npm-version/</link>
      <guid>https://blog.yfun.top/posts/2022/auto-update-npm-version/</guid>
      <pubDate>Thu, 04 Aug 2022 05:00:00 GMT</pubDate>
      
      <description>NPM 项目实现自动更新版本号，一键部署发版！</description>
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>ChenYFan 大佬的文章：<a href="https://blog.cyfan.top/p/d3c51290.html">SpeedUp!使用黑科技为你的网站提速</a> 有提到过将 Hexo 博客的全部静态文件上传至 NPM 达到加速效果。</p><p>但是 NPM 版本号不能重复，而且为了精准命中缓存和防止边缘 <code>@latest</code> 缓存过长，就必须指定版本号。</p><p>本人在 <a href="https://blog.yfun.top/posts/2876015612/">我的图床解决方案</a> 一文中使用的方法是 <code>npm version patch</code>。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.16/rawimg/20220804133426.png"></p><p>但这个方法也有一些弊端，比如 <code>patch</code> 只会更新 <code>z</code> 位数的版本号，而且并不会上传至仓库，很可能会出现版本号冲突导致上传失败。这就是为什么最后还需要重新推送 Git 提交。</p><h2 id="试试看"><a href="#试试看" class="headerlink" title="试试看"></a>试试看</h2><p>NPM 版本号遵循 <code>semver</code> 规范，格式为 <code>major.minor.patch</code>。</p><p>为了在获取最新版本号的时候不发生混乱，我选择先获取最新的版本号。</p><blockquote><p>记得把 <code>cky-blog-static</code> 改成自己的项目名。</p></blockquote><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">getVersion</span> = <span class="keyword">async</span>(<span class="params"></span>) =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> latestVersion = <span class="keyword">await</span> <span class="title function_">fetch</span>(<span class="string">&quot;https://registry.npmjs.org/cky-blog-static/latest&quot;</span>).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> res.<span class="title function_">json</span>()).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> res.<span class="property">version</span>;</span><br><span class="line">    &#125;)</span><br><span class="line">    <span class="keyword">return</span> latestVersion;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>获取到最新版本号后，就可以对版本号进行分析。</p><p>以 <code>1.4.7-b541af2ea284a39da0bbf63b88fdb65c</code> 为例，先按 <code>major.minor.patch</code> 分离。当然，也需要考虑版本号后还有 <code>build metadata</code> 的情况。</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> version = <span class="keyword">await</span> <span class="title function_">getVersion</span>();</span><br><span class="line"><span class="keyword">var</span> verArray = version.<span class="title function_">split</span>(<span class="string">&quot;.&quot;</span>);</span><br><span class="line">verArray[<span class="number">2</span>] = verArray[<span class="number">2</span>].<span class="title function_">split</span>(<span class="string">&quot;-&quot;</span>)[<span class="number">0</span>];</span><br></pre></td></tr></table></figure><p><del>为了版本号好看些</del>，可以通过判断实现满十进一。</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">if</span> (verArray[<span class="number">2</span>] &lt; <span class="number">9</span>) &#123;</span><br><span class="line"><span class="comment">// z 位小于 9，直接 +1</span></span><br><span class="line">verArray[<span class="number">2</span>] = <span class="title class_">String</span>(<span class="title class_">Number</span>(verArray[<span class="number">2</span>])+<span class="number">1</span>);</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (verArray[<span class="number">1</span>] &lt; <span class="number">9</span>) &#123;</span><br><span class="line"><span class="comment">// z 位大于等于 9，y 位小于 9，y+1, z=0</span></span><br><span class="line">verArray[<span class="number">1</span>] = <span class="title class_">String</span>(<span class="title class_">Number</span>(verArray[<span class="number">1</span>])+<span class="number">1</span>);</span><br><span class="line">verArray[<span class="number">2</span>] = <span class="number">0</span>;</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line"><span class="comment">// z 大于等于 9，y 大于等于 9，x+1,y=0,z=0</span></span><br><span class="line">verArray[<span class="number">0</span>] = <span class="title class_">String</span>(<span class="title class_">Number</span>(verArray[<span class="number">0</span>])+<span class="number">1</span>);</span><br><span class="line">verArray[<span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line">verArray[<span class="number">2</span>] = <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后，重新拼接版本号。如果担心服务器缓存版本号导致冲突，还可以加上一些随机字符串。</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> newVersion = <span class="string">`<span class="subst">$&#123;verArray[<span class="number">0</span>]&#125;</span>.<span class="subst">$&#123;verArray[<span class="number">1</span>]&#125;</span>.<span class="subst">$&#123;verArray[<span class="number">2</span>]&#125;</span>`</span></span><br><span class="line"><span class="keyword">var</span> newVersion = newVersion + <span class="string">&quot;-&quot;</span> + <span class="title function_">md5</span>(<span class="string">`<span class="subst">$&#123;<span class="keyword">new</span> <span class="built_in">Date</span>().getTime()&#125;</span><span class="subst">$&#123;newVersion&#125;</span>`</span>);</span><br></pre></td></tr></table></figure><p>将最新的版本号信息重新写入 <code>package.json</code> 文件：</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> packageJson = fs.<span class="title function_">readFileSync</span>(<span class="string">&quot;./package.json&quot;</span>);</span><br><span class="line"><span class="keyword">var</span> packageJson = <span class="title class_">JSON</span>.<span class="title function_">parse</span>(packageJson);</span><br><span class="line">packageJson.<span class="property">version</span> = newVersion;</span><br><span class="line"><span class="keyword">var</span> newPackage = <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(packageJson);</span><br><span class="line">fs.<span class="title function_">writeFileSync</span>(<span class="string">&quot;./package.json&quot;</span>, newPackage);</span><br></pre></td></tr></table></figure><h2 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h2><p><del>直接 CV 代码！</del></p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 更新 package.json 版本号</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> fetch = <span class="built_in">require</span>(<span class="string">&quot;node-fetch&quot;</span>);</span><br><span class="line"><span class="keyword">const</span> md5 = <span class="built_in">require</span>(<span class="string">&quot;js-md5&quot;</span>);</span><br><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">&quot;fs&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">getVersion</span> = <span class="keyword">async</span>(<span class="params"></span>) =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> latestVersion = <span class="keyword">await</span> <span class="title function_">fetch</span>(<span class="string">&quot;https://registry.npmjs.org/cky-blog-static/latest&quot;</span>).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> res.<span class="title function_">json</span>()).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> res.<span class="property">version</span>;</span><br><span class="line">    &#125;)</span><br><span class="line">    <span class="keyword">return</span> latestVersion;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">update</span> = <span class="keyword">async</span>(<span class="params"></span>) =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> version = <span class="keyword">await</span> <span class="title function_">getVersion</span>();</span><br><span class="line">    <span class="keyword">var</span> verArray = version.<span class="title function_">split</span>(<span class="string">&quot;.&quot;</span>);</span><br><span class="line">    verArray[<span class="number">2</span>] = verArray[<span class="number">2</span>].<span class="title function_">split</span>(<span class="string">&quot;-&quot;</span>)[<span class="number">0</span>];</span><br><span class="line">    <span class="keyword">if</span> (verArray[<span class="number">2</span>] &lt; <span class="number">9</span>) &#123;</span><br><span class="line">        verArray[<span class="number">2</span>] = <span class="title class_">String</span>(<span class="title class_">Number</span>(verArray[<span class="number">2</span>])+<span class="number">1</span>);</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (verArray[<span class="number">1</span>] &lt; <span class="number">9</span>) &#123;</span><br><span class="line">        verArray[<span class="number">1</span>] = <span class="title class_">String</span>(<span class="title class_">Number</span>(verArray[<span class="number">1</span>])+<span class="number">1</span>);</span><br><span class="line">        verArray[<span class="number">2</span>] = <span class="number">0</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        verArray[<span class="number">0</span>] = <span class="title class_">String</span>(<span class="title class_">Number</span>(verArray[<span class="number">0</span>])+<span class="number">1</span>);</span><br><span class="line">        verArray[<span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line">        verArray[<span class="number">2</span>] = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">var</span> newVersion = <span class="string">`<span class="subst">$&#123;verArray[<span class="number">0</span>]&#125;</span>.<span class="subst">$&#123;verArray[<span class="number">1</span>]&#125;</span>.<span class="subst">$&#123;verArray[<span class="number">2</span>]&#125;</span>`</span></span><br><span class="line">    <span class="keyword">var</span> newVersion = newVersion + <span class="string">&quot;-&quot;</span> + <span class="title function_">md5</span>(<span class="string">`<span class="subst">$&#123;<span class="keyword">new</span> <span class="built_in">Date</span>().getTime()&#125;</span><span class="subst">$&#123;newVersion&#125;</span>`</span>);</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(newVersion);</span><br><span class="line">    <span class="keyword">var</span> packageJson = fs.<span class="title function_">readFileSync</span>(<span class="string">&quot;./package.json&quot;</span>);</span><br><span class="line">    <span class="keyword">var</span> packageJson = <span class="title class_">JSON</span>.<span class="title function_">parse</span>(packageJson);</span><br><span class="line">    packageJson.<span class="property">version</span> = newVersion;</span><br><span class="line">    <span class="keyword">var</span> newPackage = <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(packageJson);</span><br><span class="line">    fs.<span class="title function_">writeFileSync</span>(<span class="string">&quot;./package.json&quot;</span>, newPackage);</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;Complete!!&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="title function_">update</span>();</span><br></pre></td></tr></table></figure><h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p>如果是 Hexo 文件部署，就可以直接把文件直接丢在项目根目录，Action 集成部署加上：</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">node update.js</span><br></pre></td></tr></table></figure><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.16/rawimg/20220804135818.png"></p><p><del>Blog 半年没更新，除除草 --(....</del></p>]]></content:encoded>
      
      
      <category domain="https://blog.yfun.top/categories/%E5%8D%9A%E5%AE%A2/">博客</category>
      
      
      <category domain="https://blog.yfun.top/tags/%E5%8D%9A%E5%AE%A2/">博客</category>
      
      <category domain="https://blog.yfun.top/tags/NPM/">NPM</category>
      
      <category domain="https://blog.yfun.top/tags/Node-js/">Node.js</category>
      
      
      <comments>https://blog.yfun.top/posts/2022/auto-update-npm-version/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>在 Windows 上快速安装 Hexo · 安装 Node.js</title>
      <link>https://blog.yfun.top/posts/hexo-blog/install-nodejs/</link>
      <guid>https://blog.yfun.top/posts/hexo-blog/install-nodejs/</guid>
      <pubDate>Fri, 14 Jan 2022 10:30:00 GMT</pubDate>
      
      <description>在 Windows 系统上安装并使用 Hexo 创建博客。本文讲述如何安装 Node.js 和 NPM。</description>
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p><strong>本文未经允许，禁止转载！</strong></p><p><strong>视频教程正在制作，等待更新。</strong></p><h2 id="文章目录"><a href="#文章目录" class="headerlink" title="文章目录"></a>文章目录</h2><ul><li><a href="https://blog.yfun.top/posts/hexo-blog/install-git">安装 Git</a></li><li><a href="https://blog.yfun.top/posts/hexo-blog/install-nodejs">安装 Node.js</a></li></ul><blockquote><p>更多文章等待更新...</p></blockquote><h2 id="下载"><a href="#下载" class="headerlink" title="下载"></a>下载</h2><p>前往 Node.js 官网 (<a href="https://node.js.org/">https://node.js.org</a>)</p><p><img src="https://unpkg.zhimg.com/ocoke-osg@0.0.14/rawimg/image-20220114174925675.png" alt="image-20220114174925675"></p><p>下载 Node.js 安装包（推荐下载 LTS 版本）</p><p><img src="https://unpkg.zhimg.com/ocoke-osg@0.0.14/rawimg/20220114175645.png" alt="image-20220114175645642"></p><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><p>双击运行安装文件。</p><p>点击 <code>Next</code>。</p><p><img src="https://unpkg.zhimg.com/ocoke-osg@0.0.14/rawimg/20220114175750.png" alt="image-20220114175749822"></p><p>勾选同意协议，点击 <code>Next</code>。</p><p><img src="https://unpkg.zhimg.com/ocoke-osg@0.0.14/rawimg/20220114175835.png" alt="image-20220114175835742"></p><p>修改文件安装位置，点击 <code>Next</code>。（也可以不用改）</p><p><img src="https://unpkg.zhimg.com/ocoke-osg@0.0.14/rawimg/20220114175929.png" alt="image-20220114175929226"></p><p>点击 <code>Next</code>。</p><p><img src="https://unpkg.zhimg.com/ocoke-osg@0.0.14/rawimg/20220114180010.png" alt="image-20220114180010036"></p><p><img src="https://unpkg.zhimg.com/ocoke-osg@0.0.14/rawimg/20220114180044.png" alt="image-20220114180043895"></p><p>点击 <code>Install</code> 开始安装。</p><p><img src="https://unpkg.zhimg.com/ocoke-osg@0.0.14/rawimg/20220114180108.png" alt="image-20220114180107827"></p><p>等待安装完成。<img src="https://unpkg.zhimg.com/ocoke-osg@0.0.14/rawimg/20220114180122.png" alt="image-20220114180122728"></p><p>点击 <code>Finish</code> 完成安装。</p><p><img src="https://unpkg.zhimg.com/ocoke-osg@0.0.14/rawimg/20220114180206.png" alt="image-20220114180206024"></p><h2 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h2><p>打开 <code>Git Bash</code>.</p><blockquote><p>在文件夹处右击，点击 <code>Git Bash Here</code> 进入 Git Bash。<br>或是在开始菜单中找到 Git Bash.</p></blockquote><p>输入 <code>node -v</code> 和 <code>npm -v</code> 查看版本，正确输出版本信息即代表安装成功。</p><p><img src="https://unpkg.zhimg.com/ocoke-osg@0.0.14/rawimg/20220114180458.png" alt="image-20220114180458685"></p>]]></content:encoded>
      
      
      <category domain="https://blog.yfun.top/categories/%E5%8D%9A%E5%AE%A2/">博客</category>
      
      
      <category domain="https://blog.yfun.top/tags/%E5%8D%9A%E5%AE%A2/">博客</category>
      
      <category domain="https://blog.yfun.top/tags/Hexo/">Hexo</category>
      
      <category domain="https://blog.yfun.top/tags/NPM/">NPM</category>
      
      <category domain="https://blog.yfun.top/tags/Node-js/">Node.js</category>
      
      
      <comments>https://blog.yfun.top/posts/hexo-blog/install-nodejs/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>2022: 新年快乐！</title>
      <link>https://blog.yfun.top/posts/2022/</link>
      <guid>https://blog.yfun.top/posts/2022/</guid>
      <pubDate>Fri, 31 Dec 2021 16:00:00 GMT</pubDate>
      
      <description>转眼间，2021 年已经过去。这一年非常忙碌，以至于博客一周年的纪念来不及写，许多文章计划停留在了 To Do 里。</description>
      
      
      
      <content:encoded><![CDATA[<p>转眼间，2021 年已经过去。这一年非常忙碌，以至于博客一周年的纪念来不及写，许多文章计划停留在了 To Do 里。</p><h2 id="博客"><a href="#博客" class="headerlink" title="博客"></a>博客</h2><p>由于 Cloudflare Web Analytics 是在 2021 年 4 月份启用的，再加上后来又将统计代码去除，所以关于访问量的信息不可用。</p><p>截止至 2021 年 12 月 31 日，博客共发布 15 篇文章，其中 2021 年发布 11 篇文章，原创文章 10 篇。</p><p>目前采用 <code>LeanCloud + Waline</code> 的评论方式，自 7 月份被刷评论后将评论权限改为登录（支持 <code>GitHub</code> 登录），还有部分评论在迁移中丢失，实在抱歉。</p><h2 id="主题"><a href="#主题" class="headerlink" title="主题"></a>主题</h2><p>博客自 2021 年 1 月 13 日将主题改为 <code>Miracle</code>，这是一款简洁、轻量的单栏 Hexo 主题。</p><p><code>Miralce</code> 目前的版本是 <code>2.0.1</code>，全新的版本去除 jQuery，进一步提升性能。</p><p>GitHub 仓库：<a href="https://github.com/hifun-team/hexo-theme-miracle">https://github.com/hifun-team/hexo-theme-miracle</a></p><h2 id="更多"><a href="#更多" class="headerlink" title="更多"></a>更多</h2><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">=&gt; ...</span><br></pre></td></tr></table></figure>]]></content:encoded>
      
      
      <category domain="https://blog.yfun.top/categories/%E5%8D%9A%E5%AE%A2/">博客</category>
      
      
      <category domain="https://blog.yfun.top/tags/2022/">2022</category>
      
      <category domain="https://blog.yfun.top/tags/%E6%96%B0%E5%B9%B4/">新年</category>
      
      
      <comments>https://blog.yfun.top/posts/2022/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>在 Windows 上快速安装 Hexo · 安装 Git</title>
      <link>https://blog.yfun.top/posts/hexo-blog/install-git/</link>
      <guid>https://blog.yfun.top/posts/hexo-blog/install-git/</guid>
      <pubDate>Fri, 12 Nov 2021 13:30:00 GMT</pubDate>
      
      <description>在 Windows 系统上安装并使用 Hexo 创建博客。本文讲述如何安装 Git。</description>
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p><strong>本文未经允许，禁止转载！</strong></p><p><strong>视频教程正在制作，等待更新。</strong></p><h3 id="文章目录"><a href="#文章目录" class="headerlink" title="文章目录"></a>文章目录</h3><ul><li><a href="https://blog.yfun.top/posts/hexo-blog/install-git">安装 Git</a></li></ul><blockquote><p>更多文章等待更新...</p></blockquote><h2 id="下载"><a href="#下载" class="headerlink" title="下载"></a>下载</h2><p>前往 Git 官网 (<a href="https://git-scm.com/">https://git-scm.com</a>).</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.11/rawimg/20211112204849.png"></p><p>点击页面中的 <code>Download For Windows</code> 下载 Windows 版本。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.11/rawimg/20211112211507.png"></p><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><p>双击运行安装文件。</p><p>点击 <code>Next</code>，同意协议。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.11/rawimg/20211112211528.png"></p><p>修改程序安装位置，点击 <code>Next</code>（直接装在 C 盘也不是不行）。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.11/rawimg/20211112211604.png"></p><p>组件选项，可选桌面快捷键、右键菜单、LFS 支持、自动检查更新等。</p><p>点击 <code>Next</code> 进入下一步。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.11/rawimg/20211112211609.png"></p><p>开始菜单文件夹名，保持默认就可以，点击 <code>Next</code>。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.11/rawimg/20211112211613.png"></p><p>默认编辑器，保持 Vim 就可以，点击 <code>Next</code>。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.11/rawimg/20211112211616.png"></p><p>环境变量，选择第 2 个，点击 <code>Next</code>。</p><blockquote><p>如果选择第 1 个，则只能在 Git Bash 中使用，可能会出现问题</p></blockquote><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.11/rawimg/20211112211619.png"></p><p>剩下的都可以保持默认，一路 Next，然后点击 <code>Install</code>。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.11/rawimg/20211112211622.png"><br><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.11/rawimg/20211112211624.png"><br><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.11/rawimg/20211112211627.png"><br><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.11/rawimg/20211112211630.png"><br><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.11/rawimg/20211112211633.png"></p><p>等待安装完毕。</p><h2 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h2><p>在文件夹处右击，点击 <code>Git Bash Here</code> 进入 Git Bash。</p><p>（或是在开始菜单中找到 Git Bash）</p><p>输入 <code>git --version</code> 查看版本，正确输出版本信息即代表安装成功。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.11/rawimg/7ec315b7764cc.png"></p><p>大功告成！</p>]]></content:encoded>
      
      
      <category domain="https://blog.yfun.top/categories/%E5%8D%9A%E5%AE%A2/">博客</category>
      
      
      <category domain="https://blog.yfun.top/tags/%E5%8D%9A%E5%AE%A2/">博客</category>
      
      <category domain="https://blog.yfun.top/tags/Hexo/">Hexo</category>
      
      <category domain="https://blog.yfun.top/tags/Git/">Git</category>
      
      
      <comments>https://blog.yfun.top/posts/hexo-blog/install-git/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>使用 Python 爬取故宫壁纸</title>
      <link>https://blog.yfun.top/posts/2021/dpm-wallpaper/</link>
      <guid>https://blog.yfun.top/posts/2021/dpm-wallpaper/</guid>
      <pubDate>Sat, 17 Jul 2021 05:55:00 GMT</pubDate>
      
      <description>使用 Python 和 BeautifulSoup4 爬取故宫壁纸</description>
      
      
      
      <content:encoded><![CDATA[<p>我非常喜欢故宫壁纸，但是一张一张下载的速度就太慢了。</p><p>于是，我就写了个简单的 Python 小程序自动爬取故宫壁纸。</p><h2 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h2><p>首先，我们通过简单的查看，知道故宫壁纸的页面并没有使用 JavaScript 载入。</p><p>但是故宫壁纸很多，所以还需要分类。</p><p>故宫壁纸大多都是以分类开头，如 <code>爱上紫禁城 紫藤</code>, <code>清   虚谷紫藤金鱼图轴（局部）</code> 等等，所以一般只需要做个简单的 <code>startswith()</code> 判断就 OK 了。</p><p>很多壁纸的标题都是一样的，所以还可以使用 <code>random</code> 库在文件名末尾追加一个随机数。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> requests, bs4, time, random</span><br><span class="line">path = <span class="string">&quot;./wallpaper/爱上紫禁城&quot;</span></span><br><span class="line"></span><br><span class="line">catch = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, <span class="number">119</span>):</span><br><span class="line">    <span class="comment"># 组合请求 URL</span></span><br><span class="line">    url = <span class="string">&quot;https://www.dpm.org.cn/lights/royal/p/&#123;&#125;.html&quot;</span>.<span class="built_in">format</span>(i)</span><br><span class="line">    <span class="comment"># 发送请求</span></span><br><span class="line">    response = requests.get(url)</span><br><span class="line">    <span class="comment"># 设定页面编码为 UTF-8</span></span><br><span class="line">    response.encoding = <span class="string">&quot;UTF-8&quot;</span></span><br><span class="line">    <span class="comment"># 从 HTML 中解析数据</span></span><br><span class="line">    soup = bs4.BeautifulSoup(response.text, <span class="string">&quot;lxml&quot;</span>)</span><br><span class="line">    image = soup.find_all(name=<span class="string">&quot;div&quot;</span>, class_=<span class="string">&quot;pic&quot;</span>)</span><br><span class="line">    <span class="comment"># 循环输出图片</span></span><br><span class="line">    <span class="keyword">for</span> n <span class="keyword">in</span> image:</span><br><span class="line">        catch = catch + <span class="number">1</span></span><br><span class="line">        <span class="comment"># 获得壁纸名称</span></span><br><span class="line">        <span class="comment"># 分类，不是以【爱上紫禁城】开头的壁纸都忽略</span></span><br><span class="line">        img_name = n.a.img[<span class="string">&quot;title&quot;</span>]</span><br><span class="line">        <span class="keyword">if</span> img_name.startswith(<span class="string">&quot;明&quot;</span>):</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;&#123;&#125;. &#123;&#125;&quot;</span>.<span class="built_in">format</span>(catch, n.a.img[<span class="string">&quot;title&quot;</span>]))</span><br><span class="line">            <span class="comment"># 组合获得壁纸页面</span></span><br><span class="line">            url_1080 = <span class="string">&quot;https://www.dpm.org.cn&quot;</span> + n.a[<span class="string">&quot;href&quot;</span>]</span><br><span class="line">            <span class="comment"># 请求高清版网页</span></span><br><span class="line">            response_img = requests.get(url_1080)</span><br><span class="line">            <span class="comment"># 再次解析</span></span><br><span class="line">            soup_img = bs4.BeautifulSoup(response_img.text, <span class="string">&quot;lxml&quot;</span>)</span><br><span class="line">            <span class="comment"># 获取页面中的图片</span></span><br><span class="line">            data = soup_img.find_all(name=<span class="string">&quot;img&quot;</span>)[<span class="number">0</span>]</span><br><span class="line">            <span class="comment"># 获得壁纸图片的链接</span></span><br><span class="line">            img_url = data[<span class="string">&quot;src&quot;</span>]</span><br><span class="line">            <span class="comment"># 保存图片</span></span><br><span class="line">            pic = requests.get(img_url).content</span><br><span class="line">            file_name = path + img_name + <span class="string">&quot;-&quot;</span> + <span class="built_in">str</span>(random.randint(<span class="number">100000</span>, <span class="number">999999</span>)) + <span class="string">&quot;.jpg&quot;</span></span><br><span class="line">            <span class="comment"># 写入文件</span></span><br><span class="line">            <span class="keyword">with</span> <span class="built_in">open</span>(file_name, <span class="string">&quot;wb&quot;</span>) <span class="keyword">as</span> file:</span><br><span class="line">                file.write(pic)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;!Ignore: &quot;</span> + img_name)</span><br></pre></td></tr></table></figure><h2 id="后"><a href="#后" class="headerlink" title="后"></a>后</h2><p>我把一些我下载下来的壁纸放在了 <a href="https://t.yfun.top/dpm-wallpaper">这里</a>，可以直接预览并下载。</p><p><del>OK，又水了一篇文（</del></p>]]></content:encoded>
      
      
      <category domain="https://blog.yfun.top/categories/%E6%9E%81%E5%AE%A2/">极客</category>
      
      
      <category domain="https://blog.yfun.top/tags/%E5%A3%81%E7%BA%B8/">壁纸</category>
      
      <category domain="https://blog.yfun.top/tags/Python/">Python</category>
      
      <category domain="https://blog.yfun.top/tags/%E7%88%AC%E8%99%AB/">爬虫</category>
      
      
      <comments>https://blog.yfun.top/posts/2021/dpm-wallpaper/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>使用 Prettier 格式化代码</title>
      <link>https://blog.yfun.top/posts/2021/format-code-using-prettier/</link>
      <guid>https://blog.yfun.top/posts/2021/format-code-using-prettier/</guid>
      <pubDate>Thu, 15 Jul 2021 06:45:00 GMT</pubDate>
      
      <description>格式化代码可以提高代码的可读性，使代码更加美观。</description>
      
      
      
      <content:encoded><![CDATA[<p>格式化代码可以提高代码的可读性，使代码更加美观。</p><p>Prettier 支持：</p><ul><li>JavaScript (including experimental features)</li><li>JSX</li><li>Angular</li><li>Vue</li><li>Flow</li><li>TypeScript</li><li>CSS, Less, and SCSS</li><li>HTML</li><li>JSON</li><li>GraphQL</li><li>Markdown, including GFM and MDX</li><li>YAML</li></ul><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><h3 id="插件"><a href="#插件" class="headerlink" title="插件"></a>插件</h3><p>你可以很方便的在 VSCode 等 IDE 上安装插件，插件名为 <code>Prettier</code>.</p><p>以 VSCode 为例：</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.10/rawimg/6a3ad0ea42d55.png"></p><blockquote><p>如果安装了其他格式化插件，则需要点击 [格式化文档的方式]，选择 <code>Prettier</code>。</p></blockquote><h3 id="CLI"><a href="#CLI" class="headerlink" title="CLI"></a>CLI</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">sudo npm i -g prettier</span><br></pre></td></tr></table></figure><p>相关的文档可参见官网: <a href="https://prettier.io/docs/en/cli.html">https://prettier.io/docs/en/cli.html</a></p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">prettier --write . <span class="comment"># 格式化当前文件夹的所有文件</span></span><br></pre></td></tr></table></figure><h2 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h2><p>参见官方文档：</p><ul><li><p><a href="https://prettier.io/docs/en/configuration.html">https://prettier.io/docs/en/configuration.html</a></p></li><li><p><a href="https://prettier.io/docs/en/options.html">https://prettier.io/docs/en/options.html</a></p></li></ul><p>Prettier 的配置文件有多种写法：</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.10/rawimg/a4cc583d84c12.png"></p><p>以使用 <code>YAML</code> 书写的 <code>.prettierrc</code> 为例：</p><figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 单行最长字符数</span></span><br><span class="line"><span class="attr">printWidth:</span> <span class="number">80</span></span><br><span class="line"><span class="comment"># 制表符长度</span></span><br><span class="line"><span class="attr">tabWidth:</span> <span class="number">4</span></span><br><span class="line"><span class="comment"># 是否使用 Tab</span></span><br><span class="line"><span class="attr">useTabs:</span> <span class="literal">false</span></span><br><span class="line"><span class="comment"># 是否加入分号</span></span><br><span class="line"><span class="attr">semi:</span> <span class="literal">true</span></span><br><span class="line"><span class="comment"># 单引号</span></span><br><span class="line"><span class="attr">singleQuote:</span> <span class="literal">false</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ...</span></span><br></pre></td></tr></table></figure><h2 id="效果"><a href="#效果" class="headerlink" title="效果"></a>效果</h2><p>格式化前：</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="title function_">fetch</span>(<span class="string">&quot;https://blog.yfun.top/search.json&quot;</span>).<span class="title function_">then</span>(<span class="function"><span class="params">res</span>=&gt;</span>res.<span class="title function_">json</span>()).<span class="title function_">then</span>(<span class="function">(<span class="params">content</span>) =&gt;</span> &#123;<span class="variable language_">console</span>.<span class="title function_">log</span>(content);<span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; content.<span class="property">length</span>; i++)&#123;<span class="variable language_">console</span>.<span class="title function_">log</span>(content[i][<span class="string">&#x27;title&#x27;</span>] + <span class="string">&quot; --- https://blog.yfun.top&quot;</span> + content[i][<span class="string">&#x27;url&#x27;</span>])&#125;&#125;)</span><br></pre></td></tr></table></figure><p>格式化后：</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="title function_">fetch</span>(<span class="string">&quot;https://blog.yfun.top/search.json&quot;</span>)</span><br><span class="line">    .<span class="title function_">then</span>(<span class="function">(<span class="params">res</span>) =&gt;</span> res.<span class="title function_">json</span>())</span><br><span class="line">    .<span class="title function_">then</span>(<span class="function">(<span class="params">content</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(content);</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; content.<span class="property">length</span>; i++) &#123;</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(</span><br><span class="line">                content[i][<span class="string">&quot;title&quot;</span>] +</span><br><span class="line">                    <span class="string">&quot; --- https://blog.yfun.top&quot;</span> +</span><br><span class="line">                    content[i][<span class="string">&quot;url&quot;</span>]</span><br><span class="line">            );</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br></pre></td></tr></table></figure>]]></content:encoded>
      
      
      <category domain="https://blog.yfun.top/categories/%E6%9E%81%E5%AE%A2/">极客</category>
      
      
      <category domain="https://blog.yfun.top/tags/Prettier/">Prettier</category>
      
      <category domain="https://blog.yfun.top/tags/%E4%BB%A3%E7%A0%81/">代码</category>
      
      
      <comments>https://blog.yfun.top/posts/2021/format-code-using-prettier/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>树莓派超频</title>
      <link>https://blog.yfun.top/posts/1445549919/</link>
      <guid>https://blog.yfun.top/posts/1445549919/</guid>
      <pubDate>Fri, 25 Jun 2021 07:50:00 GMT</pubDate>
      
      <description>在使用桌面系统等情况时，树莓派的性能不是很好，我们可以通过超频来提高性能。本文介绍 Ubuntu Server 20.04 下的操作方法。</description>
      
      
      
      <content:encoded><![CDATA[<div class="note note-info">            <p>本文操作系统为 Ubuntu Server 20.04，其他操作系统可能有所不同。</p>          </div><div class="note note-warning">            <p>超频是有风险的，温度也会更高，需要比较良好的散热。</p>          </div><h2 id="开始"><a href="#开始" class="headerlink" title="开始"></a>开始</h2><p>登录树莓派，进入 <code>/boot/firmware/</code> 目录：</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">cd</span> /boot/firmware/</span><br></pre></td></tr></table></figure><div class="note note-warning">            <p>网上很多教程让你修改 <code>config.txt</code> 文件，但注释里已经很明确的写了：</p><figure class="highlight text"><table><tr><td class="code"><pre><span class="line"># Please DO NOT modify this file; if you need to modify the boot config, the</span><br><span class="line"># &quot;usercfg.txt&quot; file is the place to include user changes. Please refer to</span><br><span class="line"># the README file for a description of the various configuration files on</span><br><span class="line"># the boot partition.</span><br></pre></td></tr></table></figure>          </div><p>我们修改 <code>usercfg.txt</code> 文件：</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">sudo vim usercfg.txt</span><br></pre></td></tr></table></figure><p>在文件末尾追加配置信息，以下是我的，仅供参考：</p><div class="note note-danger">            <p>我使用的是 <code>Raspberry Pi 4 (4GB)</code>，并且有小风扇和散热片。<br>请根据自己的需要和配置进行更改。</p>          </div><figure class="highlight text"><table><tr><td class="code"><pre><span class="line">force_turbo=0</span><br><span class="line">arm_freq=1850</span><br><span class="line">over_voltage=6</span><br><span class="line">gpu_freq=650</span><br><span class="line">core_freq=650</span><br><span class="line">gpu_mem=620</span><br></pre></td></tr></table></figure><h2 id="重启"><a href="#重启" class="headerlink" title="重启"></a>重启</h2><p>保存文件，使用命令重启：</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">sudo reboot</span><br></pre></td></tr></table></figure><h2 id="后"><a href="#后" class="headerlink" title="后"></a>后</h2><p>重启完毕后，再使用 <code>lscpu</code> 命令看看 CPU 状况。</p>]]></content:encoded>
      
      
      <category domain="https://blog.yfun.top/categories/%E6%8A%98%E8%85%BE/">折腾</category>
      
      
      <category domain="https://blog.yfun.top/tags/%E6%A0%91%E8%8E%93%E6%B4%BE/">树莓派</category>
      
      <category domain="https://blog.yfun.top/tags/Ubuntu/">Ubuntu</category>
      
      
      <comments>https://blog.yfun.top/posts/1445549919/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>加速 GitHub 下载</title>
      <link>https://blog.yfun.top/posts/2517388641/</link>
      <guid>https://blog.yfun.top/posts/2517388641/</guid>
      <pubDate>Fri, 18 Jun 2021 09:30:00 GMT</pubDate>
      
      <description>GitHub 在国内的访问情况不太稳定，仓库克隆速度也非常慢。这篇文章介绍几个加速的方法。</description>
      
      
      
      <content:encoded><![CDATA[<p>GitHub 在国内的访问情况不太稳定，仓库克隆速度也非常慢。</p><h2 id="✈️"><a href="#✈️" class="headerlink" title="✈️"></a>✈️</h2><p>不必多说。</p><p>例如 <a href="http://127.0.0.1:7890/">http://127.0.0.1:7890</a> 为代理地址，你可以使用下方的命令更改：</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git config --global http.proxy http://127.0.0.1:7890</span><br></pre></td></tr></table></figure><p>如果有一天不需要了，输入下面代码取消：</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git config --global --<span class="built_in">unset</span> http.proxy</span><br></pre></td></tr></table></figure><h2 id="FastGit"><a href="#FastGit" class="headerlink" title="FastGit"></a>FastGit</h2><p>使用 <a href="https://fastgit.org/">FastGit</a> 提供的 GitHub 镜像。</p><p>镜像地址：<a href="https://hub.fastgit.org/">https://hub.fastgit.org</a></p><p>支持 HTTPS 克隆，并且提供 raw.githubusercontent.com 的镜像，地址为 <a href="https://raw.fastgit.org/">https://raw.fastgit.org</a></p><h2 id="CNPM"><a href="#CNPM" class="headerlink" title="CNPM"></a>CNPM</h2><p>使用 <a href="https://cnpmjs.org/">CNPM</a> 提供的 GitHub 镜像。</p><p>镜像地址：<a href="https://github.com.cnpmjs.org/">https://github.com.cnpmjs.org</a></p><p>支持 HTTPS 克隆。</p><h2 id="Hosts"><a href="#Hosts" class="headerlink" title="Hosts"></a>Hosts</h2><p>通过更改 Hosts 文件的方式解决，但大有可能失效：</p><figure class="highlight text"><table><tr><td class="code"><pre><span class="line">199.232.69.194  github.global.ssl.fastly.net</span><br><span class="line">140.82.113.4    github.com</span><br></pre></td></tr></table></figure><p>GitHub 上的 <a href="https://github.com/521xueweihan/GitHub520">521xueweihan/GitHub520</a> 项目提供了更多关于 GitHub Hosts 列表。</p><blockquote><p><a href="https://github.com/521xueweihan/GitHub520/issues/53">521xueweihan/GitHub520 #53</a></p></blockquote><h2 id="Gitee"><a href="#Gitee" class="headerlink" title="Gitee"></a>Gitee</h2><p>网上挺多教程，使用 Gitee 的「从 GitHub 导入」功能。</p><p>不过 Gitee 限制仓库 500MB。。</p><h2 id="Coding"><a href="#Coding" class="headerlink" title="Coding"></a>Coding</h2><p>进入 Coding 控制台，点击「创建项目」。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.9/rawimg/k7uHJm.png"></p><p>项目模版选择「代码托管」就好，仅做下载的话无需开启太多。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.9/rawimg/bEO0vX.png"></p><p>填写项目基本信息，如名称、标识等。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.9/rawimg/N4xjSv.png"></p><p>新建一个镜像仓库。</p><p>「Git 仓库 URL」填写 GitHub 公开仓库 HTTPS 克隆地址，如：<a href="https://github.com/twbs/bootstrap.git">https://github.com/twbs/bootstrap.git</a></p><p>创建完成后请稍等，系统就会自动导入。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.9/rawimg/YxnnDj.png"></p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.9/rawimg/FmfWkf.png"></p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.9/rawimg/OV7w4v.png"></p><p>导入完成后，点击「克隆」就能找到克隆地址。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.9/rawimg/bWszbu.png"></p><p>克隆速度挺快的：</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">Cloning into <span class="string">&#x27;bootstrap&#x27;</span>...</span><br><span class="line">remote: Enumerating objects: 169214, <span class="keyword">done</span>.</span><br><span class="line">remote: Counting objects: 100% (169214/169214), <span class="keyword">done</span>.</span><br><span class="line">remote: Compressing objects: 100% (51634/51634), <span class="keyword">done</span>.</span><br><span class="line">Receiving objects:  57% (96728/169214), 90.58 MiB | 6.83 MiB/s </span><br></pre></td></tr></table></figure><p>此外，Coding 还支持自动同步，可以设置每天的同步时间。</p>]]></content:encoded>
      
      
      <category domain="https://blog.yfun.top/categories/%E6%9E%81%E5%AE%A2/">极客</category>
      
      
      <category domain="https://blog.yfun.top/tags/GitHub/">GitHub</category>
      
      <category domain="https://blog.yfun.top/tags/%E6%8A%98%E8%85%BE/">折腾</category>
      
      <category domain="https://blog.yfun.top/tags/Git/">Git</category>
      
      
      <comments>https://blog.yfun.top/posts/2517388641/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>GitHub Action 监测京东商品价格</title>
      <link>https://blog.yfun.top/posts/1238639652/</link>
      <guid>https://blog.yfun.top/posts/1238639652/</guid>
      <pubDate>Tue, 18 May 2021 10:32:00 GMT</pubDate>
      
      <description>利用 GitHub Action 自动监测京东商品价格，商品降价时发送通知提醒。</description>
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>我会在京东上买些电子产品和文具，那么如何获得商品降价信息，使用更低的价格购买呢？</p><h2 id="开始"><a href="#开始" class="headerlink" title="开始"></a>开始</h2><p>新建一个 <code>GitHub</code> 仓库，创建 <code>main.py</code> , <code>notify.py</code> 和 <code>.github/workflows/auto.yml</code> 三个文件，内容如下：</p>  <details class="post-details">  <summary><code>main.py</code> (点击展开)</summary><div class="details-content">  <figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="comment"># 导入模块</span></span><br><span class="line"><span class="keyword">import</span> urllib.request, random, json, notify</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 商品列表</span></span><br><span class="line">check = [</span><br><span class="line">    <span class="comment"># 闪迪（SanDisk）64GB TF（MicroSD）存储卡</span></span><br><span class="line">    <span class="string">&#x27;1887526&#x27;</span>,</span><br><span class="line">    <span class="comment"># 晨光(M&amp;G)文具 0.5mm 黑色中性笔</span></span><br><span class="line">    <span class="string">&#x27;277393&#x27;</span></span><br><span class="line"><span class="comment"># ...</span></span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 价格列表，用于临时存储获取到的价格数据</span></span><br><span class="line">price = &#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 读取上次的价格文件</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    file = <span class="built_in">open</span>(<span class="string">&#x27;./price.json&#x27;</span>, mode=<span class="string">&#x27;r&#x27;</span>)</span><br><span class="line">    lastPrice = json.loads(file.readlines()[<span class="number">0</span>])</span><br><span class="line"><span class="keyword">except</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;读取文件时出现错误！&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 遍历列表，比对价格</span></span><br><span class="line"><span class="keyword">for</span> itemId <span class="keyword">in</span> check:</span><br><span class="line">    <span class="comment"># 组合请求 URL</span></span><br><span class="line">    <span class="comment"># http://p.3.cn/prices/mgets?skuIds=J_</span></span><br><span class="line">    url = <span class="string">&#x27;http://p.3.cn/prices/mgets?skuIds=J_&#123;&#125;&#x27;</span>.<span class="built_in">format</span>(itemId)</span><br><span class="line">    <span class="comment"># User-Agent 列表</span></span><br><span class="line">    ua = [<span class="string">&#x27;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36&#x27;</span>,<span class="string">&#x27;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36&#x27;</span>,<span class="string">&#x27;Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60&#x27;</span>,<span class="string">&#x27;Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50&#x27;</span>,<span class="string">&#x27;Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50&#x27;</span>]</span><br><span class="line">    <span class="comment"># 随机获得一个 User-Agent，如不带 User-Agent 请求，就会被拒绝。</span></span><br><span class="line">    headers = &#123;</span><br><span class="line">        <span class="string">&#x27;User-Agent&#x27;</span>: ua[random.randint(<span class="number">0</span>,<span class="number">4</span>)]</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment"># 组合请求参数</span></span><br><span class="line">    request = urllib.request.Request(url = url, headers = headers)</span><br><span class="line">    <span class="comment"># 向京东发起请求</span></span><br><span class="line">    response = urllib.request.urlopen(request)</span><br><span class="line">    <span class="comment"># 将获得的数据转为 JSON</span></span><br><span class="line">    page_text = json.loads(response.read().decode(<span class="string">&#x27;utf-8&#x27;</span>))</span><br><span class="line">    thisPrice = <span class="built_in">float</span>(page_text[<span class="number">0</span>][<span class="string">&#x27;p&#x27;</span>])</span><br><span class="line">    <span class="comment"># 将价格输入至价格变量</span></span><br><span class="line">    price[itemId] = thisPrice</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 尝试对比价格</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">if</span> thisPrice &gt; lastPrice[itemId]:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;&#123;0&#125; 涨价&quot;</span>.<span class="built_in">format</span>(itemId))</span><br><span class="line"></span><br><span class="line">            <span class="built_in">print</span>(thisPrice - lastPrice[itemId])</span><br><span class="line">            notify.notify(itemId, <span class="number">0</span>, thisPrice - lastPrice[itemId], thisPrice)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">elif</span> thisPrice &lt; lastPrice[itemId]:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;&#123;0&#125; 降价&quot;</span>.<span class="built_in">format</span>(itemId))</span><br><span class="line">            <span class="built_in">print</span>(lastPrice[itemId] - thisPrice)</span><br><span class="line">            notify.notify(itemId, <span class="number">2</span>, lastPrice[itemId] - thisPrice, thisPrice)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">elif</span> lastPrice[itemId] == thisPrice:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;&#123;0&#125; 价格不变&quot;</span>.<span class="built_in">format</span>(itemId))</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;&#123;0&#125; 未知错误&quot;</span>.<span class="built_in">format</span>(itemId))</span><br><span class="line">    <span class="keyword">except</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;无法进行比对&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 将 JSON 转为 字符串</span></span><br><span class="line">priceStr = json.dumps(price)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 写入价格文件，便于下次查询</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    file = <span class="built_in">open</span>(<span class="string">&#x27;./price.json&#x27;</span>, mode = <span class="string">&#x27;w&#x27;</span>)</span><br><span class="line">    file.write(priceStr)</span><br><span class="line">    file.close()</span><br><span class="line"><span class="keyword">except</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;写入文件时出现错误&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(price)</span><br><span class="line"></span><br></pre></td></tr></table></figure>    </div></details>  <details class="post-details">  <summary><code>notify.py</code> (点击展开)</summary><div class="details-content">  <figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="keyword">import</span> urllib.request, json, urllib.parse, ssl</span><br><span class="line">ssl._create_default_https_context = ssl._create_unverified_context</span><br><span class="line"></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="string">提醒的服务，支持 `wechat` 与 `qmsg`。</span></span><br><span class="line"><span class="string">需要在下方进行下一步的配置</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line">server = [<span class="string">&#x27;qmsg&#x27;</span>]</span><br><span class="line"></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="string">WeChat 采用的是 Server 酱 (https://sc.ftqq.com/3.version) 提供的服务</span></span><br><span class="line"><span class="string">key：Server 酱提供的 Token，必填。</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line">wechat = &#123;</span><br><span class="line">    <span class="string">&quot;key&quot;</span>: <span class="string">&quot;&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="string">Qmsg 采用的是 Qmsg 酱 (https://qmsg.zendee.cn/) 提供的服务</span></span><br><span class="line"><span class="string">key：Qmsg 酱提供的 KEY，必填。</span></span><br><span class="line"><span class="string">qid：接收消息的 QQ 号（不支持 QQ 群），可以添加多个，以半角逗号分割，如：`10001,10002`（必须均在您的 Qmsg 酱 QQ 号列表中）。</span></span><br><span class="line"><span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line">qmsg = &#123;</span><br><span class="line">    <span class="string">&quot;key&quot;</span>: <span class="string">&quot;&quot;</span>,</span><br><span class="line">    <span class="string">&quot;qid&quot;</span>: <span class="string">&quot;&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">wechat_send</span>(<span class="params">key, title, text</span>):</span><br><span class="line">    url = <span class="string">&#x27;http://sc.ftqq.com/&#123;0&#125;.send?text=&#123;1&#125;&amp;desp=&#123;2&#125;&#x27;</span>.<span class="built_in">format</span>(key, urllib.parse.quote(title), urllib.parse.quote(text))</span><br><span class="line">    headers = &#123;</span><br><span class="line">        <span class="string">&#x27;User-Agent&#x27;</span>: <span class="string">&quot;Check Price Bot&quot;</span></span><br><span class="line">    &#125;</span><br><span class="line">    request = urllib.request.Request(url=url, headers=headers)</span><br><span class="line">    response = urllib.request.urlopen(request)</span><br><span class="line">    page_text = json.loads(response.read().decode(<span class="string">&#x27;utf-8&#x27;</span>))</span><br><span class="line">    <span class="keyword">if</span> page_text[<span class="string">&#x27;errno&#x27;</span>] == <span class="number">0</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">False</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">qmsg_send</span>(<span class="params">key, <span class="built_in">id</span>, text</span>):</span><br><span class="line">    url = <span class="string">&#x27;http://qmsg.zendee.cn/send/&#123;0&#125;?msg=&#123;1&#125;&amp;qq=&#123;2&#125;&#x27;</span>.<span class="built_in">format</span>(key, urllib.parse.quote(text), <span class="built_in">id</span>)</span><br><span class="line">    headers = &#123;</span><br><span class="line">        <span class="string">&#x27;User-Agent&#x27;</span>: <span class="string">&quot;Check Price Bot&quot;</span></span><br><span class="line">    &#125;</span><br><span class="line">    request = urllib.request.Request(url=url, headers=headers)</span><br><span class="line">    response = urllib.request.urlopen(request)</span><br><span class="line">    page_text = json.loads(response.read().decode(<span class="string">&#x27;utf-8&#x27;</span>))</span><br><span class="line">    <span class="keyword">if</span> page_text[<span class="string">&#x27;code&#x27;</span>] == <span class="number">0</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">False</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">notify</span>(<span class="params"><span class="built_in">id</span>,<span class="built_in">type</span>,price,nowPrice</span>):</span><br><span class="line">    <span class="string">&#x27;&#x27;&#x27;</span></span><br><span class="line"><span class="string">    type:</span></span><br><span class="line"><span class="string">        0: 涨价</span></span><br><span class="line"><span class="string">        1: 无变化</span></span><br><span class="line"><span class="string">        2: 降价</span></span><br><span class="line"><span class="string">    id: 商品 ID</span></span><br><span class="line"><span class="string">    price: 差价</span></span><br><span class="line"><span class="string">    nowPrice: 现价</span></span><br><span class="line"><span class="string">    &#x27;&#x27;&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> svId <span class="keyword">in</span> server:</span><br><span class="line">        <span class="keyword">def</span> <span class="title function_">send</span>(<span class="params">title,content</span>):</span><br><span class="line">            <span class="keyword">if</span> svId == <span class="string">&#x27;wechat&#x27;</span>:</span><br><span class="line">                wechat_send(wechat[<span class="string">&quot;key&quot;</span>],title,content)</span><br><span class="line">            <span class="keyword">if</span> svId == <span class="string">&#x27;qmsg&#x27;</span>:</span><br><span class="line">                qmsg_send(qmsg[<span class="string">&quot;key&quot;</span>],qmsg[<span class="string">&quot;qid&quot;</span>],title + <span class="string">&quot;\n&quot;</span> + content)</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">False</span></span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">type</span> == <span class="number">0</span>:</span><br><span class="line">            title = <span class="string">&quot;商品 &#123;0&#125; 涨价!&quot;</span>.<span class="built_in">format</span>(<span class="built_in">id</span>)</span><br><span class="line">            content = <span class="string">&quot;您关注的商品 &#123;0&#125; 涨价咯！价格相较于上次监测，高了 &#123;1&#125; 元，现价 &#123;2&#125; 元。商品详情：https://item.jd.com/&#123;0&#125;.html&quot;</span>.<span class="built_in">format</span>(<span class="built_in">id</span>,price,nowPrice)</span><br><span class="line">            send(title,content)</span><br><span class="line">        <span class="keyword">elif</span> <span class="built_in">type</span> == <span class="number">1</span>:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;价格没有变化，不推送通知。&quot;</span>)</span><br><span class="line">        <span class="keyword">elif</span> <span class="built_in">type</span> == <span class="number">2</span>:</span><br><span class="line">            title = <span class="string">&quot;商品 &#123;0&#125; 降价!&quot;</span>.<span class="built_in">format</span>(<span class="built_in">id</span>)</span><br><span class="line">            content = <span class="string">&quot;您关注的商品 &#123;0&#125; 降价咯！价格相较于上次监测，低了 &#123;1&#125; 元，现价 &#123;2&#125; 元。商品详情：https://item.jd.com/&#123;0&#125;.html&quot;</span>.<span class="built_in">format</span>(<span class="built_in">id</span>, price,nowPrice)</span><br><span class="line">            send(title, content)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">False</span></span><br></pre></td></tr></table></figure>    </div></details>  <details class="post-details">  <summary><code>.github/workflows/auto.yml</code> (点击展开)</summary><div class="details-content">  <figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="attr">name:</span> <span class="string">Check</span> <span class="string">Price</span></span><br><span class="line"></span><br><span class="line"><span class="attr">on:</span></span><br><span class="line">  <span class="attr">push:</span></span><br><span class="line">    <span class="attr">branches:</span> </span><br><span class="line">      <span class="bullet">-</span> <span class="string">master</span></span><br><span class="line">  <span class="attr">schedule:</span></span><br><span class="line">    <span class="comment"># 每 1 小时运行一次</span></span><br><span class="line">    <span class="bullet">-</span> <span class="attr">cron:</span> <span class="string">&quot;0 * * * *&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="attr">jobs:</span></span><br><span class="line">  <span class="attr">check:</span></span><br><span class="line">    <span class="attr">runs-on:</span> <span class="string">ubuntu-latest</span></span><br><span class="line">    <span class="attr">steps:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Clone</span> <span class="string">repository</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">actions/checkout@v2</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">&#x27;Set up Python&#x27;</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">actions/setup-python@v2</span></span><br><span class="line">        <span class="attr">with:</span></span><br><span class="line">          <span class="attr">python-version:</span> <span class="number">3.7</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Run</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">|</span></span><br><span class="line"><span class="string">          python main.py</span></span><br><span class="line"><span class="string"></span>      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Push</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">|</span></span><br><span class="line"><span class="string">          git config --local user.email &quot;icolabot@e.yfun.top&quot;</span></span><br><span class="line"><span class="string">          git config --local user.name &quot;iColaBot&quot;</span></span><br><span class="line"><span class="string">          date +&quot;%Y-%m-%d %H:%M:%S.%N&quot; &gt; date.txt</span></span><br><span class="line"><span class="string">          git add -A</span></span><br><span class="line"><span class="string">          git commit -am &quot;Update price.json&quot;</span></span><br><span class="line"><span class="string">          git push origin master</span></span><br><span class="line"><span class="string"></span></span><br></pre></td></tr></table></figure>    </div></details><h2 id="注意"><a href="#注意" class="headerlink" title="注意"></a>注意</h2><p>计划任务语法有 5 个字段，中间用空格分隔，每个字段代表一个时间单位。</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">┌───────────── 分钟 (0 - 59)</span><br><span class="line">│ ┌───────────── 小时 (0 - 23)</span><br><span class="line">│ │ ┌───────────── 日 (1 - 31)</span><br><span class="line">│ │ │ ┌───────────── 月 (1 - 12 或 JAN-DEC)</span><br><span class="line">│ │ │ │ ┌───────────── 星期 (0 - 6 或 SUN-SAT)</span><br><span class="line">│ │ │ │ │</span><br><span class="line">│ │ │ │ │</span><br><span class="line">│ │ │ │ │</span><br><span class="line">* * * * *</span><br></pre></td></tr></table></figure><p>每个时间字段的含义：</p><table><thead><tr><th align="center">符号</th><th align="center">描述</th><th align="center">举例</th></tr></thead><tbody><tr><td align="center"><code>*</code></td><td align="center">任意值</td><td align="center"><code>* * * * *</code> 每天每小时每分钟</td></tr><tr><td align="center"><code>,</code></td><td align="center">值分隔符</td><td align="center"><code>1,3,4,7 * * * *</code> 每小时的 1 3 4 7 分钟</td></tr><tr><td align="center"><code>-</code></td><td align="center">范围</td><td align="center"><code>1-6 * * * *</code> 每小时的 1-6 分钟</td></tr><tr><td align="center"><code>/</code></td><td align="center">每</td><td align="center"><code>*/15 * * * *</code> 每隔 15 分钟</td></tr></tbody></table><blockquote><p>注：由于 GitHub Actions 的限制，如果设置为 <code>* * * * *</code> 实际的执行频率为每 5 分执行一次。</p></blockquote><h2 id="后"><a href="#后" class="headerlink" title="后"></a>后</h2><p>新建文件 并 配置 <code>notify.py</code> 后，GitHub Action 就会定时执行代码。如果有降价或涨价，就会按照 <code>notify.py</code> 的配置进行通知。</p><p>京东也有降价提醒的功能，但我实在是不想给 京东 开通知权限，经常推送商品广告。</p><p>本文部分内容参考：<a href="https://github.com/justjavac/auto-green#readme">justjavac/auto-green#readme</a></p>]]></content:encoded>
      
      
      <category domain="https://blog.yfun.top/categories/%E6%9E%81%E5%AE%A2/">极客</category>
      
      
      <category domain="https://blog.yfun.top/tags/GitHub/">GitHub</category>
      
      <category domain="https://blog.yfun.top/tags/%E4%BA%AC%E4%B8%9C/">京东</category>
      
      
      <comments>https://blog.yfun.top/posts/1238639652/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>我的图床解决方案</title>
      <link>https://blog.yfun.top/posts/2876015612/</link>
      <guid>https://blog.yfun.top/posts/2876015612/</guid>
      <pubDate>Sat, 08 May 2021 10:30:00 GMT</pubDate>
      
      <description>博客的文章需要插入图片，本文将介绍我采用的图床解决方案。</description>
      
      
      
      <content:encoded><![CDATA[<p>博客的文章经常需要插入图片，如果我将文档与图片放在一起，那么图片的加载速度将会很慢，于是我使用了图床。</p><p>图床的选择有很多，<a href="https://blog.yfun.top/posts/3867215122/">笔者之前写过一篇关于免费图床的汇总</a>，本文就来介绍下我采用的方案：NPM。</p><blockquote><p>本文假设您已有 NPM 账号、GitHub 账号。</p></blockquote><h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h2><h3 id="新建仓库"><a href="#新建仓库" class="headerlink" title="新建仓库"></a>新建仓库</h3><p>新建一个 GitHub 仓库，公开或是私人都可以，此处不再赘述。</p><p>在 Git 仓库中新建 <code>rawimg/.gitkeep</code> 与 <code>webpimg/.gitkeep</code> 两个文件。</p><h3 id="获取令牌"><a href="#获取令牌" class="headerlink" title="获取令牌"></a>获取令牌</h3><p>登录 <a href="https://npmjs.com/">https://npmjs.com</a> ，点击右上角的头像，进入 <code>Access Token</code>。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.8/rawimg/1Xbrlw.png"></p><p>点击页面中的 <code>Generate New Token</code> 获取新的令牌。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.8/rawimg/OQMmsB.png"></p><p>选择 <code>Automation</code>，点击 <code>Generate Token</code> 生成令牌。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.8/rawimg/4Ob3xs.png"></p><p>将生成的令牌复制下来。</p><p>进入 GitHub 仓库，点击 <code>Settings</code>。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.8/rawimg/UfBqld.png"></p><p>点击菜单栏中的 <code>Secrets</code>。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.8/rawimg/SStNlF.png"></p><p>点击 <code>New repository secret</code> 新建一个 Secret。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.8/rawimg/UYSnao.png"></p><p>Secret Name 为 <code>NPM_TOKEN</code>，Value 是你的 NPM 令牌，点击 <code>Add secret</code> 添加。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.8/rawimg/OY4C6A.png"></p><p>在仓库中新建 <code>package.json</code> 文件，参考如下：</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ocoke-osg&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;version&quot;</span><span class="punctuation">:</span> <span class="string">&quot;0.0.0&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;description&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Somewhere to save the file from @oCoke&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;author&quot;</span><span class="punctuation">:</span> <span class="string">&quot;YFun(@oCoke)&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h2 id="图片转换与发布"><a href="#图片转换与发布" class="headerlink" title="图片转换与发布"></a>图片转换与发布</h2><p>为了方便多端写作，我使用 GitHub 临时存储所需的图片，GitHub Action 发布 NPM 包。</p><p>WebP 可以大大缩减图片的尺寸，我们还可以借助 GitHub Action 在发布前自动转换。</p>  <details class="post-details">  <summary>GitHub Action 代码 (点击展开)</summary><div class="details-content">  <figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="attr">name:</span> <span class="string">NPM</span> <span class="string">&amp;</span> <span class="string">WebP</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 在 Release 发布时 或手动执行</span></span><br><span class="line"></span><br><span class="line"><span class="attr">on:</span></span><br><span class="line">  <span class="attr">release:</span></span><br><span class="line">     <span class="attr">types:</span></span><br><span class="line">       <span class="bullet">-</span> <span class="string">published</span></span><br><span class="line"></span><br><span class="line">  <span class="attr">workflow_dispatch:</span></span><br><span class="line"></span><br><span class="line"><span class="attr">jobs:</span></span><br><span class="line">  <span class="attr">publish-npm:</span></span><br><span class="line">    <span class="attr">runs-on:</span> <span class="string">ubuntu-latest</span></span><br><span class="line">    <span class="comment"># Clone 仓库</span></span><br><span class="line">    <span class="attr">steps:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Checkout</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">actions/checkout@v2</span></span><br><span class="line">        <span class="attr">with:</span></span><br><span class="line">          <span class="attr">ref:</span> <span class="string">master</span></span><br><span class="line">      <span class="comment"># 安装 Node.js    </span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Setup</span> <span class="string">Node</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">actions/setup-node@v1</span></span><br><span class="line">        <span class="attr">with:</span></span><br><span class="line">          <span class="attr">node-version:</span> <span class="string">&quot;12.x&quot;</span></span><br><span class="line">          <span class="attr">registry-url:</span> <span class="string">https://registry.npmjs.org/</span></span><br><span class="line">        </span><br><span class="line">      <span class="comment"># rawimg/ 作为原始图片存储，webpimg/ 作为压缩图片存储处，最后合并。</span></span><br><span class="line">      <span class="comment"># 安装相关插件，转换图片。</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Install</span> <span class="string">&amp;</span> <span class="string">Convert</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">|</span></span><br><span class="line"><span class="string">          npm install -g webp-batch-convert</span></span><br><span class="line"><span class="string">          cwebp-batch --in rawimg --out webpimg -q 75 -quiet</span></span><br><span class="line"><span class="string">          mv webpimg/*.webp rawimg/</span></span><br><span class="line"><span class="string"></span>          </span><br><span class="line">      <span class="comment"># 发布 NPM 包</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Publish</span> <span class="string">Package</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">|</span></span><br><span class="line"><span class="string">          git config --global user.email &quot;icolabot@e.yfun.top&quot;</span></span><br><span class="line"><span class="string">          git config --global user.name &quot;iColaBot&quot;</span></span><br><span class="line"><span class="string">          npm version patch</span></span><br><span class="line"><span class="string">          npm publish</span></span><br><span class="line"><span class="string"></span>        <span class="attr">env:</span></span><br><span class="line">          <span class="attr">NODE_AUTH_TOKEN:</span> <span class="string">$&#123;&#123;secrets.npm_token&#125;&#125;</span></span><br><span class="line">          </span><br><span class="line">      <span class="comment"># 删除 rawimg/ 和 webpimg/ 下的所有文件</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Delete</span> <span class="string">Files</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">|</span></span><br><span class="line"><span class="string">          rm -rf webpimg/*</span></span><br><span class="line"><span class="string">          rm -rf rawimg/*</span></span><br><span class="line"><span class="string">          touch webpimg/.gitkeep</span></span><br><span class="line"><span class="string">          touch rawimg/.gitkeep</span></span><br><span class="line"><span class="string"></span>          </span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Push</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">|</span></span><br><span class="line"><span class="string">          git add -A</span></span><br><span class="line"><span class="string">          git commit -m &quot;Publish&quot;</span></span><br><span class="line"><span class="string">          git push origin master</span></span><br></pre></td></tr></table></figure>    </div></details><h2 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h2><h3 id="上传"><a href="#上传" class="headerlink" title="上传"></a>上传</h3><p>将图片文件上传至仓库的 <code>rawimg/</code> 文件夹下即可。</p><p>当然也可以使用 PicGo / UPic / HexoPlusPlus 等工具上传。</p><h3 id="发布"><a href="#发布" class="headerlink" title="发布"></a>发布</h3><p>在 GitHub 中新建 Release，将自动修改版本号并发布 NPM 包，无需手动修改 <code>package.json</code>。</p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.8/rawimg/t2afFy.png"></p><p><img src="https://cdn.jsdelivr.net/npm/ocoke-osg@0.0.8/rawimg/0wAmRV.png"></p><h3 id="访问"><a href="#访问" class="headerlink" title="访问"></a>访问</h3><h4 id="推荐的镜像"><a href="#推荐的镜像" class="headerlink" title="推荐的镜像"></a>推荐的镜像</h4><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">https://cdn.jsdelivr.net/npm/  <span class="comment"># jsDelivr</span></span><br><span class="line">https://unpkg.zhimg.com/ <span class="comment"># 知乎</span></span><br><span class="line">https://code.bdstatic.com/npm/ <span class="comment"># 百度 (不推荐)</span></span><br><span class="line">https://shadow.elemecdn.com/npm/ <span class="comment"># 饿了么</span></span><br><span class="line">https://unpkg.com/ <span class="comment"># Unpkg</span></span><br></pre></td></tr></table></figure><p>镜像推荐选择访问速度快的，比较稳定的，拉取速度快的。</p><p>我选择的是 jsDelivr，国内外速度都很优秀。</p><h4 id="链接"><a href="#链接" class="headerlink" title="链接"></a>链接</h4><p>以 jsDelivr 为例，原图链接为：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">https://cdn.jsdelivr.net/npm/[package-name]@[version]/rawimg/[filename].[suffix]</span><br></pre></td></tr></table></figure><p>WebP 图片链接为：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">https://cdn.jsdelivr.net/npm/[package-name]@[version]/rawimg/[filename].webp</span><br></pre></td></tr></table></figure><table><thead><tr><th>[值]</th><th>说明</th></tr></thead><tbody><tr><td><code>package-name</code></td><td>NPM 包的名称 (<code>package.json</code> 文件中 <code>name</code> 的值)</td></tr><tr><td><code>version</code></td><td>当前版本 (<code>package.json</code> 文件中 <code>version</code> 的值，通常需要在发布 Release 1 分钟后更新)</td></tr><tr><td><code>filename</code></td><td>文件名</td></tr><tr><td><code>suffix</code></td><td>文件后缀名</td></tr></tbody></table>]]></content:encoded>
      
      
      <category domain="https://blog.yfun.top/categories/%E5%8D%9A%E5%AE%A2/">博客</category>
      
      
      <category domain="https://blog.yfun.top/tags/%E5%9B%BE%E5%BA%8A/">图床</category>
      
      <category domain="https://blog.yfun.top/tags/%E6%8A%98%E8%85%BE/">折腾</category>
      
      <category domain="https://blog.yfun.top/tags/NPM/">NPM</category>
      
      <category domain="https://blog.yfun.top/tags/WebP/">WebP</category>
      
      
      <comments>https://blog.yfun.top/posts/2876015612/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>使用 JS 检测网址能否正常加载</title>
      <link>https://blog.yfun.top/posts/856484826/</link>
      <guid>https://blog.yfun.top/posts/856484826/</guid>
      <pubDate>Fri, 09 Apr 2021 12:42:00 GMT</pubDate>
      
      <description>有时我们需要判断用户能否加载某些网页或服务，我们可以用 JavaScript 来实现这样的功能。</description>
      
      
      
      <content:encoded><![CDATA[<p>有时我们需要判断用户能否加载某些网页或服务，如 Google、Disqus 等。</p><p>而我们可以用 JavaScript 来实现这样的检测功能。</p><h2 id="方法一"><a href="#方法一" class="headerlink" title="方法一"></a>方法一</h2><p>使用 JavaScript 创建一个图片，设置图片的链接为 检测网址 + <code>favicon.ico</code>。</p><p><code>onerror</code> 事件触发不能加载的事件，<code>onload</code> 事件触发可以加载的事件，并使用 <code>setTimeout</code> 设置超时时间。</p><p>代码：</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 新建一个图片</span></span><br><span class="line"><span class="keyword">var</span> img = <span class="keyword">new</span> <span class="title class_">Image</span>();</span><br><span class="line"><span class="comment">// 设置图片的链接</span></span><br><span class="line">img.<span class="property">src</span> = <span class="string">&quot;https://google.com/favicon.ico&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 设定超时事件，延迟 3000 毫秒</span></span><br><span class="line"><span class="keyword">var</span> timeout = <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">    <span class="comment">// 清除 onerror 和 onload 事件</span></span><br><span class="line">img.<span class="property">onerror</span> = img.<span class="property">onload</span> = <span class="literal">null</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;time out.&quot;</span>);</span><br><span class="line">    <span class="comment">// 执行无法加载的事件</span></span><br><span class="line">    <span class="title function_">canNotLoad</span>();</span><br><span class="line">&#125;,<span class="number">3000</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 设定错误事件</span></span><br><span class="line">img.<span class="property">onerror</span> = <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">    <span class="comment">// 先清除超时事件</span></span><br><span class="line"><span class="built_in">clearTimeout</span>(timeout);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;load error.&quot;</span>);</span><br><span class="line">    <span class="comment">// 执行无法加载的事件</span></span><br><span class="line">    <span class="title function_">canNotLoad</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 设定可以加载的事件</span></span><br><span class="line">img.<span class="property">onload</span> = <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">    <span class="comment">// 先清除超时事件</span></span><br><span class="line"><span class="built_in">clearTimeout</span>(timeout);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;ok.&quot;</span>);</span><br><span class="line">    <span class="comment">// 执行可以加载的事件</span></span><br><span class="line">    <span class="title function_">canLoad</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">canNotLoad</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="comment">/* code here */</span></span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;无法加载&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">canLoad</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="comment">/* code here */</span></span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;可以加载&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>缺点：不是每一个网站都存在 <code>/favicon.ico</code> 文件，但如果加载特定文件的话挺好的。</p><h2 id="方法二"><a href="#方法二" class="headerlink" title="方法二"></a>方法二</h2><p>使用 JavaScript 创建一个 iframe 标签，设置标签的链接为 检测网址。</p><p><code>onerror</code> 事件触发不能加载的事件，<code>onload</code> 事件触发可以加载的事件，并使用 <code>setTimeout</code> 设置超时时间。</p><p>代码：</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 新建 iframe 标签</span></span><br><span class="line"><span class="keyword">var</span> iframe = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&quot;iframe&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 设置 iframe 标签的网址</span></span><br><span class="line">iframe.<span class="property">src</span>=<span class="string">&quot;https://google.com&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 设定超时事件，延迟 3000 毫秒</span></span><br><span class="line"><span class="keyword">var</span> timeout = <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">    <span class="comment">// 清除 onerror 和 onload 事件</span></span><br><span class="line">    iframe.<span class="property">onerror</span> = iframe.<span class="property">onload</span> = <span class="literal">null</span>;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;time out.&quot;</span>);</span><br><span class="line">    <span class="comment">// 执行无法加载的事件</span></span><br><span class="line">    <span class="title function_">canNotLoad</span>();</span><br><span class="line">&#125;,<span class="number">3000</span>);</span><br><span class="line"><span class="comment">// 设定错误事件</span></span><br><span class="line">iframe.<span class="property">onerror</span> = <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">    <span class="comment">// 先清除超时事件</span></span><br><span class="line">    <span class="built_in">clearTimeout</span>(timeout);</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;load error.&quot;</span>);</span><br><span class="line">    <span class="comment">// 执行无法加载的事件</span></span><br><span class="line">    <span class="title function_">canNotLoad</span>();</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 设定可以加载的事件</span></span><br><span class="line">iframe.<span class="property">onload</span> = <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">    <span class="comment">// 先清除超时事件</span></span><br><span class="line">    <span class="built_in">clearTimeout</span>(timeout);</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;ok.&quot;</span>);</span><br><span class="line">    <span class="comment">// 执行可以加载的事件</span></span><br><span class="line">    <span class="title function_">canLoad</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">canNotLoad</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="comment">/* code here */</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">canLoad</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="comment">/* code here */</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 设定为不可见</span></span><br><span class="line">iframe.<span class="property">style</span> = <span class="string">&quot;display: none!important;&quot;</span>;</span><br><span class="line"><span class="comment">// 向 body 添加 iframe</span></span><br><span class="line"><span class="variable language_">document</span>.<span class="property">body</span>.<span class="title function_">appendChild</span>(iframe);</span><br></pre></td></tr></table></figure><p>缺点：页面上的其他外部链接也会被一并的加载过来，例如 JS 文件，CSS 文件，统计代码。</p><p>您也可以 <a href="https://codepen.io/ocoke-the-decoder/pen/dyNZEwq">点击此链接</a> 前往 CodePen 体验。</p>]]></content:encoded>
      
      
      <category domain="https://blog.yfun.top/categories/%E6%9E%81%E5%AE%A2/">极客</category>
      
      
      <category domain="https://blog.yfun.top/tags/JavaScript/">JavaScript</category>
      
      <category domain="https://blog.yfun.top/tags/Web/">Web</category>
      
      
      <comments>https://blog.yfun.top/posts/856484826/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>树莓派 4 揽件日志</title>
      <link>https://blog.yfun.top/posts/229627020/</link>
      <guid>https://blog.yfun.top/posts/229627020/</guid>
      <pubDate>Fri, 19 Mar 2021 11:16:00 GMT</pubDate>
      
      <description>树莓派 4 的揽件日志</description>
      
      
      
      <content:encoded><![CDATA[<p>前两天在淘宝上购买了一台 4GB 版本的 树莓派 4 。</p><p>在安装系统时我选择了 <code>Ubuntu Server</code> 系统，因为我并不打算使用图形化界面，而且 Raspbian 系统只有 32 位的 ╮(╯▽╰)╭。</p><p>而商家发的是中通快递，速度慢不说，外包装都给我压烂了。(ó﹏ò｡)</p><p>商家随主板还发货了一个闪迪 32GB TF 卡（读写速度还可以），一个电源，一个外壳还有一个小风扇。</p><p>目前先折腾折腾，玩一玩，后面继续写文章。</p><p>目前没有把网站迁移上去的想法，因为这样的话网站会变得很不稳定。</p>]]></content:encoded>
      
      
      <category domain="https://blog.yfun.top/categories/%E6%8A%98%E8%85%BE/">折腾</category>
      
      
      <category domain="https://blog.yfun.top/tags/%E6%A0%91%E8%8E%93%E6%B4%BE/">树莓派</category>
      
      <category domain="https://blog.yfun.top/tags/%E6%8F%BD%E4%BB%B6/">揽件</category>
      
      <category domain="https://blog.yfun.top/tags/%E6%8A%98%E8%85%BE/">折腾</category>
      
      
      <comments>https://blog.yfun.top/posts/229627020/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>为网站加入友好的深色模式支持</title>
      <link>https://blog.yfun.top/posts/175456095/</link>
      <guid>https://blog.yfun.top/posts/175456095/</guid>
      <pubDate>Fri, 22 Jan 2021 04:29:38 GMT</pubDate>
      
      <description>无意间看到 Sukka 大佬的文章：「你好黑暗，我的老朋友 —— 为网站添加用户友好的深色模式支持」，跟着文章重构了主题深色模式的代码，就转载过来方便学习。</description>
      
      
      
      <content:encoded><![CDATA[<div class="note note-info">            <div class="title">转载文章</div>原文标题：你好黑暗，我的老朋友 —— 为网站添加用户友好的深色模式支持<p>原文链接：<a href="https://blog.skk.moe/post/hello-darkmode-my-old-friend/">https://blog.skk.moe/post/hello-darkmode-my-old-friend/</a><br>原文作者：Sukka</p>          </div><p>前几天为我的 Hexo 主题：Miracle 加入了深色模式，但我的技术还是太辣鸡，经常出现问题。</p><p>无意间看到 Sukka 大佬的文章：「你好黑暗，我的老朋友 —— 为网站添加用户友好的深色模式支持」，跟着文章重构了主题深色模式的代码，就转载过来方便学习。</p><h2 id="什么是「深色模式」"><a href="#什么是「深色模式」" class="headerlink" title="什么是「深色模式」"></a>什么是「深色模式」</h2><p>很多操作系统在日落后会自动切换到「深色模式」、并不意味着「深色模式」就是「夜间模式」。「夜间模式」用于夜晚的弱光环境，主要目的是保护眼睛、减少强光刺激、避免影响睡眠，不难理解为什么 macOS 的 Night Shift 会自动调节屏幕色温、Android（AOSP）到了夜间可以选择启用系统级「琥珀色」滤镜。</p><img webp-comp src="https://cdn.jsdelivr.net/npm/sks@0.0.6/macos-settings.png"><p>「深色模式」更像是一个主题，即使在白天也可以使用。不论是为了在 OLED 屏幕上省电、亦或是减少白光刺激护眼、亦或是暗色模式对色盲用户更加友好，总之 macOS 率先提出了系统级的「暗色模式」、并在 WebKit 中增加了对应的 Media Query，而后 Chromium、Firefox 先后跟进，如今兼容 <code>prefers-color-scheme</code> 的浏览器占有率已经高达 81.82%。</p><h2 id="利用-Media-Query-简单实现深色模式"><a href="#利用-Media-Query-简单实现深色模式" class="headerlink" title="利用 Media Query 简单实现深色模式"></a>利用 Media Query 简单实现深色模式</h2><p>CSS 媒体查询 <code>@media</code> 是一个足够强大的特性，可以有条件地将样式应用于文档和各种上下文中。<a href="https://drafts.csswg.org/mediaqueries-5/">Media Queries Level 5 草案</a> 中提出了深色模式的判断方式 <code>prefers-color-scheme</code>，包含 <code>light</code>、<code>dark</code>、<code>no-preference</code> 三种值。而不支持 Media Queries 5 的浏览器会直接无视 CSS 中的 <code>prefers-color-scheme</code> Media Query，无需额外的代码即可优雅降级。</p><p>还记得我刚刚说过「深色模式更像一个主题」么？为网站新增深色模式就如同换肤功能；搭配 <code>prefers-color-scheme</code>，编写深色模式的思路就如同编写响应式一般、无需赘述，结合几段 Code Snippet 一笔带过：</p><h3 id="CSS-Variable-的方法实现深色模式"><a href="#CSS-Variable-的方法实现深色模式" class="headerlink" title="CSS Variable 的方法实现深色模式"></a>CSS Variable 的方法实现深色模式</h3><figure class="highlight css"><table><tr><td class="code"><pre><span class="line"><span class="selector-pseudo">:root</span> &#123;</span><br><span class="line">  <span class="attr">--text</span>: <span class="number">#333</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">@media</span> (<span class="attribute">prefers-color-scheme</span>: dark) &#123;</span><br><span class="line">  <span class="selector-pseudo">:root</span> &#123;</span><br><span class="line">    <span class="attr">--color-text</span>: <span class="number">#fff</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-tag">body</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: <span class="built_in">var</span>(--color-text);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>通过维护两套 CSS Variable，可以快速切换不同的配色方案。这种方法特点是所需代码较少，缺点是 CSS Variable 的兼容性较差，可能还需要引入额外的 Polyfill。</p><h3 id="为深色模式单独编写样式"><a href="#为深色模式单独编写样式" class="headerlink" title="为深色模式单独编写样式"></a>为深色模式单独编写样式</h3><figure class="highlight css"><table><tr><td class="code"><pre><span class="line"><span class="selector-tag">body</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: <span class="number">#333</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">@media</span> (<span class="attribute">prefers-color-scheme</span>: dark) &#123;</span><br><span class="line">  <span class="selector-tag">body</span> &#123;</span><br><span class="line">    <span class="attribute">color</span>: <span class="number">#fff</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>直接维护两套样式的方法清晰直观、任何网站都可以基于这种方法进行改造。但会造成冗余代码、较难实现统一的风格、后期不易维护。</p><h3 id="条件性加载深色模式的-CSS-文件"><a href="#条件性加载深色模式的-CSS-文件" class="headerlink" title="条件性加载深色模式的 CSS 文件"></a>条件性加载深色模式的 CSS 文件</h3><figure class="highlight css"><table><tr><td class="code"><pre><span class="line"><span class="comment">/* main.css */</span></span><br><span class="line"><span class="selector-tag">body</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: <span class="number">#333</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* dark.css */</span></span><br><span class="line"><span class="selector-tag">body</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: <span class="number">#fff</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;stylesheet&quot;</span> <span class="attr">href</span>=<span class="string">&quot;main.css&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;stylesheet&quot;</span> <span class="attr">href</span>=<span class="string">&quot;dark.css&quot;</span> <span class="attr">media</span>=<span class="string">&quot;(prefers-color-scheme: dark)&quot;</span>&gt;</span></span><br></pre></td></tr></table></figure><p>利用 <code>&lt;link&gt;</code> 标签的 Media Query，甚至可以单独加载暗色模式的 CSS 文件。</p><blockquote><p>需要注意 CSS 选择器的权重，因此作为可选的 <code>dark.css</code> 一定要放在 <code>main.css</code> 之后加载。</p></blockquote><p>除了上述三种方式以外，使用 CSS <code>filter</code> 或 <code>mix-blend-mode</code> 还可以实现对网站整体色调的改变，可以确保配色风格的统一性。</p><h2 id="「深色模式」的兼容性"><a href="#「深色模式」的兼容性" class="headerlink" title="「深色模式」的兼容性"></a>「深色模式」的兼容性</h2><p>虽然有了优雅的 <code>prefers-color-scheme</code> 可以识别操作系统的显示模式，但是对于用户来说，仅依赖 Media Query 的「深色模式」并不能带来很好的体验。<br>首先是浏览器兼容性。虽然支持该特性的浏览器的市场占有率非常喜人，但是从版本号上来看却并不乐观：</p><img webp-comp src="https://cdn.jsdelivr.net/npm/sks@0.0.6/caniuse.png"><p>考虑到使用 Chormium 70 内核甚至 Tencent X5 内核的国产浏览器，大部分用户并没有机会体验到深色模式。除此以外，操作系统级别的「深色模式」实现也会受到 OEM 厂商的影响 —— 虽然 Android 10（AOSP）提供「深色模式」，但是一加的 OxygenOS 却将其深藏在系统主题设置里，没有自动切换、在 Quick Settings 里也没有快速的切换开关。</p><h2 id="设计一个用户友好的「深色模式」"><a href="#设计一个用户友好的「深色模式」" class="headerlink" title="设计一个用户友好的「深色模式」"></a>设计一个用户友好的「深色模式」</h2><p>受限于兼容性和复杂的操作系统，大部分网站依然在使用更传统的「开关」切换 —— 通过 toggle <code>&lt;html&gt;</code> 或<br><code>&lt;body&gt;</code> 的 class 属性实现在两套样式之间切换、并将开关的状态记忆在 localStorage 中的方法虽然有效，却是无奈之举，手动切换开关相比 <code>prefers-color-scheme</code> 也不够优雅。如果将「开关」和 <code>prefers-color-scheme</code> 结合起来，就可以带来更好的用户体验：</p><ul><li>对于不兼容的浏览器或操作系统，访客依然可以通过开关手动切换显示模式</li><li>对于兼容的浏览器或操作系统，Media Query 能够实现在两种显示模式之间切换</li><li>在兼容的浏览器或操作系统上，用户还可以通过开关 override 当前的显示模式</li></ul><p>在将两者组合在一起时，不能简单地用「开关」覆盖 <code>prefers-color-scheme</code>，否则用户触发开关、状态被永久记忆在 localStorage 之后，就变成了僵硬的手动模式。<br>举个例子。访客可能在操作系统还没有自动切换到「深色模式」时通过网站上的开关切换显示模式，经过一个夜晚后到了次日白天、访客再度访问网站时，自然希望不需要再切换开关、网站就能以常规的浅色模式显示。因此设计思路是当 <code>prefers-color-scheme</code> 的值发生改变（从 与用户需要的显示模式不同 变成 相同）时清空 localStorage 中储存的开关状态，此时显示模式切换回基于 Media Query 的「自动」模式。</p><p><strong>Talk is cheap, here goes the code.</strong></p><p>首先是 CSS：</p><figure class="highlight css"><table><tr><td class="code"><pre><span class="line"><span class="selector-pseudo">:root</span> &#123;</span><br><span class="line">  <span class="attr">--color-mode</span>: <span class="string">&#x27;light&#x27;</span>;</span><br><span class="line">  <span class="attr">--text</span>: <span class="number">#333</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">@media</span> (<span class="attribute">prefers-color-scheme</span>: dark) &#123;</span><br><span class="line">  <span class="selector-pseudo">:root</span> &#123;</span><br><span class="line">    <span class="attr">--color-mode</span>: <span class="string">&#x27;dark&#x27;</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="selector-pseudo">:root</span><span class="selector-pseudo">:not</span>(<span class="selector-attr">[data-user-color-scheme]</span>) &#123;</span><br><span class="line">    <span class="attr">--text</span>: <span class="number">#eff</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-attr">[data-user-color-scheme=<span class="string">&#x27;dark&#x27;</span>]</span> &#123;</span><br><span class="line">  <span class="attr">--text</span>: <span class="number">#eff</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-tag">body</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: <span class="built_in">var</span>(--color-text);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>真是令人看的头大，让我们逐行来看都是些什么：</p><ul><li>在 <code>:root</code> 下定义了一个 CSS Variable <code>--color-mode: light</code> 和在浅色模式下用到的 CSS Variable（比如使用深色 <code>#333</code> 作为主要字体颜色）。</li><li>使用 <code>prefers-color-scheme</code> 的 Media Query 定义深色模式下的 CSS Variable： <code>--color-mode: light</code> 。深色模式的样式（如浅色 <code>#eff</code> 作为主要字体颜色）要定义在 <code>:not([data-user-color-scheme])</code> 伪类下以避免「开关」的行为覆盖浏览器的样式。</li><li>为 <code>[data-user-color-scheme=&#39;dark&#39;]</code> 再定义一遍深色模式下用到的样式。<br>有了这段 CSS，不难理解深色模式何时会生效：当操作系统使用「深色模式」且 <code>&lt;html&gt;</code> 或 <code>&lt;body&gt;</code> 标签上没有 <code>data-user-color-scheme</code> 属性时、或者存在 <code>data-user-color-scheme</code> 属性且值为 <code>dark</code> 时。</li></ul><p>然后是困难的部分了：编写 JavaScript 为「开关」添加行为。</p><p>先定义一些常量：</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> rootElement = <span class="variable language_">document</span>.<span class="property">documentElement</span>; <span class="comment">// &lt;html&gt;</span></span><br><span class="line"><span class="keyword">const</span> darkModeStorageKey = <span class="string">&#x27;user-color-scheme&#x27;</span>; <span class="comment">// 作为 localStorage 的 key</span></span><br><span class="line"><span class="keyword">const</span> darkModeMediaQueryKey = <span class="string">&#x27;--color-mode&#x27;</span>;</span><br><span class="line"><span class="keyword">const</span> rootElementDarkModeAttributeName = <span class="string">&#x27;data-user-color-scheme&#x27;</span>;</span><br><span class="line"><span class="keyword">const</span> darkModeTogglebuttonElement = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="comment">/* element id */</span>);</span><br></pre></td></tr></table></figure><p>接下来，用 <code>try &#123;&#125; catch (e) &#123;&#125;</code> 封装一下 localStorage 的操作，以应对 HTML5 Storage 被禁用、localStorage 被写满、localStorage 实现不完整的情况：</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">setLS</span> = (<span class="params">k, v</span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="variable language_">localStorage</span>.<span class="title function_">setItem</span>(k, v);</span><br><span class="line">  &#125; <span class="keyword">catch</span> (e) &#123; &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">removeLS</span> = (<span class="params">k</span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="variable language_">localStorage</span>.<span class="title function_">removeItem</span>(k);</span><br><span class="line">  &#125; <span class="keyword">catch</span> (e) &#123; &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">getLS</span> = (<span class="params">k</span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="variable language_">localStorage</span>.<span class="title function_">getItem</span>(k);</span><br><span class="line">  &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span> <span class="comment">// 与 localStorage 中没有找到对应 key 的行为一致</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们还需要一个函数读取当前 <code>prefers-color-scheme</code> 的方法。由于已经在 CSS 中定义了 <code>--color-mode</code>，所以在 JS 中直接读取就好了：</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">getModeFromCSSMediaQuery</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">const</span> res = <span class="title function_">getComputedStyle</span>(rootElement).<span class="title function_">getPropertyValue</span>(darkModeMediaQueryKey);</span><br><span class="line">  <span class="keyword">if</span> (res.<span class="property">length</span>) <span class="keyword">return</span> res.<span class="title function_">replace</span>(<span class="regexp">/\&quot;/g</span>, <span class="string">&#x27;&#x27;</span>).<span class="title function_">trim</span>();</span><br><span class="line">  <span class="keyword">return</span> res === <span class="string">&#x27;dark&#x27;</span> ? <span class="string">&#x27;dark&#x27;</span> : <span class="string">&#x27;light&#x27;</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 使用 matchMedia API 的写法会优雅的多</span></span><br><span class="line">  <span class="comment">// return window.matchMedia(&#x27;(prefers-color-scheme: dark)&#x27;).matches ? &#x27;dark&#x27; : &#x27;light&#x27;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>还记得我们需要自动取消手动模式回到 <code>prefers-color-scheme</code> 么？意味着我们需要一个函数清掉 LS、删掉 <code>&lt;html&gt;</code> 存在的 <code>data-user-color-scheme</code> 属性：</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">resetRootDarkModeAttributeAndLS</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line">  rootElement.<span class="title function_">removeAttribute</span>(rootElementDarkModeAttributeName);</span><br><span class="line">  <span class="title function_">removeLS</span>(darkModeStorageKey);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>接下来是起主要作用的函数了，负责为 <code>&lt;html&gt;</code> 标签修改 <code>data-user-color-scheme</code> 属性：</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> validColorModeKeys = &#123;</span><br><span class="line">  <span class="string">&#x27;dark&#x27;</span>: <span class="literal">true</span>,</span><br><span class="line">  <span class="string">&#x27;light&#x27;</span>: <span class="literal">true</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">applyCustomDarkModeSettings</span> = (<span class="params">mode</span>) =&gt; &#123;</span><br><span class="line">  <span class="comment">// 接受从「开关」处传来的模式，或者从 localStorage 读取</span></span><br><span class="line">  <span class="keyword">const</span> currentSetting = mode || <span class="title function_">getLS</span>(darkModeStorageKey);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (currentSetting === <span class="title function_">getModeFromCSSMediaQuery</span>()) &#123;</span><br><span class="line">    <span class="comment">// 当用户自定义的显示模式和 prefers-color-scheme 相同时重置、恢复到自动模式</span></span><br><span class="line">    <span class="title function_">resetRootDarkModeAttributeAndLS</span>();</span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (validColorModeKeys[currentSetting]) &#123; <span class="comment">// 相比 Array#indexOf，这种写法 Uglify 后字节数更少</span></span><br><span class="line">    rootElement.<span class="title function_">setAttribute</span>(rootElementDarkModeAttributeName, currentSetting);</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="comment">// 首次访问或从未使用过开关、localStorage 中没有存储的值，currentSetting 是 null</span></span><br><span class="line">    <span class="comment">// 或者 localStorage 被篡改，currentSetting 不是合法值</span></span><br><span class="line">    <span class="title function_">resetRootDarkModeAttributeAndLS</span>();</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>当然，「开关」还需要一个函数，这个函数负责获取相反的显示模式，同时还要将新的模式写入 localStorage 存储起来：</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> invertDarkModeObj = &#123;</span><br><span class="line">  <span class="string">&#x27;dark&#x27;</span>: <span class="string">&#x27;light&#x27;</span>,</span><br><span class="line">  <span class="string">&#x27;light&#x27;</span>: <span class="string">&#x27;dark&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">toggleCustomDarkMode</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">let</span> currentSetting = <span class="title function_">getLS</span>(darkModeStorageKey);</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">if</span> (validColorModeKeys[currentSetting]) &#123;</span><br><span class="line">    <span class="comment">// 从 localStorage 中读取模式，并取相反的模式</span></span><br><span class="line">    currentSetting = invertDarkModeObj[currentSetting];</span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (currentSetting === <span class="literal">null</span>) &#123;</span><br><span class="line">    <span class="comment">// localStorage 中没有相关值，或者 localStorage 抛了 Error</span></span><br><span class="line">    <span class="comment">// 从 CSS 中读取当前 prefers-color-scheme 并取相反的模式</span></span><br><span class="line">    currentSetting = invertDarkModeObj[<span class="title function_">getModeFromCSSMediaQuery</span>()];</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="comment">// 不知道出了什么幺蛾子，比如 localStorage 被篡改成非法值</span></span><br><span class="line">    <span class="keyword">return</span>; <span class="comment">// 直接 return;</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 将相反的模式写入 localStorage</span></span><br><span class="line">  <span class="title function_">setLS</span>(darkModeStorageKey, currentSetting);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> currentSetting;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>相关的函数都定义完了，是时候添加函数执行了：</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 当页面加载时，将显示模式设置为 localStorage 中自定义的值（如果有的话）</span></span><br><span class="line"><span class="title function_">applyCustomDarkModeSettings</span>();</span><br><span class="line"></span><br><span class="line">darkModeTogglebuttonElement.<span class="title function_">addEventListener</span>(<span class="string">&#x27;click&#x27;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// 当用户点击「开关」时，获得新的显示模式、写入 localStorage、并在页面上生效</span></span><br><span class="line">  <span class="title function_">applyCustomDarkModeSettings</span>(<span class="title function_">toggleCustomDarkMode</span>());</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><p>我的博客也使用的这种实现，通过 Navbar 中的按钮体验一下吧！</p>]]></content:encoded>
      
      
      <category domain="https://blog.yfun.top/categories/%E6%9E%81%E5%AE%A2/">极客</category>
      
      
      <category domain="https://blog.yfun.top/tags/%E5%8D%9A%E5%AE%A2/">博客</category>
      
      <category domain="https://blog.yfun.top/tags/JavaScript/">JavaScript</category>
      
      <category domain="https://blog.yfun.top/tags/Web/">Web</category>
      
      <category domain="https://blog.yfun.top/tags/CSS/">CSS</category>
      
      
      <comments>https://blog.yfun.top/posts/175456095/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>博客最近的一些改变</title>
      <link>https://blog.yfun.top/posts/1987652759/</link>
      <guid>https://blog.yfun.top/posts/1987652759/</guid>
      <pubDate>Wed, 13 Jan 2021 10:15:49 GMT</pubDate>
      
      <description>经过近一个月的开发，Miracle 主题已经发布至 v1.2.0 版本，也正式可以开始使用。</description>
      
      
      
      <content:encoded><![CDATA[<h2 id="主题"><a href="#主题" class="headerlink" title="主题"></a>主题</h2><p>经过近一个月的开发，Miracle 主题已经发布至 <code>v1.2.0</code> 版本，也正式可以开始使用。</p><p>主题仓库：<a href="https://t.yfun.top/api/jump?u=https://github.com/hifun-team/hexo-theme-miracle">hifun-team/hexo-theme-miracle</a></p><p>主题对输出的内容等进行优化，去除不必要的资源，速度非常之快。</p><blockquote><p>PS: 主要原因是主题的首页没有图片，这主要是因为我不想找图，而且会拖慢页面速度，一举两等。[doge]</p></blockquote><h2 id="评论"><a href="#评论" class="headerlink" title="评论"></a>评论</h2><p><del>评论更换为 Waline，这样我就有了评论后台、回复通知和关键词过滤，真不错！</del></p><blockquote><p>已更换至 Twikoo</p></blockquote><h2 id="部署"><a href="#部署" class="headerlink" title="部署"></a>部署</h2><p><del>部署换回了直接部署 ( <code>hexo d</code> )，因为主题在 <code>Action</code> 部署时一直无法解析 <code>Tag 插件</code>，而本地又可以...</del></p><p><del>而且实际上使用云部署的次数并不多，因为我每次写完文章以后都会自己看一遍，不太必要。</del></p><p>已重新切换回云部署，并且已经解决无法解析 <code>Tag 插件</code> 的问题。</p>]]></content:encoded>
      
      
      <category domain="https://blog.yfun.top/categories/%E5%8D%9A%E5%AE%A2/">博客</category>
      
      
      <category domain="https://blog.yfun.top/tags/%E5%8D%9A%E5%AE%A2/">博客</category>
      
      
      <comments>https://blog.yfun.top/posts/1987652759/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>使用 Cloudflare 加速你的网站</title>
      <link>https://blog.yfun.top/posts/995700211/</link>
      <guid>https://blog.yfun.top/posts/995700211/</guid>
      <pubDate>Tue, 15 Dec 2020 08:19:29 GMT</pubDate>
      
      <description>使用免费 CDN 服务 CloudFlare 为你的网站提速。</description>
      
      
      
      <content:encoded><![CDATA[<blockquote><p>本文部分内容<strong>已过时</strong>。</p></blockquote><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>CloudFlare 为我们提供了免费的 CDN 服务，并拥有全球各地的优秀节点，作为<strong>国外比较优秀</strong>的 CDN 服务商。</p><p>但是，来自国内的访问都会被 CloudFlare 绕到大洋彼岸的美国。再加上某神秘的阻碍，导致在国内的访问一直不是很好。Ping 出延迟基本大于 &gt;= 200ms 。</p><p>在一次偶然的机会中，发现：</p><ul><li>CloudFlare 拥有很多节点。</li><li>CloudFlare 的节点之间速度非常之快。</li><li>CloudFlare 的节点之间几乎没有延迟。</li><li>CloudFlare 所有节点均免费，基本可以随便接入。</li></ul><hr><h2 id="实践"><a href="#实践" class="headerlink" title="实践"></a>实践</h2><p>既然发现了这个好办法，那么就开始实践吧！</p><p>首先，前往「CloudFlare Partner」的站点登录。</p><blockquote><p>这里要注意！CloudFlare 官方已经停止 CNAME 接入，只能前往「CloudFlare Partner」接入。</p></blockquote><p>以 <a href="https://t.yfun.top/api/jump?u=https://cdn.moeelf.com/">萌精灵 CDN</a> 为例，打开其官网：<a href="https://t.yfun.top/api/jump?u=https://cdn.moeelf.com/">https://cdn.moeelf.com/</a> 并登录你的 CloudFlare 账号。</p><p>然后点击页面右上角的「添加域名」并加你的域名接入 CloudFlare 。</p><img webp-comp src="https://cdn.jsdelivr.net/gh/MyBlog-GitHub/image-upload@main/uPic/hPX5DZ.png"><p>输入域名并点击「添加」即可添加成功。</p><p>接下来点击主页列表里的「管理 DNS」进入管理界面。</p><img webp-comp src="https://cdn.jsdelivr.net/gh/MyBlog-GitHub/image-upload@main/uPic/mLAxAy.png"><p>接下来点击「添加新记录」添加一个新的记录。</p><img webp-comp src="https://cdn.jsdelivr.net/gh/MyBlog-GitHub/image-upload@main/uPic/UqyfQI.png"><img webp-comp src="https://cdn.jsdelivr.net/gh/MyBlog-GitHub/image-upload@main/uPic/F0QTaI.png"><p>然后进入你域名的 DNS 管理系统，注意是解析域名的地方。</p><p>找到「CNAME 接入」处的对应 CNAME 地址，将其设为相应的解析地址。</p><hr><h2 id="加速访问"><a href="#加速访问" class="headerlink" title="加速访问"></a>加速访问</h2><p>虽然默认提供给我们的节点很慢，但我们可以从 CloudFlare 公开的节点中寻找访问速度快的节点。</p><p>我找到了几个国内速度不错的节点，可以参考参考：</p><table><thead><tr><th>IP 地址</th><th>运营商</th></tr></thead><tbody><tr><td>104.27.169.248</td><td>默认</td></tr><tr><td>104.19.19.119</td><td>移动</td></tr><tr><td>141.101.115.11</td><td>移动</td></tr><tr><td>104.16.245.1</td><td>联通</td></tr><tr><td>104.16.246.1</td><td>联通</td></tr><tr><td>104.20.157.19</td><td>电信</td></tr><tr><td>141.101.114.202</td><td>电信</td></tr></tbody></table><p>然后再将原来的 CNAME 记录改为 A 记录到以上 IP，再配合智能运营商解析，达到提速。</p><p>如果你不想一个一个设置，可以直接将「默认」记录指向 <code>cf.record.yfun.top</code> ，节点基本都是上面的，但偶尔会改变。</p><p>可能会遇到的情况：<a href="#%E6%97%A0%E6%B3%95%E6%AD%A3%E5%B8%B8%E7%AD%BE%E5%8F%91-SSL-%E8%AF%81%E4%B9%A6%EF%BC%9F">无法正常签发 SSL 证书</a></p><hr><h2 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h2><h3 id="无法正常签发-SSL-证书？"><a href="#无法正常签发-SSL-证书？" class="headerlink" title="无法正常签发 SSL 证书？"></a>无法正常签发 SSL 证书？</h3><p>CloudFlare 默认会提供免费 SSL 证书服务，但使用「加速节点」可能导致无法正确签发。</p><p>解决方法：使用智能 DNS，将「境外」的记录解析至控制面板提供给您的官方地址。</p><hr><h3 id="开启-SSL-后访问错误？"><a href="#开启-SSL-后访问错误？" class="headerlink" title="开启 SSL 后访问错误？"></a>开启 SSL 后访问错误？</h3><p>试着为源站配置 SSL 证书，然后前往官方控制台（<a href="https://dash.cloudflare.com/">https://dash.cloudflare.com</a>）将域名的 SSL 设为「完全」。</p><p>注意：使用「完全」方式请务必确定源站已开启 SSL！</p><hr><h2 id="速度"><a href="#速度" class="headerlink" title="速度"></a>速度</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">PING cf.record.yfun.top (141.101.115.11): 56 data bytes</span><br><span class="line">64 bytes from 141.101.115.11: icmp_seq=0 ttl=52 time=17.007 ms</span><br><span class="line">64 bytes from 141.101.115.11: icmp_seq=1 ttl=52 time=16.363 ms</span><br><span class="line">64 bytes from 141.101.115.11: icmp_seq=2 ttl=52 time=21.385 ms</span><br><span class="line">64 bytes from 141.101.115.11: icmp_seq=3 ttl=52 time=19.305 ms</span><br><span class="line">64 bytes from 141.101.115.11: icmp_seq=4 ttl=52 time=16.341 ms</span><br><span class="line">64 bytes from 141.101.115.11: icmp_seq=5 ttl=52 time=23.181 ms</span><br><span class="line">64 bytes from 141.101.115.11: icmp_seq=6 ttl=52 time=18.888 ms</span><br><span class="line">64 bytes from 141.101.115.11: icmp_seq=7 ttl=52 time=18.087 ms</span><br><span class="line">64 bytes from 141.101.115.11: icmp_seq=8 ttl=52 time=16.153 ms</span><br><span class="line">64 bytes from 141.101.115.11: icmp_seq=9 ttl=52 time=19.363 ms</span><br><span class="line"></span><br><span class="line">--- cf.record.yfun.top ping statistics ---</span><br><span class="line">10 packets transmitted, 10 packets received, 0.0% packet loss</span><br><span class="line">round-trip min/avg/max/stddev = 16.153/18.607/23.181/2.209 ms</span><br></pre></td></tr></table></figure>]]></content:encoded>
      
      
      <category domain="https://blog.yfun.top/categories/%E6%9E%81%E5%AE%A2/">极客</category>
      
      
      <category domain="https://blog.yfun.top/tags/%E5%8D%9A%E5%AE%A2/">博客</category>
      
      <category domain="https://blog.yfun.top/tags/CDN/">CDN</category>
      
      <category domain="https://blog.yfun.top/tags/CloudFlare/">CloudFlare</category>
      
      
      <comments>https://blog.yfun.top/posts/995700211/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>利用 GitHub Action 自动部署 Hexo 博客</title>
      <link>https://blog.yfun.top/posts/2241387868/</link>
      <guid>https://blog.yfun.top/posts/2241387868/</guid>
      <pubDate>Sun, 29 Nov 2020 07:45:00 GMT</pubDate>
      
      <description>利用 GitHub Action 自动部署 Hexo 博客，随时随地更改博客文件。</description>
      
      
      
      <content:encoded><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>使用 Hexo 写博客，每次写完都要生成静态文件，而且 Hexo 基于 <code>Node.JS</code>，不能再手机上更新。</p><p>所以每次提交过后发现自己码错字了，或是有同学来换友链，总是要麻烦的改文件，再上传。</p><p>那么，Hexo 如何进行在线更新呢？</p><p>如果使用服务器的话，就丧失了使用 Hexo 的最大的优点——节省开支，而且还不如用 <code>Typecho/WordPress</code> 呢。</p><p>后来突然想到 <code>GitHub Action</code> 可以实现在线打包等操作，就想试试在线生成 Hexo 博客。</p><hr><h1 id="行动"><a href="#行动" class="headerlink" title="行动"></a>行动</h1><h2 id="获取-GitHub-令牌"><a href="#获取-GitHub-令牌" class="headerlink" title="获取 GitHub 令牌"></a>获取 GitHub 令牌</h2><p>登录你的 GitHub 账号，点击右上角的头像，点击「Settings」进入设置。</p><img webp-comp src="https://cdn.jsdelivr.net/gh/MyBlog-GitHub/image-upload@main/uPic/2rxI7G.png"><p>点击菜单栏中的「Developer settings」进入开发者设置。</p><img webp-comp src="https://cdn.jsdelivr.net/gh/MyBlog-GitHub/image-upload@main/uPic/ak0J1J.png"><p>点击菜单栏中的「Personal access tokens」进入令牌设置。</p><img webp-comp src="https://cdn.jsdelivr.net/gh/MyBlog-GitHub/image-upload@main/uPic/ZqiWw8.png"><p>点击「Generate new token」新建一个令牌。</p><img webp-comp src="https://cdn.jsdelivr.net/gh/MyBlog-GitHub/image-upload@main/uPic/JCHxQx.png"><p>勾选全部的权限，名称随意。</p><p>并点击「Generate token」完成生成。</p><img webp-comp src="https://cdn.jsdelivr.net/gh/MyBlog-GitHub/image-upload@main/uPic/6rzspL.png"><p>记得保存好这个令牌，它不会再次出现。</p><hr><h2 id="新建仓库"><a href="#新建仓库" class="headerlink" title="新建仓库"></a>新建仓库</h2><p>使用 GitHub 新建一个存放 Hexo 文件的仓库，<strong>要选私有仓库</strong>！</p><p><strong>不要勾选</strong>任何的初始化仓库选项！</p><p>在 Hexo 根目录中删除 <code>.git</code> 文件夹(隐藏文件夹)，删除主题目录下的 <code>.git</code> 文件夹。</p><p>然后在 Hexo 根目录下使用 <code>cmd</code> 或终端运行以下命令：</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git init <span class="comment"># 新建 Git 仓库</span></span><br><span class="line">git add -A  <span class="comment"># 暂存所有文件</span></span><br><span class="line">git commit -m <span class="string">&quot;Create&quot;</span> <span class="comment"># 提交更新</span></span><br><span class="line">git remote add origin https://github.com/用户名/新建的私有仓库名.git <span class="comment"># 新增远程链接</span></span><br><span class="line">git push -u origin master <span class="comment"># 推送至远程仓库</span></span><br></pre></td></tr></table></figure><hr><h2 id="配置-GitHub-Action"><a href="#配置-GitHub-Action" class="headerlink" title="配置 GitHub Action"></a>配置 GitHub Action</h2><p>进入仓库页面，点击「Action」，点击「 set up a workflow yourself 」。</p><img webp-comp src="https://cdn.jsdelivr.net/gh/MyBlog-GitHub/image-upload@main/uPic/P5LacM.png"><p>在左侧代码编辑器中将下方提供的代码粘贴进入代码框。</p><p>请仔细阅读注释，修改最后几行的提交设置。</p><p>确认无误后点击右上角的「Start Commit」。</p><blockquote><p>此处注意！「公开的仓库名」是生成后文件提交的公开仓库，供「GitHub Pages」「Vercel」等服务使用！</p></blockquote><figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="attr">name:</span> <span class="string">Deploy</span> <span class="comment"># 部署</span></span><br><span class="line"> </span><br><span class="line"><span class="attr">on:</span> <span class="comment"># 触发条件</span></span><br><span class="line">  <span class="attr">push:</span></span><br><span class="line">    <span class="attr">branches:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">master</span> <span class="comment"># 推送到 master 分支</span></span><br><span class="line"> </span><br><span class="line">  <span class="attr">release:</span></span><br><span class="line">    <span class="attr">types:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">published</span> <span class="comment"># 推送新版本号</span></span><br><span class="line"> </span><br><span class="line">  <span class="attr">workflow_dispatch:</span> <span class="comment"># 手动触发</span></span><br><span class="line"> </span><br><span class="line"><span class="attr">jobs:</span></span><br><span class="line">  <span class="attr">build:</span></span><br><span class="line">    <span class="attr">runs-on:</span> <span class="string">ubuntu-latest</span></span><br><span class="line"> </span><br><span class="line">    <span class="attr">steps:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Checkout</span> <span class="comment"># Checkout 仓库</span></span><br><span class="line">      <span class="attr">uses:</span> <span class="string">actions/checkout@v2</span></span><br><span class="line">      <span class="attr">with:</span></span><br><span class="line">        <span class="attr">ref:</span> <span class="string">master</span></span><br><span class="line"> </span><br><span class="line">    <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Setup</span> <span class="string">Node</span> <span class="comment"># 安装 Node.js</span></span><br><span class="line">      <span class="attr">uses:</span> <span class="string">actions/setup-node@v1</span></span><br><span class="line">      <span class="attr">with:</span></span><br><span class="line">        <span class="attr">node-version:</span> <span class="string">&quot;12.x&quot;</span></span><br><span class="line"> </span><br><span class="line">    <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Install</span> <span class="string">Hexo</span> <span class="comment"># 安装 Hexo</span></span><br><span class="line">      <span class="attr">run:</span> <span class="string">|</span></span><br><span class="line"><span class="string">        npm install hexo-cli -g</span></span><br><span class="line"><span class="string"></span>        </span><br><span class="line">    <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Cache</span> <span class="string">Modules</span> <span class="comment"># 缓存 Node 插件</span></span><br><span class="line">      <span class="attr">uses:</span> <span class="string">actions/cache@v1</span></span><br><span class="line">      <span class="attr">id:</span> <span class="string">cache-modules</span></span><br><span class="line">      <span class="attr">with:</span></span><br><span class="line">        <span class="attr">path:</span> <span class="string">node_modules</span></span><br><span class="line">        <span class="attr">key:</span> <span class="string">$&#123;&#123;runner.OS&#125;&#125;-$&#123;&#123;hashFiles(&#x27;**/package-lock.json&#x27;)&#125;&#125;</span></span><br><span class="line"> </span><br><span class="line">    <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Install</span> <span class="string">Dependencies</span> <span class="comment"># 如果没有缓存或 插件有更新，则安装插件</span></span><br><span class="line">      <span class="attr">if:</span> <span class="string">steps.cache-modules.outputs.cache-hit</span> <span class="type">!=</span> <span class="string">&#x27;true&#x27;</span></span><br><span class="line">      <span class="attr">run:</span> <span class="string">|</span> <span class="comment"># 如果仓库里没有 package-lock.json，上传一下，npm ci 必须要有 package-lock.json</span></span><br><span class="line">        <span class="string">npm</span> <span class="string">ci</span></span><br><span class="line">    <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Generate</span> <span class="comment"># 生成</span></span><br><span class="line">      <span class="attr">run:</span> <span class="string">|</span></span><br><span class="line"><span class="string">        hexo clean</span></span><br><span class="line"><span class="string">        hexo generate</span></span><br><span class="line"><span class="string"></span>    <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Deploy</span> <span class="comment"># 部署</span></span><br><span class="line">      <span class="attr">run:</span> <span class="string">|</span></span><br><span class="line"><span class="string">        git config --global user.name &quot;HiFun&quot;</span></span><br><span class="line"><span class="string">        git config --global user.email &quot;omycoke@outlook.com&quot;</span></span><br><span class="line"><span class="string">        export TZ=&#x27;Asia/Shanghai&#x27;</span></span><br><span class="line"><span class="string">        cd public/</span></span><br><span class="line"><span class="string">        git init</span></span><br><span class="line"><span class="string">        git add -A</span></span><br><span class="line"><span class="string">        git commit -m &quot;Create by workflows&quot;</span></span><br><span class="line"><span class="string">        git remote add origin [https://令牌@github.com/用户名/公开的仓库名]</span></span><br><span class="line"><span class="string">        git push origin master -f</span></span><br></pre></td></tr></table></figure><img webp-comp src="https://cdn.jsdelivr.net/gh/MyBlog-GitHub/image-upload@main/uPic/n65GGW.png"><hr><h1 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h1><p>每次更改完文件过后，在 Hexo 根目录运行以下命令：</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git add -A</span><br><span class="line">git commit -m <span class="string">&quot;Update&quot;</span></span><br><span class="line">git push origin master</span><br></pre></td></tr></table></figure><p>也可直接在仓库中 改文件/写博文 效果一样，GitHub 都会为你自动生成文件。</p>]]></content:encoded>
      
      
      <category domain="https://blog.yfun.top/categories/%E6%9E%81%E5%AE%A2/">极客</category>
      
      
      <category domain="https://blog.yfun.top/tags/%E5%8D%9A%E5%AE%A2/">博客</category>
      
      <category domain="https://blog.yfun.top/tags/Hexo/">Hexo</category>
      
      <category domain="https://blog.yfun.top/tags/GitHub/">GitHub</category>
      
      
      <comments>https://blog.yfun.top/posts/2241387868/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>更好的保存你的图片 —— 免费的图床</title>
      <link>https://blog.yfun.top/posts/3867215122/</link>
      <guid>https://blog.yfun.top/posts/3867215122/</guid>
      <pubDate>Fri, 27 Nov 2020 08:45:00 GMT</pubDate>
      
      <description>本文将介绍一些免费的图床，可以供博客或日常使用。</description>
      
      
      
      <content:encoded><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>本文将介绍一些<strong>免费</strong>的图床，可以供博客/日常使用。</p><p>每个图床都会给出测试链接供测试，无法访问或图片丢失则代表图床失效。</p><h1 id="公共图床"><a href="#公共图床" class="headerlink" title="公共图床"></a>公共图床</h1><h2 id="SM-MS"><a href="#SM-MS" class="headerlink" title="SM.MS"></a>SM.MS</h2><p><a href="https://sm.ms/">https://sm.ms</a></p><p>曾经图床的域名有备案并使用国内 CDN，但后来因为滥用被吊销。</p><p>现在使用的是 CloudFlare，国内访问<strong>效果不好</strong>甚至无法访问，不建议使用。</p><p>测试图片：</p><p><a href="https://i.loli.net/2020/11/27/qNudBVIcGHe14vm.jpg">SM.MS 测试图片</a></p><hr><h2 id="Imgur"><a href="#Imgur" class="headerlink" title="Imgur"></a>Imgur</h2><p><a href="https://imgur.com/">https://imgur.com</a></p><p>国外一家图片托管服务商，你可以选择注册或不注册，同样的，拖拽上传，永久保留，其SLA有着相当高的保证。</p><p>然而很可惜的是，Imgur 并不能在国内正常访问。你也可以尝试使用 <a href="#%E4%BB%A3%E7%90%86">#代理</a> 解决</p><p>测试图片：</p><p><a href="https://i.imgur.com/NATHKc2.jpeg">Imgur 测试图片</a></p><hr><h2 id="去不图床"><a href="#去不图床" class="headerlink" title="去不图床"></a>去不图床</h2><p><a href="https://7bu.top/">https://7bu.top/</a></p><p>由 <a href="https://dusays.com/241/">杜老师</a> 提供的个人公益图床，存储于阿里和腾讯的对象存储。</p><p>官方保证 SLA&gt;=99% ，目前来看是一个不错的选择。</p><p>当然，毕竟是个人维护的图床，能不能永久撑下去还是个问题。</p><p>如果您需要搭配 PicGo/uPic，请参考以下的配置：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">接口地址：https://7bu.top/api/upload</span><br><span class="line">POST 参数：image</span><br><span class="line">回调 JSON：data.url</span><br></pre></td></tr></table></figure><p>测试图片：<a href="https://7.dusays.com/2020/11/27/6689e9310134b.jpg">去不图床</a></p><blockquote><p>去不图床所使用的 CDN 面向的是中国大陆。</p></blockquote><hr><h2 id="路过图床"><a href="#路过图床" class="headerlink" title="路过图床"></a>路过图床</h2><p><a href="https://imgchr.com/">https://imgchr.com</a></p><p>支持永久存储图片，全球CDN加速。官方宣称『图床从2011年至今都稳定运行』。</p><p>测试图片：</p><p><a href="https://s3.ax1x.com/2020/11/27/DrM4XQ.jpg">路过图床</a></p><hr><h2 id="Postimage"><a href="#Postimage" class="headerlink" title="Postimage"></a>Postimage</h2><p><a href="https://postimage.org/">https://postimage.org/</a></p><p>Postimage 是一款来自国外的图床工具，支持按照一定大小缩放图片及设置图片自动删除。</p><p>在上传完成后，Postimage 会为你生成多种链接格式以满足不同需求。</p><p>Postimage 还会生成一个用于删除当前图片的链接，你只要访问那个链接即可将图片从服务器上删除。</p><p>测试图片：</p><p><a href="https://i.postimg.cc/CF6g03Jx/th.jpg?dl=1">Postimage</a></p><blockquote><p><del>2021-03-01 更新：国内已无法正常访问。</del> 2021-05-10 更新：时好时坏。</p></blockquote><hr><h1 id="折腾"><a href="#折腾" class="headerlink" title="折腾"></a>折腾</h1><h2 id="GitHub-jsDelivr"><a href="#GitHub-jsDelivr" class="headerlink" title="GitHub + jsDelivr"></a>GitHub + jsDelivr</h2><p><strong>网上有许多的教程，此处不再演示。</strong></p><p>jsDelivr 非常稳定，国外使用 CloudFlare，国内有网宿节点，速度非常之快。</p><p>测试图片：</p><p><a href="https://cdn.jsdelivr.net/gh/MyBlog-GitHub/image-upload@main/uPic/th.jpg">jsDelivr</a></p><hr><h2 id="NPM-镜像"><a href="#NPM-镜像" class="headerlink" title="NPM + 镜像"></a>NPM + 镜像</h2><p>我们可以使用 NPM 作为图床的储存。</p><p>再搭配上各种镜像调用，速度扛扛滴。</p><h3 id="使用方法"><a href="#使用方法" class="headerlink" title="使用方法"></a>使用方法</h3><blockquote><p>您应该拥有基本的 <code>npm</code> 环境，如没有，请安装。<br/><br>安装教程可以参阅互联网，有很多可供参考。</p></blockquote><p>首先，新建一个文件夹存放文件。</p><p>然后打开 <code>cmd</code> 或 终端，进入目录并输入以下命令 <strong>登录 NPM</strong>  ：</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm login</span><br></pre></td></tr></table></figure><blockquote><p>如果没有账号的，请前往 <a href="https://npmjs.com/">NPM</a> 注册账号</p></blockquote><p>紧接着输入以下命令：</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm init <span class="comment"># 生成包文件</span></span><br><span class="line">npm publish <span class="comment"># 发布包</span></span><br></pre></td></tr></table></figure><p>请注意，如果你之前用过淘宝镜像，那么请先手动切回官方源：</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm config <span class="built_in">set</span> registry https://registry.npmjs.org</span><br></pre></td></tr></table></figure><p>每次更新完包内存放的图片后，你应该修改 <code>package.json</code> 文件内的版本号（向上增加），然后再次运行 <code>npm publish</code> 发布包。</p><h3 id="推荐的镜像"><a href="#推荐的镜像" class="headerlink" title="推荐的镜像"></a>推荐的镜像</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">https://cdn.jsdelivr.net/npm/  <span class="comment"># jsDelivr，网宿节点</span></span><br><span class="line">https://unpkg.zhimg.com/ <span class="comment"># 知乎，网宿节点</span></span><br><span class="line">https://code.bdstatic.com/npm/ <span class="comment"># 百度，网宿节点（不推荐）</span></span><br><span class="line">https://shadow.elemecdn.com/npm/ <span class="comment"># 饿了么，网宿节点</span></span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">[镜像链接]/[包名]@[版本]/[文件名]</span><br><span class="line"></span><br><span class="line"><span class="comment"># https://cdn.jsdelivr.net/npm/mdt-for-typecho@0.4.2/screenshot.png</span></span><br><span class="line"><span class="comment"># https://unpkg.zhimg.com/mdt-for-typecho@0.4.2/screenshot.png</span></span><br><span class="line"><span class="comment"># https://code.bdstatic.com/npm/mdt-for-typecho@0.4.2/screenshot.png</span></span><br><span class="line"><span class="comment"># https://shadow.elemecdn.com/npm/mdt-for-typecho@0.4.2/screenshot.png</span></span><br></pre></td></tr></table></figure><h3 id="测试图片"><a href="#测试图片" class="headerlink" title="测试图片"></a>测试图片</h3><blockquote><p>图片太多，这里使用超链接，点击就可以看到。</p></blockquote><p>jsDelivr：<a href="https://cdn.jsdelivr.net/npm/mdt-for-typecho@0.4.2/screenshot.png">jsDelivr+NPM</a></p><p>知乎：<a href="https://unpkg.zhimg.com/mdt-for-typecho@0.4.2/screenshot.png">知乎+NPM</a></p><p>百度：<a href="https://code.bdstatic.com/npm/mdt-for-typecho@0.4.2/screenshot.png">百度+NPM</a></p><p>饿了么：<a href="https://shadow.elemecdn.com/npm/mdt-for-typecho@0.4.2/screenshot.png">饿了么+NPM</a></p><hr><h2 id="LeanCloud"><a href="#LeanCloud" class="headerlink" title="LeanCloud"></a>LeanCloud</h2><p><a href="https://leancloud.cn/">https://leancloud.cn</a> / <a href="https://leancloud.app/">https://leancloud.app</a><br>注册一个 LeanCloud 账号，此处不再赘述。</p><p>创建一个 App，进入 <code>存储 -&gt; 文件</code>，点击「上传」并上传图片。</p><p>上传成功后会在文件列表中显示，在列表中我们可以看到 URL 地址。</p><img webp-comp src="https://cdn.jsdelivr.net/gh/MyBlog-GitHub/image-upload@main/uPic/tlxdgw.png"><p>测试链接：</p><blockquote><p>笔者使用国际版，为避免拖慢网站加载速度，使用超链接，点击即可看到。</p></blockquote><p><a href="https://lc-gluttony.s3.amazonaws.com/lej8uP461WyF/ed16a23d417ffa0acf55.jpg/th.jpg">LeanCloud 测试链接 (国际版)</a></p><hr><h2 id="UniCloud"><a href="#UniCloud" class="headerlink" title="UniCloud"></a>UniCloud</h2><p><a href="https://unicloud.dcloud.net.cn/">https://unicloud.dcloud.net.cn/</a></p><p>注册一个 UniCloud 账号并登录，此处不再赘述。</p><p>创建一个服务空间，选择『阿里云』并起个名字。（选择阿里云不收费）</p><p>进入「云存储」，点击「上传文件」。</p><img webp-comp src="https://cdn.jsdelivr.net/gh/MyBlog-GitHub/image-upload@main/uPic/2PFj2g.png"><p>点击右侧的「详情」即可查看图片地址，预览等信息。<br><img webp-comp src="https://cdn.jsdelivr.net/gh/MyBlog-GitHub/image-upload@main/uPic/iYKvJz.png"></p><p>测试图片：</p><h2 id="UniCloud-1"><a href="#UniCloud-1" class="headerlink" title="UniCloud"></a><a href="https://vkceyugu.cdn.bspapp.com/VKCEYUGU-aliyun-qvxxidugllx169a140/51632140-309a-11eb-8a36-ebb87efcf8c0.jpg">UniCloud</a></h2><h2 id="代理"><a href="#代理" class="headerlink" title="代理"></a>代理</h2><p>Imgur 在国内已经无法访问了，但是，我们可以利用服务器代理啊！</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">https://search.pstatic.net/common/?src= <span class="comment"># 国内网宿，只能加载特定图床图片如 Imgur [1]</span></span><br><span class="line">https://imageproxy.pimg.tw/resize?url= <span class="comment"># Akamai 节点，无限制 [2]</span></span><br><span class="line">https://images.weserv.nl/?url= <span class="comment"># CloudFlare 节点 [3]</span></span><br><span class="line">https://pic1.xuehuaimg.com/proxy/ <span class="comment"># CloudFlare 节点 [4]</span></span><br></pre></td></tr></table></figure><p>我们就以代理 Imgur 的图片为例，原链接：<a href="https://i.imgur.com/NATHKc2.jpeg">https://i.imgur.com/NATHKc2.jpeg</a></p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">https://search.pstatic.net/common/?src=https://i.imgur.com/NATHKc2.jpeg</span><br><span class="line">https://imageproxy.pimg.tw/resize?url=https://i.imgur.com/NATHKc2.jpeg</span><br><span class="line">https://images.weserv.nl/?url=https://i.imgur.com/NATHKc2.jpeg</span><br><span class="line">https://pic1.xuehuaimg.com/proxy/https://i.imgur.com/NATHKc2.jpeg</span><br></pre></td></tr></table></figure><p>测试图片：</p><blockquote><p>图片太多，这里使用超链接，点击就可以看到。</p></blockquote><blockquote><p>此处的序号对应上方代理列表的序号</p></blockquote><p><a href="https://search.pstatic.net/common/?src=https://i.imgur.com/NATHKc2.jpeg">[1]</a></p><p><a href="https://imageproxy.pimg.tw/resize?url=https://i.imgur.com/NATHKc2.jpeg">[2]</a></p><p><a href="https://images.weserv.nl/?url=https://i.imgur.com/NATHKc2.jpeg">[3]</a></p><p><a href="https://pic1.xuehuaimg.com/proxy/https://i.imgur.com/NATHKc2.jpeg">[4]</a></p>]]></content:encoded>
      
      
      <category domain="https://blog.yfun.top/categories/%E6%9E%81%E5%AE%A2/">极客</category>
      
      
      <category domain="https://blog.yfun.top/tags/%E5%8D%9A%E5%AE%A2/">博客</category>
      
      <category domain="https://blog.yfun.top/tags/%E5%9B%BE%E5%BA%8A/">图床</category>
      
      
      <comments>https://blog.yfun.top/posts/3867215122/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>使用 Vercel 部署你的静态网站</title>
      <link>https://blog.yfun.top/posts/2979788395/</link>
      <guid>https://blog.yfun.top/posts/2979788395/</guid>
      <pubDate>Mon, 23 Nov 2020 10:22:13 GMT</pubDate>
      
      <description>本文将介绍使用 Vercel 部署静态网站</description>
      
      
      
      <content:encoded><![CDATA[<h2 id="注册账号"><a href="#注册账号" class="headerlink" title="注册账号"></a>注册账号</h2><p>使用浏览器访问：<a href="https://vercel.com/signup">https://vercel.com/signup</a></p><p>点击「Cotinue with GitHub」并使用 GitHub 账号注册。</p><img webp-comp src="https://cdn.jsdelivr.net/gh/MyBlog-GitHub/image-upload@main/uPic/YzQIUy.png"><details><summary>无法注册账号？</summary>Vercel 不支持 163/QQ 等国内邮箱的注册，请使用 <a href="https://outlook.live.com" target="_blank">Outlook 邮箱</a> 再次注册 GitHub 后使用新 GitHub 账号注册！</details><hr><h2 id="创建代码仓库"><a href="#创建代码仓库" class="headerlink" title="创建代码仓库"></a>创建代码仓库</h2><p>新建 GitHub 代码仓库，并在代码仓库中新建 <code>index.html</code> 文件，这将作为首页。</p><p>在 <code>index.html</code> 文件中填写基本的代码内容，如：</p><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">&quot;utf-8&quot;</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">title</span>&gt;</span>Test Page<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">p</span>&gt;</span>This page is for test.<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="使用-Vercel-部署"><a href="#使用-Vercel-部署" class="headerlink" title="使用 Vercel 部署"></a>使用 Vercel 部署</h2><p>点击<a href="https://vercel.com/dashboard">控制台</a>右上角的「Import Project」</p><img webp-comp src="https://cdn.jsdelivr.net/gh/MyBlog-GitHub/image-upload@main/uPic/ZprWh4.png"><p>点击「Import Git Repository」下方的「Continue」</p><img webp-comp src="https://cdn.jsdelivr.net/gh/MyBlog-GitHub/image-upload@main/uPic/hgeaBV.png"><p>输入 GitHub 仓库的地址并点击「Continue」</p><img webp-comp src="https://cdn.jsdelivr.net/gh/MyBlog-GitHub/image-upload@main/uPic/Uabikg.png"><p>经过基本配置后，项目就会自动部署。</p><p>点击项目控制台的「View Domain」进入域名配置</p><img webp-comp src="https://cdn.jsdelivr.net/gh/MyBlog-GitHub/image-upload@main/uPic/lLYsWv.png"><p>默认会提供 <code>*.vercel.app</code> 免费域名，也可以免费添加自己的域名。</p><img webp-comp src="https://cdn.jsdelivr.net/gh/MyBlog-GitHub/image-upload@main/uPic/ek0ZYY.png"><blockquote><p><del>目前新项目已经更换为 <code>*.vercel.app</code> 域名，如仍需要 <code>*.now.sh</code> 域名，可以直接填写，实测可以使用。</del><br/><br><del>例如我需要使用 <code>test-page-123.now.sh</code> 域名，那么我只需要在自定义域名处填写并点击「Add」即可。</del><br><br>2021-05-10 更新：已无法再添加 <code>*.now.sh</code> 域名，<code>*.now.sh</code>会自动跳转 <code>*.vercel.app</code>。</p></blockquote>]]></content:encoded>
      
      
      <category domain="https://blog.yfun.top/categories/%E6%9E%81%E5%AE%A2/">极客</category>
      
      
      <category domain="https://blog.yfun.top/tags/%E5%8D%9A%E5%AE%A2/">博客</category>
      
      
      <comments>https://blog.yfun.top/posts/2979788395/#disqus_thread</comments>
      
    </item>
    
  </channel>
</rss>
