<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>FoleyDang</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://foleydang.github.io/</id>
  <link href="https://foleydang.github.io/" rel="alternate"/>
  <link href="https://foleydang.github.io/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, FoleyDang</rights>
  <subtitle>一个专注技术的程序员</subtitle>
  <title>程序开发之道</title>
  <updated>2026-05-09T03:42:09.382Z</updated>
  <entry>
    <author>
      <name>FoleyDang</name>
    </author>
    <category term="Node.js" scheme="https://foleydang.github.io/categories/Node-js/"/>
    <category term="npm" scheme="https://foleydang.github.io/tags/npm/"/>
    <category term="Node.js" scheme="https://foleydang.github.io/tags/Node-js/"/>
    <category term="包开发" scheme="https://foleydang.github.io/tags/%E5%8C%85%E5%BC%80%E5%8F%91/"/>
    <category term="前端工程化" scheme="https://foleydang.github.io/tags/%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%8C%96/"/>
    <content>
      <![CDATA[<p>作为 JavaScript/Node.js 开发者，发布自己的 NPM 包是技术成长的重要一步。本文将从零开始，详细介绍如何开发、打包、测试并发布一个 NPM 包。</p><h2 id="为什么需要发布-NPM-包？"><a href="#为什么需要发布-NPM-包？" class="headerlink" title="为什么需要发布 NPM 包？"></a>为什么需要发布 NPM 包？</h2><ul><li><strong>代码复用</strong>：将常用工具封装成包，多项目共享</li><li><strong>开源贡献</strong>：分享你的解决方案，帮助其他开发者</li><li><strong>技术积累</strong>：规范化代码组织，提升工程质量</li><li><strong>简历加分</strong>：维护公开项目，展示技术能力</li></ul><h2 id="目录结构规范"><a href="#目录结构规范" class="headerlink" title="目录结构规范"></a>目录结构规范</h2><p>一个标准的 NPM 包项目结构：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">my-package/</span><br><span class="line">├── src/                # 源代码目录</span><br><span class="line">│   ├── index.js        # 主入口文件</span><br><span class="line">│   └── utils.js        # 辅助模块</span><br><span class="line">├── dist/               # 构建产物（可选）</span><br><span class="line">├── test/               # 测试文件</span><br><span class="line">├── package.json        # 包配置文件</span><br><span class="line">├── README.md           # 说明文档</span><br><span class="line">├── LICENSE             # 许可证文件</span><br><span class="line">├── .gitignore          # Git忽略配置</span><br><span class="line">├── .npmignore          # NPM忽略配置（可选）</span><br><span class="line">└── CHANGELOG.md        # 变更日志（推荐）</span><br></pre></td></tr></table></figure><h2 id="package-json-配置详解"><a href="#package-json-配置详解" class="headerlink" title="package.json 配置详解"></a>package.json 配置详解</h2><p>这是包的核心配置文件：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><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;my-awesome-package&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;1.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;一个实用的工具库&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;main&quot;</span><span class="punctuation">:</span> <span class="string">&quot;src/index.js&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;module&quot;</span><span class="punctuation">:</span> <span class="string">&quot;dist/index.esm.js&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;types&quot;</span><span class="punctuation">:</span> <span class="string">&quot;dist/index.d.ts&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;files&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">    <span class="string">&quot;src&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;dist&quot;</span></span><br><span class="line">  <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;scripts&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;test&quot;</span><span class="punctuation">:</span> <span class="string">&quot;jest&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;build&quot;</span><span class="punctuation">:</span> <span class="string">&quot;rollup -c&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;prepublishOnly&quot;</span><span class="punctuation">:</span> <span class="string">&quot;npm run build &amp;&amp; npm run test&quot;</span></span><br><span class="line">  <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;keywords&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">    <span class="string">&quot;utility&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;tools&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;helper&quot;</span></span><br><span class="line">  <span class="punctuation">]</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;FoleyDang&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;license&quot;</span><span class="punctuation">:</span> <span class="string">&quot;MIT&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;repository&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;git&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;url&quot;</span><span class="punctuation">:</span> <span class="string">&quot;git+https://github.com/foleydang/my-package.git&quot;</span></span><br><span class="line">  <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;bugs&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;url&quot;</span><span class="punctuation">:</span> <span class="string">&quot;https://github.com/foleydang/my-package/issues&quot;</span></span><br><span class="line">  <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;homepage&quot;</span><span class="punctuation">:</span> <span class="string">&quot;https://github.com/foleydang/my-package#readme&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;dependencies&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;devDependencies&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;jest&quot;</span><span class="punctuation">:</span> <span class="string">&quot;^29.0.0&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;rollup&quot;</span><span class="punctuation">:</span> <span class="string">&quot;^3.0.0&quot;</span></span><br><span class="line">  <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;engines&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;node&quot;</span><span class="punctuation">:</span> <span class="string">&quot;&gt;=14.0.0&quot;</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h3 id="关键字段说明"><a href="#关键字段说明" class="headerlink" title="关键字段说明"></a>关键字段说明</h3><table><thead><tr><th>字段</th><th>说明</th></tr></thead><tbody><tr><td><code>name</code></td><td>包名，必须唯一，建议使用小写+连字符</td></tr><tr><td><code>version</code></td><td>版本号，遵循 SemVer 规范</td></tr><tr><td><code>main</code></td><td>CommonJS 入口</td></tr><tr><td><code>module</code></td><td>ES Module 入口</td></tr><tr><td><code>types</code></td><td>TypeScript 类型声明文件</td></tr><tr><td><code>files</code></td><td>发布时包含的文件/目录</td></tr><tr><td><code>exports</code></td><td>现代包导出配置（推荐）</td></tr></tbody></table><h3 id="exports-配置（推荐）"><a href="#exports-配置（推荐）" class="headerlink" title="exports 配置（推荐）"></a>exports 配置（推荐）</h3><p>现代 NPM 包推荐使用 <code>exports</code> 字段：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;exports&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;.&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;import&quot;</span><span class="punctuation">:</span> <span class="string">&quot;./dist/index.esm.js&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;require&quot;</span><span class="punctuation">:</span> <span class="string">&quot;./src/index.js&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;types&quot;</span><span class="punctuation">:</span> <span class="string">&quot;./dist/index.d.ts&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;./utils&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;import&quot;</span><span class="punctuation">:</span> <span class="string">&quot;./dist/utils.esm.js&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;require&quot;</span><span class="punctuation">:</span> <span class="string">&quot;./src/utils.js&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h2 id="版本号规范（SemVer）"><a href="#版本号规范（SemVer）" class="headerlink" title="版本号规范（SemVer）"></a>版本号规范（SemVer）</h2><p>版本号格式：<code>主版本.次版本.补丁版本</code>（如 <code>1.2.3</code>）</p><ul><li><strong>主版本（Major）</strong>：不兼容的 API 变化</li><li><strong>次版本（Minor）</strong>：向后兼容的功能新增</li><li><strong>补丁版本（Patch）</strong>：向后兼容的问题修复</li></ul><p>常用命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 补丁版本（1.0.0 -&gt; 1.0.1）</span></span><br><span class="line">npm version patch</span><br><span class="line"></span><br><span class="line"><span class="comment"># 次版本（1.0.0 -&gt; 1.1.0）</span></span><br><span class="line">npm version minor</span><br><span class="line"></span><br><span class="line"><span class="comment"># 主版本（1.0.0 -&gt; 2.0.0）</span></span><br><span class="line">npm version major</span><br><span class="line"></span><br><span class="line"><span class="comment"># 预发布版本</span></span><br><span class="line">npm version prerelease --preid=beta  <span class="comment"># 1.0.0 -&gt; 1.0.1-beta.0</span></span><br></pre></td></tr></table></figure><h2 id="包代码开发"><a href="#包代码开发" class="headerlink" title="包代码开发"></a>包代码开发</h2><h3 id="入口文件示例"><a href="#入口文件示例" class="headerlink" title="入口文件示例"></a>入口文件示例</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// src/index.js</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 格式化日期</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> &#123;<span class="type">Date|string|number</span>&#125; date 日期对象/字符串/时间戳</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> &#123;<span class="type">string</span>&#125; format 格式模板</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@returns</span> &#123;<span class="type">string</span>&#125; 格式化后的日期字符串</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">formatDate</span>(<span class="params">date, format = <span class="string">&#x27;YYYY-MM-DD&#x27;</span></span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> d = <span class="keyword">new</span> <span class="title class_">Date</span>(date);</span><br><span class="line">  <span class="keyword">const</span> map = &#123;</span><br><span class="line">    <span class="attr">YYYY</span>: d.<span class="title function_">getFullYear</span>(),</span><br><span class="line">    <span class="attr">MM</span>: <span class="title class_">String</span>(d.<span class="title function_">getMonth</span>() + <span class="number">1</span>).<span class="title function_">padStart</span>(<span class="number">2</span>, <span class="string">&#x27;0&#x27;</span>),</span><br><span class="line">    <span class="attr">DD</span>: <span class="title class_">String</span>(d.<span class="title function_">getDate</span>()).<span class="title function_">padStart</span>(<span class="number">2</span>, <span class="string">&#x27;0&#x27;</span>),</span><br><span class="line">    <span class="attr">HH</span>: <span class="title class_">String</span>(d.<span class="title function_">getHours</span>()).<span class="title function_">padStart</span>(<span class="number">2</span>, <span class="string">&#x27;0&#x27;</span>),</span><br><span class="line">    <span class="attr">mm</span>: <span class="title class_">String</span>(d.<span class="title function_">getMinutes</span>()).<span class="title function_">padStart</span>(<span class="number">2</span>, <span class="string">&#x27;0&#x27;</span>),</span><br><span class="line">    <span class="attr">ss</span>: <span class="title class_">String</span>(d.<span class="title function_">getSeconds</span>()).<span class="title function_">padStart</span>(<span class="number">2</span>, <span class="string">&#x27;0&#x27;</span>)</span><br><span class="line">  &#125;;</span><br><span class="line">  <span class="keyword">return</span> format.<span class="title function_">replace</span>(<span class="regexp">/YYYY|MM|DD|HH|mm|ss/g</span>, <span class="function"><span class="params">match</span> =&gt;</span> map[match]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 深度克隆对象</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> &#123;<span class="type">*</span>&#125; obj 要克隆的对象</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@returns</span> &#123;<span class="type">*</span>&#125; 克隆后的对象</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">deepClone</span>(<span class="params">obj</span>) &#123;</span><br><span class="line">  <span class="keyword">if</span> (obj === <span class="literal">null</span> || <span class="keyword">typeof</span> obj !== <span class="string">&#x27;object&#x27;</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> obj;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (<span class="title class_">Array</span>.<span class="title function_">isArray</span>(obj)) &#123;</span><br><span class="line">    <span class="keyword">return</span> obj.<span class="title function_">map</span>(<span class="function"><span class="params">item</span> =&gt;</span> <span class="title function_">deepClone</span>(item));</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">const</span> cloned = &#123;&#125;;</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">const</span> key <span class="keyword">in</span> obj) &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="title class_">Object</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">hasOwnProperty</span>.<span class="title function_">call</span>(obj, key)) &#123;</span><br><span class="line">      cloned[key] = <span class="title function_">deepClone</span>(obj[key]);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> cloned;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">  formatDate,</span><br><span class="line">  deepClone</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="ES-Module-版本"><a href="#ES-Module-版本" class="headerlink" title="ES Module 版本"></a>ES Module 版本</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// dist/index.esm.js</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">formatDate</span>(<span class="params">date, format = <span class="string">&#x27;YYYY-MM-DD&#x27;</span></span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> d = <span class="keyword">new</span> <span class="title class_">Date</span>(date);</span><br><span class="line">  <span class="keyword">const</span> map = &#123;</span><br><span class="line">    <span class="attr">YYYY</span>: d.<span class="title function_">getFullYear</span>(),</span><br><span class="line">    <span class="attr">MM</span>: <span class="title class_">String</span>(d.<span class="title function_">getMonth</span>() + <span class="number">1</span>).<span class="title function_">padStart</span>(<span class="number">2</span>, <span class="string">&#x27;0&#x27;</span>),</span><br><span class="line">    <span class="attr">DD</span>: <span class="title class_">String</span>(d.<span class="title function_">getDate</span>()).<span class="title function_">padStart</span>(<span class="number">2</span>, <span class="string">&#x27;0&#x27;</span>),</span><br><span class="line">    <span class="attr">HH</span>: <span class="title class_">String</span>(d.<span class="title function_">getHours</span>()).<span class="title function_">padStart</span>(<span class="number">2</span>, <span class="string">&#x27;0&#x27;</span>),</span><br><span class="line">    <span class="attr">mm</span>: <span class="title class_">String</span>(d.<span class="title function_">getMinutes</span>()).<span class="title function_">padStart</span>(<span class="number">2</span>, <span class="string">&#x27;0&#x27;</span>),</span><br><span class="line">    <span class="attr">ss</span>: <span class="title class_">String</span>(d.<span class="title function_">getSeconds</span>()).<span class="title function_">padStart</span>(<span class="number">2</span>, <span class="string">&#x27;0&#x27;</span>)</span><br><span class="line">  &#125;;</span><br><span class="line">  <span class="keyword">return</span> format.<span class="title function_">replace</span>(<span class="regexp">/YYYY|MM|DD|HH|mm|ss/g</span>, <span class="function"><span class="params">match</span> =&gt;</span> map[match]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">deepClone</span>(<span class="params">obj</span>) &#123;</span><br><span class="line">  <span class="keyword">if</span> (obj === <span class="literal">null</span> || <span class="keyword">typeof</span> obj !== <span class="string">&#x27;object&#x27;</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> obj;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (<span class="title class_">Array</span>.<span class="title function_">isArray</span>(obj)) &#123;</span><br><span class="line">    <span class="keyword">return</span> obj.<span class="title function_">map</span>(<span class="function"><span class="params">item</span> =&gt;</span> <span class="title function_">deepClone</span>(item));</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">const</span> cloned = &#123;&#125;;</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">const</span> key <span class="keyword">in</span> obj) &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="title class_">Object</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">hasOwnProperty</span>.<span class="title function_">call</span>(obj, key)) &#123;</span><br><span class="line">      cloned[key] = <span class="title function_">deepClone</span>(obj[key]);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> cloned;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="使用-Rollup-构建"><a href="#使用-Rollup-构建" class="headerlink" title="使用 Rollup 构建"></a>使用 Rollup 构建</h2><p>Rollup 是打包库的最佳选择，输出干净、高效。</p><h3 id="安装依赖"><a href="#安装依赖" class="headerlink" title="安装依赖"></a>安装依赖</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install --save-dev rollup @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-terser</span><br></pre></td></tr></table></figure><h3 id="rollup-config-js"><a href="#rollup-config-js" class="headerlink" title="rollup.config.js"></a>rollup.config.js</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> resolve <span class="keyword">from</span> <span class="string">&#x27;@rollup/plugin-node-resolve&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> commonjs <span class="keyword">from</span> <span class="string">&#x27;@rollup/plugin-commonjs&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> terser <span class="keyword">from</span> <span class="string">&#x27;@rollup/plugin-terser&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> [</span><br><span class="line">  <span class="comment">// ES Module 输出</span></span><br><span class="line">  &#123;</span><br><span class="line">    <span class="attr">input</span>: <span class="string">&#x27;src/index.js&#x27;</span>,</span><br><span class="line">    <span class="attr">output</span>: &#123;</span><br><span class="line">      <span class="attr">file</span>: <span class="string">&#x27;dist/index.esm.js&#x27;</span>,</span><br><span class="line">      <span class="attr">format</span>: <span class="string">&#x27;esm&#x27;</span>,</span><br><span class="line">      <span class="attr">sourcemap</span>: <span class="literal">true</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="attr">plugins</span>: [<span class="title function_">resolve</span>(), <span class="title function_">commonjs</span>()]</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// CommonJS 输出</span></span><br><span class="line">  &#123;</span><br><span class="line">    <span class="attr">input</span>: <span class="string">&#x27;src/index.js&#x27;</span>,</span><br><span class="line">    <span class="attr">output</span>: &#123;</span><br><span class="line">      <span class="attr">file</span>: <span class="string">&#x27;dist/index.cjs.js&#x27;</span>,</span><br><span class="line">      <span class="attr">format</span>: <span class="string">&#x27;cjs&#x27;</span>,</span><br><span class="line">      <span class="attr">sourcemap</span>: <span class="literal">true</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="attr">plugins</span>: [<span class="title function_">resolve</span>(), <span class="title function_">commonjs</span>()]</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// UMD 输出（浏览器兼容）</span></span><br><span class="line">  &#123;</span><br><span class="line">    <span class="attr">input</span>: <span class="string">&#x27;src/index.js&#x27;</span>,</span><br><span class="line">    <span class="attr">output</span>: &#123;</span><br><span class="line">      <span class="attr">file</span>: <span class="string">&#x27;dist/index.umd.js&#x27;</span>,</span><br><span class="line">      <span class="attr">format</span>: <span class="string">&#x27;umd&#x27;</span>,</span><br><span class="line">      <span class="attr">name</span>: <span class="string">&#x27;MyPackage&#x27;</span>,</span><br><span class="line">      <span class="attr">sourcemap</span>: <span class="literal">true</span>,</span><br><span class="line">      <span class="attr">plugins</span>: [<span class="title function_">terser</span>()]</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="attr">plugins</span>: [<span class="title function_">resolve</span>(), <span class="title function_">commonjs</span>()]</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>使用 Jest 编写单元测试：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install --save-dev jest</span><br></pre></td></tr></table></figure><h3 id="test-index-test-js"><a href="#test-index-test-js" class="headerlink" title="test/index.test.js"></a>test/index.test.js</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> &#123; formatDate, deepClone &#125; = <span class="built_in">require</span>(<span class="string">&#x27;../src/index&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="title function_">describe</span>(<span class="string">&#x27;formatDate&#x27;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">  <span class="title function_">test</span>(<span class="string">&#x27;格式化默认格式&#x27;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> date = <span class="keyword">new</span> <span class="title class_">Date</span>(<span class="string">&#x27;2026-05-09&#x27;</span>);</span><br><span class="line">    <span class="title function_">expect</span>(<span class="title function_">formatDate</span>(date)).<span class="title function_">toBe</span>(<span class="string">&#x27;2026-05-09&#x27;</span>);</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  <span class="title function_">test</span>(<span class="string">&#x27;自定义格式&#x27;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> date = <span class="keyword">new</span> <span class="title class_">Date</span>(<span class="string">&#x27;2026-05-09 11:30:00&#x27;</span>);</span><br><span class="line">    <span class="title function_">expect</span>(<span class="title function_">formatDate</span>(date, <span class="string">&#x27;YYYY/MM/DD HH:mm:ss&#x27;</span>)).<span class="title function_">toBe</span>(<span class="string">&#x27;2026/05/09 11:30:00&#x27;</span>);</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  <span class="title function_">test</span>(<span class="string">&#x27;时间戳输入&#x27;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> timestamp = <span class="number">1746662400000</span>; <span class="comment">// 2026-05-09 00:00:00 UTC</span></span><br><span class="line">    <span class="title function_">expect</span>(<span class="title function_">formatDate</span>(timestamp, <span class="string">&#x27;YYYY-MM-DD&#x27;</span>)).<span class="title function_">toBe</span>(<span class="string">&#x27;2026-05-09&#x27;</span>);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="title function_">describe</span>(<span class="string">&#x27;deepClone&#x27;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">  <span class="title function_">test</span>(<span class="string">&#x27;克隆简单对象&#x27;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> obj = &#123; <span class="attr">a</span>: <span class="number">1</span>, <span class="attr">b</span>: <span class="string">&#x27;test&#x27;</span> &#125;;</span><br><span class="line">    <span class="keyword">const</span> cloned = <span class="title function_">deepClone</span>(obj);</span><br><span class="line">    <span class="title function_">expect</span>(cloned).<span class="title function_">toEqual</span>(obj);</span><br><span class="line">    <span class="title function_">expect</span>(cloned).<span class="property">not</span>.<span class="title function_">toBe</span>(obj);</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  <span class="title function_">test</span>(<span class="string">&#x27;克隆嵌套对象&#x27;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> obj = &#123; <span class="attr">a</span>: &#123; <span class="attr">b</span>: &#123; <span class="attr">c</span>: <span class="number">1</span> &#125; &#125; &#125;;</span><br><span class="line">    <span class="keyword">const</span> cloned = <span class="title function_">deepClone</span>(obj);</span><br><span class="line">    cloned.<span class="property">a</span>.<span class="property">b</span>.<span class="property">c</span> = <span class="number">2</span>;</span><br><span class="line">    <span class="title function_">expect</span>(obj.<span class="property">a</span>.<span class="property">b</span>.<span class="property">c</span>).<span class="title function_">toBe</span>(<span class="number">1</span>);</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  <span class="title function_">test</span>(<span class="string">&#x27;克隆数组&#x27;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> arr = [<span class="number">1</span>, &#123; <span class="attr">a</span>: <span class="number">2</span> &#125;, [<span class="number">3</span>, <span class="number">4</span>]];</span><br><span class="line">    <span class="keyword">const</span> cloned = <span class="title function_">deepClone</span>(arr);</span><br><span class="line">    <span class="title function_">expect</span>(cloned).<span class="title function_">toEqual</span>(arr);</span><br><span class="line">    cloned[<span class="number">1</span>].<span class="property">a</span> = <span class="number">3</span>;</span><br><span class="line">    <span class="title function_">expect</span>(arr[<span class="number">1</span>].<span class="property">a</span>).<span class="title function_">toBe</span>(<span class="number">2</span>);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h2 id="README-md-编写规范"><a href="#README-md-编写规范" class="headerlink" title="README.md 编写规范"></a>README.md 编写规范</h2><p>README 是包的门面，必须清晰完整：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="section"># my-awesome-package</span></span><br><span class="line"></span><br><span class="line"><span class="quote">&gt; 一个实用的 JavaScript 工具库</span></span><br><span class="line"></span><br><span class="line"><span class="section">## 安装</span></span><br><span class="line"></span><br><span class="line">\<span class="code">`\`</span>\`bash</span><br><span class="line">npm install my-awesome-package</span><br><span class="line"><span class="section"># 或</span></span><br><span class="line">yarn add my-awesome-package</span><br><span class="line"><span class="section"># 或</span></span><br><span class="line">pnpm add my-awesome-package</span><br><span class="line">\<span class="code">`\`</span>\`</span><br><span class="line"></span><br><span class="line"><span class="section">## 使用</span></span><br><span class="line"></span><br><span class="line">\<span class="code">`\`</span>\`javascript</span><br><span class="line">// CommonJS</span><br><span class="line">const &#123; formatDate, deepClone &#125; = require(&#x27;my-awesome-package&#x27;);</span><br><span class="line"></span><br><span class="line">// ES Module</span><br><span class="line">import &#123; formatDate, deepClone &#125; from &#x27;my-awesome-package&#x27;;</span><br><span class="line"></span><br><span class="line">// 格式化日期</span><br><span class="line">formatDate(new Date(), &#x27;YYYY-MM-DD HH:mm:ss&#x27;);</span><br><span class="line">// 输出: &#x27;2026-05-09 11:30:00&#x27;</span><br><span class="line"></span><br><span class="line">// 深度克隆</span><br><span class="line">const cloned = deepClone(&#123; a: &#123; b: 1 &#125; &#125;);</span><br><span class="line">\<span class="code">`\`</span>\`</span><br><span class="line"></span><br><span class="line"><span class="section">## API 文档</span></span><br><span class="line"></span><br><span class="line"><span class="section">### formatDate(date, format)</span></span><br><span class="line"></span><br><span class="line">| 参数 | 类型 | 说明 |</span><br><span class="line">|------|------|------|</span><br><span class="line">| date | Date/string/number | 日期对象、字符串或时间戳 |</span><br><span class="line">| format | string | 格式模板，默认 &#x27;YYYY-MM-DD&#x27; |</span><br><span class="line"></span><br><span class="line"><span class="section">### deepClone(obj)</span></span><br><span class="line"></span><br><span class="line">| 参数 | 类型 | 说明 |</span><br><span class="line">|------|------|------|</span><br><span class="line">| obj | any | 要克隆的对象 |</span><br><span class="line"></span><br><span class="line"><span class="section">## 许可证</span></span><br><span class="line"></span><br><span class="line">MIT</span><br></pre></td></tr></table></figure><h2 id="NPM-注册与登录"><a href="#NPM-注册与登录" class="headerlink" title="NPM 注册与登录"></a>NPM 注册与登录</h2><h3 id="1-注册账号"><a href="#1-注册账号" class="headerlink" title="1. 注册账号"></a>1. 注册账号</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 在官网注册：https://www.npmjs.com/signup</span></span><br><span class="line"><span class="comment"># 或使用命令行</span></span><br><span class="line">npm adduser</span><br></pre></td></tr></table></figure><p>按提示输入用户名、密码、邮箱，完成邮箱验证。</p><h3 id="2-登录验证"><a href="#2-登录验证" class="headerlink" title="2. 登录验证"></a>2. 登录验证</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">npm login</span><br><span class="line"></span><br><span class="line"><span class="comment"># 验证登录状态</span></span><br><span class="line">npm <span class="built_in">whoami</span></span><br></pre></td></tr></table></figure><h3 id="3-配置-2FA（推荐）"><a href="#3-配置-2FA（推荐）" class="headerlink" title="3. 配置 2FA（推荐）"></a>3. 配置 2FA（推荐）</h3><p>在 npmjs.com 站点设置页面启用双因素认证，保护账号安全。</p><h2 id="本地测试发布"><a href="#本地测试发布" class="headerlink" title="本地测试发布"></a>本地测试发布</h2><p>发布前先本地测试：</p><h3 id="使用-npm-link"><a href="#使用-npm-link" class="headerlink" title="使用 npm link"></a>使用 npm link</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 在包目录</span></span><br><span class="line">npm <span class="built_in">link</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 在测试项目目录</span></span><br><span class="line">npm <span class="built_in">link</span> my-awesome-package</span><br><span class="line"></span><br><span class="line"><span class="comment"># 现在可以在测试项目中引用</span></span><br><span class="line">const pkg = require(<span class="string">&#x27;my-awesome-package&#x27;</span>);</span><br></pre></td></tr></table></figure><h3 id="使用-npm-pack"><a href="#使用-npm-pack" class="headerlink" title="使用 npm pack"></a>使用 npm pack</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 打包成 .tgz 文件</span></span><br><span class="line">npm pack</span><br><span class="line"></span><br><span class="line"><span class="comment"># 在测试项目中安装</span></span><br><span class="line">npm install ./my-awesome-package-1.0.0.tgz</span><br></pre></td></tr></table></figure><h2 id="正式发布"><a href="#正式发布" class="headerlink" title="正式发布"></a>正式发布</h2><h3 id="发布流程"><a href="#发布流程" class="headerlink" title="发布流程"></a>发布流程</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1. 确保代码已提交</span></span><br><span class="line">git status</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 运行测试</span></span><br><span class="line">npm <span class="built_in">test</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 构建（如有）</span></span><br><span class="line">npm run build</span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 发布</span></span><br><span class="line">npm publish</span><br><span class="line"></span><br><span class="line"><span class="comment"># 发布成功后会收到邮件通知</span></span><br></pre></td></tr></table></figure><h3 id="发布特定标签"><a href="#发布特定标签" class="headerlink" title="发布特定标签"></a>发布特定标签</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 发布 beta 版本</span></span><br><span class="line">npm publish --tag beta</span><br><span class="line"></span><br><span class="line"><span class="comment"># 发布 next 版本</span></span><br><span class="line">npm publish --tag next</span><br><span class="line"></span><br><span class="line"><span class="comment"># 用户安装指定标签</span></span><br><span class="line">npm install my-awesome-package@beta</span><br></pre></td></tr></table></figure><h3 id="发布-scoped-包"><a href="#发布-scoped-包" class="headerlink" title="发布 scoped 包"></a>发布 scoped 包</h3><p> scoped 包（如 <code>@username/package</code>）默认为私有包，需要付费。</p><p>发布公开 scoped 包：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm publish --access public</span><br></pre></td></tr></table></figure><h2 id="常见问题处理"><a href="#常见问题处理" class="headerlink" title="常见问题处理"></a>常见问题处理</h2><h3 id="包名已存在"><a href="#包名已存在" class="headerlink" title="包名已存在"></a>包名已存在</h3><ul><li>换一个包名</li><li>使用 scoped 包：<code>@yourname/package-name</code></li><li>检查是否拼写错误</li></ul><h3 id="发布失败"><a href="#发布失败" class="headerlink" title="发布失败"></a>发布失败</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 检查登录状态</span></span><br><span class="line">npm <span class="built_in">whoami</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 检查包配置</span></span><br><span class="line">npm publish --dry-run</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看将要发布的文件</span></span><br><span class="line">npm pack --dry-run</span><br></pre></td></tr></table></figure><h3 id="更新已发布的包"><a href="#更新已发布的包" class="headerlink" title="更新已发布的包"></a>更新已发布的包</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1. 修改代码</span></span><br><span class="line"><span class="comment"># 2. 更新版本号</span></span><br><span class="line">npm version patch/minor/major</span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 发布</span></span><br><span class="line">npm publish</span><br></pre></td></tr></table></figure><h3 id="删除已发布的包"><a href="#删除已发布的包" class="headerlink" title="删除已发布的包"></a>删除已发布的包</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 撤销 24 小时内发布的版本</span></span><br><span class="line">npm unpublish my-awesome-package@1.0.0</span><br><span class="line"></span><br><span class="line"><span class="comment"># 撤销整个包（谨慎操作）</span></span><br><span class="line">npm unpublish my-awesome-package --force</span><br><span class="line"></span><br><span class="line"><span class="comment"># 弃用包（推荐，不删除）</span></span><br><span class="line">npm deprecate my-awesome-package <span class="string">&quot;此包已弃用，请使用 xxx 替代&quot;</span></span><br></pre></td></tr></table></figure><h2 id="最佳实践"><a href="#最佳实践" class="headerlink" title="最佳实践"></a>最佳实践</h2><ol><li><strong>语义化版本</strong>：严格遵循 SemVer 规范</li><li><strong>完善的测试</strong>：发布前确保测试通过</li><li><strong>清晰的文档</strong>：README 必须包含安装、使用、API 说明</li><li><strong>变更日志</strong>：维护 CHANGELOG.md 记录版本变化</li><li><strong>类型声明</strong>：提供 TypeScript 类型文件（.d.ts）</li><li><strong>持续集成</strong>：使用 GitHub Actions 自动测试发布</li></ol><h3 id="GitHub-Actions-自动发布"><a href="#GitHub-Actions-自动发布" class="headerlink" title="GitHub Actions 自动发布"></a>GitHub Actions 自动发布</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># .github/workflows/publish.yml</span></span><br><span class="line"><span class="attr">name:</span> <span class="string">Publish</span> <span class="string">to</span> <span class="string">NPM</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">tags:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&#x27;v*&#x27;</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:</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">uses:</span> <span class="string">actions/checkout@v3</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">uses:</span> <span class="string">actions/setup-node@v3</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">&#x27;18&#x27;</span></span><br><span class="line">          <span class="attr">registry-url:</span> <span class="string">&#x27;https://registry.npmjs.org&#x27;</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">run:</span> <span class="string">npm</span> <span class="string">ci</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">run:</span> <span class="string">npm</span> <span class="string">test</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">run:</span> <span class="string">npm</span> <span class="string">run</span> <span class="string">build</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">run:</span> <span class="string">npm</span> <span class="string">publish</span></span><br><span class="line">        <span class="attr">env:</span></span><br><span class="line">          <span class="attr">NODE_AUTH_TOKEN:</span> <span class="string">$&#123;&#123;</span> <span class="string">secrets.NPM_TOKEN</span> <span class="string">&#125;&#125;</span></span><br></pre></td></tr></table></figure><p>在 GitHub 项目设置 → Secrets 中添加 <code>NPM_TOKEN</code>（从 npmjs.com 获取）。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>发布 NPM 包的完整流程：</p><ol><li>规划包的功能和结构</li><li>编写代码和测试</li><li>配置 package.json</li><li>编写 README 和 LICENSE</li><li>本地测试（npm link / npm pack）</li><li>注册登录 NPM</li><li>发布（npm publish）</li><li>维护更新（版本管理）</li></ol><p>掌握了这些，你就可以将自己的代码分享给全世界了！</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://docs.npmjs.com/">NPM 官方文档</a></li><li><a href="https://semver.org/">SemVer 规范</a></li><li><a href="https://rollupjs.org/">Rollup 官方文档</a></li><li><a href="https://jestjs.io/">Jest 测试框架</a></li></ul>]]>
    </content>
    <id>https://foleydang.github.io/2026/05/09/NPM%E5%8C%85%E5%BC%80%E5%8F%91%E4%B8%8E%E5%8F%91%E5%B8%83%E5%AE%8C%E6%95%B4%E6%8C%87%E5%8D%97/</id>
    <link href="https://foleydang.github.io/2026/05/09/NPM%E5%8C%85%E5%BC%80%E5%8F%91%E4%B8%8E%E5%8F%91%E5%B8%83%E5%AE%8C%E6%95%B4%E6%8C%87%E5%8D%97/"/>
    <published>2026-05-09T03:20:00.000Z</published>
    <summary>
      <![CDATA[<p>作为 JavaScript/Node.js 开发者，发布自己的 NPM 包是技术成长的重要一步。本文将从零开始，详细介绍如何开发、打包、测试并发布一个 NPM 包。</p>
<h2 id="为什么需要发布-NPM-包？"><a href="#为什么需要发布-NPM-包？"]]>
    </summary>
    <title>NPM包开发与发布完整指南</title>
    <updated>2026-05-09T03:42:09.382Z</updated>
  </entry>
  <entry>
    <author>
      <name>FoleyDang</name>
    </author>
    <category term="网络安全" scheme="https://foleydang.github.io/categories/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/"/>
    <category term="WireGuard" scheme="https://foleydang.github.io/tags/WireGuard/"/>
    <category term="VPN" scheme="https://foleydang.github.io/tags/VPN/"/>
    <category term="Linux" scheme="https://foleydang.github.io/tags/Linux/"/>
    <category term="阿里云" scheme="https://foleydang.github.io/tags/%E9%98%BF%E9%87%8C%E4%BA%91/"/>
    <content>
      <![CDATA[<p>WireGuard 是一个现代化、高性能的 VPN 协议，相比 OpenVPN 更轻量、更快速。本文记录了在阿里云 ECS 上部署 WireGuard VPN 服务器的完整过程，包括服务器端配置、客户端配置、防火墙设置以及常见问题排查。</p><h2 id="为什么选择-WireGuard？"><a href="#为什么选择-WireGuard？" class="headerlink" title="为什么选择 WireGuard？"></a>为什么选择 WireGuard？</h2><p>WireGuard 相比传统 VPN 方案的优势：</p><ul><li><strong>性能优异</strong>：内核级实现，延迟更低</li><li><strong>配置简洁</strong>：无需复杂的证书管理，只需公钥/私钥配对</li><li><strong>安全性高</strong>：使用现代加密协议（ChaCha20、Poly1305 等）</li><li><strong>跨平台</strong>：支持 Linux、Windows、macOS、iOS、Android</li></ul><h2 id="服务器端部署"><a href="#服务器端部署" class="headerlink" title="服务器端部署"></a>服务器端部署</h2><h3 id="1-安装-WireGuard"><a href="#1-安装-WireGuard" class="headerlink" title="1. 安装 WireGuard"></a>1. 安装 WireGuard</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># CentOS/RHEL</span></span><br><span class="line">yum install wireguard-tools</span><br><span class="line"></span><br><span class="line"><span class="comment"># Ubuntu/Debian</span></span><br><span class="line">apt install wireguard</span><br></pre></td></tr></table></figure><h3 id="2-生成密钥对"><a href="#2-生成密钥对" class="headerlink" title="2. 生成密钥对"></a>2. 生成密钥对</h3><p>WireGuard 使用<strong>公钥加密</strong>来验证身份，每个参与者都需要一对密钥（私钥 + 公钥）。</p><h4 id="为什么需要两对密钥？"><a href="#为什么需要两对密钥？" class="headerlink" title="为什么需要两对密钥？"></a>为什么需要两对密钥？</h4><p>打个比方：</p><ul><li><strong>服务器</strong>就像一个「门」，它有自己的钥匙（私钥）和门牌号（公钥）</li><li><strong>客户端</strong>就像一个「人」，有自己的身份证（私钥）和证件号（公钥）</li></ul><p>要进入这扇门：</p><ol><li>人需要知道门的公钥（门牌号），才能找到正确的门</li><li>门需要知道人的公钥（证件号），才能验证是不是允许这个人进来</li></ol><p>这就是为什么双方都需要<strong>互相交换公钥</strong>：</p><table><thead><tr><th>密钥</th><th>保存在哪里</th><th>作用</th></tr></thead><tbody><tr><td>服务器私钥</td><td>服务器配置 <code>[Interface]</code></td><td>服务器身份验证，绝不外泄</td></tr><tr><td>服务器公钥</td><td>客户端配置 <code>[Peer]</code></td><td>客户端用它找到服务器</td></tr><tr><td>客户端私钥</td><td>客户端配置 <code>[Interface]</code></td><td>客户端身份验证，绝不外泄</td></tr><tr><td>客户端公钥</td><td>服务器配置 <code>[Peer]</code></td><td>服务器用它验证客户端身份</td></tr></tbody></table><h4 id="密钥流向图"><a href="#密钥流向图" class="headerlink" title="密钥流向图"></a>密钥流向图</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">服务器配置文件                     客户端配置文件</span><br><span class="line">┌─────────────────────┐           ┌─────────────────────┐</span><br><span class="line">│ [Interface]         │           │ [Interface]         │</span><br><span class="line">│ PrivateKey = 服务器私钥  │           │ PrivateKey = 客户端私钥  │</span><br><span class="line">│                     │           │                     │</span><br><span class="line">│ [Peer]              │           │ [Peer]              │</span><br><span class="line">│ PublicKey = 客户端公钥   │◄──交换──►│ PublicKey = 服务器公钥   │</span><br><span class="line">│ AllowedIPs = ...    │           │ Endpoint = ...      │</span><br><span class="line">└─────────────────────┘           └─────────────────────┘</span><br></pre></td></tr></table></figure><p><strong>记住这个原则</strong>：</p><ul><li>自己的私钥 → 写在自己的 <code>[Interface]</code> 里</li><li>对方的公钥 → 写在自己的 <code>[Peer]</code> 里</li></ul><h4 id="生成密钥"><a href="#生成密钥" class="headerlink" title="生成密钥"></a>生成密钥</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建配置目录</span></span><br><span class="line"><span class="built_in">mkdir</span> -p /etc/wireguard</span><br><span class="line"><span class="built_in">cd</span> /etc/wireguard</span><br><span class="line"></span><br><span class="line"><span class="comment"># 生成服务器密钥对</span></span><br><span class="line">wg genkey | <span class="built_in">tee</span> server_private.key | wg pubkey &gt; server_public.key</span><br><span class="line"></span><br><span class="line"><span class="comment"># 生成客户端密钥对</span></span><br><span class="line">wg genkey | <span class="built_in">tee</span> client_private.key | wg pubkey &gt; client_public.key</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看生成的密钥</span></span><br><span class="line"><span class="built_in">cat</span> server_private.key  <span class="comment"># 服务器私钥 → 写入服务器 [Interface]</span></span><br><span class="line"><span class="built_in">cat</span> server_public.key   <span class="comment"># 服务器公钥 → 写入客户端 [Peer]</span></span><br><span class="line"><span class="built_in">cat</span> client_private.key  <span class="comment"># 客户端私钥 → 写入客户端 [Interface]</span></span><br><span class="line"><span class="built_in">cat</span> client_public.key   <span class="comment"># 客户端公钥 → 写入服务器 [Peer]</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>重要</strong>：私钥绝对不能泄露！公钥可以公开分享给对方。</p></blockquote><h3 id="3-创建服务器配置文件"><a href="#3-创建服务器配置文件" class="headerlink" title="3. 创建服务器配置文件"></a>3. 创建服务器配置文件</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cat</span> &gt; /etc/wireguard/wg0.conf &lt;&lt; <span class="string">EOF</span></span><br><span class="line"><span class="string">[Interface]</span></span><br><span class="line"><span class="string">PrivateKey = &lt;服务器私钥&gt;</span></span><br><span class="line"><span class="string">Address = 10.100.0.1/24</span></span><br><span class="line"><span class="string">ListenPort = 443</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">[Peer]</span></span><br><span class="line"><span class="string">PublicKey = &lt;客户端公钥&gt;</span></span><br><span class="line"><span class="string">AllowedIPs = 10.100.0.2/32</span></span><br><span class="line"><span class="string">EOF</span></span><br></pre></td></tr></table></figure><p><strong>配置说明：</strong></p><ul><li><code>ListenPort = 443</code>：使用 HTTPS 端口，避免运营商封锁非标准端口（如 51820）</li><li><code>Address = 10.100.0.1/24</code>：VPN 内网地址段，服务器使用 <code>.1</code></li><li><code>AllowedIPs = 10.100.0.2/32</code>：只允许该客户端使用 <code>.2</code> 地址</li></ul><h3 id="4-启动-WireGuard-服务"><a href="#4-启动-WireGuard-服务" class="headerlink" title="4. 启动 WireGuard 服务"></a>4. 启动 WireGuard 服务</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 启动服务</span></span><br><span class="line">wg-quick up wg0</span><br><span class="line"></span><br><span class="line"><span class="comment"># 或使用 systemd 管理</span></span><br><span class="line">systemctl start wg-quick@wg0</span><br><span class="line">systemctl <span class="built_in">enable</span> wg-quick@wg0  <span class="comment"># 开机自启</span></span><br></pre></td></tr></table></figure><p>验证服务状态：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">wg show</span><br></pre></td></tr></table></figure><p>输出示例：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">interface: wg0</span><br><span class="line">  public key: &lt;服务器公钥&gt;</span><br><span class="line">  private key: (hidden)</span><br><span class="line">  listening port: 443</span><br><span class="line"></span><br><span class="line">peer: &lt;客户端公钥&gt;</span><br><span class="line">  allowed ips: 10.100.0.2/32</span><br></pre></td></tr></table></figure><h2 id="防火墙与-NAT-配置"><a href="#防火墙与-NAT-配置" class="headerlink" title="防火墙与 NAT 配置"></a>防火墙与 NAT 配置</h2><h3 id="1-开放防火墙端口"><a href="#1-开放防火墙端口" class="headerlink" title="1. 开放防火墙端口"></a>1. 开放防火墙端口</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># firewalld</span></span><br><span class="line">firewall-cmd --permanent --add-port=443/udp</span><br><span class="line">firewall-cmd --reload</span><br><span class="line"></span><br><span class="line"><span class="comment"># iptables</span></span><br><span class="line">iptables -A INPUT -p udp --dport 443 -j ACCEPT</span><br></pre></td></tr></table></figure><h3 id="2-配置-NAT-规则（让客户端能上网）"><a href="#2-配置-NAT-规则（让客户端能上网）" class="headerlink" title="2. 配置 NAT 规则（让客户端能上网）"></a>2. 配置 NAT 规则（让客户端能上网）</h3><p>WireGuard 客户端通过 VPN 连接后，需要 NAT 规则才能访问外网：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 使用 firewalld 持久化 NAT 规则</span></span><br><span class="line">firewall-cmd --permanent --direct --add-rule ipv4 nat POSTROUTING 0 -s 10.100.0.0/24 -o eth0 -j MASQUERADE</span><br><span class="line">firewall-cmd --reload</span><br><span class="line"></span><br><span class="line"><span class="comment"># 或使用 iptables（重启后会丢失）</span></span><br><span class="line">iptables -t nat -A POSTROUTING -s 10.100.0.0/24 -o eth0 -j MASQUERADE</span><br></pre></td></tr></table></figure><h3 id="3-开启-IP-转发"><a href="#3-开启-IP-转发" class="headerlink" title="3. 开启 IP 转发"></a>3. 开启 IP 转发</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 检查是否已开启</span></span><br><span class="line"><span class="built_in">cat</span> /proc/sys/net/ipv4/ip_forward</span><br><span class="line"></span><br><span class="line"><span class="comment"># 如果为 0，需要开启</span></span><br><span class="line">sysctl -w net.ipv4.ip_forward=1</span><br><span class="line"></span><br><span class="line"><span class="comment"># 持久化设置</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;net.ipv4.ip_forward=1&quot;</span> &gt;&gt; /etc/sysctl.conf</span><br><span class="line">sysctl -p</span><br></pre></td></tr></table></figure><h3 id="4-FORWARD-规则持久化"><a href="#4-FORWARD-规则持久化" class="headerlink" title="4. FORWARD 规则持久化"></a>4. FORWARD 规则持久化</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 0 -i wg0 -o eth0 -j ACCEPT</span><br><span class="line">firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 0 -i eth0 -o wg0 -m state --state ESTABLISHED,RELATED -j ACCEPT</span><br><span class="line">firewall-cmd --reload</span><br></pre></td></tr></table></figure><h3 id="5-阿里云安全组配置"><a href="#5-阿里云安全组配置" class="headerlink" title="5. 阿里云安全组配置"></a>5. 阿里云安全组配置</h3><p>在阿里云控制台添加安全组规则：</p><table><thead><tr><th>方向</th><th>协议</th><th>端口</th><th>来源</th></tr></thead><tbody><tr><td>入站</td><td>UDP</td><td>443</td><td>0.0.0.0/0</td></tr><tr><td>出站</td><td>UDP</td><td>全部</td><td>0.0.0.0/0</td></tr></tbody></table><h2 id="客户端配置"><a href="#客户端配置" class="headerlink" title="客户端配置"></a>客户端配置</h2><h3 id="配置文件内容"><a href="#配置文件内容" class="headerlink" title="配置文件内容"></a>配置文件内容</h3><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">[Interface]</span></span><br><span class="line"><span class="attr">PrivateKey</span> = &lt;客户端私钥&gt;</span><br><span class="line"><span class="attr">Address</span> = <span class="number">10.100</span>.<span class="number">0.2</span>/<span class="number">24</span></span><br><span class="line"><span class="attr">DNS</span> = <span class="number">8.8</span>.<span class="number">8.8</span></span><br><span class="line"></span><br><span class="line"><span class="section">[Peer]</span></span><br><span class="line"><span class="attr">PublicKey</span> = &lt;服务器公钥&gt;</span><br><span class="line"><span class="attr">Endpoint</span> = &lt;服务器公网IP&gt;:<span class="number">443</span></span><br><span class="line"><span class="attr">AllowedIPs</span> = <span class="number">0.0</span>.<span class="number">0.0</span>/<span class="number">0</span></span><br><span class="line"><span class="attr">PersistentKeepalive</span> = <span class="number">25</span></span><br></pre></td></tr></table></figure><p><strong>配置说明：</strong></p><ul><li><code>AllowedIPs = 0.0.0.0/0</code>：所有流量都走 VPN</li><li><code>PersistentKeepalive = 25</code>：保持连接活跃，避免断开</li></ul><h3 id="生成二维码（方便手机导入）"><a href="#生成二维码（方便手机导入）" class="headerlink" title="生成二维码（方便手机导入）"></a>生成二维码（方便手机导入）</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装 qrencode</span></span><br><span class="line">yum install qrencode</span><br><span class="line"></span><br><span class="line"><span class="comment"># 生成二维码</span></span><br><span class="line">qrencode -t PNG -o wireguard-qr.png -r client.conf</span><br><span class="line"></span><br><span class="line"><span class="comment"># 或直接显示在终端</span></span><br><span class="line">qrencode -t ANSIUTF8 -r client.conf</span><br></pre></td></tr></table></figure><h3 id="各平台客户端下载"><a href="#各平台客户端下载" class="headerlink" title="各平台客户端下载"></a>各平台客户端下载</h3><table><thead><tr><th>平台</th><th>下载方式</th></tr></thead><tbody><tr><td>iOS</td><td>App Store 搜索 “WireGuard”</td></tr><tr><td>Android</td><td>Google Play 或 F-Droid</td></tr><tr><td>Windows</td><td><a href="https://www.wireguard.com/install/">https://www.wireguard.com/install/</a></td></tr><tr><td>macOS</td><td>App Store 或 brew install wireguard-tools</td></tr></tbody></table><h2 id="常见问题排查"><a href="#常见问题排查" class="headerlink" title="常见问题排查"></a>常见问题排查</h2><h3 id="1-能连接但无法上网"><a href="#1-能连接但无法上网" class="headerlink" title="1. 能连接但无法上网"></a>1. 能连接但无法上网</h3><p>检查 NAT 规则：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">iptables -t nat -L POSTROUTING -n -v</span><br></pre></td></tr></table></figure><p>如果没有 VPN 网段的 MASQUERADE 规则：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">iptables -t nat -A POSTROUTING -s 10.100.0.0/24 -o eth0 -j MASQUERADE</span><br></pre></td></tr></table></figure><h3 id="2-握手失败（无-latest-handshake）"><a href="#2-握手失败（无-latest-handshake）" class="headerlink" title="2. 握手失败（无 latest handshake）"></a>2. 握手失败（无 latest handshake）</h3><p>检查密钥是否匹配：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 服务器端：客户端公钥应该和配置文件一致</span></span><br><span class="line">wg show | grep peer</span><br><span class="line"></span><br><span class="line"><span class="comment"># 客户端配置里的服务器公钥应该和服务器实际公钥一致</span></span><br><span class="line"><span class="built_in">cat</span> /etc/wireguard/wg0.conf | grep PrivateKey | awk <span class="string">&#x27;&#123;print $3&#125;&#x27;</span> | wg pubkey</span><br></pre></td></tr></table></figure><h3 id="3-数据包发送但无响应"><a href="#3-数据包发送但无响应" class="headerlink" title="3. 数据包发送但无响应"></a>3. 数据包发送但无响应</h3><p>可能是端口被封锁。尝试：</p><ul><li>改用 UDP 443（HTTPS 端口）</li><li>检查阿里云安全组是否开放 UDP 出站</li></ul><h3 id="4-连接后流量不走-VPN"><a href="#4-连接后流量不走-VPN" class="headerlink" title="4. 连接后流量不走 VPN"></a>4. 连接后流量不走 VPN</h3><p>检查客户端 AllowedIPs：</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">AllowedIPs</span> = <span class="number">0.0</span>.<span class="number">0.0</span>/<span class="number">0</span>  <span class="comment"># 全局代理</span></span><br><span class="line"><span class="attr">AllowedIPs</span> = <span class="number">10.100</span>.<span class="number">0.0</span>/<span class="number">24</span>  <span class="comment"># 只代理 VPN 内网</span></span><br></pre></td></tr></table></figure><h3 id="5-规则重启后丢失"><a href="#5-规则重启后丢失" class="headerlink" title="5. 规则重启后丢失"></a>5. 规则重启后丢失</h3><p>确保使用 firewalld 持久化：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 检查持久化规则</span></span><br><span class="line">firewall-cmd --permanent --direct --get-all-rules</span><br></pre></td></tr></table></figure><h2 id="服务管理命令"><a href="#服务管理命令" class="headerlink" title="服务管理命令"></a>服务管理命令</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 启动</span></span><br><span class="line">wg-quick up wg0</span><br><span class="line">systemctl start wg-quick@wg0</span><br><span class="line"></span><br><span class="line"><span class="comment"># 停止</span></span><br><span class="line">wg-quick down wg0</span><br><span class="line">systemctl stop wg-quick@wg0</span><br><span class="line"></span><br><span class="line"><span class="comment"># 状态</span></span><br><span class="line">wg show</span><br><span class="line">systemctl status wg-quick@wg0</span><br><span class="line"></span><br><span class="line"><span class="comment"># 重启</span></span><br><span class="line">systemctl restart wg-quick@wg0</span><br></pre></td></tr></table></figure><h2 id="添加更多客户端"><a href="#添加更多客户端" class="headerlink" title="添加更多客户端"></a>添加更多客户端</h2><p>每个客户端需要独立的密钥对和 IP 地址：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 生成新客户端密钥</span></span><br><span class="line">wg genkey | <span class="built_in">tee</span> client2_private.key | wg pubkey &gt; client2_public.key</span><br><span class="line"></span><br><span class="line"><span class="comment"># 在服务器端添加 peer</span></span><br><span class="line">wg <span class="built_in">set</span> wg0 peer &lt;客户端2公钥&gt; allowed-ips 10.100.0.3/32</span><br><span class="line"></span><br><span class="line"><span class="comment"># 更新服务器配置文件（持久化）</span></span><br><span class="line"><span class="comment"># 在 /etc/wireguard/wg0.conf 添加新的 [Peer] 块</span></span><br></pre></td></tr></table></figure><h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>WireGuard 的部署比 OpenVPN 简单得多，核心要点：</p><ol><li><strong>密钥匹配</strong>：服务器和客户端的公钥/私钥必须正确配对</li><li><strong>端口选择</strong>：用 UDP 443 避免运营商封锁</li><li><strong>NAT 规则</strong>：确保 VPN 网段有 MASQUERADE</li><li><strong>规则持久化</strong>：用 firewalld direct 规则或 iptables-save</li></ol><p>配置完成后，重启服务会自动恢复，无需手动干预。</p>]]>
    </content>
    <id>https://foleydang.github.io/2026/04/19/WireGuard%20VPN%20%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%83%A8%E7%BD%B2%E6%8C%87%E5%8D%97/</id>
    <link href="https://foleydang.github.io/2026/04/19/WireGuard%20VPN%20%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%83%A8%E7%BD%B2%E6%8C%87%E5%8D%97/"/>
    <published>2026-04-19T07:12:00.000Z</published>
    <summary>
      <![CDATA[<p>WireGuard 是一个现代化、高性能的 VPN 协议，相比 OpenVPN 更轻量、更快速。本文记录了在阿里云 ECS 上部署 WireGuard VPN 服务器的完整过程，包括服务器端配置、客户端配置、防火墙设置以及常见问题排查。</p>
<h2 id="为什么选择-]]>
    </summary>
    <title>WireGuard VPN 服务器部署指南</title>
    <updated>2026-04-19T07:21:15.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>FoleyDang</name>
    </author>
    <category term="终端工具" scheme="https://foleydang.github.io/categories/%E7%BB%88%E7%AB%AF%E5%B7%A5%E5%85%B7/"/>
    <category term="Mac" scheme="https://foleydang.github.io/tags/Mac/"/>
    <category term="Starship" scheme="https://foleydang.github.io/tags/Starship/"/>
    <category term="终端美化" scheme="https://foleydang.github.io/tags/%E7%BB%88%E7%AB%AF%E7%BE%8E%E5%8C%96/"/>
    <category term="Shell" scheme="https://foleydang.github.io/tags/Shell/"/>
    <content>
      <![CDATA[<p>作为一名程序员，终端是日常工作中最频繁使用的工具之一。一个美观、高效的终端环境不仅能提升工作效率，还能让编程体验更加愉悦。本文将介绍如何使用 <strong>Starship</strong> 打造一个现代化、跨平台的终端提示符。</p><h2 id="什么是-Starship？"><a href="#什么是-Starship？" class="headerlink" title="什么是 Starship？"></a>什么是 Starship？</h2><p>Starship 是一个轻量、快速、跨 shell 的现代化提示符工具：</p><ul><li><strong>跨平台</strong>：支持 macOS、Linux、Windows</li><li><strong>跨 Shell</strong>：支持 Bash、Zsh、Fish、PowerShell 等</li><li><strong>高度可定制</strong>：丰富的配置选项</li><li><strong>性能优异</strong>：使用 Rust 编写，启动速度极快</li></ul><h2 id="安装前置条件"><a href="#安装前置条件" class="headerlink" title="安装前置条件"></a>安装前置条件</h2><p>安装一个 Nerd Font 字体，否则图标可能显示为乱码：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 推荐 MesloLGS NF 字体</span></span><br><span class="line">brew tap homebrew/cask-fonts</span><br><span class="line">brew install --cask font-meslo-lg-nerd-font</span><br></pre></td></tr></table></figure><p>安装后在终端设置中将字体改为 <code>MesloLGS NF</code>。</p><h2 id="安装-Starship"><a href="#安装-Starship" class="headerlink" title="安装 Starship"></a>安装 Starship</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># macOS</span></span><br><span class="line">brew install starship</span><br><span class="line"></span><br><span class="line"><span class="comment"># Linux</span></span><br><span class="line">curl -sS https://starship.rs/install.sh | sh</span><br></pre></td></tr></table></figure><h2 id="配置-Shell"><a href="#配置-Shell" class="headerlink" title="配置 Shell"></a>配置 Shell</h2><p>根据你使用的 Shell 添加初始化命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Zsh</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;eval &quot;$(starship init zsh)&quot;&#x27;</span> &gt;&gt; ~/.zshrc</span><br><span class="line"><span class="built_in">source</span> ~/.zshrc</span><br><span class="line"></span><br><span class="line"><span class="comment"># Bash</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;eval &quot;$(starship init bash)&quot;&#x27;</span> &gt;&gt; ~/.bashrc</span><br><span class="line"><span class="built_in">source</span> ~/.bashrc</span><br></pre></td></tr></table></figure><h2 id="创建配置文件"><a href="#创建配置文件" class="headerlink" title="创建配置文件"></a>创建配置文件</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">mkdir</span> -p ~/.config</span><br><span class="line"><span class="built_in">touch</span> ~/.config/starship.toml</span><br></pre></td></tr></table></figure><h3 id="推荐配置（青色背景风格）"><a href="#推荐配置（青色背景风格）" class="headerlink" title="推荐配置（青色背景风格）"></a>推荐配置（青色背景风格）</h3><p>一个简洁美观的配置效果：</p><figure class="highlight toml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># ~/.config/starship.toml</span></span><br><span class="line"></span><br><span class="line"><span class="attr">format</span> = <span class="string">&quot;&quot;&quot;[$username@$hostname$directory$git_branch$git_status](bg:cyan fg:black) $character&quot;&quot;&quot;</span></span><br><span class="line"><span class="attr">add_newline</span> = <span class="literal">false</span></span><br><span class="line"></span><br><span class="line"><span class="section">[username]</span></span><br><span class="line"><span class="attr">show_always</span> = <span class="literal">true</span></span><br><span class="line"><span class="attr">format</span> = <span class="string">&quot;$user&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[hostname]</span></span><br><span class="line"><span class="attr">ssh_only</span> = <span class="literal">false</span></span><br><span class="line"><span class="attr">format</span> = <span class="string">&quot;$hostname&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[directory]</span></span><br><span class="line"><span class="attr">format</span> = <span class="string">&quot;&gt;$path&quot;</span></span><br><span class="line"><span class="attr">truncation_length</span> = <span class="number">3</span></span><br><span class="line"></span><br><span class="line"><span class="section">[git_branch]</span></span><br><span class="line"><span class="attr">format</span> = <span class="string">&quot;&gt;$branch&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[git_status]</span></span><br><span class="line"><span class="attr">format</span> = <span class="string">&quot;$all_status&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[character]</span></span><br><span class="line"><span class="attr">success_symbol</span> = <span class="string">&quot;[❯](bold cyan)&quot;</span></span><br><span class="line"><span class="attr">error_symbol</span> = <span class="string">&quot;[❯](bold red)&quot;</span></span><br></pre></td></tr></table></figure><p>效果展示：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">foleydang@hostname&gt;workspace&gt;master ❯</span><br></pre></td></tr></table></figure><h3 id="Powerline-风格配置"><a href="#Powerline-风格配置" class="headerlink" title="Powerline 风格配置"></a>Powerline 风格配置</h3><p>如果你喜欢更丰富的效果：</p><figure class="highlight toml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">format</span> = <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">[](#9A348E)$directory\</span></span><br><span class="line"><span class="string">[](fg:#9A348E bg:#DA627D)$git_branch$git_status\</span></span><br><span class="line"><span class="string">[](fg:#DA627D bg:#FCA17D)$nodejs$python$rust\</span></span><br><span class="line"><span class="string">[](fg:#FCA17D bg:#86BBD8)$time\</span></span><br><span class="line"><span class="string">[ ](fg:#86BBD8)\</span></span><br><span class="line"><span class="string">$line_break$character&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[directory]</span></span><br><span class="line"><span class="attr">style</span> = <span class="string">&quot;bg:#9A348E&quot;</span></span><br><span class="line"><span class="attr">truncation_length</span> = <span class="number">3</span></span><br><span class="line"><span class="attr">truncation_symbol</span> = <span class="string">&quot;…/&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[git_branch]</span></span><br><span class="line"><span class="attr">symbol</span> = <span class="string">&quot; &quot;</span></span><br><span class="line"><span class="attr">style</span> = <span class="string">&quot;bg:#DA627D&quot;</span></span><br><span class="line"><span class="attr">format</span> = <span class="string">&#x27;[$symbol$branch ]($style)&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[git_status]</span></span><br><span class="line"><span class="attr">style</span> = <span class="string">&quot;bg:#DA627D&quot;</span></span><br><span class="line"><span class="attr">format</span> = <span class="string">&#x27;[$all_status$ahead_behind ]($style)&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[nodejs]</span></span><br><span class="line"><span class="attr">symbol</span> = <span class="string">&quot; &quot;</span></span><br><span class="line"><span class="attr">style</span> = <span class="string">&quot;bg:#FCA17D&quot;</span></span><br><span class="line"><span class="attr">format</span> = <span class="string">&#x27;[$symbol($version )]($style)&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[python]</span></span><br><span class="line"><span class="attr">symbol</span> = <span class="string">&quot; &quot;</span></span><br><span class="line"><span class="attr">style</span> = <span class="string">&quot;bg:#FCA17D&quot;</span></span><br><span class="line"><span class="attr">format</span> = <span class="string">&#x27;[$symbol($version )]($style)&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[time]</span></span><br><span class="line"><span class="attr">disabled</span> = <span class="literal">false</span></span><br><span class="line"><span class="attr">time_format</span> = <span class="string">&quot;%R&quot;</span></span><br><span class="line"><span class="attr">style</span> = <span class="string">&quot;bg:#86BBD8&quot;</span></span><br><span class="line"><span class="attr">format</span> = <span class="string">&#x27;[$time ]($style)&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[character]</span></span><br><span class="line"><span class="attr">success_symbol</span> = <span class="string">&quot;[➜](bold green)&quot;</span></span><br><span class="line"><span class="attr">error_symbol</span> = <span class="string">&quot;[➜](bold red)&quot;</span></span><br></pre></td></tr></table></figure><h2 id="一键安装脚本"><a href="#一键安装脚本" class="headerlink" title="一键安装脚本"></a>一键安装脚本</h2><p>保存为 <code>install-starship.sh</code>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"><span class="comment"># Starship 一键安装脚本</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装 Starship</span></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">command</span> -v brew &amp;&gt; /dev/null; <span class="keyword">then</span></span><br><span class="line">  brew install starship</span><br><span class="line"><span class="keyword">elif</span> <span class="built_in">command</span> -v curl &amp;&gt; /dev/null; <span class="keyword">then</span></span><br><span class="line">  curl -sS https://starship.rs/install.sh | sh</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;需要 Homebrew 或 curl 来安装 Starship&quot;</span></span><br><span class="line">  <span class="built_in">exit</span> 1</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建配置文件</span></span><br><span class="line"><span class="built_in">mkdir</span> -p ~/.config</span><br><span class="line"><span class="built_in">cat</span> &gt; ~/.config/starship.toml &lt;&lt; <span class="string">&#x27;STARSHIP_EOF&#x27;</span></span><br><span class="line">format = <span class="string">&quot;&quot;</span><span class="string">&quot;[<span class="variable">$username</span>@$hostname$directory$git_branch<span class="variable">$git_status</span>](bg:cyan fg:black) <span class="variable">$character</span>&quot;</span><span class="string">&quot;&quot;</span></span><br><span class="line">add_newline = <span class="literal">false</span></span><br><span class="line"></span><br><span class="line">[username]</span><br><span class="line">show_always = <span class="literal">true</span></span><br><span class="line">format = <span class="string">&quot;<span class="variable">$user</span>&quot;</span></span><br><span class="line"></span><br><span class="line">[hostname]</span><br><span class="line">ssh_only = <span class="literal">false</span></span><br><span class="line">format = <span class="string">&quot;<span class="variable">$hostname</span>&quot;</span></span><br><span class="line"></span><br><span class="line">[directory]</span><br><span class="line">format = <span class="string">&quot;&gt;<span class="variable">$path</span>&quot;</span></span><br><span class="line">truncation_length = 3</span><br><span class="line"></span><br><span class="line">[git_branch]</span><br><span class="line">format = <span class="string">&quot;&gt;<span class="variable">$branch</span>&quot;</span></span><br><span class="line"></span><br><span class="line">[git_status]</span><br><span class="line">format = <span class="string">&quot;<span class="variable">$all_status</span>&quot;</span></span><br><span class="line"></span><br><span class="line">[character]</span><br><span class="line">success_symbol = <span class="string">&quot;[❯](bold cyan)&quot;</span></span><br><span class="line">error_symbol = <span class="string">&quot;[❯](bold red)&quot;</span></span><br><span class="line">STARSHIP_EOF</span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加到 shell 配置</span></span><br><span class="line"><span class="keyword">if</span> [ -f ~/.zshrc ]; <span class="keyword">then</span></span><br><span class="line">  grep -q <span class="string">&quot;starship init zsh&quot;</span> ~/.zshrc || <span class="built_in">echo</span> <span class="string">&#x27;eval &quot;$(starship init zsh)&quot;&#x27;</span> &gt;&gt; ~/.zshrc</span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;✅ 已添加到 ~/.zshrc&quot;</span></span><br><span class="line"><span class="keyword">elif</span> [ -f ~/.bashrc ]; <span class="keyword">then</span></span><br><span class="line">  grep -q <span class="string">&quot;starship init bash&quot;</span> ~/.bashrc || <span class="built_in">echo</span> <span class="string">&#x27;eval &quot;$(starship init bash)&quot;&#x27;</span> &gt;&gt; ~/.bashrc</span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;✅ 已添加到 ~/.bashrc&quot;</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;✅ Starship 配置完成&quot;</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;运行 source ~/.zshrc 或 source ~/.bashrc 使配置生效&quot;</span></span><br></pre></td></tr></table></figure><p>运行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">chmod</span> +x install-starship.sh</span><br><span class="line">./install-starship.sh</span><br></pre></td></tr></table></figure><h2 id="远程主机迁移"><a href="#远程主机迁移" class="headerlink" title="远程主机迁移"></a>远程主机迁移</h2><h3 id="复制配置到远程主机"><a href="#复制配置到远程主机" class="headerlink" title="复制配置到远程主机"></a>复制配置到远程主机</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 复制配置文件</span></span><br><span class="line">scp ~/.config/starship.toml user@remote:~/.config/</span><br><span class="line"></span><br><span class="line"><span class="comment"># 在远程主机安装</span></span><br><span class="line">ssh user@remote <span class="string">&quot;curl -sS https://starship.rs/install.sh | sh&quot;</span></span><br><span class="line">ssh user@remote <span class="string">&quot;echo &#x27;eval \&quot;\$(starship init zsh)\&quot;&#x27; &gt;&gt; ~/.zshrc&quot;</span></span><br></pre></td></tr></table></figure><h3 id="使用一键脚本"><a href="#使用一键脚本" class="headerlink" title="使用一键脚本"></a>使用一键脚本</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">scp install-starship.sh user@remote:~/ssh user@remote <span class="string">&quot;chmod +x ~/install-starship.sh &amp;&amp; ~/install-starship.sh&quot;</span></span><br></pre></td></tr></table></figure><h2 id="自定义颜色"><a href="#自定义颜色" class="headerlink" title="自定义颜色"></a>自定义颜色</h2><p>修改 <code>bg:cyan fg:black</code> 可以更换配色：</p><p><strong>可用颜色：</strong></p><ul><li><code>black</code>, <code>white</code>, <code>red</code>, <code>green</code>, <code>yellow</code>, <code>blue</code>, <code>magenta</code>, <code>cyan</code></li><li>或使用 hex：<code>bg:#1e1e2e fg:#cdd6f4</code></li></ul><p><strong>示例（紫色背景）：</strong></p><figure class="highlight toml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">format</span> = <span class="string">&quot;&quot;&quot;[$username@$hostname$directory](bg:magenta fg:white) $character&quot;&quot;&quot;</span></span><br></pre></td></tr></table></figure><h2 id="配合-iTerm2-使用"><a href="#配合-iTerm2-使用" class="headerlink" title="配合 iTerm2 使用"></a>配合 iTerm2 使用</h2><p>进一步优化终端体验：</p><ol><li><strong>设置字体</strong>：Preferences → Profiles → Text → Font → MesloLGS NF</li><li><strong>启用真彩色</strong>：Preferences → Profiles → Terminal → Enable True Color</li><li><strong>推荐配色</strong>：Dracula 或 Snazzy 主题</li></ol><h2 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h2><h3 id="图标显示为乱码"><a href="#图标显示为乱码" class="headerlink" title="图标显示为乱码"></a>图标显示为乱码</h3><p>确保终端字体设置为 Nerd Font。</p><h3 id="启动速度慢"><a href="#启动速度慢" class="headerlink" title="启动速度慢"></a>启动速度慢</h3><p>减少不必要的模块可以提升启动速度。</p><h3 id="颜色不显示"><a href="#颜色不显示" class="headerlink" title="颜色不显示"></a>颜色不显示</h3><ul><li>不要手动设置 TERM 环境变量</li><li>某些终端（如 basic TUI）不支持颜色</li></ul><h2 id="配置文件位置"><a href="#配置文件位置" class="headerlink" title="配置文件位置"></a>配置文件位置</h2><table><thead><tr><th>文件</th><th>位置</th></tr></thead><tbody><tr><td>Starship 配置</td><td><code>~/.config/starship.toml</code></td></tr><tr><td>Zsh 初始化</td><td><code>~/.zshrc</code></td></tr><tr><td>Bash 初始化</td><td><code>~/.bashrc</code></td></tr></tbody></table><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>Starship 让终端焕然一新，推荐配置组合：</p><ul><li><strong>终端</strong>：iTerm2</li><li><strong>Shell</strong>：Zsh</li><li><strong>提示符</strong>：Starship</li><li><strong>字体</strong>：MesloLGS NF</li><li><strong>配色</strong>：青色背景 + 黑色字体</li></ul><p>Happy Coding!</p><hr><blockquote><p>参考：<a href="https://starship.rs/">Starship 官方文档</a></p></blockquote>]]>
    </content>
    <id>https://foleydang.github.io/2026/04/19/Mac%E7%BB%88%E7%AB%AF%E7%BE%8E%E5%8C%96-Starship%E9%85%8D%E7%BD%AE%E6%8C%87%E5%8D%97/</id>
    <link href="https://foleydang.github.io/2026/04/19/Mac%E7%BB%88%E7%AB%AF%E7%BE%8E%E5%8C%96-Starship%E9%85%8D%E7%BD%AE%E6%8C%87%E5%8D%97/"/>
    <published>2026-04-19T06:20:00.000Z</published>
    <summary>
      <![CDATA[<p>作为一名程序员，终端是日常工作中最频繁使用的工具之一。一个美观、高效的终端环境不仅能提升工作效率，还能让编程体验更加愉悦。本文将介绍如何使用 <strong>Starship</strong> 打造一个现代化、跨平台的终端提示符。</p>
<h2 id="什么是-Stars]]>
    </summary>
    <title>Mac终端美化 - Starship配置指南</title>
    <updated>2026-04-19T07:09:56.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>FoleyDang</name>
    </author>
    <category term="技术" scheme="https://foleydang.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
    <category term="Hexo" scheme="https://foleydang.github.io/tags/Hexo/"/>
    <category term="博客" scheme="https://foleydang.github.io/tags/%E5%8D%9A%E5%AE%A2/"/>
    <content>
      <![CDATA[<p>使用 Hexo 搭建博客，且采用 NexT 主题，详细介绍写一篇新博客的步骤。</p><h2 id="创建新博客文章"><a href="#创建新博客文章" class="headerlink" title="创建新博客文章"></a>创建新博客文章</h2><p>在终端里，切换到博客根目录（也就是 foleydang 目录），然后运行下面的命令来创建新的博客文章：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> /Users/bytedance/foleydang</span><br><span class="line">hexo new <span class="string">&quot;你的文章标题&quot;</span></span><br></pre></td></tr></table></figure><p>运行该命令后，Hexo 会在 source/_posts 目录下生成一个新的 Markdown 文件，文件名是你设置的文章标题。例如，创建名为 “新博客文章” 的文章：</p><h2 id="编辑博客文章"><a href="#编辑博客文章" class="headerlink" title="编辑博客文章"></a>编辑博客文章</h2><p>使用文本编辑器（像 Visual Studio Code、Sublime Text 等）打开刚生成的 Markdown 文件，文件开头会有类似下面的 Front-Matter 信息：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">title: 新博客文章</span><br><span class="line">tags:</span><br><span class="line">---</span><br></pre></td></tr></table></figure><p>你可以按照需求补充或修改这些信息，比如添加 date（文章发布日期）、categories（文章分类）、tags（文章标签）等：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">title: 新博客文章</span><br><span class="line"><span class="built_in">date</span>: 2025-07-16</span><br><span class="line">categories:</span><br><span class="line">- 技术</span><br><span class="line">tags:</span><br><span class="line">- Hexo</span><br><span class="line">- 博客</span><br><span class="line">---</span><br></pre></td></tr></table></figure><p>接着在 Front-Matter 之后撰写文章内容，Markdown 语法支持标题、列表、图片、链接等常见格式。例如：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">title: 新博客文章</span><br><span class="line"><span class="built_in">date</span>: 2025-07-16</span><br><span class="line">categories:</span><br><span class="line">- 技术</span><br><span class="line">tags:</span><br><span class="line">- Hexo</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">这是我的第一篇使用 Hexo 搭建的博客文章。</span><br><span class="line"></span><br><span class="line"><span class="comment">### 搭建过程</span></span><br><span class="line">在搭建博客时，我使用了 Hexo 这个静态网站生成器，并且选择了 NexT 主题。具体步骤如下：</span><br><span class="line">1. 安装 Hexo</span><br><span class="line">2. 创建新的 Hexo 项目</span><br><span class="line">3. 安装 NexT 主题</span><br><span class="line">4. 配置博客信息</span><br><span class="line"></span><br><span class="line"><span class="comment">### 总结</span></span><br><span class="line">通过这次搭建博客的经历，我对静态网站生成器有了更深入的了解。</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="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">hexo generate</span><br><span class="line">hexo server</span><br></pre></td></tr></table></figure><p>或者使用简写命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">hexo g</span><br><span class="line">hexo s</span><br></pre></td></tr></table></figure><p>启动成功后，在浏览器访问 <a href="http://localhost:4000/">http://localhost:4000</a> 就能预览新写的博客文章。</p><h2 id="部署博客到远程仓库"><a href="#部署博客到远程仓库" class="headerlink" title="部署博客到远程仓库"></a>部署博客到远程仓库</h2><p>若要将博客部署到 GitHub Pages 等远程仓库，需先在 _config.yml 文件里配置部署信息，当前配置如下：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># _config.yml</span></span><br><span class="line"><span class="attr">deploy:</span></span><br><span class="line">  <span class="attr">type:</span> <span class="string">git</span></span><br><span class="line">  <span class="attr">repo:</span> <span class="string">git@github.com:foleydang/foleydang.github.io.git</span></span><br><span class="line">  <span class="attr">branch:</span> <span class="string">master</span></span><br></pre></td></tr></table></figure><p>配置完成后，在终端运行以下命令部署博客：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo deploy</span><br></pre></td></tr></table></figure><p>或者使用简写命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo d</span><br></pre></td></tr></table></figure><p>部署成功后，访问 <a href="https://foleydang.github.io/">https://foleydang.github.io</a> 就能看到新发布的博客文章。</p>]]>
    </content>
    <id>https://foleydang.github.io/2025/07/16/%E4%BD%BF%E7%94%A8Hexo%E5%86%99%E5%8D%9A%E5%AE%A2/</id>
    <link href="https://foleydang.github.io/2025/07/16/%E4%BD%BF%E7%94%A8Hexo%E5%86%99%E5%8D%9A%E5%AE%A2/"/>
    <published>2025-07-16T13:03:39.000Z</published>
    <summary>
      <![CDATA[<p>使用 Hexo 搭建博客，且采用 NexT 主题，详细介绍写一篇新博客的步骤。</p>
<h2 id="创建新博客文章"><a href="#创建新博客文章" class="headerlink" title="创建新博客文章"></a>创建新博客文章</h2><p>在终端]]>
    </summary>
    <title>使用Hexo写博客</title>
    <updated>2025-07-17T03:53:05.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>FoleyDang</name>
    </author>
    <category term="技术" scheme="https://foleydang.github.io/categories/%E6%8A%80%E6%9C%AF/"/>
    <category term="Hexo" scheme="https://foleydang.github.io/tags/Hexo/"/>
    <category term="博客" scheme="https://foleydang.github.io/tags/%E5%8D%9A%E5%AE%A2/"/>
    <content>
      <![CDATA[<p>Welcome to <a href="https://hexo.io/">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues">GitHub</a>.</p><h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo new <span class="string">&quot;My New Post&quot;</span></span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/writing.html">Writing</a></p><h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo server</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/server.html">Server</a></p><h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo generate</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/generating.html">Generating</a></p><h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo deploy</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/one-command-deployment.html">Deployment</a></p>]]>
    </content>
    <id>https://foleydang.github.io/2024/01/16/hello-world/</id>
    <link href="https://foleydang.github.io/2024/01/16/hello-world/"/>
    <published>2024-01-16T03:51:45.000Z</published>
    <summary>
      <![CDATA[<p>Welcome to <a href="https://hexo.io/">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/">documentation</a> for]]>
    </summary>
    <title>Hello World</title>
    <updated>2025-07-17T03:52:38.000Z</updated>
  </entry>
</feed>
