<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>代码构建你的世界</title>
  
  <subtitle>Stay hungry, stay foolish</subtitle>
  <link href="https://www.toimc.com/atom.xml" rel="self"/>
  
  <link href="https://www.toimc.com/"/>
  <updated>2025-10-06T05:13:13.975Z</updated>
  <id>https://www.toimc.com/</id>
  
  <author>
    <name>var author=&#39;Brian&#39;</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>从Nestjs10迁移到Nestjs11</title>
    <link href="https://www.toimc.com/migrate-nestjs11-from-nestjs10/"/>
    <id>https://www.toimc.com/migrate-nestjs11-from-nestjs10/</id>
    <published>2025-02-07T15:53:44.000Z</published>
    <updated>2025-10-06T05:13:13.975Z</updated>
    
    <content type="html"><![CDATA[<p>本文提供了从 <strong>NestJS 10</strong> 迁移到 <strong>NestJS 11</strong> 的完整指南。NestJS 11 引入了许多新功能，同时包含了一些 <strong>轻微的不兼容更改</strong>，但 <strong>大多数用户不会受到影响</strong>。你可以查看完整的变更列表以获取详细信息。</p><span id="more"></span><h1 id="NestJS-11-迁移指南"><a href="#NestJS-11-迁移指南" class="headerlink" title="NestJS 11 迁移指南"></a><strong>NestJS 11 迁移指南</strong></h1><h2 id="升级依赖包"><a href="#升级依赖包" class="headerlink" title="升级依赖包"></a><strong>升级依赖包</strong></h2><p>你可以手动更新依赖包，但官方推荐使用 <a href="https://www.npmjs.com/package/npm-check-updates">npm-check-updates (ncu)</a> 进行自动更新，以简化升级过程。</p><h2 id="Express-v5-迁移"><a href="#Express-v5-迁移" class="headerlink" title="Express v5 迁移"></a><strong>Express v5 迁移</strong></h2><p><strong>Express v5</strong> 于 <strong>2024 年正式发布</strong>，并在 <strong>2025 年成为稳定版本</strong>。在 <strong>NestJS 11</strong> 中，Express v5 现在是默认集成的版本。</p><h3 id="路由匹配规则变更"><a href="#路由匹配规则变更" class="headerlink" title="路由匹配规则变更"></a><strong>路由匹配规则变更</strong></h3><p>Express v5 调整了路径匹配算法，主要变更包括：</p><ol><li><strong>通配符 * 必须带名称</strong>：</li></ol><ul><li>旧语法（Express v4）：<code>/*</code></li><li>新语法（Express v5）：<code>/*splat</code> 或 <code>/&#123;*splat&#125;</code>（splat 只是一个变量名，可以自定义）</li></ul><ol start="2"><li><strong>?（可选参数）不再支持</strong>：</li></ol><ul><li>旧语法：<code>/:file?.ext</code></li><li>新语法：<code>/:file&#123;.:ext&#125;</code></li></ul><ol start="3"><li><strong>正则表达式字符不再支持</strong>。</li><li><strong>部分特殊字符需要转义</strong>（如 <code>()[]?+!</code>）。</li><li><strong>参数名必须是合法的 JavaScript 标识符</strong>，或使用 <code>:&quot;paramName&quot;</code> 进行引用。</li></ol><figure class="highlight ts"><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">// 旧方式（Express v4）</span></span><br><span class="line"><span class="meta">@Get</span>(<span class="string">&#x27;users/*&#x27;</span>)</span><br><span class="line"><span class="title function_">findAll</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="string">&#x27;此路由在 Express v5 可能无法正常工作&#x27;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 新方式（Express v5）</span></span><br><span class="line"><span class="meta">@Get</span>(<span class="string">&#x27;users/*splat&#x27;</span>) <span class="comment">// 需要给通配符命名</span></span><br><span class="line"><span class="title function_">findAll</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="string">&#x27;此路由在 Express v5 可正常工作&#x27;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>⚠ <strong>注意</strong>：</p><ul><li><p><code>*splat</code> 只是一个占位符名称，可以随意更改，如 <code>*wildcard</code>。</p></li><li><p>如果你希望匹配 <strong>根路径</strong>，需要使用 <code>/&#123;*splat&#125;</code> 语法：</p></li></ul><h3 id="中间件路径调整"><a href="#中间件路径调整" class="headerlink" title="中间件路径调整"></a><strong>中间件路径调整</strong></h3><p>在 Express v5 中，如果你有适用于所有路由的中间件，需要更新路径：</p><figure class="highlight ts"><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">// 旧方式（Express v4）</span></span><br><span class="line"><span class="title function_">forRoutes</span>(<span class="string">&#x27;*&#x27;</span>); <span class="comment">// Express v5 可能不兼容</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 新方式（Express v5）</span></span><br><span class="line"><span class="title function_">forRoutes</span>(<span class="string">&#x27;&#123;*splat&#125;&#x27;</span>); <span class="comment">// 现在推荐的方式</span></span><br></pre></td></tr></table></figure><h2 id="Express-v5-的查询参数解析变更"><a href="#Express-v5-的查询参数解析变更" class="headerlink" title="Express v5 的查询参数解析变更"></a><strong>Express v5 的查询参数解析变更</strong></h2><p><strong>适用于 Express v5</strong></p><ul><li><p>之前，Express 默认使用 qs 库解析查询参数，支持嵌套对象和数组。</p></li><li><p>现在，默认使用 <strong>简单解析器</strong>，<strong>不再支持嵌套对象和数组</strong>。</p></li></ul><p>例如，以下查询字符串：</p><figure class="highlight xquery"><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">?filter</span>[<span class="keyword">where</span>]<span class="built_in">[name</span>]=John<span class="built_in">&amp;filter</span>[<span class="keyword">where</span>][age]=<span class="number">30</span></span><br><span class="line">?<span class="type">item</span>[]=<span class="number">1</span>&amp;<span class="type">item</span>[]=<span class="number">2</span></span><br></pre></td></tr></table></figure><p>在 Express v5 <strong>不会被正确解析</strong>。</p><p><strong>如何恢复原行为</strong>:</p><figure class="highlight typescript"><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="keyword">import</span> &#123; <span class="title class_">NestFactory</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/core&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">NestExpressApplication</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/platform-express&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">AppModule</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./app.module&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">bootstrap</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> app = <span class="keyword">await</span> <span class="title class_">NestFactory</span>.<span class="property">create</span>&lt;<span class="title class_">NestExpressApplication</span>&gt;(<span class="title class_">AppModule</span>);</span><br><span class="line">  app.<span class="title function_">set</span>(<span class="string">&#x27;query parser&#x27;</span>, <span class="string">&#x27;extended&#x27;</span>); <span class="comment">// 重新启用 qs 库解析方式</span></span><br><span class="line">  <span class="keyword">await</span> app.<span class="title function_">listen</span>(<span class="number">3000</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="title function_">bootstrap</span>();</span><br></pre></td></tr></table></figure><h2 id="Fastify-v5-迁移"><a href="#Fastify-v5-迁移" class="headerlink" title="Fastify v5 迁移"></a><strong>Fastify v5 迁移</strong></h2><p><strong>NestJS 11</strong> 现在默认支持 <strong>Fastify v5</strong>：</p><ul><li><p><strong>几乎无痛升级</strong>，大部分用户无需调整。</p></li><li><p><strong>路径匹配规则未变</strong>，通配符 * 仍然适用。</p></li></ul><p>中间件注册调整</p><p>Fastify v5 不再支持 (.*) 语法匹配所有路径，需使用 <strong>命名通配符</strong>：</p><figure class="highlight js"><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">// 旧方式（Fastify v4）</span></span><br><span class="line">.<span class="title function_">forRoutes</span>(<span class="string">&#x27;(.*)&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 新方式（Fastify v5）</span></span><br><span class="line">.<span class="title function_">forRoutes</span>(<span class="string">&#x27;*splat&#x27;</span>);</span><br></pre></td></tr></table></figure><p><code>splat</code> 只是一个占位符名称，可自定义。</p><h2 id="模块解析算法优化"><a href="#模块解析算法优化" class="headerlink" title="模块解析算法优化"></a><strong>模块解析算法优化</strong></h2><p><strong>变更点</strong></p><ul><li><p><strong>NestJS 10 及之前</strong>：使用 <strong>哈希计算</strong> 来标识动态模块。</p></li><li><p><strong>NestJS 11</strong>：改为 <strong>对象引用</strong>，提升性能，减少内存占用。</p></li></ul><p>⚠ <strong>影响</strong></p><ul><li><p>以前，重复导入相同的动态模块会 <strong>自动去重</strong>。</p></li><li><p>现在，如果你希望模块 <strong>共享相同实例</strong>，需要手动管理：</p></li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> myModule = <span class="title class_">DynamicModule</span>.<span class="title function_">forRoot</span>(&#123;&#125;);</span><br></pre></td></tr></table></figure><h2 id="生命周期钩子执行顺序调整"><a href="#生命周期钩子执行顺序调整" class="headerlink" title="生命周期钩子执行顺序调整"></a><strong>生命周期钩子执行顺序调整</strong></h2><ul><li><p>之前，OnModuleDestroy、OnApplicationShutdown 等 <strong>按初始化顺序执行</strong>。</p></li><li><p>现在，<strong>执行顺序反转</strong>（与初始化顺序相反）。</p></li></ul><p>示例：</p><figure class="highlight css"><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="selector-tag">A</span> 依赖 <span class="selector-tag">B</span>，<span class="selector-tag">B</span> 依赖 C</span><br><span class="line"><span class="selector-tag">A</span> -&gt; <span class="selector-tag">B</span> -&gt; C</span><br></pre></td></tr></table></figure><ul><li><p><strong>模块初始化顺序</strong>：C -&gt; B -&gt; A</p></li><li><p><strong>模块销毁顺序</strong>：A -&gt; B -&gt; C（反转）</p></li></ul><p>⚠ <strong>影响</strong>：</p><p><strong>全局模块</strong> 被视为依赖所有模块，因此 <strong>最先初始化，最后销毁</strong>。</p><h2 id="CacheModule-迁移"><a href="#CacheModule-迁移" class="headerlink" title="CacheModule 迁移"></a><strong>CacheModule 迁移</strong></h2><p><strong>NestJS 11</strong> 的 @nestjs&#x2F;cache-manager：</p><ul><li><p>迁移至 <strong>Keyv</strong>，提供统一的 <strong>键值存储接口</strong>。</p></li><li><p><strong>Redis 配置方式变更</strong>：</p></li></ul><p><strong>旧方式（NestJS 10）</strong>:</p><figure class="highlight js"><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="title class_">CacheModule</span>.<span class="title function_">registerAsync</span>(&#123;</span><br><span class="line">  <span class="attr">useFactory</span>: <span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> store = <span class="keyword">await</span> <span class="title function_">redisStore</span>(&#123;</span><br><span class="line">      <span class="attr">socket</span>: &#123; <span class="attr">host</span>: <span class="string">&#x27;localhost&#x27;</span>, <span class="attr">port</span>: <span class="number">6379</span> &#125;,</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">return</span> &#123; store &#125;;</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;),</span><br></pre></td></tr></table></figure><p><strong>新方式（NestJS 11）</strong></p><figure class="highlight js"><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="title class_">CacheModule</span>.<span class="title function_">registerAsync</span>(&#123;</span><br><span class="line">  <span class="attr">useFactory</span>: <span class="keyword">async</span> () =&gt; (&#123;</span><br><span class="line">    <span class="attr">stores</span>: [<span class="keyword">new</span> <span class="title class_">KeyvRedis</span>(<span class="string">&#x27;redis://localhost:6379&#x27;</span>)],</span><br><span class="line">  &#125;),</span><br><span class="line">&#125;),</span><br></pre></td></tr></table></figure><h2 id="Node-js-版本要求"><a href="#Node-js-版本要求" class="headerlink" title="Node.js 版本要求"></a><strong>Node.js 版本要求</strong></h2><ul><li><p><strong>Node.js 16 已不再支持</strong>（于 2023-09-11 结束生命周期）。</p></li><li><p><strong>Node.js 18 也被移除</strong>（安全支持将于 2025-04-30 结束）。</p></li><li><p><strong>NestJS 11 需要 Node.js 20 或更高版本</strong>。</p></li></ul><p>⚠ <strong>建议</strong>：</p><p>始终使用 <strong>最新的 LTS 版本</strong> 以获得最佳体验。</p><h2 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h2><p><strong>Mau 是 NestJS 官方推出的云端部署平台</strong>：</p><ul><li><p><strong>一键部署到 AWS</strong></p></li><li><p><strong>环境变量管理</strong></p></li><li><p><strong>实时应用监控</strong></p></li><li><p><strong>无需管理基础设施</strong></p></li></ul><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">npm install -g @nestjs/mau</span><br><span class="line"></span><br><span class="line"><span class="comment"># 部署</span></span><br><span class="line">mau deploy</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;本文提供了从 &lt;strong&gt;NestJS 10&lt;/strong&gt; 迁移到 &lt;strong&gt;NestJS 11&lt;/strong&gt; 的完整指南。NestJS 11 引入了许多新功能，同时包含了一些 &lt;strong&gt;轻微的不兼容更改&lt;/strong&gt;，但 &lt;strong&gt;大多数用户不会受到影响&lt;/strong&gt;。你可以查看完整的变更列表以获取详细信息。&lt;/p&gt;</summary>
    
    
    
    
    <category term="nestjs" scheme="https://www.toimc.com/tags/nestjs/"/>
    
    <category term="nestjs11" scheme="https://www.toimc.com/tags/nestjs11/"/>
    
  </entry>
  
  <entry>
    <title>Nestjs11更新了什么？</title>
    <link href="https://www.toimc.com/nestjs11/"/>
    <id>https://www.toimc.com/nestjs11/</id>
    <published>2025-02-07T15:25:37.000Z</published>
    <updated>2025-10-06T05:13:13.975Z</updated>
    
    <content type="html"><![CDATA[<p>NestJS 11 带来了 <strong>日志增强、微服务优化、启动速度提升</strong> 以及对 <strong>Express v5 &#x2F; Fastify v5</strong> 的支持。新增 <strong>JSON 日志</strong> 让容器化部署更高效，<strong>微服务 API</strong> 变得更灵活，应用启动速度显著提升。同时，最新的 Express v5 需要调整通配符路由。更多更新细节，请查看完整解析，让你的 NestJS 应用更加高效！🔥</p><span id="more"></span><h3 id="Logger-增强"><a href="#Logger-增强" class="headerlink" title="Logger 增强"></a><strong>Logger 增强</strong></h3><ul><li><strong>JSON 日志支持</strong>：<code>ConsoleLogger</code> 现在支持 JSON 格式的日志输出，适用于容器化环境。</li><li><strong>自定义日志前缀</strong>：可以自定义日志前缀，并且支持重新启用颜色输出以便于本地开发。</li></ul><figure class="highlight js"><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="keyword">const</span> app = <span class="keyword">await</span> <span class="title class_">NestFactory</span>.<span class="title function_">create</span>(<span class="title class_">AppModule</span>, &#123;</span><br><span class="line">  <span class="attr">logger</span>: <span class="keyword">new</span> <span class="title class_">ConsoleLogger</span>(&#123;</span><br><span class="line">    <span class="attr">json</span>: <span class="literal">true</span>,</span><br><span class="line">    <span class="attr">colors</span>: <span class="literal">true</span>,</span><br><span class="line">  &#125;),</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h3 id="更灵活的微服务"><a href="#更灵活的微服务" class="headerlink" title="更灵活的微服务"></a><strong>更灵活的微服务</strong></h3><ul><li><strong><code>unwrap</code> 方法</strong>：可以直接访问底层客户端实例，支持自定义操作。</li><li><strong>事件监听</strong>：新增 <code>on</code> 方法，用于监听底层客户端的事件，如断开连接或错误。</li><li><strong>状态观察流</strong>：新增 <code>status</code> 观察流，实时监控客户端实例的状态。</li></ul><figure class="highlight js"><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="keyword">const</span> connection = serviceRef.<span class="property">unwrap</span>&lt;<span class="title class_">NatsConnection</span>&gt;();</span><br><span class="line">serviceRef.<span class="property">on</span>&lt;<span class="title class_">NatsEvents</span>&gt;(<span class="string">&#x27;disconnect&#x27;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;Client disconnected&#x27;</span>);</span><br><span class="line">&#125;);</span><br><span class="line">serviceRef.<span class="property">status</span>.<span class="title function_">subscribe</span>(<span class="function">(<span class="params">status</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;Status:&#x27;</span>, status);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h3 id="更快的应用启动"><a href="#更快的应用启动" class="headerlink" title="更快的应用启动"></a><strong>更快的应用启动</strong></h3><ul><li><strong>模块不透明键生成优化</strong>：通过使用对象引用而不是哈希生成模块键，显著提升了启动性能。</li></ul><h3 id="Express-v5-和-Fastify-v5-支持"><a href="#Express-v5-和-Fastify-v5-支持" class="headerlink" title="Express v5 和 Fastify v5 支持"></a><strong>Express v5 和 Fastify v5 支持</strong></h3><ul><li><strong>Express v5</strong>：更新了路径路由匹配算法，需要注意一些语法变化。</li><li><strong>Fastify v5</strong>：大多数用户应该可以平滑升级。</li></ul><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Get(<span class="string">&#x27;users/*splat&#x27;</span>)</span></span><br><span class="line">findAll() &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="string">&#x27;This route will work in Express v5&#x27;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="其他有价值的更新"><a href="#其他有价值的更新" class="headerlink" title="其他有价值的更新"></a><strong>其他有价值的更新</strong></h3><ul><li>终止生命周期钩子顺序反转：<code>OnModuleDestroy</code> 和 <code>OnApplicationShutdown</code> 的顺序被反转。</li><li><code>CacheModule</code> 更新：使用 <code>cache-manager</code> v6，支持 <code>Keyv</code> 作为统一的键值存储接口。</li><li>新的 <code>ParseDatePipe</code>：简化了日期处理。</li><li>微服务选项支持从 DI 容器提供：增加了灵活性。</li><li><code>IntrinsicException</code>：引入了不会自动记录框架日志的异常。</li><li><code>@nestjs/cqrs</code> 增强：支持请求范围的提供者和强类型的命令、事件和查询。</li><li><code>Reflector#getAll</code> 和 <code>Reflector#getAllAndMerge</code>：现在可以正确推断转换后的元数据类型。</li><li><code>ConfigService#get</code> 更新：现在以不同的顺序读取配置值，并引入了 <code>skipProcessEnv</code> 选项。</li></ul><h3 id="迁移指南"><a href="#迁移指南" class="headerlink" title="迁移指南"></a><strong>迁移指南</strong></h3><ul><li>提供了详细的 <a href="./migrate-nestjs11-from-nestjs10.md">迁移指南</a>，帮助用户从 NestJS 10 平滑升级到 NestJS 11。</li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;NestJS 11 带来了 &lt;strong&gt;日志增强、微服务优化、启动速度提升&lt;/strong&gt; 以及对 &lt;strong&gt;Express v5 &amp;#x2F; Fastify v5&lt;/strong&gt; 的支持。新增 &lt;strong&gt;JSON 日志&lt;/strong&gt; 让容器化部署更高效，&lt;strong&gt;微服务 API&lt;/strong&gt; 变得更灵活，应用启动速度显著提升。同时，最新的 Express v5 需要调整通配符路由。更多更新细节，请查看完整解析，让你的 NestJS 应用更加高效！🔥&lt;/p&gt;</summary>
    
    
    
    
    <category term="nestjs" scheme="https://www.toimc.com/tags/nestjs/"/>
    
    <category term="nestjs11" scheme="https://www.toimc.com/tags/nestjs11/"/>
    
  </entry>
  
  <entry>
    <title>配置 Ubuntu 上发送 SMTP 邮件</title>
    <link href="https://www.toimc.com/ubuntu-smtp-mail/"/>
    <id>https://www.toimc.com/ubuntu-smtp-mail/</id>
    <published>2024-09-12T04:00:34.000Z</published>
    <updated>2025-10-06T05:13:13.975Z</updated>
    
    <content type="html"><![CDATA[<p>介绍了在 Ubuntu 系统上使用 mailx 和 Postfix 发送 SMTP 邮件的配置方法。步骤包括安装 mailx 和 Postfix，配置 Postfix 通过外部 SMTP 服务器（例如 Amazon SES）发送邮件，设置 TLS 加密和 SASL 认证，生成并保护认证文件，以及如何测试邮件发送。博客还介绍了如何通过 ~&#x2F;.mailrc 文件设置自定义发件人地址。</p><span id="more"></span><p>Ubuntu 上发送 SMTP 邮件，使用 mailx 和 Postfix 结合是一个常见的方式。以下是如何正确配置并通过 mailx 使用 SMTP 发送邮件的步骤。</p><ol><li>安装 mailx 和 Postfix<br>确保你已经安装了 mailx 和 Postfix。</li></ol><figure class="highlight cmake"><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">sudo apt update</span><br><span class="line">sudo apt <span class="keyword">install</span> mailx postfix</span><br></pre></td></tr></table></figure><ol start="2"><li>配置 Postfix<br>配置 Postfix 为通过外部 SMTP 服务器发送邮件。这里以 Gmail 为例，如果你使用其他 SMTP 服务器（例如 Amazon SES），替换相应的配置信息。</li></ol><p>修改 <code>/etc/postfix/main.cf</code></p><p>编辑 Postfix 配置文件 <code>/etc/postfix/main.cf</code>：</p><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><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 设置SMTP服务器的标识符</span></span><br><span class="line"><span class="attr">smtpd_banner</span> = <span class="variable">$myhostname</span> ESMTP <span class="variable">$mail_name</span> (Ubuntu)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 禁用本地邮件提醒（通常用于本地用户终端的邮件通知）</span></span><br><span class="line"><span class="attr">biff</span> = <span class="literal">no</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 禁止在本地发送邮件时自动添加域名后缀</span></span><br><span class="line"><span class="attr">append_dot_mydomain</span> = <span class="literal">no</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 禁用 Postfix README 文档目录</span></span><br><span class="line"><span class="attr">readme_directory</span> = <span class="literal">no</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置Postfix兼容级别为3.6，以确保使用最新的默认行为</span></span><br><span class="line"><span class="attr">compatibility_level</span> = <span class="number">3.6</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># TLS证书文件路径</span></span><br><span class="line"><span class="attr">smtpd_tls_cert_file</span>=/etc/ssl/certs/ssl-cert-snakeoil.pem</span><br><span class="line"></span><br><span class="line"><span class="comment"># TLS私钥文件路径</span></span><br><span class="line"><span class="attr">smtpd_tls_key_file</span>=/etc/ssl/private/ssl-cert-snakeoil.key</span><br><span class="line"></span><br><span class="line"><span class="comment"># 可选TLS安全级别，如果客户端支持TLS则使用</span></span><br><span class="line"><span class="attr">smtpd_tls_security_level</span>=may</span><br><span class="line"></span><br><span class="line"><span class="comment"># 指定CA证书的路径，用于验证客户端证书</span></span><br><span class="line"><span class="attr">smtp_tls_CApath</span>=/etc/ssl/certs</span><br><span class="line"></span><br><span class="line"><span class="comment"># 用于存储TLS会话缓存</span></span><br><span class="line"><span class="attr">smtp_tls_session_cache_database</span> = btree:<span class="variable">$&#123;data_directory&#125;</span>/smtp_scache</span><br><span class="line"></span><br><span class="line"><span class="comment"># 允许本地网络和经过SASL验证的客户端转发邮件</span></span><br><span class="line"><span class="attr">smtpd_relay_restrictions</span> = permit_mynetworks permit_sasl_authenticated defer_unauth_destination</span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置主机名，通常是服务器的FQDN（完全限定域名）</span></span><br><span class="line"><span class="attr">myhostname</span> = localhost.localdomain</span><br><span class="line"></span><br><span class="line"><span class="comment"># 指定用户别名文件，用于邮件转发</span></span><br><span class="line"><span class="attr">alias_maps</span> = hash:/etc/aliases</span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置别名数据库</span></span><br><span class="line"><span class="attr">alias_database</span> = hash:/etc/aliases</span><br><span class="line"></span><br><span class="line"><span class="comment"># 指定邮件的源地址（发送者域名）</span></span><br><span class="line"><span class="attr">myorigin</span> = /etc/mailname</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置本地邮件的目的地（接收者域名）</span></span><br><span class="line"><span class="attr">mydestination</span> = <span class="variable">$myhostname</span>, toimc.com, localhost.localdomain, localhost.localdomain, localhost</span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置邮件中继服务器（Amazon SES SMTP服务器）</span></span><br><span class="line"><span class="attr">relayhost</span> = [email-smtp.us-east-<span class="number">1</span>.amazonaws.com]:<span class="number">587</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 指定可以访问的网络范围（通常为本地网络）</span></span><br><span class="line"><span class="attr">mynetworks</span> = <span class="number">127.0</span>.<span class="number">0.0</span>/<span class="number">8</span> [::ffff:<span class="number">127.0</span>.<span class="number">0.0</span>]/<span class="number">104</span> [::<span class="number">1</span>]/<span class="number">128</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置邮箱大小限制为0，表示不限制邮箱大小</span></span><br><span class="line"><span class="attr">mailbox_size_limit</span> = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 指定邮箱地址的分隔符，允许如user+info@example.com的邮箱地址格式</span></span><br><span class="line"><span class="attr">recipient_delimiter</span> = +</span><br><span class="line"></span><br><span class="line"><span class="comment"># 监听所有网络接口，接受所有外部连接</span></span><br><span class="line"><span class="attr">inet_interfaces</span> = all</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启用IPv4和IPv6协议</span></span><br><span class="line"><span class="attr">inet_protocols</span> = all</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启用SMTP的SASL认证</span></span><br><span class="line"><span class="attr">smtp_sasl_auth_enable</span> = <span class="literal">yes</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 指定SASL认证的用户名和密码文件</span></span><br><span class="line"><span class="attr">smtp_sasl_password_maps</span> = hash:/etc/postfix/sasl_passwd</span><br><span class="line"></span><br><span class="line"><span class="comment"># 禁用匿名登录</span></span><br><span class="line"><span class="attr">smtp_sasl_security_options</span> = noanonymous</span><br><span class="line"></span><br><span class="line"><span class="comment"># 强制使用TLS加密连接</span></span><br><span class="line"><span class="attr">smtp_tls_security_level</span> = encrypt</span><br><span class="line"></span><br><span class="line"><span class="comment"># 记录STARTTLS的提供信息</span></span><br><span class="line"><span class="attr">smtp_tls_note_starttls_offer</span> = <span class="literal">yes</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 启用SMTP的TLS加密</span></span><br><span class="line"><span class="attr">smtp_use_tls</span> = <span class="literal">yes</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 禁用SMTPUTF8支持，避免与不支持该扩展的服务器出现兼容性问题</span></span><br><span class="line"><span class="attr">smtputf8_enable</span> = <span class="literal">no</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><ol start="3"><li>(可选)创建<code>~/.mailrc</code>指定发送人：</li></ol><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">set</span> <span class="attribute">from</span>=<span class="literal">no</span>-reply@toimc.com</span><br></pre></td></tr></table></figure><ol start="4"><li>创建密码文件 <code>vi /etc/postfix/sasl_passwd</code></li></ol><p>类似下面的结构： [smtp.domain]:port username:password</p><p>例如：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[<span class="symbol">email-smtp.us-east-1.amazonaws.com</span>]:<span class="link">587 AKIAT7WPVYWC25C2WBJW:你的SMTP密码</span></span><br></pre></td></tr></table></figure><p>权限 <code>sudo chmod 600 /etc/postfix/sasl_passwd</code></p><p>哈希 <code>sudo postmap /etc/postfix/sasl_passwd</code></p><p>重启服务 <code>systemctl restart postfix</code></p><ol start="5"><li>测试</li></ol><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">echo</span> <span class="string">&quot;Test email body&quot;</span> | mail -s <span class="string">&quot;这是一个邮件测试&quot;</span> yourmail<span class="variable">@domain</span>.com</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;介绍了在 Ubuntu 系统上使用 mailx 和 Postfix 发送 SMTP 邮件的配置方法。步骤包括安装 mailx 和 Postfix，配置 Postfix 通过外部 SMTP 服务器（例如 Amazon SES）发送邮件，设置 TLS 加密和 SASL 认证，生成并保护认证文件，以及如何测试邮件发送。博客还介绍了如何通过 ~&amp;#x2F;.mailrc 文件设置自定义发件人地址。&lt;/p&gt;</summary>
    
    
    
    
    <category term="Linux" scheme="https://www.toimc.com/tags/Linux/"/>
    
  </entry>
  
  <entry>
    <title>啥？新媒体人必学好视频参数</title>
    <link href="https://www.toimc.com/video-parameters/"/>
    <id>https://www.toimc.com/video-parameters/</id>
    <published>2022-09-09T02:51:46.000Z</published>
    <updated>2025-10-06T05:13:13.974Z</updated>
    
    <content type="html"><![CDATA[<p>理解常见的视频参数、设备参数、输出参数、发布网站的参数，有利于视频从业工作者制作出高质量合规的视频。</p><span id="more"></span><h2 id="常见的参数"><a href="#常见的参数" class="headerlink" title="常见的参数"></a>常见的参数</h2><h3 id="分辨率"><a href="#分辨率" class="headerlink" title="分辨率"></a>分辨率</h3><p>分辨率：屏幕分辨率是指纵横向上的像素点数，单位是px，常规：1920x1080，3840x2160，等；</p><p>常见的高清 &#x3D; 540p &#x3D; 960×540，超清720p &#x3D; 1280x720，蓝光1080p&#x3D;1920x1080，4K&#x3D;3840x2160(4096×2160)</p><h3 id="帧数-帧速率fps"><a href="#帧数-帧速率fps" class="headerlink" title="帧数(帧速率fps)"></a>帧数(帧速率fps)</h3><p>帧数：为帧生成数量的简称；帧率(Frame rate)&#x3D;帧数(Frames)&#x2F;时间(Time)，单位为帧每秒(f&#x2F;s, frames per second, fps)；常见的帧数：</p><ul><li>24P：电影标准</li><li>25P：PAL标准</li><li>30P：NTSC标准</li><li>60P：适合减慢做慢动作</li><li>120P：做更慢的慢动作（画质损失严重）</li></ul><h3 id="码流-比特率bps"><a href="#码流-比特率bps" class="headerlink" title="码流(比特率bps)"></a>码流(比特率bps)</h3><p>比特率是指每秒传送的比特(bit)数，也叫<strong>码率</strong>、<strong>码流</strong>。单位为 bps(Bit Per Second)，比特率越高，传送的数据越大，音画质越好。</p><p>码流(比特率) &#x3D;采样率 x 采用位数 x声道数</p><p>或者 </p><p>码流&#x3D;文件大小（兆位）&#x2F;视频持续时间（秒）例如：2400MBx8 &#x2F; 480 秒 &#x3D; 40Mbps</p><p>编码：所谓视频编码方式就是指通过压缩技术，将原始视频格式的文件转换成另一种视频格式文件的方式。</p><blockquote><p>Wiki：视频流传输中最为重要的编解码标准有国际电联的H.261、H.263、H.264，运动静止图像专家组的M-JPEG和国际标准化组织运动图像专家组的MPEG系列标准，此外在互联网上被广泛应用的还有Real-Networks的RealVideo、微软公司的WMV以及Apple公司的QuickTime等。 </p></blockquote><table><thead><tr><th>分辨率</th><th>帧数</th><th>声音</th><th>文件名</th><th>编码</th><th>码流</th></tr></thead><tbody><tr><td>8K</td><td>24</td><td>WAV(PCM)</td><td>MOV</td><td>H.264</td><td>50Mbps</td></tr><tr><td>4K</td><td>25</td><td>AAC</td><td>M4V</td><td>H.265</td><td>15Mbps</td></tr><tr><td>2K</td><td>30</td><td>MP3</td><td>MP4</td><td>MPEG-2</td><td>10Mbps</td></tr><tr><td>1080p</td><td>50</td><td>FLAC</td><td>AVI</td><td>MPEG-4</td><td>6Mbps</td></tr><tr><td>720p</td><td>60</td><td></td><td>MKV</td><td>RV40</td><td>4Mbps</td></tr><tr><td></td><td>PAL</td><td></td><td></td><td>WMV</td><td>2Mbps</td></tr></tbody></table><blockquote><p>文件名与编码不是对应的。</p></blockquote><h3 id="采样率"><a href="#采样率" class="headerlink" title="采样率"></a>采样率</h3><p>采样率（也称为采样速度或者采样频率）定义了<strong>每秒从连续信号中提取并组成离散信号的采样个数</strong>，它用赫兹（Hz）来表示。采样率是指将模拟信号转换成数字信号时的采样频率，也就是单位时间内采样多少点。一个采样点数据有多少个比特。</p><p>16位的声音和24位的画面基本已经是普通人类的极限了，更高位数就只能靠仪器才能分辨出来了。比如电话就是3kHZ取样的7位声音，而CD是44.1kHZ取样的16位声音，所以CD就比电话更清楚。举例：</p><ul><li><p>电话：每秒3000次取样，每个取样是7比特，那么电话的比特率是21000。 </p></li><li><p>CD：每秒 44100次取样，两个声道，每个取样是13位PCM编码，所以CD的比特率是44100x2x13&#x3D;1146600</p><p>也就是说CD每秒的数据量大约是 144KB，而一张CD的容量是74分等于4440秒，就是639360KB＝640MB。</p></li></ul><h3 id="文件大小计算"><a href="#文件大小计算" class="headerlink" title="文件大小计算"></a>文件大小计算</h3><p>计算输出文件大小公式：（音频编码率（KBit为单位）&#x2F;8 +视频编码率（KBit为单位）&#x2F;8）×影片总长度（秒为单位）&#x3D;文件大小（MB为单位）</p><h2 id="输出格式考虑维度"><a href="#输出格式考虑维度" class="headerlink" title="输出格式考虑维度"></a>输出格式考虑维度</h2><h3 id="产品参数"><a href="#产品参数" class="headerlink" title="产品参数"></a>产品参数</h3><h4 id="IPhone-13-Pro"><a href="#IPhone-13-Pro" class="headerlink" title="IPhone 13 Pro"></a>IPhone 13 Pro</h4><p><img src="https://static.www.toimc.com/blog/picgo/2022/09/09/image-20220823103645739-a40347-754885.png" alt="image-20220823103645739"></p><h4 id="DJI-Mavic-Air2"><a href="#DJI-Mavic-Air2" class="headerlink" title="DJI Mavic Air2"></a>DJI Mavic Air2</h4><p><img src="https://static.www.toimc.com/blog/picgo/2022/09/09/image-20220823103416581-a9ddcd-fac386.png" alt="image-20220823103416581"></p><h4 id="DJI-Mavic-3"><a href="#DJI-Mavic-3" class="headerlink" title="DJI Mavic 3"></a>DJI Mavic 3</h4><p><img src="https://static.www.toimc.com/blog/picgo/2022/09/09/image-20220823103513522-7078b4-fd58da.png" alt="image-20220823103513522"></p><p><img src="https://static.www.toimc.com/blog/picgo/2022/09/09/image-20220823103500799-b9e7de-65b75e.png" alt="image-20220823103500799"></p><h3 id="素材参数"><a href="#素材参数" class="headerlink" title="素材参数"></a>素材参数</h3><p>可以通过常见的软件查看视频素材的参数：</p><ul><li>MacOS：QuickTime Player</li><li>Windows：PotPlayer</li></ul><p><img src="https://static.www.toimc.com/blog/picgo/2025/10/06/image-20220823104135826-9fda7f-8c8e8e-bfd3ca.webp" alt="image-20220823104135826"></p><h3 id="项目参数"><a href="#项目参数" class="headerlink" title="项目参数"></a>项目参数</h3><p>以Final Cut Pro X为例：</p><p><img src="https://static.www.toimc.com/blog/picgo/2022/09/09/image-20220823104305267-1d7498-93d412.png" alt="image-20220823104305267"></p><h2 id="针对特定的网站"><a href="#针对特定的网站" class="headerlink" title="针对特定的网站"></a>针对特定的网站</h2><h3 id="B站"><a href="#B站" class="headerlink" title="B站"></a>B站</h3><h4 id="总览"><a href="#总览" class="headerlink" title="总览"></a>总览</h4><p><img src="https://static.www.toimc.com/blog/picgo/2022/09/09/6561b6b39eafb29a3eea2cab9610380be27a2044.png-942w_372h_progressive-207398-3a6496.webp" alt="img"></p><h4 id="参数要求"><a href="#参数要求" class="headerlink" title="参数要求"></a>参数要求</h4><p>允许上传的格式：mp4,flv,avi,wmv,mov,webm,mpeg4,ts,mpg,rm,rmvb,mkv,m4v</p><ul><li>网页端、桌面客户端推荐上传的格式为：mp4,flv</li></ul><p>网页端上传的文件大小上限为8G，视频内容时长最大10小时</p><p>视频平均码率不超过6000kbps(H264&#x2F;AVC编码)</p><ul><li>推荐视频码率：1080p大于6000kbps；4k大于20000kbps；8k大于40000kbps</li></ul><p>分辨率最大支持 8192X4320</p><ul><li>推荐视频分辨率：1920X1080 或者 3840X2160</li></ul><p>音频采样率48000Hz</p><ul><li>推荐音频码率：320kbps</li></ul><p>逐行扫描</p><p>智能识别输出HDR</p><p>智能识别全景视频</p><p>关键帧平均至少10秒一个</p><p>色彩空间</p><p>位深8bit</p><p>声道数≤2</p><p>采样率&#x3D;44100 </p><blockquote><p>B站说明：</p><p><a href="https://www.bilibili.com/read/cv527957/">https://www.bilibili.com/read/cv527957/</a></p></blockquote><h3 id="Youtube"><a href="#Youtube" class="headerlink" title="Youtube"></a>Youtube</h3><h4 id="帧速率"><a href="#帧速率" class="headerlink" title="帧速率"></a>帧速率</h4><p>内容应以录制时的帧速率进行编码和上传。</p><p>常用的帧速率包括 24、25、30、48、50 和 60 帧&#x2F;秒（其他帧速率也可接受）。</p><p>隔行扫描的内容在上传之前应进行反交错处理。例如，分辨率为 1080i 且帧速率为 60 的内容应反交错处理为分辨率为 1080p 且帧速率为 30 的内容。每秒 60 个隔行扫描场应当反交错处理成每秒 30 个逐行扫描帧。</p><p>建议您在上传时采用以下比特率。音频比特率与视频分辨率没有关系。</p><h4 id="比特率"><a href="#比特率" class="headerlink" title="比特率"></a>比特率</h4><h5 id="上传-SDR-视频时推荐采用的视频比特率"><a href="#上传-SDR-视频时推荐采用的视频比特率" class="headerlink" title="上传 SDR 视频时推荐采用的视频比特率"></a>上传 SDR 视频时推荐采用的视频比特率</h5><p>要在 4K 分辨率下观看新上传的 4K 视频，请使用支持 VP9 的浏览器或设备。</p><table><thead><tr><th align="left">类型</th><th align="left">视频比特率（标准帧速率） （24、25、30）</th><th align="left">视频比特率（高帧速率） （48、50、60）</th></tr></thead><tbody><tr><td align="left">8K</td><td align="left">80 - 160 Mbps</td><td align="left">120 - 240 Mbps</td></tr><tr><td align="left">2160p (4K)</td><td align="left">35 - 45 Mbps</td><td align="left">53 - 68 Mbps</td></tr><tr><td align="left">1440p (2K)</td><td align="left">16 Mbps</td><td align="left">24 Mbps</td></tr><tr><td align="left">1080p</td><td align="left">8 Mbps</td><td align="left">12 Mbps</td></tr><tr><td align="left">720p</td><td align="left">5 Mbps</td><td align="left">7.5 Mbps</td></tr><tr><td align="left">480p</td><td align="left">2.5 Mbps</td><td align="left">4 Mbps</td></tr><tr><td align="left">360p</td><td align="left">1 Mbps</td><td align="left">1.5 Mbps</td></tr></tbody></table><h5 id="上传-HDR-视频时推荐采用的视频比特率"><a href="#上传-HDR-视频时推荐采用的视频比特率" class="headerlink" title="上传 HDR 视频时推荐采用的视频比特率"></a>上传 HDR 视频时推荐采用的视频比特率</h5><table><thead><tr><th align="left">类型</th><th align="left">视频比特率（标准帧速率） （24、25、30）</th><th align="left">视频比特率（高帧速率） （48、50、60）</th></tr></thead><tbody><tr><td align="left">8K</td><td align="left">100 - 200 Mbps</td><td align="left">150 - 300 Mbps</td></tr><tr><td align="left">2160p (4K)</td><td align="left">44 - 56 Mbps</td><td align="left">66 - 85 Mbps</td></tr><tr><td align="left">1440p (2K)</td><td align="left">20 Mbps</td><td align="left">30 Mbps</td></tr><tr><td align="left">1080p</td><td align="left">10 Mbps</td><td align="left">15 Mbps</td></tr><tr><td align="left">720p</td><td align="left">6.5 Mbps</td><td align="left">9.5 Mbps</td></tr><tr><td align="left">480p</td><td align="left">不受支持</td><td align="left">不受支持</td></tr><tr><td align="left">360p</td><td align="left">不受支持</td><td align="left">不受支持</td></tr></tbody></table><h5 id="上传时推荐采用的音频比特率"><a href="#上传时推荐采用的音频比特率" class="headerlink" title="上传时推荐采用的音频比特率"></a>上传时推荐采用的音频比特率</h5><table><thead><tr><th align="left">类型</th><th align="left">音频比特率</th></tr></thead><tbody><tr><td align="left">单声道</td><td align="left">128 kbps</td></tr><tr><td align="left">双声道立体声</td><td align="left">384 kbps</td></tr><tr><td align="left">5.1 立体声</td><td align="left">512 kbps</td></tr></tbody></table><h4 id="宽高比"><a href="#宽高比" class="headerlink" title="宽高比"></a>宽高比</h4><p>YouTube 在计算机上的标准宽高比为 16:9。在上传其他宽高比（例如竖版或方形）的视频时，播放器会根据视频的尺寸自动调整自身的尺寸。此设置会根据宽高比和设备提供最佳的观看体验。</p><h4 id="编码器"><a href="#编码器" class="headerlink" title="编码器"></a>编码器</h4><ul><li>逐行扫描（请勿使用隔行扫描）</li><li>高配置</li><li>2 个连续 B 帧</li><li>闭合 GOP。GOP 为帧速率的一半。</li><li>CABAC</li><li>可变比特率。无需限制比特率，不过我们在下面提供了推荐比特率，以供参考</li><li>色度二次抽样：4:2:0</li></ul><h4 id="音频编解码"><a href="#音频编解码" class="headerlink" title="音频编解码"></a>音频编解码</h4><ul><li>声道：双声道立体声，或双声道 + 5.1 立体声</li><li>采样率：96khz 或 48khz</li></ul><h4 id="格式"><a href="#格式" class="headerlink" title="格式"></a>格式</h4><ul><li>不含编辑列表（否则视频可能无法正确得到处理）</li><li>文件以 moov atom 开头（快速开始）</li></ul><h3 id="慕课网"><a href="#慕课网" class="headerlink" title="慕课网"></a>慕课网</h3><p>分辨率：1920x1080</p><p>码率：4000kbps 以上</p><p>帧率：30fps</p><p>声音：没有特殊要求，48kHZ -&gt; 我个人觉得是不要有炸音，能听清楚就可以通过后期处理；</p><p>其他要求：VSCode + 24行代码显示</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;理解常见的视频参数、设备参数、输出参数、发布网站的参数，有利于视频从业工作者制作出高质量合规的视频。&lt;/p&gt;</summary>
    
    
    
    
    <category term="视频剪辑" scheme="https://www.toimc.com/tags/%E8%A7%86%E9%A2%91%E5%89%AA%E8%BE%91/"/>
    
    <category term="新媒体" scheme="https://www.toimc.com/tags/%E6%96%B0%E5%AA%92%E4%BD%93/"/>
    
    <category term="视频参数" scheme="https://www.toimc.com/tags/%E8%A7%86%E9%A2%91%E5%8F%82%E6%95%B0/"/>
    
  </entry>
  
  <entry>
    <title>苹果M1上安装Centos8及Docker</title>
    <link href="https://www.toimc.com/m1-with-centos8/"/>
    <id>https://www.toimc.com/m1-with-centos8/</id>
    <published>2022-08-29T15:59:27.000Z</published>
    <updated>2025-10-06T05:13:13.974Z</updated>
    
    <content type="html"><![CDATA[<p>Arm版本的Docker安装，主要步骤：</p><p>1.下载Centos镜像; 2.安装Parallels Desktop 17以上的版本；3.更换Centos源；4.解决冲突；5.安装Docker+Docker-compose；6.更换Docker源</p><span id="more"></span><h2 id="下载Centos镜像"><a href="#下载Centos镜像" class="headerlink" title="下载Centos镜像"></a>下载Centos镜像</h2><p>推荐无界面安装：</p><ul><li><p>CentOS-8-4-2105-aarch64-dvd-custom-202107301400.iso</p><p>链接: <a href="https://pan.baidu.com/s/1fNLUXW7Ok2gWM8zzkTaz4g?pwd=c38j">https://pan.baidu.com/s/1fNLUXW7Ok2gWM8zzkTaz4g?pwd=c38j</a> 提取码: c38j </p><blockquote><p>这里唯一注意的就是<code>aarch64</code>，这里对应M1芯片<code>arm</code>架构。</p></blockquote></li></ul><h2 id="下载Parallels"><a href="#下载Parallels" class="headerlink" title="下载Parallels"></a>下载Parallels</h2><p>两种方案：</p><ul><li>使用macwk下载 + pd runner</li><li>正版购买（按年付 + 25%的折扣的优惠码），买断不适合macOS喜欢更新的小伙伴；</li></ul><p>下载后，安装激活，并新建：</p><p><img src="https://static.www.toimc.com/blog/picgo/2025/10/06/image-20220829233311280-920b38-270bd4.webp" alt="image-20220829233311280"></p><p>选择下载的iso路径，开始安装。</p><h2 id="安装Centos"><a href="#安装Centos" class="headerlink" title="安装Centos"></a>安装Centos</h2><p>这里注意全程是<code>命令行</code>界面操作，需要设置的有：</p><ul><li>安装位置确认；-&gt;输入5 -&gt; 回车-&gt;再按提示<code>c</code>回车-&gt;一路按照提示；</li><li>root用户密码；-&gt;输入8-&gt;回车-&gt;按照提示输入密码<code>123456</code>-&gt;弱密码<code>yes</code>-&gt;回车</li><li>创建用户；</li><li>（可选）设置网络；</li></ul><p><img src="https://static.www.toimc.com/blog/picgo/2025/10/06/image-20220829230822902-70b392-7a173d.webp" alt="image-20220829230822902"></p><p>当上面的设置好了之后，按<code>b</code>，即可以开始安装了。</p><p>配置网络：</p><p><code>/etc/sysconfig/network-scripts</code>目录下存放着网卡的配置文件，文件名称是<code>ifcfg- 网卡名称</code>：</p><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><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">TYPE</span>=Ethernet</span><br><span class="line"><span class="attr">PROXY_METHOD</span>=none</span><br><span class="line"><span class="attr">BROWSER_ONLY</span>=<span class="literal">no</span></span><br><span class="line"><span class="attr">BOOTPROTO</span>=dhcp</span><br><span class="line"><span class="attr">DEFROUTE</span>=<span class="literal">yes</span></span><br><span class="line"><span class="attr">IPV4_FAILURE_FATAL</span>=<span class="literal">no</span></span><br><span class="line"><span class="attr">IPV6INIT</span>=<span class="literal">yes</span></span><br><span class="line"><span class="attr">IPV6_AUTOCONF</span>=<span class="literal">yes</span></span><br><span class="line"><span class="attr">IPV6_DEFROUTE</span>=<span class="literal">yes</span></span><br><span class="line"><span class="attr">IPV6_FAILURE_FATAL</span>=<span class="literal">no</span></span><br><span class="line"><span class="attr">IPV6_ADDR_GEN_MODE</span>=stable-privacy</span><br><span class="line"><span class="attr">NAME</span>=ens33</span><br><span class="line"><span class="attr">UUID</span>=a498f9b8-ffce-ace8-<span class="number">16</span>fe-f4106a97f5bf</span><br><span class="line"><span class="attr">DEVICE</span>=ens33</span><br><span class="line"><span class="comment"># 这里修改一下</span></span><br><span class="line"><span class="attr">ONBOOT</span>=<span class="literal">yes</span></span><br></pre></td></tr></table></figure><blockquote><p>如果使用dhcp自动获取ip，只需将<code>ONBOOT=no</code>修改为<code>ONBOOT=yes</code>即可。</p></blockquote><p>如果是静态IP：</p><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><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></pre></td><td class="code"><pre><span class="line"><span class="attr">TYPE</span>=Ethernet</span><br><span class="line"><span class="attr">PROXY_METHOD</span>=none</span><br><span class="line"><span class="attr">BROWSER_ONLY</span>=<span class="literal">no</span></span><br><span class="line"><span class="attr">BOOTPROTO</span>=static   <span class="comment">#将dhcp修改为stati表示使用静态ip</span></span><br><span class="line"><span class="attr">DEFROUTE</span>=<span class="literal">yes</span></span><br><span class="line"><span class="attr">IPADDR</span>=<span class="number">192.168</span>.<span class="number">0.10</span>   <span class="comment">#设置IP地址</span></span><br><span class="line"><span class="attr">NETMASK</span>=<span class="number">255.255</span>.<span class="number">255.0</span>    <span class="comment">#设置子网掩码</span></span><br><span class="line"><span class="attr">GATEWAY</span>=<span class="number">192.168</span>.<span class="number">0.1</span>    <span class="comment">#设置网关</span></span><br><span class="line"><span class="attr">DNS1</span>=<span class="number">114.114</span>.<span class="number">114.114</span>     <span class="comment">#设置dns</span></span><br><span class="line"><span class="attr">IPV4_FAILURE_FATAL</span>=<span class="literal">no</span></span><br><span class="line"><span class="attr">IPV6INIT</span>=<span class="literal">yes</span></span><br><span class="line"><span class="attr">IPV6_AUTOCONF</span>=<span class="literal">yes</span></span><br><span class="line"><span class="attr">IPV6_DEFROUTE</span>=<span class="literal">yes</span></span><br><span class="line"><span class="attr">IPV6_FAILURE_FATAL</span>=<span class="literal">no</span></span><br><span class="line"><span class="attr">IPV6_ADDR_GEN_MODE</span>=stable-privacy</span><br><span class="line"><span class="attr">NAME</span>=ens33</span><br><span class="line"><span class="attr">UUID</span>=a498f9b8-ffce-ace8-<span class="number">16</span>fe-f4106a97f5bf</span><br><span class="line"><span class="attr">DEVICE</span>=ens33</span><br><span class="line"><span class="comment">#将no改为yes</span></span><br><span class="line"><span class="attr">ONBOOT</span>=<span class="literal">yes</span>  </span><br></pre></td></tr></table></figure><p>使用 <code>nmcli c reload</code>重启一下。</p><h2 id="更换Centos源"><a href="#更换Centos源" class="headerlink" title="更换Centos源"></a>更换Centos源</h2><p>基本思路：</p><ul><li>删除所有官方源；</li><li>下载阿里源；</li></ul><p>操作步骤：</p><figure class="highlight awk"><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">cd <span class="regexp">/etc/yum</span>.repos.d/</span><br><span class="line"></span><br><span class="line">mkdir -p ../backup</span><br><span class="line"></span><br><span class="line">mv .<span class="regexp">/Centos-Linux-* ../</span>backup</span><br><span class="line"></span><br><span class="line">wget -O <span class="regexp">/etc/yum</span>.repos.d<span class="regexp">/CentOS-Base.repo https:/</span><span class="regexp">/mirrors.aliyun.com/</span>repo/Centos-<span class="number">8</span>.repo</span><br><span class="line"></span><br><span class="line">yum makecache</span><br><span class="line"></span><br><span class="line">yum -y update</span><br></pre></td></tr></table></figure><h2 id="解决冲突"><a href="#解决冲突" class="headerlink" title="解决冲突"></a>解决冲突</h2><h3 id="Problem-1-problem-with-installed-package-podman"><a href="#Problem-1-problem-with-installed-package-podman" class="headerlink" title="Problem 1: problem with installed package podman"></a>Problem 1: problem with installed package podman</h3><p>错误：</p><figure class="highlight subunit"><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></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# sudo yum install docker-ce docker-ce-cli containerd.io</span><br><span class="line">Failed to set locale, defaulting to C.UTF<span class="string">-8</span></span><br><span class="line">Docker CE Stable - aarch64                                                                    669  B/s | 3.5 kB     00:05    </span><br><span class="line"><span class="keyword">Error: </span></span><br><span class="line"><span class="keyword"> </span>Problem 1: problem with installed package podman<span class="string">-3</span>.3.1<span class="string">-9</span>.module_el8.5.0<span class="string">+988</span>+b1f0b741.aarch64</span><br><span class="line">  - package podman<span class="string">-3</span>.3.1<span class="string">-9</span>.module_el8.5.0<span class="string">+988</span>+b1f0b741.aarch64 requires runc &gt;= 1.0.0<span class="string">-57</span>, but none of the providers can be installed</span><br><span class="line">  - package containerd.io<span class="string">-1</span>.6.8<span class="string">-3</span>.1.el8.aarch64 conflicts with runc provided by runc<span class="string">-1</span>.0.2<span class="string">-1</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64</span><br><span class="line">  - package containerd.io<span class="string">-1</span>.6.8<span class="string">-3</span>.1.el8.aarch64 obsoletes runc provided by runc<span class="string">-1</span>.0.2<span class="string">-1</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64</span><br><span class="line">  - cannot install the best candidate for the job</span><br><span class="line">  - package runc<span class="string">-1</span>.0.0<span class="string">-66</span>.rc10.module_el8.5.0<span class="string">+1004</span>+c00a74f5.aarch64 is filtered out by modular filtering</span><br><span class="line">  - package runc<span class="string">-1</span>.0.0<span class="string">-72</span>.rc92.module_el8.5.0<span class="string">+1006</span><span class="string">+8</span>d0e68a2.aarch64 is filtered out by modular filtering</span><br><span class="line"> Problem 2: problem with installed package buildah<span class="string">-1</span>.22.3<span class="string">-2</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64</span><br><span class="line">  - package buildah<span class="string">-1</span>.22.3<span class="string">-2</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64 requires runc &gt;= 1.0.0<span class="string">-26</span>, but none of the providers can be installed</span><br><span class="line">  - package docker-ce<span class="string">-3</span>:20.10.17<span class="string">-3</span>.el8.aarch64 requires containerd.io &gt;= 1.4.1, but none of the providers can be installed</span><br><span class="line">  - package containerd.io<span class="string">-1</span>.4.10<span class="string">-3</span>.1.el8.aarch64 conflicts with runc provided by runc<span class="string">-1</span>.0.2<span class="string">-1</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64</span><br><span class="line">  - package containerd.io<span class="string">-1</span>.4.10<span class="string">-3</span>.1.el8.aarch64 obsoletes runc provided by runc<span class="string">-1</span>.0.2<span class="string">-1</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64</span><br><span class="line">  - package containerd.io<span class="string">-1</span>.4.11<span class="string">-3</span>.1.el8.aarch64 conflicts with runc provided by runc<span class="string">-1</span>.0.2<span class="string">-1</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64</span><br><span class="line">  - package containerd.io<span class="string">-1</span>.4.11<span class="string">-3</span>.1.el8.aarch64 obsoletes runc provided by runc<span class="string">-1</span>.0.2<span class="string">-1</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64</span><br><span class="line">  - package containerd.io<span class="string">-1</span>.4.12<span class="string">-3</span>.1.el8.aarch64 conflicts with runc provided by runc<span class="string">-1</span>.0.2<span class="string">-1</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64</span><br><span class="line">  - package containerd.io<span class="string">-1</span>.4.12<span class="string">-3</span>.1.el8.aarch64 obsoletes runc provided by runc<span class="string">-1</span>.0.2<span class="string">-1</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64</span><br><span class="line">  - package containerd.io<span class="string">-1</span>.4.13<span class="string">-3</span>.1.el8.aarch64 conflicts with runc provided by runc<span class="string">-1</span>.0.2<span class="string">-1</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64</span><br><span class="line">  - package containerd.io<span class="string">-1</span>.4.13<span class="string">-3</span>.1.el8.aarch64 obsoletes runc provided by runc<span class="string">-1</span>.0.2<span class="string">-1</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64</span><br><span class="line">  - package containerd.io<span class="string">-1</span>.4.3<span class="string">-3</span>.1.el8.aarch64 conflicts with runc provided by runc<span class="string">-1</span>.0.2<span class="string">-1</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64</span><br><span class="line">  - package containerd.io<span class="string">-1</span>.4.3<span class="string">-3</span>.1.el8.aarch64 obsoletes runc provided by runc<span class="string">-1</span>.0.2<span class="string">-1</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64</span><br><span class="line">  - package containerd.io<span class="string">-1</span>.4.3<span class="string">-3</span>.2.el8.aarch64 conflicts with runc provided by runc<span class="string">-1</span>.0.2<span class="string">-1</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64</span><br><span class="line">  - package containerd.io<span class="string">-1</span>.4.3<span class="string">-3</span>.2.el8.aarch64 obsoletes runc provided by runc<span class="string">-1</span>.0.2<span class="string">-1</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64</span><br><span class="line">  - package containerd.io<span class="string">-1</span>.4.4<span class="string">-3</span>.1.el8.aarch64 conflicts with runc provided by runc<span class="string">-1</span>.0.2<span class="string">-1</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64</span><br><span class="line">  - package containerd.io<span class="string">-1</span>.4.4<span class="string">-3</span>.1.el8.aarch64 obsoletes runc provided by runc<span class="string">-1</span>.0.2<span class="string">-1</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64</span><br><span class="line">  - package containerd.io<span class="string">-1</span>.4.6<span class="string">-3</span>.1.el8.aarch64 conflicts with runc provided by runc<span class="string">-1</span>.0.2<span class="string">-1</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64</span><br><span class="line">  - package containerd.io<span class="string">-1</span>.4.6<span class="string">-3</span>.1.el8.aarch64 obsoletes runc provided by runc<span class="string">-1</span>.0.2<span class="string">-1</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64</span><br><span class="line">  - package containerd.io<span class="string">-1</span>.4.8<span class="string">-3</span>.1.el8.aarch64 conflicts with runc provided by runc<span class="string">-1</span>.0.2<span class="string">-1</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64</span><br><span class="line">  - package containerd.io<span class="string">-1</span>.4.8<span class="string">-3</span>.1.el8.aarch64 obsoletes runc provided by runc<span class="string">-1</span>.0.2<span class="string">-1</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64</span><br><span class="line">  - package containerd.io<span class="string">-1</span>.4.9<span class="string">-3</span>.1.el8.aarch64 conflicts with runc provided by runc<span class="string">-1</span>.0.2<span class="string">-1</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64</span><br><span class="line">  - package containerd.io<span class="string">-1</span>.4.9<span class="string">-3</span>.1.el8.aarch64 obsoletes runc provided by runc<span class="string">-1</span>.0.2<span class="string">-1</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64</span><br><span class="line">  - package containerd.io<span class="string">-1</span>.5.10<span class="string">-3</span>.1.el8.aarch64 conflicts with runc provided by </span><br></pre></td></tr></table></figure><p>解决办法：</p><figure class="highlight smali"><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">yum erase podman buildah</span><br><span class="line"> </span><br><span class="line"><span class="comment"># or</span></span><br><span class="line">dnf remove podman buildah</span><br><span class="line"> </span><br><span class="line">dnf clean all &amp;&amp; dnf<span class="built_in"> check </span>&amp;&amp; dnf check-update</span><br></pre></td></tr></table></figure><h3 id="Problem-2-problem-with-installed-package-buildah"><a href="#Problem-2-problem-with-installed-package-buildah" class="headerlink" title="Problem 2: problem with installed package buildah"></a>Problem 2: problem with installed package buildah</h3><p>解决办法（同上）：</p><figure class="highlight smali"><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">yum erase buildah</span><br><span class="line"></span><br><span class="line"><span class="comment"># or </span></span><br><span class="line">dnf remove buildah</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">dnf clean all &amp;&amp; dnf<span class="built_in"> check </span>&amp;&amp; dnf check-update</span><br></pre></td></tr></table></figure><h3 id="Problem-problem-with-installed-package-containers-common"><a href="#Problem-problem-with-installed-package-containers-common" class="headerlink" title="Problem: problem with installed package containers-common"></a>Problem: problem with installed package containers-common</h3><p>错误：</p><figure class="highlight subunit"><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">[root@localhost ~]# sudo yum install docker-ce docker-ce-cli containerd.io docker-compose-plugin</span><br><span class="line">Failed to set locale, defaulting to C.UTF<span class="string">-8</span></span><br><span class="line">Last metadata expiration check: 0:01:24 ago on Mon Aug 29 09:35:22 2022.</span><br><span class="line"><span class="keyword">Error: </span></span><br><span class="line"><span class="keyword"> </span>Problem: problem with installed package containers-common<span class="string">-2</span>:1<span class="string">-2</span>.module_el8.5.0<span class="string">+890</span><span class="string">+6</span>b136101.noarch</span><br><span class="line">  - package containers-common<span class="string">-2</span>:1<span class="string">-2</span>.module_el8.5.0<span class="string">+890</span><span class="string">+6</span>b136101.noarch requires runc, but none of the providers can be installed</span><br><span class="line">  - package containerd.io<span class="string">-1</span>.6.8<span class="string">-3</span>.1.el8.aarch64 conflicts with runc provided by runc<span class="string">-1</span>.0.2<span class="string">-1</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64</span><br><span class="line">  - package containerd.io<span class="string">-1</span>.6.8<span class="string">-3</span>.1.el8.aarch64 obsoletes runc provided by runc<span class="string">-1</span>.0.2<span class="string">-1</span>.module_el8.5.0<span class="string">+911</span>+f19012f9.aarch64</span><br><span class="line">  - cannot install the best candidate for the job</span><br><span class="line">  - package runc<span class="string">-1</span>.0.0<span class="string">-56</span>.rc5.dev.git2abd837.module_el8.3.0<span class="string">+569</span><span class="string">+1</span>bada2e4.aarch64 is filtered out by modular filtering</span><br><span class="line">  - package runc<span class="string">-1</span>.0.0<span class="string">-66</span>.rc10.module_el8.5.0<span class="string">+1004</span>+c00a74f5.aarch64 is filtered out by modular filtering</span><br><span class="line">  - package runc<span class="string">-1</span>.0.0<span class="string">-72</span>.rc92.module_el8.5.0<span class="string">+1006</span><span class="string">+8</span>d0e68a2.aarch64 is filtered out by modular filtering</span><br><span class="line">(try to add &#x27;--allowerasing&#x27; to command line to replace conflicting packages or &#x27;--skip-broken&#x27; to skip uninstallable packages or &#x27;--nobest&#x27; to use not only best candidate packages)</span><br></pre></td></tr></table></figure><p>解决办法：</p><figure class="highlight maxima"><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">dnf <span class="built_in">remove</span> -y containers-common-<span class="number">2</span>:<span class="number">1</span>-<span class="number">2.</span>module_el8<span class="number">.5</span><span class="number">.0</span>+<span class="number">890</span>+<span class="number">6b136101</span></span><br><span class="line"></span><br><span class="line">dnf clean all &amp;&amp; dnf check &amp;&amp; dnf check-update</span><br></pre></td></tr></table></figure><h2 id="安装Docker-Docker-compose"><a href="#安装Docker-Docker-compose" class="headerlink" title="安装Docker+Docker-compose"></a>安装Docker+Docker-compose</h2><p>推荐使用<a href="http://get.daocloud.io/">daocloud加速源</a>：</p><figure class="highlight awk"><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"># 安装docker</span></span><br><span class="line">curl -sSL https:<span class="regexp">//g</span>et.daocloud.io/docker | sh</span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装docker-compose</span></span><br><span class="line">curl -L https:<span class="regexp">//g</span>et.daocloud.io<span class="regexp">/docker/</span>compose<span class="regexp">/releases/</span>download<span class="regexp">/v2.10.2/</span>docker-compose-`uname -s`-`uname -m` &gt; <span class="regexp">/usr/</span>local<span class="regexp">/bin/</span>docker-compose</span><br><span class="line">chmod +x <span class="regexp">/usr/</span>local<span class="regexp">/bin/</span>docker-compose</span><br></pre></td></tr></table></figure><h2 id="更换Docker源"><a href="#更换Docker源" class="headerlink" title="更换Docker源"></a>更换Docker源</h2><p>两种方法：</p><ul><li>一键配置</li><li>手动配置</li></ul><p>一键配置：</p><figure class="highlight awk"><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"># daocloud</span></span><br><span class="line">curl -sSL https:<span class="regexp">//g</span>et.daocloud.io<span class="regexp">/daotools/</span>set_mirror.sh | sh -s http:<span class="regexp">//</span>f1361db2.m.daocloud.io</span><br></pre></td></tr></table></figure><p>手动配置：</p><figure class="highlight awk"><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">sudo mkdir -p <span class="regexp">/etc/</span>docker</span><br><span class="line">sudo tee <span class="regexp">/etc/</span>docker/daemon.json &lt;&lt;-<span class="string">&#x27;EOF&#x27;</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;registry-mirrors&quot;</span>: [<span class="string">&quot;https://docker.mirrors.ustc.edu.cn&quot;</span>]</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br><span class="line">sudo systemctl daemon-reload</span><br><span class="line">sudo systemctl restart docker</span><br></pre></td></tr></table></figure><p>上面的命令，其实就是在文件 <code>/etc/docker/daemon.json</code> 中写入如下内容：</p><figure class="highlight prolog"><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">&#123;</span><br><span class="line">  <span class="string">&quot;registry-mirrors&quot;</span>: [<span class="string">&quot;https://docker.mirrors.ustc.edu.cn&quot;</span>]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><ul><li>科大镜像：<strong><a href="https://docker.mirrors.ustc.edu.cn/">https://docker.mirrors.ustc.edu.cn/</a></strong></li><li>网易：<strong><a href="https://hub-mirror.c.163.com/">https://hub-mirror.c.163.com/</a></strong></li><li>阿里云：<strong>https:&#x2F;&#x2F;&lt;你的ID&gt;.mirror.aliyuncs.com</strong>，获取地址：<a href="https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors">https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors</a></li></ul></blockquote><p>然后，把docker服务重启了一下。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;Arm版本的Docker安装，主要步骤：&lt;/p&gt;
&lt;p&gt;1.下载Centos镜像; 2.安装Parallels Desktop 17以上的版本；3.更换Centos源；4.解决冲突；5.安装Docker+Docker-compose；6.更换Docker源&lt;/p&gt;</summary>
    
    
    
    
    <category term="Centos8" scheme="https://www.toimc.com/tags/Centos8/"/>
    
    <category term="M1" scheme="https://www.toimc.com/tags/M1/"/>
    
    <category term="Parallels Desktop" scheme="https://www.toimc.com/tags/Parallels-Desktop/"/>
    
  </entry>
  
  <entry>
    <title>升级！Hexo博客使用Picgo+七牛云图床+Typora来写博客</title>
    <link href="https://www.toimc.com/typora-picgo/"/>
    <id>https://www.toimc.com/typora-picgo/</id>
    <published>2022-08-24T14:12:18.000Z</published>
    <updated>2025-10-06T05:13:13.974Z</updated>
    
    <content type="html"><![CDATA[<p>今天给博客升级了一下依赖，同时使用Picgo自动上传 + 七牛云对象云存储来做图床，使用Typora自动上传图片功能，写博客真的不要太爽。下面给大家介绍一下，配置的方法。</p><span id="more"></span><p>准备工作：</p><ul><li><p>七牛云：免费版本的对象云存储就行了，有10G；</p></li><li><p><a href="https://github.com/Molunerfinn/PicGo">Picgo</a>：</p><ul><li>GUI版本，下载地址：<a href="https://picgo-1251750343.cos.ap-chengdu.myqcloud.com/2.3.1-beta.5/PicGo-2.3.1-beta.5-arm64.dmg">MacOS-Apple</a></li><li><a href="https://picgo-1251750343.cos.ap-chengdu.myqcloud.com/2.3.1-beta.5/PicGo-2.3.1-beta.5-x64.dmg">MacOS-Intel</a> </li><li><a href="https://picgo-1251750343.cos.ap-chengdu.myqcloud.com/2.3.1-beta.5/PicGo-Setup-2.3.1-beta.5-x64.exe">Windows-64</a>；</li></ul></li><li><p>Typora：<a href="https://typora.io/%EF%BC%8CPS%EF%BC%9A%E4%B8%8B%E8%BD%BDBeta%E7%89%88%E6%9C%AC%E6%98%AF%E5%85%8D%E8%B4%B9%E7%9A%84%EF%BC%9B">https://typora.io/，PS：下载Beta版本是免费的；</a></p></li></ul><h1 id="基本步骤"><a href="#基本步骤" class="headerlink" title="基本步骤"></a>基本步骤</h1><h2 id="七牛云获取密钥"><a href="#七牛云获取密钥" class="headerlink" title="七牛云获取密钥"></a>七牛云获取密钥</h2><p>选择对应的产品：</p><p><img src="https://static.www.toimc.com/blog/picgo/2025/10/06/image-20220824222133410-ef7d3a-a85be7.webp" alt="image-20220824222133410"></p><p>点击立即试用，登录七牛云账号，点击新建：</p><p><img src="https://static.www.toimc.com/blog/picgo/2022/08/24/image-20220824222240882-604a9f.png" alt="image-20220824222240882"></p><p>注意空间名是小写+短横线，而博客一般会选择公开：</p><img src="https://static.www.toimc.com/blog/picgo/2022/08/24/image-20220824222358455-669124.png" alt="image-20220824222358455" style="width: 50%;" /><p>接下来就可以在：个人中心-&gt;密钥管理，来创建密钥，一个账户最多使用2个密钥。</p><p><img src="https://static.www.toimc.com/blog/picgo/2022/08/24/image-20220824222811685-223c59.png" alt="image-20220824222811685"></p><p>下面把上面的<code>AK</code>,<code>SK</code>保存好，用于配置Picgo。</p><h2 id="配置Picgo"><a href="#配置Picgo" class="headerlink" title="配置Picgo"></a>配置Picgo</h2><p>下载Picgo之后，打开软件设置页面，点击图床设置，找到七牛云：</p><p><img src="https://static.www.toimc.com/blog/picgo/2022/08/24/image-20220824230110456-fbac9e.png" alt="image-20220824230110456"></p><p>说明：</p><ul><li>AccessKey：对应AK</li><li>SecretKey：对应SK</li><li>存储空间名：刚刚我们建立的，比如<code>：toimc-public</code></li><li>访问网址：这个七牛云会默认生成一个，但是有效期是30天，最好是自己申请一个CV域名，走HTTPS；</li><li>存储区域：这里有一个区域列表，<a href="https://developer.qiniu.com/kodo/1671/region-endpoint-fq">请查看</a></li></ul><p>后续进阶：</p><p>可以安装一些常用的插件，<a href="https://github.com/PicGo/Awesome-PicGo">awesome-picgo</a>：</p><ul><li>图片上传压缩<a href="https://github.com/JuZiSang/picgo-plugin-compress">picgo-plugin-compress</a>：结合了tinypng，需要注册tinypng的账号，生成API；</li><li>图片路径修改<a href="https://github.com/liuwave/picgo-plugin-rename-file">picgo-plugin-rename-file</a>：我个人的习惯是按照年月日+随机数进行划分。</li></ul><h2 id="配置Typora"><a href="#配置Typora" class="headerlink" title="配置Typora"></a>配置Typora</h2><p>这块是最简单的，打开Typora的配置，选择图像，按照如下配置：</p><p><img src="https://static.www.toimc.com/blog/picgo/2022/08/24/image-20220824231303076-3deae9.png" alt="image-20220824231303076"></p><p>接下来，当我们拷贝图片到Typora的时候，它就会自动使用PicGo上传到七牛云啦~</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;今天给博客升级了一下依赖，同时使用Picgo自动上传 + 七牛云对象云存储来做图床，使用Typora自动上传图片功能，写博客真的不要太爽。下面给大家介绍一下，配置的方法。&lt;/p&gt;</summary>
    
    
    
    
    <category term="博客" scheme="https://www.toimc.com/tags/%E5%8D%9A%E5%AE%A2/"/>
    
    <category term="Hexo" scheme="https://www.toimc.com/tags/Hexo/"/>
    
    <category term="Picgo" scheme="https://www.toimc.com/tags/Picgo/"/>
    
  </entry>
  
  <entry>
    <title>互联网副业指南：有什么副业可以月入1w+?</title>
    <link href="https://www.toimc.com/side-bussiness/"/>
    <id>https://www.toimc.com/side-bussiness/</id>
    <published>2022-08-24T02:30:43.000Z</published>
    <updated>2025-10-06T05:13:13.974Z</updated>
    
    <content type="html"><![CDATA[<p>都想发展副业，增加收入，那目前哪些副业可以做呢？有什么副业，可以月入1w+？整天看着小视频中的人在炫富，真的这么好赚钱吗？副业赚的钱比主业赚的多是什么体验？你是否也有类似的困扰？宝妈、自由职业者、打工族均可做，想要靠副业赚钱的朋友，建议点击进来看。</p><p>PS: 谈钱并不可耻，君子爱财取之有道。我们下面谈到的“副业”都是在国家法律法规范畴内，不投机不搞灰色产业（如币圈等），拒绝“黄赌毒”。</p><span id="more"></span><blockquote><p>撰文人：我是Brian，9年副业+创业多元化的变现实战经验，百万级+内容创造者，大厂背景，全栈开发出身，多领域实操与项目经验。创立独立工作室，热爱分享技术。自认为非常有经验与实力来与大家交流分享，希望这些内容能帮助大家走出迷茫，找到方向。</p></blockquote><p>说到副业，大多数的人非常功利心，觉得“副业”就是搞钱。谈“副业”就要聊有什么渠道、通过什么方式能赚钱，而且越“快”越好，越多越好——真有这好事，那不就闷声发大财，不会有人去分享的。要知道，同行有了竞争，久而久之就没有了利润空间。所以，真正“分享”的人要么收费（知识付费），要么就是图你的其他东西（比如渠道、信息、流量、名气等）。记住，人来人往皆为利往~说的这么功利，这就是现实，这些“发知识付费”财的博主们，也是卖的一种焦虑、一种满足于贪欲的情绪…</p><p>说点实在的，干副业干得好干成主业的人，<strong>都是非常自律而又方向非常明确的人</strong>，树立正确的价值观，对于走好副业道路非常重要。</p><h2 id="副业-x3D-搞钱？"><a href="#副业-x3D-搞钱？" class="headerlink" title="副业&#x3D;搞钱？"></a>副业&#x3D;搞钱？</h2><p>根据Mob研究院《2022年90后高收入人群洞察》，有70%的人没有开展副业，30%的人开展了副业。</p><p>很多小伙伴没有开始副业的原因：对于生活非常的迷茫，自己有一份相对安逸的工作和一颗怕丢脸、懒惰的心。一般会走向两个做副业的极端：不知道怎么开始和相做非常多的副业搞大钱。</p><p><strong>迷茫的人：</strong></p><p>先要正视一下自己，分析清楚当下的形势。举两个案例：</p><ul><li>快40，未婚无子女无房，工作一般，想开始学习新的技术开始在外面接单。<br>找到我的时候，直接就劝退他目前的这个方向了。首先，经济上不稳定，不能给家庭以保障；其次，年龄上比较大了，与年轻一代的去比拼接项目，肯定是吃亏的，弯道想要超车，需要付出成倍的努力——出师未捷身先死，可能就会病倒给家里带来负担；最后，他自己没有项目来源，技术也很一般，而且目前工作的学习时间不多。</li></ul><p>状态分析：未明确自己现阶段最重要的事情，在危机来临的时候，急于找到“出口”而乱找方向。</p><p>我的建议是：1. 通过找关系送礼找一份相对稳定的工作进入到体制最好，这样可以有一份比较稳定的收入；再来准备自己的人生下一阶段，等结婚生育之后，再来考虑自己的兴趣问题；2. 因为该小伙伴学习过Python，可以从事Python相关的幼儿编程教育工作，收入不错，而且稳定。后续继续跟进…</p><ul><li>35岁，一线大厂，育有一女，年收入20w，房贷月9k左右，有年龄危机。<br>这位小伙伴是目前比较典型的案例了，做过一些副业的尝试（不是眼高手低的那种），但是收入都不行。碍于面子，没有在外面找项目去做。在35岁之前，走完了人生中最重要的几步：买房成家育子，老婆在家全职了3年之后，应该小孩子是上幼儿园了，所以又找了一份工作收入一般。尝试过卖书，也搞过一段时间的口播，但是收资不行。非常的迷茫，怕失去现在的工作后，生活的压力把自己的小家压垮了。</li></ul><p>状态分析：有危机意识，生活经济上的压力不大，在当下最重要的事情是做好长远规划。</p><p>我的建议是：1. 可以参考抖音鱼皮的案例，走编程圈子的路线；2. 自己把下面一个阶段最重要的事情想清楚，平衡自己的家庭与事业，自己下面的发展可以根据自己的性格选择走管理或者继续走打工人的路线，同时做好二手准备（创业+降薪后面临的生活压力问题）；3. 不要有生活上的压力，3w的收入不一定比3k的收入的人要开心，即使没有工作了，去跑外卖一个月最差也有6-7的收入，跑快车一个月也有1w的收入；4. 走技术路线，找找相关的项目，同时积累人脉，找机会创业或者拿股份。</p><p><strong>搞“大”钱的人：</strong></p><p>很多人也许是出于生活的压力，也许是对美好未来的憧憬，也许是对家人的期许，也许是对孩子们的责任，让大家都像打了鸡血一样，连轴转，做着各种“副业”，乐此不彼。却没有停下过脚步去思考，去做长期规划。物质层面思考没有错，但是，<strong>一个人的时间是有限的，透支自己的精力，往往会带来身体+精神上的双重压力。</strong></p><p>曾经的自己也是一个“奋青”，觉得自己的技术无所不能，也有非常多的软件项目的机会去赚钱，所以接了很多项目去做。虽然学习非常多的东西，但是自己身体也搞垮了。而且越到后面同质化的东西越多，能够学习的东西也变少了，让自己成了一个赚钱的机器。赚的再多的钱，没有了健康什么都白搭。</p><p>网络上，大多数摆在明面上的副业赚“大”钱的案例，除了极个别赚到了不错的收益，很多都没有赚到钱，营销要与商品本身配合的好，还要有运气。所以，我们的小伙伴们，要尽可能早的放弃对于爆红爆火的幻想。比如：现在来学现在就能赚钱、现在就想要有渠道、现在就要有销路…等我就赚“大”钱，这种不切实际的想法。急于求成，只会以小打小闹，失败告终。</p><p>大家也不能走极端，不能完全的躺平，要从现在明白这样一个道理：做副业并不是单纯的搞钱，<strong>做副业其实是一个长期积累的过程，赚钱只是水到渠成的事</strong>。把节奏放慢下来，边做边思考，做长期的规划才是正确的做法。做得长远的副业，是对一个人综合能力的考量，比如：运营能力、学习能力、反思能力、新事物的接受能力…等。尽早的有这种想法，为未来做好准备的人，才能在机会到来的时候，能抓得住它。</p><h2 id="副业其实是一场修行"><a href="#副业其实是一场修行" class="headerlink" title="副业其实是一场修行"></a>副业其实是一场修行</h2><p>副业这场修行，要从两个方向来学习：时间管理&amp;认知突破。</p><h3 id="时间管理"><a href="#时间管理" class="headerlink" title="时间管理"></a>时间管理</h3><p>这虽然是一个老生常谈的话题，但是做好的人很少。那原因是什么呢？主要原因是：行动力与习惯差，人的惰性太强，理性会控制不了自己的身体。</p><p>怎么提高自己的行动力？</p><ul><li>结团行动，互相鼓励与监督</li><li>花钱克制自己，找人监督；</li><li>自我约束，告别手机，使用番茄学习法（20+20或者40+20这样灵活组合）；</li><li>自我约束 + 冥想 + 健身：后面的这两者是最常用的提升专注力的方法，后期有机会给大家分享。<br>提高了行动力，接下来就是学习时间的管理了，按照计划行事：</li></ul><h4 id="理解时间是有长短的"><a href="#理解时间是有长短的" class="headerlink" title="理解时间是有长短的"></a>理解时间是有长短的</h4><p>这一点很好理解，每天的工作时长与休息时长是固定的，才能保证睡眠的同时赚到钱，身体是一切活动的本钱。所以，要尽可能的减少熬夜，减少透支自己的身体，减少那种单纯使用时间换金钱的工作内容的投入。</p><p>明白这一点，就知道为什么打工人赚钱不能致富了。因为打工人的服务对象是有限的，一个人不可能同时打几份工。服务的对象少了，也就赚的钱少了~</p><p>平时，也不要去浪费时间，每个人的一生的长短也是有一个大概的长度的，所以要珍惜现在，珍惜眼前人，活在当下。</p><h4 id="时间可以复制"><a href="#时间可以复制" class="headerlink" title="时间可以复制"></a>时间可以复制</h4><p>听时间能复制，是不是非常的荒谬？这里的时间的复制，主要是指利用现代的科学技术，把文字、声音、图片、视频留存下来了，通过复制这些内容，让更多的人受益。随时互联网技术的发展，如今人们生活消遣的方式变成了看手机，手机上大多数内容以短视频为主。接入视频，数字类型的商品的拷贝，成本几乎为零。所以，做好时间的复制，可以让更多的人接触到更优秀的内容。比如：当老师从讲台前走到镜头前，他的声音与画面，他的知识就会被更多人接受到。而一个好的老师，会影响到一个同学的一生。</p><p>同理，如果利用时间可以复制的特性，我们可能把自己现在从事的很多事情搬到网上，通过文字、声音、图片、视频的方式让更多的人看到用到，从而也可以从中谋取自己的利益。</p><h4 id="购买别人的时间"><a href="#购买别人的时间" class="headerlink" title="购买别人的时间"></a>购买别人的时间</h4><p>购买时间其实就是雇人做事，让自己的精力放在最核心的事情上，最有价值的事情上，这样才能事半功倍。所有赚钱的事情，要上规模才能有几何效应。很多人赚着钱，怕来人把自己的生意给抢走了，或者不太相信雇的人，所以事事亲为，这样就回到了“时间有长短”的概念，自己的服务对象就有限，规模就会有上限。</p><p>找准自己的赛道，看看未来几年可能的发展态势，找更多的人加入到自己的团队，从0到1，其实并没有大家想像的那么难，创业先从注册公司开始。有可能很多小伙伴已经创业了，发现招人非常的难。是的，“千金难买寸光阴”，现在的HR招人非常的难，更何况是找到一个合适的人，这一点上需要投入的精力会占公司的2-30%，前期甚至更多。</p><h4 id="时间的多元化收益"><a href="#时间的多元化收益" class="headerlink" title="时间的多元化收益"></a>时间的多元化收益</h4><p>你会不会坐地铁的时候听歌看视频？你会不会在上厕所的时候刷小视频？你会不会在吃饭的时候刷微博看知乎掘金？…这些都是时间的多元化收益，虽然有的听起来看起来不是那么的“健康”（吃饭的时候应该认真一些）。</p><p>多元化收益是我们通过把同一份时间进行分拆让多个事情能够并行的一种手法，说人话就是：一心二用。哇，你这有点反人类啊？老师们从小就教导我们不能一心二用，为什么你要推荐大家一心二用呢？因为，很多平时我们的碎片时间其实是可以利用起来的，比如：上厕所发发闲鱼单、公交地铁上10站路看看自己感兴趣的视频（学习）、睡觉或者跑步的时候可以听听英语音乐学英语等。</p><h3 id="认知突破"><a href="#认知突破" class="headerlink" title="认知突破"></a>认知突破</h3><h4 id="卖时间"><a href="#卖时间" class="headerlink" title="卖时间"></a>卖时间</h4><p><strong>副业赚点小钱，每个人都可以，只是你没有动力、没有动手，空有一个点子。</strong>其实，有很多低门槛的副业，像做骑手、点赞、砍单等来钱真的很快，只是有的人会觉得赚这点钱很不屑。这类特点是做不大，形成不了规模，收入不稳定。</p><p>这类的副业，最主要的特点是这个世界上最通用，且最容易卖的一种资源——时间。每个人的时间是有限的，所以决定你的上限。</p><p>那独立的个人就赚不到大钱了吗？当然有，比如一些行业里面的顶级智囊，享有资源与超强整合资源能力的人们，他们的服务对象可能只有极少数的人，但是这些极少数的人却给他们提供了无穷的财富，<strong>因为，他们提供的服务是极优的，给这些富人们大大地节约了时间。</strong></p><h4 id="卖能力"><a href="#卖能力" class="headerlink" title="卖能力"></a>卖能力</h4><p>比单纯的卖时间，再进一个维度，就是把卖时间变成卖能力。</p><p>打磨自己的核心竞争力，并且要有一定的心理承受能力，因为用时间换钱变成用能力换钱的时候，这个世界会360度无死角的“鞭打”你。但是，随着一次次的打击，能力也会一次次的提升。</p><h4 id="卖资源"><a href="#卖资源" class="headerlink" title="卖资源"></a>卖资源</h4><p>比卖能力更进一步的，是卖资源，做资源的整合者，管理者。</p><p>一个好的创业者企业家，要有发现市场资源和消费者的尖锐眼光、行动力、专注力和毅力，才有可能成功。</p><p>朋友让你稳当点、父母让你早结婚、对象让你别做梦，让你放弃了一个又一个的机会和想法。等过了几年，在新闻上你看到了自己的点子成功了，你最多只能说一句“我要做我早成功了”。就像打牌一样，犹犹豫豫不敢下注，告诉自己只要我不上桌就没人能赢我的钱，也许你永远不会失去手中的筹码，但这就是一辈子所有的财富了。而你羡慕的那些人，一直坐到桌子上，不停的ALL IN（全押，风险越大收益越大，成规模才能赚钱），本来就一无所有，还怕失去什么呢。——这里要注意，创业不要借“高利”贷（年化低于6%都可以考虑）。</p><p>明白了时间管理的方法与找准认知突破的方向后，接下来就是具体的副业内容了。</p><p>根据前文统计报告，其中指出了超过一半的人想从事副业。躺赚是所有小伙伴心中的梦想——梦想很美好，但现实很骨感。有一些副业，听上去很高大上，而实际上的收入真是有那么赚钱？我们来看下。</p><h2 id="副业真这么赚钱？"><a href="#副业真这么赚钱？" class="headerlink" title="副业真这么赚钱？"></a>副业真这么赚钱？</h2><p>先说个结论：大多数副业是不怎么赚钱的，费精力。能把副业做成主业的，是有强大的兴趣+机遇+努力才能成功。</p><p>先要给小伙伴泼冷水了，那些看似光鲜的网络搞钱背后，大多是割韭菜。</p><p>其中，亲身经历过的就有：写作变现课、读书、播音…这些副业的投入产出比不大，所以，费时费人，急缺钱用，而且有时间，就可以做做跑腿（代驾、专车等），这种来钱快，有时间跑就有钱赚。</p><p>像在很多社区平台上流行的：</p><ul><li>微商（前几年），主要的模式是发展下线，实质上自己带货赚的少，精力花费大；</li><li>游戏主播：这个不算是副业了，算是半个主业了，头部主播已经满额，后期想做出来难变现；参考变现做得好的：猫萌；</li><li>写作、播音、做图变现：这几个算是一类，大多是卖自己的课程，课程内容偏理论多一些，实操的不多。而真正变现的，会有，但是对个人能力要求高，性价比一般，需要有一个长期积累的过程，从业人数非常的多；要做差异化的东西，容易出圈，但是，做出来是比较难的；</li><li>短视频相关，如TikTok、带货、影视剪辑等：这一类算是技能型的副业，大多数人可以学，但是，没人会告诉你其中的坑。1. 门槛低，很多机构的从业者在学校就开始学习这些工具的使用，所以网络上同质的内容很多；2. 变现其实要考虑规模，一个人做大多数时候只会有几块几十每条视频（这个是非常不错的播放数据）的收入，所以真正做这个赚钱的：要么是有多个账号多平台+多设备发（这里会有手机+手机号成本的投入），要么是有上百万千万的粉丝做广告变现；</li><li>跑腿、代驾、点赞砍刀、代练等：这一类是投入多少时间就会有多长的回报，回报快但是无法持久回报。一个真实数据：一线城市，全职滴滴师傅，一般一个月休息2-4天，一周休息一天看天气与心情，收入大概在1w左右。其中，每天的收入400-600不等（夜班比白班赚的多100-200），还要除去一些成本：租车、吃饭、充电或者加气、车辆维修、偶尔会有违章等，基本一天的工作时长在10小时，夜班的时间会短一些。很多都会有职业病，干不特别长久，有很多师傅都会有自己回归实业的一些打算（做一做小生意）；</li><li>…<br>还有很多类似的案例，可能有一些案例我们同学们第一次听觉得很新奇，是不是？这种感觉就对了，兴趣是最好的老师，先动起来，去尝试才知道是否适合自己。而找到适合自己的，就跟上面我们在罗列这些副业一样，要让自己接触到更多的分类，了解市场&amp;行情，这样才能做到“百战百胜”，这就是视野的重要性了。</li></ul><p>做好副业要打开自己的视野，勇于去想去拼，才会有结果——PS：好鸡汤啊，好毒。</p><p>那有什么副业可以做呢？</p><p>同类型的问题还有：</p><ul><li>什么样的副业才算是好副业呢？</li><li>怎样去开始自己的副业呢？</li><li>怎么提升自己的意识？</li><li>…<br>下面我们一起来探讨一下。</li></ul><h2 id="有哪些副业是好副业？"><a href="#有哪些副业是好副业？" class="headerlink" title="有哪些副业是好副业？"></a>有哪些副业是好副业？</h2><p>好副业的一些参考标准：</p><ul><li>持续带来收入，投入产出比高，一份投入多份产出；</li><li>阳光行业，未来发展清晰；</li><li>和自己专业、工作、以往的经历有关，能够有先天的“资源”；</li><li>对应自己的兴趣，做事情有激情；<br>那如何选择呢？有哪些具体的方向呢？</li></ul><p><strong>从兴趣出发</strong></p><p>技术出生的人是比较容易开展自己的副业的，可选的方向比较多。</p><p>未来主要方向是：除了技术输出以外，还可以学习做一些兴趣相关的，“混搭”才更有料，比如：用视频的方式记录一个阳光宅男的生活、情景短片、科普视频等，即好玩又有趣，还学习了东西，交到了朋友，何乐而不为？ 程序员开展自己的副业，先要了解互联网行业的发展，这样可以初步确定一些朝阳行业或者方向，然后，从自己的兴趣出发，选择那些可以让自己忘记时间的事情着手，把兴趣变成职业应该是每个人追求的方向。</p><p>推荐：短视频社交、短视频科普等</p><p><strong>从已有资源出发</strong></p><p>有很多小伙伴本身在自己的行业中已经从业多年了，其实手上有很多资源（人脉、权力、货物、渠道等），想搞钱只是没有去往这方向想，或者想了之后，看到了很多人做的其实不好，自己就打了退堂鼓了。记住风险与机遇共存，风险越大对自己的挑战越大，同时回报也越大。我们可以不去赌，可以找人一起分担这个风险。如果自己不会，就找专业的人咨询；如果自己没有方向，就可以看看有没有同样的国内国外的同行在做的副业，学习商业模式，其实就可以很快上手了。因为你在自己的行业，其实是了解行业内幕的，无非是动手能力的强弱，以及产品&amp;项目&amp;方向上是否有竞争力。</p><p>推荐：专业咨询+多人合伙。</p><p><strong>平凡的人最灵活</strong></p><p>有的人时间很紧，没有什么时间——那就好好上班，如果精力还有，才考虑副业的问题，别“捡了芝麻丢了西瓜”；</p><p>有的人时间很多，却不知道做啥——从兴趣出发，找几个你关注的人或者熟悉的人，问问行业消息再作打算；</p><p>有的人想法多，却没有资金——写做一些小闭环或者从一些自己比较好切入的点（熟悉的）入手，这样会有成就感，也能积累做事的信心。做多了之后，“胆子”也会大一些，面对不确定性的时候，就会更加从容。每个人都害怕失败，所以习惯在自己的舒适区，有了想法，更要有勇气走出去；</p><p>有的人有资金，却没有动手能力——找专业的人合伙，你出钱他出力这是一条出路；找时间在网上充实自己，投资自己是收益率最高的投资，找机会去做项目投资（实体）；</p><h2 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h2><p>做副业经常会碰到突发事情，并不是掌握单一技能就能做的很顺，往往是多维度多方面的技能锻炼，我在副业+创业的过程中，踩过、碰过很多坑，也有在金钱和时间上等方面有损失。</p><ul><li><p>费用问题：最开始做副业项目，很多时候，只想找到一个机会而已，并不在乎自己的利益，所以被压榨的很利害，赚的也比较少。</p><p>解决方案：在最初协商的时候，订立合同而非口头承诺；相对大一点的项目，会收取预付款；确定阶段性的付款方式。</p></li><li><p>信息问题：没有合适的切入点，不确定在哪里找合作平台，或者在哪里获取一些副业信息。</p><p>解决方案：在朋友圈中做好宣传，朋友介绍到的项目一般可信度都比较高；找行业内的头部平台，通过网站排名、统计信息、社群推荐等渠道来了解与比较，而且要具备一定的项目分析的能力，最后就是多去试了。</p></li><li><p>信用问题：无论是找公司、个人还是平台，都会存在信用问题。</p><p>解决方案：找中介平台、找担保人、以公司名义来进行动作，所以，在外面做副业，最好是有一个自己的公司。</p></li><li><p>方法问题：技术出生的我，初期做事情会追求极致，给自己招来了很多不便，增加了沟通成本，也可能会告吹好的项目。</p><p>解决办法：学会权衡的艺术，在质量与速度之间做好平衡，很多时候，我们看结果为导向，并不是看过程。</p></li><li><p>时间问题：工作与生活不能很好的协调，身体会因为长时间的透支而变差，这样会反过来影响到工作。</p><p>解决办法：在疫情期间我购买了跑步机（目前月跑在4次左右，1次5公里，初期会多一些），平时在家会带着家人做做操，跑跑步、跳绳，这个频次不用太高，一周3次即可，还有就是大量饮水+吃营养素，定期体验（我有自己的牙医+健康师）。</p></li><li><p>心态问题：即使到了现在我也会有迷茫的时候，不清楚自己追求的东西、自己做事的方向是不对的。</p></li><li><p>解决办法：我们人这一生，除了低头做事，还要学会抬头看路，探寻自己人生的意义。</p><ul><li><p>阅读：我会阅读人文传记、历史科普、心理学、管理学、经济学相关的书籍，来充实自己的精神世界。</p></li><li><p>善于倾听：也会通过社群社区的交流，找到自己的项目&amp;产品缺点，及时调整。</p></li><li><p>总结与计划：总结过往，勇于面对挫折与失败，重新启航，我会有长期5年计划 + 短期月度年度计划，给自己定义一个大的方向，做事更稳健更有目标感。</p></li></ul></li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;都想发展副业，增加收入，那目前哪些副业可以做呢？有什么副业，可以月入1w+？整天看着小视频中的人在炫富，真的这么好赚钱吗？副业赚的钱比主业赚的多是什么体验？你是否也有类似的困扰？宝妈、自由职业者、打工族均可做，想要靠副业赚钱的朋友，建议点击进来看。&lt;/p&gt;
&lt;p&gt;PS: 谈钱并不可耻，君子爱财取之有道。我们下面谈到的“副业”都是在国家法律法规范畴内，不投机不搞灰色产业（如币圈等），拒绝“黄赌毒”。&lt;/p&gt;</summary>
    
    
    
    <category term="副业指南" scheme="https://www.toimc.com/categories/%E5%89%AF%E4%B8%9A%E6%8C%87%E5%8D%97/"/>
    
    
    <category term="副业" scheme="https://www.toimc.com/tags/%E5%89%AF%E4%B8%9A/"/>
    
  </entry>
  
  <entry>
    <title>Vue.js设计与实现（1）</title>
    <link href="https://www.toimc.com/vue1/"/>
    <id>https://www.toimc.com/vue1/</id>
    <published>2022-03-03T16:03:29.000Z</published>
    <updated>2025-10-06T05:13:13.974Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Vue-js设计与实现（1）"><a href="#Vue-js设计与实现（1）" class="headerlink" title="Vue.js设计与实现（1）"></a>Vue.js设计与实现（1）</h1><p>本系统是读书笔记，纯属回忆&amp;总结。</p><p>新书《Vue.js设计与实现》作者是霍春阳，想通过博客记录一下自己学习过程中的心得与总结。</p><span id="more"></span><h2 id="第1章-权衡的艺术"><a href="#第1章-权衡的艺术" class="headerlink" title="第1章 权衡的艺术"></a>第1章 权衡的艺术</h2><p>第1章：介绍了声明式与命令式的概念与性能区别，虚拟DOM性能以及Vue.js在设计之初对于运行时的考量。</p>]]></content>
    
    
    <summary type="html">&lt;h1 id=&quot;Vue-js设计与实现（1）&quot;&gt;&lt;a href=&quot;#Vue-js设计与实现（1）&quot; class=&quot;headerlink&quot; title=&quot;Vue.js设计与实现（1）&quot;&gt;&lt;/a&gt;Vue.js设计与实现（1）&lt;/h1&gt;&lt;p&gt;本系统是读书笔记，纯属回忆&amp;amp;总结。&lt;/p&gt;
&lt;p&gt;新书《Vue.js设计与实现》作者是霍春阳，想通过博客记录一下自己学习过程中的心得与总结。&lt;/p&gt;</summary>
    
    
    
    <category term="Vue" scheme="https://www.toimc.com/categories/Vue/"/>
    
    <category term="Vue3" scheme="https://www.toimc.com/categories/Vue/Vue3/"/>
    
    
    <category term="Vue.js" scheme="https://www.toimc.com/tags/Vue-js/"/>
    
  </entry>
  
  <entry>
    <title>如何调试node&amp;npm？</title>
    <link href="https://www.toimc.com/node-vscode-webstorm-debug/"/>
    <id>https://www.toimc.com/node-vscode-webstorm-debug/</id>
    <published>2021-04-03T08:13:35.000Z</published>
    <updated>2025-10-06T05:13:13.974Z</updated>
    
    <content type="html"><![CDATA[<p>如何使用尝试的IDE（VSCode &amp; Webstorm）调试node&amp;npm程序？本篇通过真实的案例（图片教程）介绍了调试方法</p><span id="more"></span><p>调试技能是程序员的基础技能，学会调试程序：</p><ul><li>可以方便的找到程序的入口</li><li>可以找到运行过程中的逻辑问题（Bug）</li><li>可以理解程序执行的逻辑</li><li>可以了解函数之间的调用关系</li><li>…</li></ul><h2 id="调试原理"><a href="#调试原理" class="headerlink" title="调试原理"></a>调试原理</h2><p>在node的命令中提供了一个参数<code>--inspect-brk</code>，或者<code>--inspect=9100</code>指定调试端口，通过加入这个<code>inspect</code>参数，即可以实现调试Node程序的目的。</p><p>例如，如果需要调试webpack：</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">node --inspect-brk .<span class="regexp">/node_modules/</span>.bin/webpack</span><br><span class="line">Debugger listening on ws:<span class="regexp">//</span><span class="number">127.0</span>.<span class="number">0.1</span>:<span class="number">9229</span>/<span class="number">42</span>e65994-<span class="number">934</span>b-<span class="number">470</span>c-b531-<span class="number">347</span>ba54c7ac8</span><br><span class="line">For help, see: https:<span class="regexp">//</span>nodejs.org<span class="regexp">/en/</span>docs/inspector</span><br></pre></td></tr></table></figure><p>然后就可以打开chrome进行连接了：</p><p><img src="https://static.www.toimc.com/blog/img/2021/nestjs-example-project-8/nestjs-node.png?watermark/2/text/VG9pbWPlh7rlk4E=/fontsize/200"></p><p>如果提示<code>Debugger Attached</code>，说明调试连接成功:<br><img src="https://static.www.toimc.com/blog/img/2021/nestjs-example-project-8/nestjs-node2.jpg?watermark/2/text/VG9pbWPlh7rlk4E=/fontsize/200"></p><h2 id="结合VSCode"><a href="#结合VSCode" class="headerlink" title="结合VSCode"></a>结合VSCode</h2><p>需要借助到vscode强大的调试功能（PS：webstorm等IDE的配置思路一致）</p><p><img src="https://static.www.toimc.com/blog/img/2021/nestjs-example-project-8/nestjs-node3.jpg?watermark/2/text/VG9pbWPlh7rlk4E=/fontsize/200"></p><h3 id="小齿轮快速添加"><a href="#小齿轮快速添加" class="headerlink" title="小齿轮快速添加"></a>小齿轮快速添加</h3><p><img src="https://static.www.toimc.com/blog/img/2021/nestjs-example-project-8/nestjs-node4.jpg?watermark/2/text/VG9pbWPlh7rlk4E=/fontsize/200"></p><p>点击小齿轮默认添加<code>node.js</code>结果：</p><figure class="highlight awk"><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></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="regexp">//</span> 使用 IntelliSense 了解相关属性。 </span><br><span class="line">  <span class="regexp">//</span> 悬停以查看现有属性的描述。</span><br><span class="line">  <span class="regexp">//</span> 欲了解更多信息，请访问: https:<span class="regexp">//g</span>o.microsoft.com<span class="regexp">/fwlink/</span>?linkid=<span class="number">830387</span></span><br><span class="line">  <span class="string">&quot;version&quot;</span>: <span class="string">&quot;0.2.0&quot;</span>,</span><br><span class="line">  <span class="string">&quot;configurations&quot;</span>: [</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="string">&quot;type&quot;</span>: <span class="string">&quot;pwa-node&quot;</span>,</span><br><span class="line">      <span class="string">&quot;request&quot;</span>: <span class="string">&quot;launch&quot;</span>,</span><br><span class="line">      <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Launch Program&quot;</span>,</span><br><span class="line">      <span class="string">&quot;skipFiles&quot;</span>: [</span><br><span class="line">        <span class="string">&quot;&lt;node_internals&gt;/**&quot;</span></span><br><span class="line">      ],</span><br><span class="line">      <span class="string">&quot;program&quot;</span>: <span class="string">&quot;$&#123;file&#125;&quot;</span>,</span><br><span class="line">      <span class="string">&quot;outFiles&quot;</span>: [</span><br><span class="line">        <span class="string">&quot;$&#123;workspaceFolder&#125;/**/*.js&quot;</span></span><br><span class="line">      ]</span><br><span class="line">    &#125;</span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>从上面的配置中可以读出一点信息：</p><ul><li>vscode调试是在<code>Launch Program</code>，打开一个程序，或者说运行一个程序</li><li>文件是哪个呢？<code>$&#123;file&#125;</code>即你打开的<code>.js</code>文件</li></ul><p>关于上面的配置信息可以参考：</p><ul><li><p><a href="https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_launch-configuration"><code>Lanch Configuration</code>文档</a></p></li><li><p><a href="https://code.visualstudio.com/docs/editor/debugging#_launchjson-attributes"><code>Launch.json</code>属性文档</a></p></li></ul><p>调试尝试：</p><p><img src="https://static.www.toimc.com/blog/img/2021/nestjs-example-project-8/nestjs-node6.jpg?watermark/2/text/VG9pbWPlh7rlk4E=/fontsize/200"></p><h3 id="手动添加脚本"><a href="#手动添加脚本" class="headerlink" title="手动添加脚本"></a>手动添加脚本</h3><p><img src="https://static.www.toimc.com/blog/img/2021/nestjs-example-project-8/nestjs-node5.jpg?watermark/2/text/VG9pbWPlh7rlk4E=/fontsize/200"></p><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></pre></td><td class="code"><pre><span class="line"><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;node&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;request&quot;</span><span class="punctuation">:</span> <span class="string">&quot;launch&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Launch via NPM&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;runtimeExecutable&quot;</span><span class="punctuation">:</span> <span class="string">&quot;npm&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;runtimeArgs&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">    <span class="string">&quot;run-script&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="comment">// 下面的一行调整成你的启动脚本</span></span><br><span class="line">    <span class="string">&quot;start:dev&quot;</span></span><br><span class="line">  <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;port&quot;</span><span class="punctuation">:</span> <span class="number">9229</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;skipFiles&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">    <span class="string">&quot;&lt;node_internals&gt;/**&quot;</span></span><br><span class="line">  <span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br></pre></td></tr></table></figure><p>打断点进行调试：</p><p><img src="https://static.www.toimc.com/blog/img/2021/nestjs-example-project-8/nestjs-node7.jpg?watermark/2/text/VG9pbWPlh7rlk4E=/fontsize/200"></p><p>发起请求，就可以看到停到断点了：</p><p><img src="https://static.www.toimc.com/blog/img/2021/nestjs-example-project-8/nestjs-node8.jpg?watermark/2/text/VG9pbWPlh7rlk4E=/fontsize/200"></p><p>还可以在调试Console中看到各种调试信息。</p><h2 id="Webstorm配置"><a href="#Webstorm配置" class="headerlink" title="Webstorm配置"></a>Webstorm配置</h2><p><img src="https://static.www.toimc.com/blog/img/2021/nestjs-example-project-8/nestjs-node9.jpg?watermark/2/text/VG9pbWPlh7rlk4E=/fontsize/200"></p><p>如图，原理一样，配置简单（点击放大）</p><p>步骤：</p><ul><li>添加配置</li><li>选择<code>npm</code></li><li>选择调试脚本</li><li>打上断点</li><li>开启调试</li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;如何使用尝试的IDE（VSCode &amp;amp; Webstorm）调试node&amp;amp;npm程序？本篇通过真实的案例（图片教程）介绍了调试方法&lt;/p&gt;</summary>
    
    
    
    
    <category term="node.js" scheme="https://www.toimc.com/tags/node-js/"/>
    
  </entry>
  
  <entry>
    <title>《准备》读书心得</title>
    <link href="https://www.toimc.com/book-reviews-prepared/"/>
    <id>https://www.toimc.com/book-reviews-prepared/</id>
    <published>2021-03-30T11:46:26.000Z</published>
    <updated>2025-10-06T05:13:13.972Z</updated>
    
    <content type="html"><![CDATA[<p>本文是《准备》(作者：黛安娜・塔文纳)的阅读摘抄与个人感想，这本书重点介绍了美国萨米特公立学校的教书育人的方法，按照她的办学思想与模式，在美国已经有15所学校，成功受到多位知名的教育家联名推荐。</p><span id="more"></span><h2 id="何为教育"><a href="#何为教育" class="headerlink" title="何为教育"></a>何为教育</h2><h3 id="目标"><a href="#目标" class="headerlink" title="目标"></a>目标</h3><p>我们不能够放弃任何一个孩子，教育的目标就是让每一个孩子都能够获得人生的成功，并且能够快乐地拥有掌握幸福的能力。</p><h3 id="本质"><a href="#本质" class="headerlink" title="本质"></a>本质</h3><p>教育的本质在于改变孩子，帮助这些孩子变得更好、更幸福。教育的本质不在于面试他们，不在于筛选出来好的孩子放在一起。</p><p>这一点有点类似于孔夫子的“有教无类”，教育家是贩夫走卒都能培养。甚至有人还曾经走过弯路，干过坏事，没关系，来，我都教你，然后慢慢把你变成好人，这个才是教育的本质。</p><h2 id="人才需求在变化"><a href="#人才需求在变化" class="headerlink" title="人才需求在变化"></a>人才需求在变化</h2><p>传统的人才要求：</p><ul><li>简单的培训</li><li>有耐力</li></ul><p>今天对人才的需求：</p><ul><li>解决复杂问题</li><li>批判思维</li><li>创造力</li><li>人力管理</li><li>协作能力</li><li>高情商</li></ul><p>因为对人才的需求变化了，所以我们的教育也要变化。</p><p>我们来看看萨米特高中是怎么做到的。</p><h2 id="新的尝试"><a href="#新的尝试" class="headerlink" title="新的尝试"></a>新的尝试</h2><p>在萨米特高中，每个班级没有老师讲课，他们的办法：</p><ul><li>把知识点 + 所有的课程列表提供给同学</li><li>老师出课题，是一个问题或者挑战，让同学们自己解决实际的问题</li></ul><p>比如：</p><ul><li>工业革命：产品的故事</li><li>电力屋：从工程师的角度去设计一个电力屋</li><li>亲爱的编辑 ：要求学生亲生扮演作者</li></ul><blockquote><p>“这些课题项目并非临时植入的，它们是学生的日常作业，深入探讨、规划、调研、制作模型、写作和大量的批判性思考，取代了单纯的授课，学生和老师一同完成这些课题……”最后这句话很重要，“它们不是甜品，它们是主菜。”</p></blockquote><h2 id="学习方法"><a href="#学习方法" class="headerlink" title="学习方法"></a>学习方法</h2><h3 id="项目式学习"><a href="#项目式学习" class="headerlink" title="项目式学习"></a>项目式学习</h3><p>目的：在于培养决策力，打造基于项目式的课程体系</p><p>方法：在网络上自觉基础知识，合作完成，综合应用学科知识</p><h3 id="自主学习"><a href="#自主学习" class="headerlink" title="自主学习"></a>自主学习</h3><p>能学会自主学习，有三个关键词：<strong>精通、自主与目标</strong></p><p>我们今天的学生与线上课程的时间是冲突的，而且学生的自主性差，如果不需要听老师慢节奏的讲课 + 繁重的作业，让学生们自己追着课程上的话，那就是海阔凭鱼跃。</p><p>那怎么办？</p><ul><li><p>SMART原则：Specific特定的目标，Measurable，可衡量的；Achievable可以实现的；Relevant，现实的Time-bound，有时间要求的；</p><p>不是放羊，而是要不断的确定目标，新目标，然后调整状态。</p></li><li><p>随机应变：如果这部分学不会，就继续学；如果已经学会了，那就继续往下走；</p></li><li><p>寻求挑战：不能只学自己会的东西，自己喜欢的，要学习自己不会的，挑战新的目标；</p></li><li><p>坚持不懈：学习不是一蹴而就，必须能够忍耐，能吃苦；</p></li><li><p>直面挫折：这次没有做好，不会不要紧，直面挫折，下次再来；</p></li><li><p>适时求助：真的不会了，找人问。</p></li></ul><p>如何培养呢？</p><p>老师是关键性作用的，要不断地在学习的过程中强调这些特性。</p><blockquote><p>我们作为老师，我们作为学校，要教给你的是这六样东西，学那些知识是你自己的事，你自己去想办法。但是我们要教给你的，是这六个素质。这六项素质教会你了，你就能够活到老学到老，你就能够真的知道学习是怎么回事了。</p></blockquote><p>我们老讲“授人以鱼不如授人以渔”，这句话是中国的俗语。</p><p>但是什么才是那个打鱼的“渔”？我们过去觉得，解题思路就是打鱼的“渔”。不对，解题思路是那个“鱼”，就吃的那个鱼。但是学习的这种态度、这种精神、这种设定目标的方法，寻求挑战的这种状态，然后适时求助的这种方法论，这个才是真正能够帮助一个人活到老学到老的工具。</p><p>失败在哪种情况下是有意义的？</p><ul><li><p>学到了东西，并且有动力再次尝试；——强调不用过度的担心失败</p><p>e.g. : 打游戏，game over了，还可以重启；</p></li><li><p>失败不会永久地排队未来的可能性；——我们创造了特别大的面对失败的压力</p><p>e.g. : 高考模拟考试作弊</p></li></ul><p>有本书叫作《不管教的勇气》，那本书的作者就说：“如果一个家长不能够让孩子意识到他是为了自己学习，那么你使多大的劲儿都没用。”就是你最多把他逼迫得假装在学习。</p><h3 id="反思式学习"><a href="#反思式学习" class="headerlink" title="反思式学习"></a>反思式学习</h3><p>认知：你知道很多东西</p><p>元认知：你知道自己知道哪些东西</p><p>如果你知道自己知道哪些东西，那么相应的，你也会知道自己不知道哪些东西，你对自己的认知，有一个边界的了解，这个叫作“元认知”。所以要培养孩子能够反思、能够知道自己该学习些什么东西、能够知道自己需要些什么，这个是非常重要的一件事。</p><p>如何实现的呢？</p><p>解除老师的改作业、教学等工作，而让老师成为一个导师，负责15-20人，带领学生参与打分的过程。</p><p>比如：说你的一个作业，是“旅行箱”这样的作业。做完了以后，让其他组的孩子给这组的学生打分。然后老师打出自己的分，再来跟孩子们校正。老师告诉孩子们为什么打这样的分、原因是什么，帮助孩子不断地修订自己打分的标准。逐渐你就会发现，孩子们学会了判断力。这种对于作品的判断能力，其实就是非常重要的反思的能力，它替代了老师下的那个评语。</p><p>目的：</p><ul><li>同学们不用只专注于分数</li><li>能够从方法论的角度提出意见</li><li>观察自己的学习、观察他人的学习</li></ul><p>最终，提高自己的元认知。</p><h3 id="建立良好的人际关系"><a href="#建立良好的人际关系" class="headerlink" title="建立良好的人际关系"></a>建立良好的人际关系</h3><p>很多学霸走上社会后并不成功，原因就是学霸一辈子只相信自己的能力强就够了，他不需要跟别人合作。但是，能够令你成功的最重要的能力，并不是竞争的能力。</p><p><strong>我们在这个社会上，你真的能够做出贡献，最核心的能力是合作的能力。</strong></p><p>怎么让大家来建立人际关系呢？</p><p>通过问题引导 - GROW模型</p><ul><li><p>G（Goal setting）：代表确认员工业绩<a href="https://wiki.mbalib.com/wiki/%E7%9B%AE%E6%A0%87">目标</a>；</p></li><li><p>R（Reality Check）是现状，要搞清楚目前的现状、客观事实是什么；寻找动因；</p></li><li><p>O（Options）代表寻找解决方案；</p></li><li><p>W （What? When? Who? Will? What should be done? When by whom and does the will exist to do it?） 代表制定行动计划和评审时间。</p></li></ul><p>人际关系中非常关键的点：</p><ul><li>每个人都可以寻求属于自己的成功，我们的成功无需建立在牺牲他人的利益之上。</li><li>人跟人的成功维度是极多的，绝不意味着你战胜了别人就能够成功，你把别人搞糟糕了你就可以成功。</li></ul><p>任何一个团队，在组建的时候都会面临着建立期、激荡期、规范期和执行期，最后还有一个解散期。</p><p>很多团队刚建立的时候整天吵架，打得一塌糊涂，团队有激荡期是很正常的一件事。</p><p>为了能够更好地决策，他们引入了一套决策机制DPIV，把决策过程中的人分成了几个类型。</p><ul><li><p>D（Decision）就是决定者，这种人是做决定的；——把握方向</p></li><li><p>V（Vote）有否决权的人——守住底线</p></li><li><p>P（Proposal），可以提议案的人——有一定的知识储备，有基本的原则</p></li><li><p>I（Input），就是你可以做意见提供者；——各种各样的思想</p></li><li><p>MBI，是被通知的那一方。</p><p>做一个决策的时候，通常团队里边就这五种人。</p></li></ul><h2 id="如何才算是做好了准备"><a href="#如何才算是做好了准备" class="headerlink" title="如何才算是做好了准备"></a>如何才算是做好了准备</h2><h3 id="评判标准"><a href="#评判标准" class="headerlink" title="评判标准"></a>评判标准</h3><ul><li>健全的人格<ul><li>依赖</li><li>压力管理</li><li>自我调节</li></ul></li><li>入学准备<ul><li>自我认知</li><li>共情和人际交往能力</li><li>执行力，即SMART原则</li><li>自我和对学校的看法</li><li>成长型思维，把失败视作是学习的机会</li><li>自我效能感，自我价值很高，也知道自己能做的事</li><li>归属感，集体的归属感</li><li>学校的重要性</li></ul></li><li>毅力<ul><li>适应性</li><li>能动性</li><li>学术韧性</li></ul></li><li>独立性和可持续性<ul><li>自主性</li><li>好奇心</li><li>目标感</li></ul></li></ul><h3 id="具体做法"><a href="#具体做法" class="headerlink" title="具体做法"></a>具体做法</h3><ol><li><p>设定一个目标，准备三套方案，保证实现该目标。原则：</p><ul><li>你想要怎样的生活？目标要基于对自我清晰的认知</li><li>你想要做的事情必须是切实可行，而且有意义的</li><li>在做选择前，一定要全方位的考虑可能性，你的决定一定要得到周围人与家里人的支持</li></ul></li><li><p>出于好奇心持续的学习：</p></li></ol><ul><li>知识仍旧很重要，要改变的是获取知识的方法；</li><li>通过自己最感举的内容找到与世界对接的入口</li><li>体验、探索、追求</li><li>发掘真实的兴趣</li><li>学会利用网络上的课程知识</li><li>终身学习</li></ul><ol start="3"><li>专项技能培养，雇主希望在求职者身上看到的是：</li></ol><ul><li>Leader Ship（领导力）</li><li>团队合作能力</li><li>沟通技巧</li><li>解决问题的能力</li><li>口头表达能力</li><li>较强的职业道德</li><li>主动性</li><li>定量分析能力</li><li>灵活性&#x2F;适应性</li><li>专业技术能力</li><li>人际交往能力</li><li>计算机能力</li><li>注重细节的组织能力</li><li>开朗外向的性格</li><li>战略规划能力</li><li>创造力</li><li>举止得体</li><li>创业精神</li><li>敢于承担风险</li></ul><h3 id="通用技能有哪些"><a href="#通用技能有哪些" class="headerlink" title="通用技能有哪些"></a>通用技能有哪些</h3><ol><li><p>文本分析</p><ul><li>主题、中心思想</li><li>观点、目标</li><li>主旨展开</li><li>结构</li><li>词汇选择</li></ul></li><li><p>作品展示 </p><ul><li>精准表达</li><li>使用多媒体沟通</li><li>口头汇报</li></ul></li><li><p>调研</p><ul><li>提问题</li><li>定义一个设计问题</li><li>规划和开展调查</li><li>提出假设</li></ul></li><li><p>分析和整合</p><ul><li>组织和展示信息</li><li>识别模式和关联</li><li>对比</li><li>建模</li><li>阐释数据&#x2F;信息以提出有效这个点</li><li>建立联系和进行看你方便</li><li>评估论点</li><li>评估不同的设计方案</li><li>给出基于证据的解释</li></ul></li><li><p>说与听</p><ul><li>参与基于证据的讨论</li><li>沟通的规范、积极倾听</li></ul></li><li><p>写作</p><ul><li>有力的这个点</li><li>提供相关信息、对论点进行解释说明</li><li>叙事</li><li>反驳</li><li>选择论据</li><li>解释论据</li><li>整合论据</li><li>组织（过渡、连贯、结构）</li><li>引入的结论</li></ul></li><li><p>资源使用</p><ul><li>选择相关的资源</li><li>资源的背景调查</li><li>整合不同的资源</li><li>建模</li></ul></li></ol>]]></content>
    
    
    <summary type="html">&lt;p&gt;本文是《准备》(作者：黛安娜・塔文纳)的阅读摘抄与个人感想，这本书重点介绍了美国萨米特公立学校的教书育人的方法，按照她的办学思想与模式，在美国已经有15所学校，成功受到多位知名的教育家联名推荐。&lt;/p&gt;</summary>
    
    
    
    
    <category term="读书笔记" scheme="https://www.toimc.com/tags/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
    
    <category term="教育" scheme="https://www.toimc.com/tags/%E6%95%99%E8%82%B2/"/>
    
  </entry>
  
  <entry>
    <title>nestjs搭建通用业务框架（7）：TypeORM进阶CURD操作</title>
    <link href="https://www.toimc.com/nestjs-example-project-7/"/>
    <id>https://www.toimc.com/nestjs-example-project-7/</id>
    <published>2021-03-24T16:31:59.000Z</published>
    <updated>2025-10-06T05:13:13.973Z</updated>
    
    <content type="html"><![CDATA[<p>这是《nestjs搭建通用业务框架》系列的第7篇，主要介绍了<code>nestjs</code>中的TypeORM的具体使用方案。两种情况：1. 有数据库设计的情况，使用<code>typeorm-model-generator</code>产生实体类；2. 无数据库的情况，可以手动创建实体类后，使用typeorm的<code>synchronize</code>属性，来同步创建表格；</p><span id="more"></span><h2 id="TypeORM上手"><a href="#TypeORM上手" class="headerlink" title="TypeORM上手"></a>TypeORM上手</h2><p>在前置的篇章中<a href="https://www.toimc.com/nestjs-example-project-5/">《nestjs搭建通用业务框架（5）：数据库+配置》</a>，我们有介绍如何集成TypeORM到我们的nest项目中。平时的使用，无非两种情况：</p><ol><li>我们清楚的知道数据库及表格设计，我们不想去建TypeORM的实体ts文件；</li><li>我们清楚的知道实体类的字段类型，我们不想去建数据库，全交给ORM；</li></ol><p>我们先来看第一种情况：</p><h3 id="如何产生typeORM的实体类"><a href="#如何产生typeORM的实体类" class="headerlink" title="如何产生typeORM的实体类"></a>如何产生typeORM的实体类</h3><h4 id="typeorm-model-generator："><a href="#typeorm-model-generator：" class="headerlink" title="typeorm-model-generator："></a><a href="https://github.com/Kononnable/typeorm-model-generator"><code>typeorm-model-generator</code></a>：</h4><ul><li><p>针对 <code>mysql</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npx typeorm-model-generator -h localhost -d your-mysql-db -u root -x your-mysql-password -e mssql -o .</span><br></pre></td></tr></table></figure></li><li><p>针对<code>progres</code>:</p><figure class="highlight llvm"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npx typeorm-model-generator -h localhost -d postgres -u postgres -<span class="keyword">x</span> <span class="title">!Passw0rd</span> -e postgres -o .</span><br></pre></td></tr></table></figure></li></ul><p>参数说明：</p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">Usage: typeorm-model-generator -h &lt;host&gt; -d &lt;database&gt; -<span class="selector-tag">p</span> <span class="selector-attr">[port]</span> -u &lt;user&gt; -x</span><br><span class="line"><span class="selector-attr">[password]</span> -e <span class="selector-attr">[engine]</span></span><br><span class="line"></span><br><span class="line">Options:</span><br><span class="line">  <span class="attr">--help</span>                 Show help                                     <span class="selector-attr">[boolean]</span></span><br><span class="line">  <span class="attr">--version</span>              Show version number                           <span class="selector-attr">[boolean]</span></span><br><span class="line">  -h, <span class="attr">--host</span>             IP address/Hostname <span class="keyword">for</span> database server</span><br><span class="line">                                                          <span class="selector-attr">[default: <span class="string">&quot;127.0.0.1&quot;</span>]</span></span><br><span class="line">  -d, <span class="attr">--database</span>         Database <span class="built_in">name</span>(or path <span class="keyword">for</span> sqlite)            <span class="selector-attr">[required]</span></span><br><span class="line">  -u, <span class="attr">--user</span>             Username <span class="keyword">for</span> database server</span><br><span class="line">  -x, <span class="attr">--pass</span>             Password <span class="keyword">for</span> database server              <span class="selector-attr">[default: <span class="string">&quot;&quot;</span>]</span></span><br><span class="line">  -<span class="selector-tag">p</span>, <span class="attr">--port</span>             Port number <span class="keyword">for</span> database server</span><br><span class="line">  -e, <span class="attr">--engine</span>           Database engine</span><br><span class="line">          <span class="selector-attr">[choices: <span class="string">&quot;mssql&quot;</span>, <span class="string">&quot;postgres&quot;</span>, <span class="string">&quot;mysql&quot;</span>, <span class="string">&quot;mariadb&quot;</span>, <span class="string">&quot;oracle&quot;</span>, <span class="string">&quot;sqlite&quot;</span>]</span></span><br><span class="line">                                                              <span class="selector-attr">[default: <span class="string">&quot;mssql&quot;</span>]</span></span><br><span class="line">  -o, <span class="attr">--output</span>           Where to place generated models</span><br><span class="line">                            <span class="selector-attr">[default: <span class="string">&quot;./output&quot;</span>]</span></span><br><span class="line">  -s, <span class="attr">--schema</span>           Schema name to create model from. Only <span class="keyword">for</span> mssql</span><br><span class="line">                         and postgres. You can pass multiple values</span><br><span class="line">                         separated by comma eg. -s scheme1,scheme2,scheme3</span><br><span class="line">  <span class="attr">--ssl</span>                                               <span class="selector-attr">[boolean]</span> <span class="selector-attr">[default: false]</span></span><br></pre></td></tr></table></figure><ul><li><code>-e</code> 对应的数据库的类型</li><li><code>-p</code>数据库服务器的端口</li><li><code>-d</code>数据库的名称</li><li><code>-u</code>数据库的用户名</li><li><code>-s</code>为<code>mssql</code>与<code>postgres</code>设计的一个参数，可以设置schema的名称</li></ul><p>比如，我们的项目中是这样用的：</p><p>由于事先已经建好了<code>users</code>这个表格，里面就只有<code>id</code>, <code>username</code>，<code>password</code>三个字段。</p><figure class="highlight llvm"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npx typeorm-model-generator -h localhost -d <span class="keyword">nest</span>-<span class="keyword">common</span> -u root -<span class="keyword">x</span> <span class="number">123456</span> -e mysql -o .</span><br></pre></td></tr></table></figure><p>产生三个文件:</p><figure class="highlight pgsql"><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><br><span class="line">├── entities</span><br><span class="line">│   └── Users.ts</span><br><span class="line">├── ormconfig.json</span><br><span class="line">└── tsconfig.json</span><br></pre></td></tr></table></figure><p>这里只需要这个文件<code>Users.ts</code>，然后大家可以把这个文件放置到<code>src/entities</code>或者其他的src的目录中，重启项目。</p><p><code>Users.ts</code>文件的内容：</p><figure class="highlight typescript"><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="keyword">import</span> &#123; <span class="title class_">Column</span>, <span class="title class_">Entity</span>, <span class="title class_">Index</span>, <span class="title class_">PrimaryGeneratedColumn</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;typeorm&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Index</span>(<span class="string">&#x27;username_Idx&#x27;</span>, [<span class="string">&#x27;username&#x27;</span>], &#123; <span class="attr">unique</span>: <span class="literal">true</span> &#125;)</span><br><span class="line"><span class="meta">@Entity</span>(<span class="string">&#x27;users&#x27;</span>, &#123; <span class="attr">schema</span>: <span class="string">&#x27;nest-common&#x27;</span> &#125;)</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">Users</span> &#123;</span><br><span class="line">  <span class="meta">@PrimaryGeneratedColumn</span>(&#123; <span class="attr">type</span>: <span class="string">&#x27;int&#x27;</span>, <span class="attr">name</span>: <span class="string">&#x27;id&#x27;</span> &#125;)</span><br><span class="line">  <span class="attr">id</span>: <span class="built_in">number</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Column</span>(<span class="string">&#x27;varchar&#x27;</span>, &#123; <span class="attr">name</span>: <span class="string">&#x27;username&#x27;</span>, <span class="attr">unique</span>: <span class="literal">true</span>, <span class="attr">length</span>: <span class="number">32</span> &#125;)</span><br><span class="line">  <span class="attr">username</span>: <span class="built_in">string</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Column</span>(<span class="string">&#x27;varchar&#x27;</span>, &#123; <span class="attr">name</span>: <span class="string">&#x27;password&#x27;</span>, <span class="attr">length</span>: <span class="number">255</span> &#125;)</span><br><span class="line">  <span class="attr">password</span>: <span class="built_in">string</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>后续，我们建议使用<code>nest g res [Module-Name]</code>的方式来进行创建基础的CURD + 文件结构。</p></blockquote><p>下面再来看第二种情况：</p><h3 id="关于配置entities路径"><a href="#关于配置entities路径" class="headerlink" title="关于配置entities路径"></a>关于配置<code>entities</code>路径</h3><p>推荐配置<code>src/config/devlopment.ts</code>：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> path <span class="keyword">from</span> <span class="string">&#x27;path&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> <span class="title function_">resolve</span> = (<span class="params">dir</span>) =&gt;</span><br><span class="line">  path.<span class="title function_">join</span>(path.<span class="title function_">resolve</span>(__dirname, <span class="string">&#x27;../../&#x27;</span>), dir);</span><br><span class="line"><span class="comment">// 这里大家可以打印一下路径</span></span><br><span class="line"><span class="comment">// __dirname： /Users/macos/Projects/nestjs/nestjs-common-template/dist/config</span></span><br><span class="line"><span class="comment">// 惊讶的发现，目录是dist目录中的config路径</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> config = &#123;</span><br><span class="line">  <span class="attr">db</span>: &#123;</span><br><span class="line">    <span class="comment">// https://typeorm.io/#/connection-options/common-connection-options</span></span><br><span class="line">    <span class="attr">synchronize</span>: <span class="literal">true</span>,</span><br><span class="line">    <span class="attr">logging</span>: <span class="literal">true</span>,</span><br><span class="line">    <span class="attr">host</span>: process.<span class="property">env</span>.<span class="property">DB_HOST</span> || <span class="string">&#x27;127.0.0.1&#x27;</span>,</span><br><span class="line">    <span class="attr">port</span>: process.<span class="property">env</span>.<span class="property">DB_PORT</span> || <span class="number">3306</span>,</span><br><span class="line">    <span class="attr">username</span>: process.<span class="property">env</span>.<span class="property">DB_USER</span> || <span class="string">&#x27;root&#x27;</span>,</span><br><span class="line">    <span class="attr">password</span>: process.<span class="property">env</span>.<span class="property">DB_PASSWORD</span> || <span class="string">&#x27;123456&#x27;</span>,</span><br><span class="line">    <span class="attr">database</span>: process.<span class="property">env</span>.<span class="property">DB_NAME</span> || <span class="string">&#x27;nest-common&#x27;</span>,</span><br><span class="line">    <span class="comment">// 这里对应的路径是 /Users/macos/Projects/nestjs/nestjs-common-template/dist/**/*.entity&#123;.ts,.js&#125;</span></span><br><span class="line">    <span class="comment">// 即 dist 目录中，所有以 .entity.ts 或者 .entity.js 结尾的文件</span></span><br><span class="line">    <span class="attr">entities</span>: [<span class="title function_">resolve</span>(<span class="string">&#x27;dist&#x27;</span>) + <span class="string">&#x27;/**/*.entity&#123;.ts,.js&#125;&#x27;</span>],</span><br><span class="line">    <span class="attr">extra</span>: &#123;</span><br><span class="line">      <span class="attr">connectionLimit</span>: <span class="number">30</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>这里几个有意思的属性：</p><ul><li><p><code>synchronize</code>：<code>true</code>代表会从<code>entities</code>属性产生SQL，并创建表格；</p><blockquote><p>如果，你的数据库中没有，那么则会创建表的SQL，并自动创建</p><p>测试：</p><ol><li>删除<code>users</code>表格</li><li>重启npm调试进程</li><li>刷新数据库，<code>users</code>表又回来了</li></ol></blockquote></li><li><p><code>logging</code>：<code>true</code>代表执行SQL会打印在控制台中</p></li><li><p><code>entities</code>：TypeORM读取的实体类的文件夹，及文件名。</p></li></ul><h3 id="快速创建CURD服务"><a href="#快速创建CURD服务" class="headerlink" title="快速创建CURD服务"></a>快速创建CURD服务</h3><h4 id="官方nest-g方案"><a href="#官方nest-g方案" class="headerlink" title="官方nest g方案"></a>官方<code>nest g</code>方案</h4><p>使用命令<code>nest g res users</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></pre></td><td class="code"><pre><span class="line">nest g res <span class="built_in">users</span></span><br><span class="line">? What transport layer <span class="keyword">do</span> you use? (Use arrow keys)</span><br><span class="line">❯ REST API </span><br><span class="line">  GraphQL (code first) </span><br><span class="line">  GraphQL (schema first) </span><br><span class="line">  Microservice (non-HTTP) </span><br><span class="line">  WebSockets </span><br><span class="line">? Would you like to generate CRUD entry points? (Y/n) </span><br><span class="line">CREATE src/users/users.controller.spec.ts (566 bytes)</span><br><span class="line">CREATE src/users/users.controller.ts (894 bytes)</span><br><span class="line">CREATE src/users/users.module.ts (247 bytes)</span><br><span class="line">CREATE src/users/users.service.spec.ts (453 bytes)</span><br><span class="line">CREATE src/users/users.service.ts (609 bytes)</span><br><span class="line">CREATE src/users/dto/create-user.dto.ts (30 bytes)</span><br><span class="line">CREATE src/users/dto/update-user.dto.ts (169 bytes)</span><br><span class="line">CREATE src/users/entities/user.entity.ts (21 bytes)</span><br><span class="line">UPDATE src/app.module.ts (1150 bytes)</span><br></pre></td></tr></table></figure><p>是不是很方便。</p><p>使用ORM步骤：</p><ol><li><p>打开<code>users/entities/user.entity.ts</code>，把之前生成的实体类加入进来，或者自己进行编辑。</p></li><li><p>修改<code>users/users.module.ts</code>，导入ORM：</p><figure class="highlight typescript"><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="keyword">import</span> &#123; <span class="title class_">Module</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/common&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">UsersService</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./users.service&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">UsersController</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./users.controller&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">Users</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./entities/user.entity&#x27;</span>;</span><br><span class="line"><span class="comment">// 这里导入</span></span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">TypeOrmModule</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/typeorm&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Module</span>(&#123;</span><br><span class="line">  <span class="comment">// 这里导入</span></span><br><span class="line">  <span class="attr">imports</span>: [<span class="title class_">TypeOrmModule</span>.<span class="title function_">forFeature</span>([<span class="title class_">Users</span>])],</span><br><span class="line">  <span class="attr">controllers</span>: [<span class="title class_">UsersController</span>],</span><br><span class="line">  <span class="attr">providers</span>: [<span class="title class_">UsersService</span>],</span><br><span class="line">&#125;)</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">UsersModule</span> &#123;&#125;</span><br></pre></td></tr></table></figure></li><li><p>修改<code>users/users.service.ts</code>文件，添加get请求，并通过ORM操作数据库：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ...</span></span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">InjectRepository</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/typeorm&#x27;</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@Injectable</span>()</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">UsersService</span> &#123;</span><br><span class="line">  <span class="title function_">constructor</span>(<span class="params"></span></span><br><span class="line"><span class="params">  <span class="comment">// 这里添加了Repository</span></span></span><br><span class="line"><span class="params">    <span class="meta">@InjectRepository</span>(Users)</span></span><br><span class="line"><span class="params">    <span class="keyword">private</span> <span class="keyword">readonly</span> usersRepository: Repository&lt;Users&gt;,</span></span><br><span class="line"><span class="params">  </span>) &#123;&#125;</span><br><span class="line">  </span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">  </span><br><span class="line">  <span class="keyword">async</span> <span class="title function_">findAll</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="comment">// 返回usres表中的所有数据</span></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">await</span> <span class="variable language_">this</span>.<span class="property">usersRepository</span>.<span class="title function_">find</span>(),</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>添加<code>users</code>一条信息，并测试：<code>localhost:3000/users</code> GET请求</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">[</span></span><br><span class="line">    <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;id&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;username&quot;</span><span class="punctuation">:</span> <span class="string">&quot;123&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;password&quot;</span><span class="punctuation">:</span> <span class="string">&quot;123&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">]</span></span><br></pre></td></tr></table></figure><p>直接返回一个<code>json</code>数组。</p></li></ol><h4 id="nestjsx方案"><a href="#nestjsx方案" class="headerlink" title="nestjsx方案"></a><code>nestjsx</code>方案</h4><ol><li><p>什么是<code>nestjsx</code>？</p><p>一个致力于开发nestjs的扩展的github组织。</p></li><li><p>为什么要用<code>nestjsx</code>?</p><p>官方：NestJS可能是几年前Node.js社区发生的最好的事情之一，为广泛的后端开发方面提供了一个真正重要的架构解决方案。但是，尽管它允许有效地创建RESTful应用程序，但缺少了一个重要的CRUD脚手架功能，而这个功能在许多其他HTTP框架中是存在的。这就是<code>Nestjsx/crud</code>问世的原因。</p></li><li><p><code>nestjsx</code>的各个包的作用是什么</p><p><code>@nestjsx/crud</code> - 核心包，它提供了<code>@Crud()</code>装饰器，用于端点生成、全局配置、验证、辅助装饰器（文档）。<br><code>@nestjsx/crud-typeorm</code> - TypeORM包，它为关系型数据库提供了带有<code>CRUD</code>数据库操作方法的基础<code>TypeOrmCrudService</code><br><code>@nestjsx/crud-request</code> - 请求生成器&#x2F;解析器包，它提供了用于前端的<code>RequestQueryBuilder</code>类和用于内部处理和验证后端查询&#x2F;路径参数的<code>RequestQueryParser</code></p></li></ol><p>上手使用：</p><figure class="highlight coffeescript"><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"><span class="built_in">npm</span> i @nestjsx/crud <span class="keyword">class</span>-transformer <span class="keyword">class</span>-validator</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用 TypeORM</span></span><br><span class="line"><span class="built_in">npm</span> i @nestjsx/crud-typeorm @nestjs/typeorm typeorm @nestjs/swagger</span><br></pre></td></tr></table></figure><p>修改<code>users.service.ts</code>：</p><figure class="highlight typescript"><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="keyword">import</span> &#123; <span class="title class_">Injectable</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/common&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">InjectRepository</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/typeorm&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">TypeOrmCrudService</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjsx/crud-typeorm&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">Users</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./entities/user.entity&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Injectable</span>()</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">UsersService</span> <span class="keyword">extends</span> <span class="title class_ inherited__">TypeOrmCrudService</span>&lt;<span class="title class_">Users</span>&gt; &#123;</span><br><span class="line">  <span class="title function_">constructor</span>(<span class="params"><span class="meta">@InjectRepository</span>(Users) repo</span>) &#123;</span><br><span class="line">    <span class="variable language_">super</span>(repo);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>修改<code>users.controller.ts</code>：</p><figure class="highlight typescript"><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="keyword">import</span> &#123; <span class="title class_">Controller</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/common&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">UsersService</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./users.service&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">Users</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./entities/user.entity&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">Crud</span>, <span class="title class_">CrudController</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjsx/crud&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Crud</span>(&#123;</span><br><span class="line">  <span class="attr">model</span>: &#123;</span><br><span class="line">    <span class="attr">type</span>: <span class="title class_">Users</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;)</span><br><span class="line"><span class="meta">@Controller</span>(<span class="string">&#x27;users&#x27;</span>)</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">UsersController</span> <span class="keyword">implements</span> <span class="title class_">CrudController</span>&lt;<span class="title class_">Users</span>&gt; &#123;</span><br><span class="line">  <span class="title function_">constructor</span>(<span class="params"><span class="keyword">public</span> service: UsersService</span>) &#123;&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>再次请求<code>localhost:3000/users</code>，结果与之前的相同。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;这是《nestjs搭建通用业务框架》系列的第7篇，主要介绍了&lt;code&gt;nestjs&lt;/code&gt;中的TypeORM的具体使用方案。两种情况：1. 有数据库设计的情况，使用&lt;code&gt;typeorm-model-generator&lt;/code&gt;产生实体类；2. 无数据库的情况，可以手动创建实体类后，使用typeorm的&lt;code&gt;synchronize&lt;/code&gt;属性，来同步创建表格；&lt;/p&gt;</summary>
    
    
    
    <category term="nestjs搭建通用业务框架" scheme="https://www.toimc.com/categories/nestjs%E6%90%AD%E5%BB%BA%E9%80%9A%E7%94%A8%E4%B8%9A%E5%8A%A1%E6%A1%86%E6%9E%B6/"/>
    
    
    <category term="node.js" scheme="https://www.toimc.com/tags/node-js/"/>
    
    <category term="nestjs" scheme="https://www.toimc.com/tags/nestjs/"/>
    
    <category term="web框架" scheme="https://www.toimc.com/tags/web%E6%A1%86%E6%9E%B6/"/>
    
  </entry>
  
  <entry>
    <title>nestjs搭建通用业务框架（6）：高性能框架fastify</title>
    <link href="https://www.toimc.com/nestjs-example-project-6/"/>
    <id>https://www.toimc.com/nestjs-example-project-6/</id>
    <published>2021-03-23T14:04:59.000Z</published>
    <updated>2025-10-06T05:13:13.973Z</updated>
    
    <content type="html"><![CDATA[<p>这是《nestjs搭建通用业务框架》系列的第6篇，本篇带领大家升级nestjs中默认的web服务器Express，改成高性能的 fastify。</p><span id="more"></span><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本篇主要的目的，是搭建一个高性能的web服务器，便于后续开展通用业务（鉴权）的开发。</p><h2 id="深入fastify"><a href="#深入fastify" class="headerlink" title="深入fastify"></a>深入<code>fastify</code></h2><p>以下列出的是 Fastify 已经实现的主要功能及原理：</p><ul><li><strong>高性能：</strong> 据我们所知，Fastify 是这一领域中最快的 web 框架之一，另外，取决于代码的复杂性，Fastify 最多可以处理每秒 3 万次的请求。</li><li><strong>可扩展：</strong> Fastify 通过其提供的钩子（hook）、插件和装饰器（decorator）提供完整的可扩展性。</li><li><strong>基于 Schema：</strong> 即使这不是强制性的，我们仍建议使用 <a href="http://json-schema.org/">JSON Schema</a> 来做路由（route）验证及输出内容的序列化，Fastify 在内部将 schema 编译为高效的函数并执行。</li><li><strong>日志：</strong> 日志是非常重要且代价高昂的。我们选择了最好的日志记录程序来尽量消除这一成本，这就是 <a href="https://github.com/pinojs/pino">Pino</a>!</li><li><strong>对开发人员友好：</strong> 框架的使用很友好，帮助开发人员处理日常工作，并且不牺牲性能和安全性。</li><li><strong>支持 TypeScript ：</strong> 我们努力维护一个 <a href="https://www.typescriptlang.org/">TypeScript</a> 类型声明文件，以便支持不断成长的 TypeScript 社区。</li></ul><p>学习资料，推荐<a href="https://www.fastify.cn/">fastify中文网</a></p><h2 id="改良性能"><a href="#改良性能" class="headerlink" title="改良性能"></a>改良性能</h2><p>在阅读与实践本部分的时候，一定要清晰的清楚自己的目标：提升接口的性能。如果，在已经成型的nest(Express)项目中，如果要进行Express -&gt; Fastify的操作，一定要注意Express的中间件与Fastify可能不兼容，会导致切换失败。所以，在最初设计框架的时候就应该考虑到两个框架的优缺点，来进行选择。</p><table><thead><tr><th>框架</th><th>优点</th><th>缺点</th></tr></thead><tbody><tr><td>Express</td><td>文档丰富，生态丰富</td><td>性能相对较差，笨重</td></tr><tr><td>Fastify</td><td>性能优，轻量</td><td>学习与使用成本</td></tr></tbody></table><blockquote><p>总结：Fastify对于初学者来说，学习成本与Express一致，所以建立直接上手。Express对于熟悉http模块的同学来说，上手成本低。对于性能有要求的场景，Fastify是大家不二的选择。</p></blockquote><p>在nest中默认会使用Express框架，作为web服务器的底层服务。为了提升性能的需求，nest提供了其他库的兼容性，例如<a href="https://www.fastify.io/">Fastify</a></p><p>Fastify的性能是优于Express的，见下表：</p><p><strong>测试环境</strong>： EX41S-SSD, Intel Core i7, 4Ghz, 64GB RAM, 4C&#x2F;8T, SSD.</p><p><strong>方法</strong>：<code>autocannon -c 100 -d 40 -p 10 localhost:3000</code> * 2, taking the second average</p><table><thead><tr><th>框架</th><th>版本</th><th>是否有路由功能?</th><th>请求&#x2F;秒</th></tr></thead><tbody><tr><td>Express</td><td>4.17.1</td><td>✓</td><td>15,978</td></tr><tr><td>hapi</td><td>19.1.0</td><td>✓</td><td>45,815</td></tr><tr><td>Restify</td><td>8.5.1</td><td>✓</td><td>49,279</td></tr><tr><td>Koa</td><td>2.13.0</td><td>✗</td><td>54,848</td></tr><tr><td><strong>Fastify</strong></td><td><strong>3.0.0</strong></td><td><strong>✓</strong></td><td><strong>78,956</strong></td></tr><tr><td>-</td><td></td><td></td><td></td></tr><tr><td><code>http.Server</code></td><td>12.18.2</td><td>✗</td><td>70,380</td></tr></tbody></table><p>数据测试库<a href="https://github.com/fastify/benchmarks">fastify&#x2F;benchmarks</a></p><p>集成步骤：</p><ol><li><p>安装依赖<code>@nestjs/platform-fastify</code></p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm <span class="selector-tag">i</span> <span class="attr">--save</span> <span class="keyword">@nestjs</span>/platform-fastify</span><br></pre></td></tr></table></figure></li><li><p>配置适配器</p><p>安装 fastify 后，我们可以使用 <code>FastifyAdapter</code>，修改<code>src/main.ts</code>文件</p><figure class="highlight typescript"><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="keyword">import</span> &#123; <span class="title class_">NestFactory</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/core&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">FastifyAdapter</span>, <span class="title class_">NestFastifyApplication</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/platform-fastify&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">ApplicationModule</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./app.module&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">bootstrap</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> app = <span class="keyword">await</span> <span class="title class_">NestFactory</span>.<span class="property">create</span>&lt;<span class="title class_">NestFastifyApplication</span>&gt;(<span class="title class_">ApplicationModule</span>, <span class="keyword">new</span> <span class="title class_">FastifyAdapter</span>());</span><br><span class="line">  <span class="comment">// 默认情况下，`Fastify`仅在 `localhost 127.0.0.1` 接口上监听</span></span><br><span class="line">  <span class="comment">// 改成 0.0.0.0 接受其他主机上的连接</span></span><br><span class="line">  <span class="keyword">await</span> app.<span class="title function_">listen</span>(<span class="number">3000</span>, <span class="string">&#x27;0.0.0.0&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="title function_">bootstrap</span>();</span><br></pre></td></tr></table></figure></li><li><p>运行<code>npm run start:dev</code>，打开<code>localhost:3000</code>验证服务是否OK。</p></li></ol><p>看<code>Hello world</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></pre></td><td class="code"><pre><span class="line">[下午8:43:23] File change detected. Starting incremental compilation...</span><br><span class="line"></span><br><span class="line">[下午8:43:23] Found 0 errors. Watching <span class="keyword">for</span> file changes.</span><br><span class="line"></span><br><span class="line">[Nest] 4682   - 2021/03/24 下午8:43:24   [NestFactory] Starting Nest application...</span><br><span class="line">[Nest] 4682   - 2021/03/24 下午8:43:24   [InstanceLoader] DatabaseModule dependencies initialized +44ms</span><br><span class="line">[Nest] 4682   - 2021/03/24 下午8:43:24   [InstanceLoader] TypeOrmModule dependencies initialized +1ms</span><br><span class="line">[Nest] 4682   - 2021/03/24 下午8:43:24   [InstanceLoader] ConfigHostModule dependencies initialized +0ms</span><br><span class="line">[Nest] 4682   - 2021/03/24 下午8:43:24   [InstanceLoader] AppModule dependencies initialized +0ms</span><br><span class="line">[Nest] 4682   - 2021/03/24 下午8:43:24   [InstanceLoader] ConfigModule dependencies initialized +1ms</span><br><span class="line">[Nest] 4682   - 2021/03/24 下午8:43:24   [InstanceLoader] ConfigModule dependencies initialized +0ms</span><br><span class="line">[Nest] 4682   - 2021/03/24 下午8:43:24   [InstanceLoader] ConfigModule dependencies initialized +0ms</span><br><span class="line">query: START TRANSACTION</span><br><span class="line">query: SELECT * FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = <span class="string">&#x27;nest-common&#x27;</span> AND `TABLE_NAME` = <span class="string">&#x27;typeorm_metadata&#x27;</span></span><br><span class="line">query: COMMIT</span><br><span class="line">[Nest] 4682   - 2021/03/24 下午8:43:25   [InstanceLoader] TypeOrmCoreModule dependencies initialized +118ms</span><br><span class="line">[Nest] 4682   - 2021/03/24 下午8:43:25   [RoutesResolver] AppController &#123;&#125;: +8ms</span><br><span class="line">[Nest] 4682   - 2021/03/24 下午8:43:25   [RouterExplorer] Mapped &#123;, GET&#125; route +3ms</span><br><span class="line">[Nest] 4682   - 2021/03/24 下午8:43:25   [NestApplication] Nest application successfully started +3ms</span><br></pre></td></tr></table></figure><p>就OK了。</p><h2 id="关于路径别名"><a href="#关于路径别名" class="headerlink" title="关于路径别名"></a>关于路径别名</h2><p>我们常见的用法，比如：<code>@</code>代表<code>src</code></p><p>在nest项目中配置路径别名比较简单，可以在<code>tsconfig.json</code>中添加<code>paths</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;compilerOptions&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></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="attr">&quot;paths&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">[</span></span><br><span class="line">        <span class="string">&quot;src/*&quot;</span></span><br><span class="line">      <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>然后，重新使用<code>npm run start:dev</code>进行测试即可。</p><p>后面，项目目录再深，也好写文件路径了，再也不用看到<code>../../../</code>无穷无尽的关联路径了。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;这是《nestjs搭建通用业务框架》系列的第6篇，本篇带领大家升级nestjs中默认的web服务器Express，改成高性能的 fastify。&lt;/p&gt;</summary>
    
    
    
    <category term="nestjs搭建通用业务框架" scheme="https://www.toimc.com/categories/nestjs%E6%90%AD%E5%BB%BA%E9%80%9A%E7%94%A8%E4%B8%9A%E5%8A%A1%E6%A1%86%E6%9E%B6/"/>
    
    
    <category term="node.js" scheme="https://www.toimc.com/tags/node-js/"/>
    
    <category term="nestjs" scheme="https://www.toimc.com/tags/nestjs/"/>
    
    <category term="web框架" scheme="https://www.toimc.com/tags/web%E6%A1%86%E6%9E%B6/"/>
    
  </entry>
  
  <entry>
    <title>使用docker&amp;docker-compose创建常见的数据库(MySQL/Postgres/MongoDB)</title>
    <link href="https://www.toimc.com/use-docker-install-common-database/"/>
    <id>https://www.toimc.com/use-docker-install-common-database/</id>
    <published>2021-03-20T01:42:58.000Z</published>
    <updated>2025-10-06T05:13:13.973Z</updated>
    
    <content type="html"><![CDATA[<p>本文介绍了常见的数据库MySQL、Postgre、MongoDB使用docker安装方式快速安装。</p><span id="more"></span><h2 id="MySQL"><a href="#MySQL" class="headerlink" title="MySQL"></a>MySQL</h2><h3 id="docker-run命令"><a href="#docker-run命令" class="headerlink" title="docker run命令"></a>docker run命令</h3><p>配置了utf8的格式与utf8-bin的编码：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker run -itd --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -p 3306:3306 -v /my/own/datadir:/var/lib/mysql lw96/mysql5.7</span><br></pre></td></tr></table></figure><p>参数说明：</p><ul><li><code>some-mysql</code>容器名称</li><li><code>MYSQL_ROOT_PASSWORD</code>设置的root用户的密码</li><li><code>-p</code>是指定<code>宿主端口:容器内服务端口</code>，默认<code>3306</code></li><li><code>lw96.mysql5.7</code>这个是我自己封装的MySQL镜像，设置好了utf8的编码</li><li><code>-v</code>设置数据库文件的映射，把宿主机上的目录<code>/my/own/datadir</code>与容器内的<code>/var/lib/mysql</code>作关联关系</li></ul><h3 id="使用docker-compose"><a href="#使用docker-compose" class="headerlink" title="使用docker-compose"></a>使用<code>docker-compose</code></h3><p>如果是<code>docker-compose.yml</code>脚本：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">version:</span> <span class="string">&#x27;3&#x27;</span></span><br><span class="line"><span class="attr">services:</span></span><br><span class="line">  <span class="attr">mysql:</span></span><br><span class="line">    <span class="attr">image:</span> <span class="string">lw96/mysql5.7</span></span><br><span class="line">    <span class="attr">container_name:</span> <span class="string">&#x27;some-mysql&#x27;</span></span><br><span class="line">    <span class="attr">restart:</span> <span class="string">always</span></span><br><span class="line">    <span class="attr">environment:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">MYSQL_ROOT_PASSWORD=my-secret-pw</span></span><br><span class="line">    <span class="attr">volumes:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">/my/own/datadir:/var/lib/mysql</span></span><br><span class="line">    <span class="attr">ports:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&quot;3306:3306&quot;</span></span><br></pre></td></tr></table></figure><h2 id="Progres"><a href="#Progres" class="headerlink" title="Progres"></a>Progres</h2><h3 id="docker-run命令-1"><a href="#docker-run命令-1" class="headerlink" title="docker run命令"></a>docker run命令</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">docker run -itd --name postgres_container --restart=unless-stopped -p 5432:5432 -v <span class="variable">$pwd</span>/postgres:/data/postgres -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=changeme -e PGDATA=/data/postgres postgres</span><br></pre></td></tr></table></figure><p>参数说明：</p><ul><li><code>postgres_container</code>容器名称</li><li><code>-p</code>是指定<code>宿主端口:容器内服务端口</code>，默认<code>5432</code></li><li><code>POSTGRES_USER</code>数据库用户，<code>POSTGRES_PASSWORD</code>数据库密码，<code>PGDATA</code>数据库在容器内的数据路径</li><li><code>-v</code>设置数据库文件的映射，把宿主机上的目录<code>$pwd/postgres</code>与容器内的<code>/data/postgres</code>作关联关系</li></ul><h3 id="使用docker-compose-1"><a href="#使用docker-compose-1" class="headerlink" title="使用docker-compose"></a>使用<code>docker-compose</code></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><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">version:</span> <span class="string">&#x27;3.5&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="attr">services:</span></span><br><span class="line">  <span class="attr">postgres:</span></span><br><span class="line">    <span class="attr">container_name:</span> <span class="string">postgres_container</span></span><br><span class="line">    <span class="attr">image:</span> <span class="string">postgres</span></span><br><span class="line">    <span class="attr">environment:</span></span><br><span class="line">      <span class="attr">POSTGRES_USER:</span> <span class="string">$&#123;POSTGRES_USER:-postgres&#125;</span></span><br><span class="line">      <span class="attr">POSTGRES_PASSWORD:</span> <span class="string">$&#123;POSTGRES_PASSWORD:-changeme&#125;</span></span><br><span class="line">      <span class="attr">PGDATA:</span> <span class="string">/data/postgres</span></span><br><span class="line">    <span class="attr">volumes:</span></span><br><span class="line">       <span class="bullet">-</span> <span class="string">postgres:/data/postgres</span></span><br><span class="line">    <span class="attr">ports:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&quot;5432:5432&quot;</span></span><br><span class="line">    <span class="attr">networks:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">postgres</span></span><br><span class="line">    <span class="attr">restart:</span> <span class="string">unless-stopped</span></span><br><span class="line">  </span><br><span class="line">  <span class="attr">pgadmin:</span></span><br><span class="line">    <span class="attr">container_name:</span> <span class="string">pgadmin_container</span></span><br><span class="line">    <span class="attr">image:</span> <span class="string">dpage/pgadmin4</span></span><br><span class="line">    <span class="attr">environment:</span></span><br><span class="line">      <span class="attr">PGADMIN_DEFAULT_EMAIL:</span> <span class="string">$&#123;PGADMIN_DEFAULT_EMAIL:-pgadmin4@pgadmin.org&#125;</span></span><br><span class="line">      <span class="attr">PGADMIN_DEFAULT_PASSWORD:</span> <span class="string">$&#123;PGADMIN_DEFAULT_PASSWORD:-admin&#125;</span></span><br><span class="line">      <span class="attr">PGADMIN_CONFIG_SERVER_MODE:</span> <span class="string">&#x27;False&#x27;</span></span><br><span class="line">    <span class="attr">volumes:</span></span><br><span class="line">       <span class="bullet">-</span> <span class="string">pgadmin:/root/.pgadmin</span></span><br><span class="line"></span><br><span class="line">    <span class="attr">ports:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&quot;$&#123;PGADMIN_PORT:-5050&#125;:80&quot;</span></span><br><span class="line">    <span class="attr">networks:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">postgres</span></span><br><span class="line">    <span class="attr">restart:</span> <span class="string">unless-stopped</span></span><br><span class="line"></span><br><span class="line"><span class="attr">networks:</span></span><br><span class="line">  <span class="attr">postgres:</span></span><br><span class="line">    <span class="attr">driver:</span> <span class="string">bridge</span></span><br><span class="line"></span><br><span class="line"><span class="attr">volumes:</span></span><br><span class="line">    <span class="attr">postgres:</span></span><br><span class="line">    <span class="attr">pgadmin:</span></span><br></pre></td></tr></table></figure><p>上面的配置，会默认启动一个web的管理界面<code>pgadmin</code>，用于管理<code>postgres</code>数据。</p><ul><li>访问postgres，使用navicat填入如下的信息：<br><code>localhost:5432</code><br>用户名: <code>postgres</code> (默认)<br>密码：<code>changeme</code>（默认）</li><li>或者使用web浏览器进入PgAdmin<br>网址：<a href="http://localhost:5050/">http://localhost:5050</a><br>用户名: <a href="mailto:&#x70;&#103;&#97;&#100;&#x6d;&#x69;&#x6e;&#52;&#x40;&#112;&#x67;&#x61;&#100;&#109;&#105;&#110;&#46;&#111;&#x72;&#x67;">&#x70;&#103;&#97;&#100;&#x6d;&#x69;&#x6e;&#52;&#x40;&#112;&#x67;&#x61;&#100;&#109;&#105;&#110;&#46;&#111;&#x72;&#x67;</a> (默认)<br>密码：<code>admin</code>（默认)</li></ul><h2 id="MongoDB"><a href="#MongoDB" class="headerlink" title="MongoDB"></a>MongoDB</h2><h3 id="docker-run命令-2"><a href="#docker-run命令-2" class="headerlink" title="docker run命令"></a>docker run命令</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">docker run -itd --name mongodb -p 27017:27017 -v /data/db:/data/db --restart=always -e MONGO_INITDB_ROOT_USERNAME=root -e MONGO_INITDB_ROOT_PASSWORD=changeme mongo</span><br></pre></td></tr></table></figure><p>参数说明：</p><ul><li><p><code>mongodb</code>容器名称</p></li><li><p><code>-p</code>是指定<code>宿主端口:容器内服务端口</code>，默认<code>27017</code></p></li><li><p><code>-v</code>设置数据库文件的映射</p></li><li><p><code>MONGO_INITDB_ROOT_USERNAME</code>数据库初始的管理员用户名，<code>MONGO_INITDB_ROOT_PASSWORD</code>初始的管理员密码</p></li></ul><h3 id="使用docker-compose-2"><a href="#使用docker-compose-2" class="headerlink" title="使用docker-compose"></a>使用<code>docker-compose</code></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></pre></td><td class="code"><pre><span class="line"><span class="attr">version:</span> <span class="string">&#x27;3&#x27;</span></span><br><span class="line"><span class="attr">services:</span></span><br><span class="line">  <span class="attr">mongo:</span></span><br><span class="line">    <span class="attr">image:</span> <span class="string">mongo</span></span><br><span class="line">    <span class="attr">container_name:</span> <span class="string">&#x27;mongodb&#x27;</span></span><br><span class="line">    <span class="attr">restart:</span> <span class="string">always</span></span><br><span class="line">    <span class="attr">environment:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">MONGO_INITDB_ROOT_USERNAME=root</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">MONGO_INITDB_ROOT_PASSWORD=changeme</span></span><br><span class="line">    <span class="attr">volumes:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">/home/mongo/data/db:/data/db</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">/etc/localtime:/etc/localtime</span></span><br><span class="line">    <span class="attr">ports:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&quot;27017:27017&quot;</span></span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;本文介绍了常见的数据库MySQL、Postgre、MongoDB使用docker安装方式快速安装。&lt;/p&gt;</summary>
    
    
    
    <category term="Docker入门" scheme="https://www.toimc.com/categories/Docker%E5%85%A5%E9%97%A8/"/>
    
    
    <category term="mysql" scheme="https://www.toimc.com/tags/mysql/"/>
    
    <category term="docker" scheme="https://www.toimc.com/tags/docker/"/>
    
    <category term="progres" scheme="https://www.toimc.com/tags/progres/"/>
    
    <category term="mongoDB" scheme="https://www.toimc.com/tags/mongoDB/"/>
    
  </entry>
  
  <entry>
    <title>hexo主题next中gitalk配置与评论初始化</title>
    <link href="https://www.toimc.com/hexo-usage-3/"/>
    <id>https://www.toimc.com/hexo-usage-3/</id>
    <published>2021-03-19T09:41:16.000Z</published>
    <updated>2025-10-06T05:13:13.973Z</updated>
    
    <content type="html"><![CDATA[<p>本文的环境是hexo 5.x版本，next 8.x主题，介绍在next主题中如何配置gitalk与评论初始化，缓存issues初始化结果，加速初始化过程，解决问题：“未找到相关的 Issues 进行评论，请联系xxx初始化创建”和“Request failed with status code 403”。</p><span id="more"></span><p>基本的流程：</p><ul><li>申请Github账号 —— OAuth应用</li><li>配置gitalk</li><li>申请Github - Token</li><li>配置自动化脚本</li><li>配置nginx（解决403问题）</li><li>测试</li></ul><h2 id="next配置gitalk"><a href="#next配置gitalk" class="headerlink" title="next配置gitalk"></a><code>next</code>配置gitalk</h2><p>在next项目<code>_config.yml</code>中，已经有明确的配置项：</p><p>第一个地方，需要注意配置gitalk与其他评论工具的顺序以及默认的评论工具。</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Multiple Comment System Support</span></span><br><span class="line"><span class="attr">comments:</span></span><br><span class="line">  <span class="comment"># Available values: tabs | buttons</span></span><br><span class="line">  <span class="attr">style:</span> <span class="string">tabs</span></span><br><span class="line">  <span class="comment"># Choose a comment system to be displayed by default.</span></span><br><span class="line">  <span class="comment"># Available values: disqus | disqusjs | changyan | livere | gitalk | utterances</span></span><br><span class="line">  <span class="attr">active:</span> <span class="string">gitalk</span></span><br><span class="line">  <span class="comment"># Setting `true` means remembering the comment system selected by the visitor.</span></span><br><span class="line">  <span class="attr">storage:</span> <span class="literal">true</span></span><br><span class="line">  <span class="comment"># Lazyload all comment systems.</span></span><br><span class="line">  <span class="attr">lazyload:</span> <span class="literal">false</span></span><br><span class="line">  <span class="comment"># Modify texts or order for any navs, here are some examples.</span></span><br><span class="line">  <span class="attr">nav:</span></span><br><span class="line">    <span class="attr">gitalk:</span></span><br><span class="line">     <span class="attr">order:</span> <span class="number">-2</span></span><br><span class="line">    <span class="attr">disqusjs:</span></span><br><span class="line">     <span class="attr">text:</span> <span class="string">Load</span> <span class="string">Disqus</span></span><br><span class="line">     <span class="attr">order:</span> <span class="number">-1</span></span><br></pre></td></tr></table></figure><p>第二个地方：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Gitalk</span></span><br><span class="line"><span class="comment"># For more information: https://gitalk.github.io</span></span><br><span class="line"><span class="attr">gitalk:</span></span><br><span class="line">  <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line">  <span class="attr">github_id:</span> <span class="string">toimc</span> <span class="comment"># GitHub repo owner</span></span><br><span class="line">  <span class="attr">repo:</span> <span class="string">toimc.github.io</span> <span class="comment"># Repository name to store issues</span></span><br><span class="line">  <span class="attr">client_id:</span> <span class="string">c2d14f07d902bf339af2</span> <span class="comment"># GitHub Application Client ID</span></span><br><span class="line">  <span class="attr">client_secret:</span> <span class="string">a41a05429a0ca340152b13fccx48245674437ba7</span> <span class="comment"># GitHub Application Client Secret</span></span><br><span class="line">  <span class="attr">admin_user:</span> <span class="string">toimc</span> <span class="comment"># GitHub repo owner and collaborators, only these guys can initialize gitHub issues</span></span><br><span class="line">  <span class="attr">distraction_free_mode:</span> <span class="literal">true</span> <span class="comment"># Facebook-like distraction free mode</span></span><br><span class="line">  <span class="comment"># When the official proxy is not available, you can change it to your own proxy address</span></span><br><span class="line">  <span class="attr">proxy:</span> <span class="string">/login/oauth/access_token</span> <span class="comment"># This is official proxy adress</span></span><br><span class="line">  <span class="comment"># proxy: https://cors-anywhere.herokuapp.com/https://github.com/login/oauth/access_token # This is official proxy adress</span></span><br><span class="line">  <span class="comment"># Gitalk&#x27;s display language depends on user&#x27;s browser or system environment</span></span><br><span class="line">  <span class="comment"># If you want everyone visiting your site to see a uniform language, you can set a force language value</span></span><br><span class="line">  <span class="comment"># Available values: en | es-ES | fr | ru | zh-CN | zh-TW</span></span><br><span class="line">  <span class="attr">language:</span> <span class="string">zh-CN</span></span><br></pre></td></tr></table></figure><p>上面的配置项中有几个需要说明的：</p><ul><li><code>client_id</code>与<code>client_secret</code>是Github 的 OAuth 认证（下面会介绍）</li><li><code>github_id</code>与<code>admin_user</code>这里建议填一样，填成自己的github账号名（不是邮箱）</li><li><code>proxy</code>默认即是上面的地址，它其实会回调到这里<code>https://github.com/login/oauth/access_token</code>来，大家出403的问题，主要是由于跨域。</li></ul><h2 id="Github的OAuth-认证"><a href="#Github的OAuth-认证" class="headerlink" title="Github的OAuth 认证"></a>Github的OAuth 认证</h2><p>前提是自己得有一个<a href="https://github.com/">github</a>账号，才能注册OAuth application，<a href="https://github.com/settings/applications/new">OAuth应用注册地址</a></p><p><img src="https://static.www.toimc.com/blog/img/2021/hexo-usage-3/blog-1.jpg?watermark/2/text/VG9pbWPlh7rlk4E=/fontsize/200"></p><p><img src="https://static.www.toimc.com/blog/img/2021/hexo-usage-3/blog-2.jpg?watermark/2/text/VG9pbWPlh7rlk4E=/fontsize/200"></p><h2 id="gitalk自动初始化"><a href="#gitalk自动初始化" class="headerlink" title="gitalk自动初始化"></a>gitalk自动初始化</h2><p>原理：通过sitemap中的信息，请求github开放api达到自动产生issues的目的</p><p>基本的要求：github API需要请求token</p><h3 id="申请github-Token"><a href="#申请github-Token" class="headerlink" title="申请github Token"></a>申请github Token</h3><p>我们需要使用 Personal access tokens 方式，这种方式限制每小时 5000 次，结合缓存功能，基本满足需求。</p><p>从 Github 的 <a href="https://github.com/settings/tokens">Personal access tokens</a> 页面，点击 <a href="https://github.com/settings/tokens/new">Generate new token</a></p><p><img src="https://static.www.toimc.com/blog/img/2021/hexo-usage-3/blog-3.jpg?watermark/2/text/VG9pbWPlh7rlk4E=/fontsize/200"></p><h3 id="安装npm依赖项"><a href="#安装npm依赖项" class="headerlink" title="安装npm依赖项"></a>安装npm依赖项</h3><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></pre></td><td class="code"><pre><span class="line"><span class="attr">&quot;hexo-generator-sitemap&quot;</span><span class="punctuation">:</span> <span class="string">&quot;^2.1.0&quot;</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line"><span class="attr">&quot;md5&quot;</span><span class="punctuation">:</span> <span class="string">&quot;^2.3.0&quot;</span><span class="punctuation">,</span></span><br><span class="line"><span class="attr">&quot;request&quot;</span><span class="punctuation">:</span> <span class="string">&quot;^2.88.2&quot;</span><span class="punctuation">,</span></span><br><span class="line"><span class="attr">&quot;xml-parser&quot;</span><span class="punctuation">:</span> <span class="string">&quot;^1.2.1&quot;</span></span><br></pre></td></tr></table></figure><p>安装命令：</p><figure class="highlight coffeescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">npm</span> i -D md5 moment request xml-parser</span><br><span class="line"></span><br><span class="line"><span class="built_in">npm</span> i -S hexo-generator-sitemap</span><br></pre></td></tr></table></figure><h3 id="配置sitemap"><a href="#配置sitemap" class="headerlink" title="配置sitemap"></a>配置sitemap</h3><h4 id="在根目录中创建sitemap-template-xml"><a href="#在根目录中创建sitemap-template-xml" class="headerlink" title="在根目录中创建sitemap_template.xml"></a>在根目录中创建<code>sitemap_template.xml</code></h4><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">urlset</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.sitemaps.org/schemas/sitemap/0.9&quot;</span>&gt;</span></span><br><span class="line">  &#123;% for post in posts %&#125;</span><br><span class="line">  <span class="tag">&lt;<span class="name">url</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">loc</span>&gt;</span>&#123;&#123; post.permalink | uriencode &#125;&#125;<span class="tag">&lt;/<span class="name">loc</span>&gt;</span></span><br><span class="line">    &#123;% if post.updated %&#125;</span><br><span class="line">    <span class="tag">&lt;<span class="name">lastmod</span>&gt;</span>&#123;&#123; post.updated.toISOString() &#125;&#125;<span class="tag">&lt;/<span class="name">lastmod</span>&gt;</span></span><br><span class="line">    &#123;% elif post.date %&#125;</span><br><span class="line">    <span class="tag">&lt;<span class="name">lastmod</span>&gt;</span>&#123;&#123; post.date.toISOString() &#125;&#125;<span class="tag">&lt;/<span class="name">lastmod</span>&gt;</span></span><br><span class="line">    &#123;% endif %&#125;</span><br><span class="line">    <span class="tag">&lt;<span class="name">date</span>&gt;</span>&#123;&#123; post.date &#125;&#125;<span class="tag">&lt;/<span class="name">date</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>&#123;&#123; post.title + &#x27; | &#x27; + config.title &#125;&#125;<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line">    &#123;# nunjucks 模版语法 https://github.com/mozilla/nunjucks #&#125;</span><br><span class="line">    <span class="tag">&lt;<span class="name">desc</span>&gt;</span>&#123;&#123; post.description | default(post.excerpt) | default(post.content) | default(config.description) | striptags | truncate(200, true, &#x27;&#x27;) &#125;&#125;<span class="tag">&lt;/<span class="name">desc</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">  &#123;% endfor %&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">urlset</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="修改根-config-yml"><a href="#修改根-config-yml" class="headerlink" title="修改根_config.yml"></a>修改根<code>_config.yml</code></h4><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">sitemap:</span></span><br><span class="line">  <span class="attr">path:</span> <span class="string">sitemap.xml</span></span><br><span class="line">  <span class="attr">template:</span> <span class="string">./sitemap_template.xml</span></span><br><span class="line">  <span class="attr">rel:</span> <span class="literal">false</span></span><br><span class="line">  <span class="attr">tag:</span> <span class="literal">true</span></span><br><span class="line">  <span class="attr">category:</span> <span class="literal">false</span></span><br></pre></td></tr></table></figure><h4 id="生成sitemap-xml文件"><a href="#生成sitemap-xml文件" class="headerlink" title="生成sitemap.xml文件"></a>生成<code>sitemap.xml</code>文件</h4><figure class="highlight verilog"><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 clean</span><br><span class="line">hexo <span class="keyword">generate</span></span><br></pre></td></tr></table></figure><h3 id="根目录添加talk-auto-init-js"><a href="#根目录添加talk-auto-init-js" class="headerlink" title="根目录添加talk-auto-init.js"></a>根目录添加<code>talk-auto-init.js</code></h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">&#x27;fs&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">&#x27;path&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> url = <span class="built_in">require</span>(<span class="string">&#x27;url&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> request = <span class="built_in">require</span>(<span class="string">&#x27;request&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> xmlParser = <span class="built_in">require</span>(<span class="string">&#x27;xml-parser&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> md5 = <span class="built_in">require</span>(<span class="string">&#x27;md5&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 配置信息</span></span><br><span class="line"><span class="keyword">const</span> config = &#123;</span><br><span class="line">  <span class="attr">username</span>: <span class="string">&#x27;toimc&#x27;</span>, <span class="comment">// GitHub repository 所有者，可以是个人或者组织。对应Gitalk配置中的owner</span></span><br><span class="line">  <span class="attr">repo</span>: <span class="string">&quot;toimc.github.io&quot;</span>, <span class="comment">// 储存评论issue的github仓库名，仅需要仓库名字即可。对应 Gitalk配置中的repo</span></span><br><span class="line">  <span class="attr">token</span>: <span class="string">&#x27;xxxxxx&#x27;</span>, <span class="comment">// 前面申请的 personal access token</span></span><br><span class="line">  <span class="attr">sitemap</span>: path.<span class="title function_">join</span>(__dirname, <span class="string">&#x27;./public/sitemap.xml&#x27;</span>), <span class="comment">// 自己站点的 sitemap 文件地址</span></span><br><span class="line">  <span class="attr">cache</span>: <span class="literal">true</span>, <span class="comment">// 是否启用缓存，启用缓存会将已经初始化的数据写入配置的 gitalkCacheFile 文件，下一次直接通过缓存文件判断</span></span><br><span class="line">  <span class="attr">gitalkCacheFile</span>: path.<span class="title function_">join</span>(__dirname, <span class="string">&#x27;./gitalk-init-cache.json&#x27;</span>), <span class="comment">// 用于保存 gitalk 已经初始化的 id 列表</span></span><br><span class="line">  <span class="attr">gitalkErrorFile</span>: path.<span class="title function_">join</span>(__dirname, <span class="string">&#x27;./gitalk-init-error.json&#x27;</span>), <span class="comment">// 用于保存 gitalk 初始化报错的数据</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> api = <span class="string">&#x27;https://api.github.com/repos/&#x27;</span> + config.<span class="property">username</span> + <span class="string">&#x27;/&#x27;</span> + config.<span class="property">repo</span> + <span class="string">&#x27;/issues&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* 读取 sitemap 文件</span></span><br><span class="line"><span class="comment">* 远程 sitemap 文件获取可参考 https://www.npmjs.com/package/sitemapper</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">sitemapXmlReader</span> = (<span class="params">file</span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> data = fs.<span class="title function_">readFileSync</span>(file, <span class="string">&#x27;utf8&#x27;</span>);</span><br><span class="line">    <span class="keyword">const</span> sitemap = <span class="title function_">xmlParser</span>(data);</span><br><span class="line">    <span class="keyword">let</span> ret = [];</span><br><span class="line">    sitemap.<span class="property">root</span>.<span class="property">children</span>.<span class="title function_">forEach</span>(<span class="keyword">function</span> (<span class="params">url</span>) &#123;</span><br><span class="line">      <span class="keyword">const</span> loc = url.<span class="property">children</span>.<span class="title function_">find</span>(<span class="keyword">function</span> (<span class="params">item</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> item.<span class="property">name</span> === <span class="string">&#x27;loc&#x27;</span>;</span><br><span class="line">      &#125;);</span><br><span class="line">      <span class="keyword">if</span> (!loc) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">const</span> title = url.<span class="property">children</span>.<span class="title function_">find</span>(<span class="keyword">function</span> (<span class="params">item</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> item.<span class="property">name</span> === <span class="string">&#x27;title&#x27;</span>;</span><br><span class="line">      &#125;);</span><br><span class="line">      <span class="keyword">const</span> desc = url.<span class="property">children</span>.<span class="title function_">find</span>(<span class="keyword">function</span> (<span class="params">item</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> item.<span class="property">name</span> === <span class="string">&#x27;desc&#x27;</span>;</span><br><span class="line">      &#125;);</span><br><span class="line">      <span class="keyword">const</span> date = url.<span class="property">children</span>.<span class="title function_">find</span>(<span class="keyword">function</span> (<span class="params">item</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> item.<span class="property">name</span> === <span class="string">&#x27;date&#x27;</span>;</span><br><span class="line">      &#125;);</span><br><span class="line">      ret.<span class="title function_">push</span>(&#123;</span><br><span class="line">        <span class="attr">url</span>: loc.<span class="property">content</span>,</span><br><span class="line">        <span class="attr">title</span>: title.<span class="property">content</span>,</span><br><span class="line">        <span class="attr">desc</span>: desc.<span class="property">content</span>,</span><br><span class="line">        <span class="attr">date</span>: date.<span class="property">content</span>,</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">return</span> ret;</span><br><span class="line">  &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">    <span class="keyword">return</span> [];</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取 gitalk 使用的 id</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">getGitalkId</span> = (<span class="params">&#123;</span></span><br><span class="line"><span class="params">  url: u,</span></span><br><span class="line"><span class="params">  date</span></span><br><span class="line"><span class="params">&#125;</span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">const</span> link = url.<span class="title function_">parse</span>(u);</span><br><span class="line">  <span class="comment">// 链接不存在，不需要初始化</span></span><br><span class="line">  <span class="keyword">if</span> (!link || !link.<span class="property">pathname</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (!date) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="title function_">md5</span>(link.<span class="property">pathname</span>);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* 通过以请求判断是否已经初始化</span></span><br><span class="line"><span class="comment">* <span class="doctag">@param</span> &#123;<span class="type">string</span>&#125; gitalk 初始化的id</span></span><br><span class="line"><span class="comment">* <span class="doctag">@return</span> &#123;<span class="type">[boolean, boolean]</span>&#125; 第一个值表示是否出错，第二个值 false 表示没初始化， true 表示已经初始化</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">getIsInitByRequest</span> = (<span class="params">id</span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">const</span> options = &#123;</span><br><span class="line">    <span class="attr">headers</span>: &#123;</span><br><span class="line">      <span class="string">&#x27;Authorization&#x27;</span>: <span class="string">&#x27;token &#x27;</span> + config.<span class="property">token</span>,</span><br><span class="line">      <span class="string">&#x27;User-Agent&#x27;</span>: <span class="string">&#x27;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36&#x27;</span>,</span><br><span class="line">      <span class="string">&#x27;Accept&#x27;</span>: <span class="string">&#x27;application/json&#x27;</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="attr">url</span>: api + <span class="string">&#x27;?labels=&#x27;</span> + id + <span class="string">&#x27;,Gitalk&#x27;</span>,</span><br><span class="line">    <span class="attr">method</span>: <span class="string">&#x27;GET&#x27;</span></span><br><span class="line">  &#125;;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="title function_">request</span>(options, <span class="keyword">function</span> (<span class="params">err, response, body</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (err) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="title function_">resolve</span>([err, <span class="literal">false</span>]);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> (response.<span class="property">statusCode</span> != <span class="number">200</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="title function_">resolve</span>([response, <span class="literal">false</span>]);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">const</span> res = <span class="title class_">JSON</span>.<span class="title function_">parse</span>(body);</span><br><span class="line">      <span class="keyword">if</span> (res.<span class="property">length</span> &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="title function_">resolve</span>([<span class="literal">false</span>, <span class="literal">true</span>]);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">return</span> <span class="title function_">resolve</span>([<span class="literal">false</span>, <span class="literal">false</span>]);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* 通过缓存判断是否已经初始化</span></span><br><span class="line"><span class="comment">* <span class="doctag">@param</span> &#123;<span class="type">string</span>&#125; gitalk 初始化的id</span></span><br><span class="line"><span class="comment">* <span class="doctag">@return</span> &#123;<span class="type">boolean</span>&#125; false 表示没初始化， true 表示已经初始化</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">const</span> getIsInitByCache = (<span class="function">() =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// 判断缓存文件是否存在</span></span><br><span class="line">  <span class="keyword">let</span> gitalkCache = <span class="literal">false</span>;</span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    gitalkCache = <span class="built_in">require</span>(config.<span class="property">gitalkCacheFile</span>);</span><br><span class="line">  &#125; <span class="keyword">catch</span> (e) &#123;&#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">function</span> (<span class="params">id</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (!gitalkCache) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (gitalkCache.<span class="title function_">find</span>(<span class="function">(<span class="params">&#123;</span></span></span><br><span class="line"><span class="params"><span class="function">        id: itemId</span></span></span><br><span class="line"><span class="params"><span class="function">      &#125;</span>) =&gt;</span> (itemId === id))) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;)();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 根据缓存，判断链接是否已经初始化</span></span><br><span class="line"><span class="comment">// 第一个值表示是否出错，第二个值 false 表示没初始化， true 表示已经初始化</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">idIsInit</span> = <span class="keyword">async</span> (<span class="params">id</span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">if</span> (!config.<span class="property">cache</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">await</span> <span class="title function_">getIsInitByRequest</span>(id);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 如果通过缓存查询到的数据是未初始化，则再通过请求判断是否已经初始化，防止多次初始化</span></span><br><span class="line">  <span class="keyword">if</span> (<span class="title function_">getIsInitByCache</span>(id) === <span class="literal">false</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">await</span> <span class="title function_">getIsInitByRequest</span>(id);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> [<span class="literal">false</span>, <span class="literal">true</span>];</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 初始化</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">gitalkInit</span> = (<span class="params">&#123;</span></span><br><span class="line"><span class="params">  url,</span></span><br><span class="line"><span class="params">  id,</span></span><br><span class="line"><span class="params">  title,</span></span><br><span class="line"><span class="params">  desc</span></span><br><span class="line"><span class="params">&#125;</span>) =&gt; &#123;</span><br><span class="line">  <span class="comment">//创建issue</span></span><br><span class="line">  <span class="keyword">const</span> reqBody = &#123;</span><br><span class="line">    <span class="string">&#x27;title&#x27;</span>: title,</span><br><span class="line">    <span class="string">&#x27;labels&#x27;</span>: [id, <span class="string">&#x27;Gitalk&#x27;</span>],</span><br><span class="line">    <span class="string">&#x27;body&#x27;</span>: url + <span class="string">&#x27;\r\n\r\n&#x27;</span> + desc</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> options = &#123;</span><br><span class="line">    <span class="attr">headers</span>: &#123;</span><br><span class="line">      <span class="string">&#x27;Authorization&#x27;</span>: <span class="string">&#x27;token &#x27;</span> + config.<span class="property">token</span>,</span><br><span class="line">      <span class="string">&#x27;User-Agent&#x27;</span>: <span class="string">&#x27;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36&#x27;</span>,</span><br><span class="line">      <span class="string">&#x27;Accept&#x27;</span>: <span class="string">&#x27;application/json&#x27;</span>,</span><br><span class="line">      <span class="string">&#x27;Content-Type&#x27;</span>: <span class="string">&#x27;application/json;charset=UTF-8&#x27;</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="attr">url</span>: api,</span><br><span class="line">    <span class="attr">body</span>: <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(reqBody),</span><br><span class="line">    <span class="attr">method</span>: <span class="string">&#x27;POST&#x27;</span></span><br><span class="line">  &#125;;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="title function_">request</span>(options, <span class="keyword">function</span> (<span class="params">err, response, body</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (err) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="title function_">resolve</span>([err, <span class="literal">false</span>]);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> (response.<span class="property">statusCode</span> != <span class="number">201</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="title function_">resolve</span>([response, <span class="literal">false</span>]);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">return</span> <span class="title function_">resolve</span>([<span class="literal">false</span>, <span class="literal">true</span>]);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* 写入内容</span></span><br><span class="line"><span class="comment">* <span class="doctag">@param</span> &#123;<span class="type">string</span>&#125; fileName 文件名</span></span><br><span class="line"><span class="comment">* <span class="doctag">@param</span> &#123;<span class="type">string</span>&#125; content 内容</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">write</span> = <span class="keyword">async</span> (<span class="params">fileName, content, flag = <span class="string">&#x27;w+&#x27;</span></span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve</span>) =&gt;</span> &#123;</span><br><span class="line">    fs.<span class="title function_">open</span>(fileName, flag, <span class="keyword">function</span> (<span class="params">err, fd</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (err) &#123;</span><br><span class="line">        <span class="title function_">resolve</span>([err, <span class="literal">false</span>]);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      fs.<span class="title function_">writeFile</span>(fd, content, <span class="keyword">function</span> (<span class="params">err</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (err) &#123;</span><br><span class="line">          <span class="title function_">resolve</span>([err, <span class="literal">false</span>]);</span><br><span class="line">          <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        fs.<span class="title function_">close</span>(fd, <span class="function">(<span class="params">err</span>) =&gt;</span> &#123;</span><br><span class="line">          <span class="keyword">if</span> (err) &#123;</span><br><span class="line">            <span class="title function_">resolve</span>([err, <span class="literal">false</span>]);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="title function_">resolve</span>([<span class="literal">false</span>, <span class="literal">true</span>]);</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">init</span> = <span class="keyword">async</span> (<span class="params"></span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">const</span> urls = <span class="title function_">sitemapXmlReader</span>(config.<span class="property">sitemap</span>);</span><br><span class="line">  <span class="comment">// 报错的数据</span></span><br><span class="line">  <span class="keyword">const</span> errorData = [];</span><br><span class="line">  <span class="comment">// 已经初始化的数据</span></span><br><span class="line">  <span class="keyword">const</span> initializedData = [];</span><br><span class="line">  <span class="comment">// 成功初始化数据</span></span><br><span class="line">  <span class="keyword">const</span> successData = [];</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">const</span> item <span class="keyword">of</span> urls) &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123;</span><br><span class="line">      url,</span><br><span class="line">      date,</span><br><span class="line">      title,</span><br><span class="line">      desc</span><br><span class="line">    &#125; = item;</span><br><span class="line">    <span class="keyword">const</span> id = <span class="title function_">getGitalkId</span>(&#123;</span><br><span class="line">      url,</span><br><span class="line">      date</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">if</span> (!id) &#123;</span><br><span class="line">      <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`id: 生成失败 [ <span class="subst">$&#123;id&#125;</span> ] `</span>);</span><br><span class="line">      errorData.<span class="title function_">push</span>(&#123;</span><br><span class="line">        ...item,</span><br><span class="line">        <span class="attr">info</span>: <span class="string">&#x27;id 生成失败&#x27;</span>,</span><br><span class="line">      &#125;);</span><br><span class="line">      <span class="keyword">continue</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">const</span> [err, res] = <span class="keyword">await</span> <span class="title function_">idIsInit</span>(id);</span><br><span class="line">    <span class="keyword">if</span> (err) &#123;</span><br><span class="line">      <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`Error: 查询评论异常 [ <span class="subst">$&#123;title&#125;</span> ] , 信息：`</span>, err || <span class="string">&#x27;无&#x27;</span>);</span><br><span class="line">      errorData.<span class="title function_">push</span>(&#123;</span><br><span class="line">        ...item,</span><br><span class="line">        <span class="attr">info</span>: <span class="string">&#x27;查询评论异常&#x27;</span>,</span><br><span class="line">      &#125;);</span><br><span class="line">      <span class="keyword">continue</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (res === <span class="literal">true</span>) &#123;</span><br><span class="line">      <span class="comment">// console.log(`--- Gitalk 已经初始化 --- [ $&#123;title&#125; ] `);</span></span><br><span class="line">      initializedData.<span class="title function_">push</span>(&#123;</span><br><span class="line">        id,</span><br><span class="line">        url,</span><br><span class="line">        title,</span><br><span class="line">      &#125;);</span><br><span class="line">      <span class="keyword">continue</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`Gitalk 初始化开始... [ <span class="subst">$&#123;title&#125;</span> ] `</span>);</span><br><span class="line">    <span class="keyword">const</span> [e, r] = <span class="keyword">await</span> <span class="title function_">gitalkInit</span>(&#123;</span><br><span class="line">      id,</span><br><span class="line">      url,</span><br><span class="line">      title,</span><br><span class="line">      desc</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">if</span> (e || !r) &#123;</span><br><span class="line">      <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`Error: Gitalk 初始化异常 [ <span class="subst">$&#123;title&#125;</span> ] , 信息：`</span>, e || <span class="string">&#x27;无&#x27;</span>);</span><br><span class="line">      errorData.<span class="title function_">push</span>(&#123;</span><br><span class="line">        ...item,</span><br><span class="line">        <span class="attr">info</span>: <span class="string">&#x27;初始化异常&#x27;</span>,</span><br><span class="line">      &#125;);</span><br><span class="line">      <span class="keyword">continue</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    successData.<span class="title function_">push</span>(&#123;</span><br><span class="line">      id,</span><br><span class="line">      url,</span><br><span class="line">      title,</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`Gitalk 初始化成功! [ <span class="subst">$&#123;title&#125;</span> ] - <span class="subst">$&#123;id&#125;</span>`</span>);</span><br><span class="line">    <span class="keyword">continue</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;&#x27;</span>); <span class="comment">// 空输出，用于换行</span></span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;--------- 运行结果 ---------&#x27;</span>);</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;&#x27;</span>); <span class="comment">// 空输出，用于换行</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (errorData.<span class="property">length</span> !== <span class="number">0</span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`报错数据： <span class="subst">$&#123;errorData.length&#125;</span> 条。参考文件 <span class="subst">$&#123;config.gitalkErrorFile&#125;</span>。`</span>);</span><br><span class="line">    <span class="keyword">await</span> <span class="title function_">write</span>(config.<span class="property">gitalkErrorFile</span>, <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(errorData, <span class="literal">null</span>, <span class="number">2</span>));</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`本次成功： <span class="subst">$&#123;successData.length&#125;</span> 条。`</span>);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 写入缓存</span></span><br><span class="line">  <span class="keyword">if</span> (config.<span class="property">cache</span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`写入缓存： <span class="subst">$&#123;(initializedData.length + successData.length)&#125;</span> 条，已初始化 <span class="subst">$&#123;initializedData.length&#125;</span> 条，本次成功： <span class="subst">$&#123;successData.length&#125;</span> 条。参考文件 <span class="subst">$&#123;config.gitalkCacheFile&#125;</span>。`</span>);</span><br><span class="line">    <span class="keyword">await</span> <span class="title function_">write</span>(config.<span class="property">gitalkCacheFile</span>, <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(initializedData.<span class="title function_">concat</span>(successData), <span class="literal">null</span>, <span class="number">2</span>));</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`已初始化： <span class="subst">$&#123;initializedData.length&#125;</span> 条。`</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_">init</span>();</span><br></pre></td></tr></table></figure><p>修改<code>scripts</code>中的脚本，添加<code>&quot;talk&quot;: &quot;node talk-auto-init.js&quot;</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></pre></td><td class="code"><pre><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;start&quot;</span><span class="punctuation">:</span> <span class="string">&quot;hexo clean &amp;&amp; hexo g &amp;&amp; hexo s&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;publish&quot;</span><span class="punctuation">:</span> <span class="string">&quot;hexo g &amp;&amp; hexo s&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;d&quot;</span><span class="punctuation">:</span> <span class="string">&quot;npm run prod &amp;&amp; hexo deploy &amp;&amp; npm run talk&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;talk&quot;</span><span class="punctuation">:</span> <span class="string">&quot;node talk-auto-init.js&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br></pre></td></tr></table></figure><h3 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h3><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm <span class="built_in">run</span> talk</span><br></pre></td></tr></table></figure><p>已经缓存过后的结果：</p><figure class="highlight awk"><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">➜ npm run talk</span><br><span class="line"></span><br><span class="line">&gt; hexo-site@<span class="number">2.0</span>.<span class="number">0</span> talk <span class="regexp">/Users/m</span>acos<span class="regexp">/Projects/</span>hexo-blog</span><br><span class="line">&gt; node talk-auto-init.js</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">--------- 运行结果 ---------</span><br><span class="line"></span><br><span class="line">本次成功： <span class="number">0</span> 条。</span><br><span class="line">写入缓存： <span class="number">73</span> 条，已初始化 <span class="number">73</span> 条，本次成功： <span class="number">0</span> 条。参考文件 ~/gitalk-init-cache.json。</span><br></pre></td></tr></table></figure><h2 id="403错误"><a href="#403错误" class="headerlink" title="403错误"></a>403错误</h2><h3 id="nginx解决方案（推荐）"><a href="#nginx解决方案（推荐）" class="headerlink" title="nginx解决方案（推荐）"></a>nginx解决方案（推荐）</h3><p>在nginx的博客配置中加入如下内容：</p><figure class="highlight nginx"><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="section">server</span> &#123;</span><br><span class="line">    <span class="attribute">listen</span> <span class="number">443</span> ssl http2;</span><br><span class="line">// ...</span><br><span class="line"></span><br><span class="line">    <span class="section">location</span> = /login/oauth/access_token &#123;</span><br><span class="line">        <span class="attribute">add_header</span> Access-Control-Allow-Origin <span class="string">&#x27;https://www.toimc.com&#x27;</span>; // 这里改成你自己的域名，并删除注释</span><br><span class="line">        <span class="attribute">add_header</span> Access-Control-Allow-Methods <span class="string">&#x27;GET, POST, OPTIONS&#x27;</span>;</span><br><span class="line">        <span class="attribute">add_header</span> Access-Control-Allow-Headers <span class="string">&#x27;DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization&#x27;</span>;</span><br><span class="line">        <span class="attribute">if</span> (<span class="variable">$request_method</span> = <span class="string">&#x27;OPTIONS&#x27;</span>) &#123;</span><br><span class="line">              <span class="attribute">return</span> <span class="number">204</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="attribute">proxy_pass</span> https://github.com;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="自建一个workers"><a href="#自建一个workers" class="headerlink" title="自建一个workers"></a>自建一个workers</h3><p>地址：<a href="https://workers.cloudflare.com/">CloudFlare Workers</a></p><p>参考项目：<a href="Zibri/cloudflare-cors-anywhere">Zibri&#x2F;cloudflare-cors-anywhere</a></p><p>比如我们部署的地址：<a href="https://github-proxy.toimc.workers.dev/https://github.com/login/oauth/access_token">右键复制 Link</a> 大家也可以使用</p><p>参考文章：<a href="https://blog.dsrkafuu.su/post/2020/cloudflare-worker-cors-anywhere/">使用 CloudFlare Workers 实现 CORS Anywhere</a></p><h3 id="使用其他人搭建的代理"><a href="#使用其他人搭建的代理" class="headerlink" title="使用其他人搭建的代理"></a>使用其他人搭建的代理</h3><p>比如这个<a href="https://github.com/gitalk/gitalk/issues/429">issues</a>介绍到的：</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">https:<span class="regexp">//</span>shielded-brushlands-<span class="number">08810</span>.herokuapp.com<span class="regexp">/https:/</span><span class="regexp">/github.com/</span>login<span class="regexp">/oauth/</span>access_token</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;本文的环境是hexo 5.x版本，next 8.x主题，介绍在next主题中如何配置gitalk与评论初始化，缓存issues初始化结果，加速初始化过程，解决问题：“未找到相关的 Issues 进行评论，请联系xxx初始化创建”和“Request failed with status code 403”。&lt;/p&gt;</summary>
    
    
    
    
  </entry>
  
  <entry>
    <title>hexo博客代码折叠功能</title>
    <link href="https://www.toimc.com/hexo-usage-2/"/>
    <id>https://www.toimc.com/hexo-usage-2/</id>
    <published>2021-03-19T06:59:48.000Z</published>
    <updated>2025-10-06T05:13:13.973Z</updated>
    
    <content type="html"><![CDATA[<p>在Heox中即使再长的代码，默认发布出来很长，也不会进行折叠。这怎么能忍？本文介绍如何在Hexo中实现类型代码折叠功能，让文章看起来更加清爽</p><span id="more"></span><h2 id="先上效果"><a href="#先上效果" class="headerlink" title="先上效果"></a>先上效果</h2><p>费话不多说，看看效果：</p><p><img src="https://static.www.toimc.com/blog/img/2021/hexo-usage-2/fold.gif?watermark/2/text/VG9pbWPlh7rlk4E=/fontsize/200"></p><p>本博客目前采用的方案，即是这种。</p><h2 id="前题-amp-思路"><a href="#前题-amp-思路" class="headerlink" title="前题&amp;思路"></a>前题&amp;思路</h2><p>本博客环境：<code>hexo@5.4.0</code>，<code>next@8.2.2</code>，以下所有代码，建立在这个基础之上。</p><p>由 jQuery 选择器选择代码模块<code>.highlight</code>相关的DOM节点，给超过某个高度的代码模块添加展开收起的盒子，让盒子实现展开隐藏效果。</p><h2 id="折叠逻辑"><a href="#折叠逻辑" class="headerlink" title="折叠逻辑"></a>折叠逻辑</h2><h3 id="添加code-unfold-js"><a href="#添加code-unfold-js" class="headerlink" title="添加code-unfold.js"></a>添加<code>code-unfold.js</code></h3><p>由于是在<code>next</code>主题中添加js逻辑，所以我们把<code>code-unfold.js</code>放置在了<code>themes/next/source/js/code-unfold.js</code>：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> <span class="variable constant_">CODE_MAX_HEIGHT</span> = <span class="number">200</span>;</span><br><span class="line"><span class="keyword">var</span> containers = [];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 展开</span></span><br><span class="line">$(<span class="string">&#x27;body&#x27;</span>).<span class="title function_">on</span>(<span class="string">&#x27;click&#x27;</span>, <span class="string">&#x27;.js_unfold_code_btn&#x27;</span>, <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">  $(<span class="variable language_">this</span>).<span class="title function_">closest</span>(<span class="string">&#x27;.js_highlight_container&#x27;</span>).<span class="title function_">addClass</span>(<span class="string">&#x27;on&#x27;</span>);</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">// 收起</span></span><br><span class="line">$(<span class="string">&#x27;body&#x27;</span>).<span class="title function_">on</span>(<span class="string">&#x27;click&#x27;</span>, <span class="string">&#x27;.js_retract_code_btn&#x27;</span>, <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">var</span> $container = $(<span class="variable language_">this</span>).<span class="title function_">closest</span>(<span class="string">&#x27;.js_highlight_container&#x27;</span>).<span class="title function_">removeClass</span>(<span class="string">&#x27;on&#x27;</span>);</span><br><span class="line">  <span class="keyword">var</span> winTop = $(<span class="variable language_">window</span>).<span class="title function_">scrollTop</span>();</span><br><span class="line">  <span class="keyword">var</span> offsetTop = $container.<span class="title function_">offset</span>().<span class="property">top</span>;</span><br><span class="line">  $(<span class="variable language_">this</span>).<span class="title function_">css</span>(<span class="string">&#x27;top&#x27;</span>, <span class="number">0</span>);</span><br><span class="line">  <span class="keyword">if</span> (winTop &gt; offsetTop) &#123;</span><br><span class="line">    <span class="comment">// 设置滚动条位置</span></span><br><span class="line">    $(<span class="string">&#x27;body, html&#x27;</span>).<span class="title function_">animate</span>(&#123;</span><br><span class="line">      <span class="attr">scrollTop</span>: $container.<span class="title function_">offset</span>().<span class="property">top</span> - <span class="variable constant_">CODE_MAX_HEIGHT</span></span><br><span class="line">    &#125;, <span class="number">600</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">// 滚动事件，触发动画效果</span></span><br><span class="line">$(<span class="variable language_">window</span>).<span class="title function_">on</span>(<span class="string">&#x27;scroll&#x27;</span>, <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">var</span> scrollTop = $(<span class="variable language_">window</span>).<span class="title function_">scrollTop</span>();</span><br><span class="line">  <span class="keyword">var</span> temp = [];</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; containers.<span class="property">length</span>; i++) &#123;</span><br><span class="line">    <span class="keyword">var</span> item = containers[i];</span><br><span class="line">    <span class="keyword">var</span> &#123; $container, height, $hide, hasHorizontalScrollbar &#125; = item;</span><br><span class="line">    <span class="keyword">if</span> ($container.<span class="title function_">closest</span>(<span class="string">&#x27;body&#x27;</span>).<span class="property">length</span> === <span class="number">0</span>) &#123;</span><br><span class="line">      <span class="comment">// 如果 $container 元素已经不在页面上, 则删除该元素</span></span><br><span class="line">      <span class="comment">// 防止pjax页面跳转之后，元素未删除</span></span><br><span class="line">      <span class="keyword">continue</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    temp.<span class="title function_">push</span>(item);</span><br><span class="line">    <span class="keyword">if</span> (!$container.<span class="title function_">hasClass</span>(<span class="string">&#x27;on&#x27;</span>)) &#123;</span><br><span class="line">      <span class="keyword">continue</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">var</span> offsetTop = $container.<span class="title function_">offset</span>().<span class="property">top</span>;</span><br><span class="line">    <span class="keyword">var</span> hideBtnHeight = $hide.<span class="title function_">outerHeight</span>();</span><br><span class="line">    <span class="comment">// 减去按钮高度，减去底部滚动条高度</span></span><br><span class="line">    <span class="keyword">var</span> maxTop = <span class="built_in">parseInt</span>(height - (hasHorizontalScrollbar ? <span class="number">17</span> : <span class="number">0</span>) - hideBtnHeight);</span><br><span class="line">    <span class="keyword">let</span> top = <span class="built_in">parseInt</span>(</span><br><span class="line">      <span class="title class_">Math</span>.<span class="title function_">min</span>(</span><br><span class="line">        <span class="title class_">Math</span>.<span class="title function_">max</span>(scrollTop - offsetTop, <span class="number">0</span>), <span class="comment">// 如果小于 0 ，则取 0</span></span><br><span class="line">        maxTop,<span class="comment">// 如果大于 height ，则取 height</span></span><br><span class="line">      )</span><br><span class="line">    );</span><br><span class="line">    <span class="comment">// 根据 sin 曲线设置&quot;收起代码&quot;位置</span></span><br><span class="line">    <span class="keyword">var</span> halfHeight = <span class="built_in">parseInt</span>($(<span class="variable language_">window</span>).<span class="title function_">height</span>() / <span class="number">2</span> * <span class="title class_">Math</span>.<span class="title function_">sin</span>((top / maxTop) * <span class="number">90</span> * (<span class="number">2</span> * <span class="title class_">Math</span>.<span class="property">PI</span>/<span class="number">360</span>)));</span><br><span class="line">    $hide.<span class="title function_">css</span>(<span class="string">&#x27;top&#x27;</span>, <span class="title class_">Math</span>.<span class="title function_">min</span>(top + halfHeight, maxTop));</span><br><span class="line">  &#125;</span><br><span class="line">  containers = temp;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 添加隐藏容器</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">addCodeWrap</span>(<span class="params">$node</span>) &#123;</span><br><span class="line">  <span class="keyword">var</span> $container = $node.<span class="title function_">wrap</span>(<span class="string">&#x27;&lt;div class=&quot;js_highlight_container highlight-container&quot;&gt;&lt;div class=&quot;highlight-wrap&quot;&gt;&lt;/div&gt;&lt;/div&gt;&#x27;</span>).<span class="title function_">closest</span>(<span class="string">&#x27;.js_highlight_container&#x27;</span>);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 底部 &quot;展开代码&quot; 与 侧边栏 &quot;收起代码&quot;</span></span><br><span class="line">  <span class="keyword">var</span> $btn = $(<span class="string">`</span></span><br><span class="line"><span class="string">    &lt;div class=&quot;highlight-footer&quot;&gt;</span></span><br><span class="line"><span class="string">      &lt;a class=&quot;js_unfold_code_btn show-btn&quot; href=&quot;javascript:;&quot;&gt;展开代码&lt;i class=&quot;fa fa-angle-down&quot; aria-hidden=&quot;true&quot;&gt;&lt;/i&gt;&lt;/a&gt;</span></span><br><span class="line"><span class="string">    &lt;/div&gt;</span></span><br><span class="line"><span class="string">    &lt;a class=&quot;js_retract_code_btn hide-btn&quot; href=&quot;javascript:;&quot;&gt;&lt;i class=&quot;fa fa-angle-up&quot; aria-hidden=&quot;true&quot;&gt;&lt;/i&gt;收起代码&lt;/a&gt;</span></span><br><span class="line"><span class="string">  `</span>);</span><br><span class="line"></span><br><span class="line">  $container.<span class="title function_">append</span>($btn);</span><br><span class="line">  <span class="keyword">return</span> $container;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">codeUnfold</span> () &#123;</span><br><span class="line">  $(<span class="string">&#x27;.highlight&#x27;</span>).<span class="title function_">each</span>(<span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">    <span class="comment">// 防止重复渲染</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">__render__</span> === <span class="literal">true</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">__render__</span> = <span class="literal">true</span>;</span><br><span class="line">    <span class="keyword">var</span> $this = $(<span class="variable language_">this</span>);</span><br><span class="line">    <span class="keyword">var</span> height = $(<span class="variable language_">this</span>).<span class="title function_">outerHeight</span>();</span><br><span class="line">    <span class="keyword">if</span> (height &gt; <span class="variable constant_">CODE_MAX_HEIGHT</span>) &#123;</span><br><span class="line">      <span class="comment">// 添加展开&amp;收起容器</span></span><br><span class="line">      <span class="keyword">var</span> $container = <span class="title function_">addCodeWrap</span>($this, height);</span><br><span class="line">      containers.<span class="title function_">push</span>(&#123;</span><br><span class="line">        $container,</span><br><span class="line">        height,</span><br><span class="line">        <span class="attr">$hide</span>: $container.<span class="title function_">find</span>(<span class="string">&#x27;.js_retract_code_btn&#x27;</span>),</span><br><span class="line">        <span class="attr">hasHorizontalScrollbar</span>: <span class="variable language_">this</span>.<span class="property">scrollWidth</span> &gt; <span class="variable language_">this</span>.<span class="property">offsetWidth</span>,</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="添加jquery"><a href="#添加jquery" class="headerlink" title="添加jquery"></a>添加<code>jquery</code></h3><p>由于需要使用<code>jquery</code>在<code>next</code>主题文件中</p><p>方案一：</p><p>修改配置：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">fancybox:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><p>因为<code>fancybox</code>会依赖<code>jquery</code>，所以设置成<code>true</code></p><p>方案二：在<code>next</code>主题全局引用jquery</p><h3 id="引用code-unfold-js"><a href="#引用code-unfold-js" class="headerlink" title="引用code-unfold.js"></a>引用<code>code-unfold.js</code></h3><p>修改文件<code>themes/next/layout/_scripts/index.njk</code></p><figure class="highlight javascript"><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">&#123;&#123;- <span class="title function_">next_js</span>(<span class="string">&#x27;code-unfold.js&#x27;</span>) &#125;&#125;</span><br></pre></td></tr></table></figure><p>下面找到文件<code>themes/next/source/js/next-boot.js</code>：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">NexT</span>.<span class="property">boot</span>.<span class="property">refresh</span> = <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">  <span class="comment">// 添加一行代码</span></span><br><span class="line">  <span class="title function_">codeUnfold</span>()</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// ...</span></span><br></pre></td></tr></table></figure><h2 id="添加样式"><a href="#添加样式" class="headerlink" title="添加样式"></a>添加样式</h2><h3 id="创建highlight-styl"><a href="#创建highlight-styl" class="headerlink" title="创建highlight.styl"></a>创建<code>highlight.styl</code></h3><p>可以添加<code>theme/next/source/css/_common/components/highlight.styl</code>文件：</p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 展开收起效果</span></span><br><span class="line"><span class="selector-class">.highlight-container</span></span><br><span class="line">  <span class="attribute">position</span>: relative</span><br><span class="line">  <span class="attribute">background-color</span>: highlight-background</span><br><span class="line">  &amp;<span class="selector-class">.on</span></span><br><span class="line">    <span class="selector-class">.highlight-footer</span></span><br><span class="line">      <span class="attribute">display</span>: none</span><br><span class="line">    <span class="selector-class">.hide-btn</span></span><br><span class="line">      <span class="attribute">display</span>: flex</span><br><span class="line">    <span class="selector-class">.highlight-wrap</span></span><br><span class="line">      <span class="attribute">max-height</span>: none</span><br><span class="line">  <span class="selector-class">.highlight-wrap</span></span><br><span class="line">    <span class="attribute">overflow</span>: hidden</span><br><span class="line">    <span class="attribute">max-height</span>: <span class="number">200px</span></span><br><span class="line">  <span class="selector-class">.highlight-footer</span></span><br><span class="line">    <span class="attribute">position</span> absolute</span><br><span class="line">    <span class="attribute">width</span>: <span class="number">100%</span></span><br><span class="line">    <span class="attribute">left</span>: <span class="number">0</span></span><br><span class="line">    <span class="attribute">bottom</span>: <span class="number">0</span></span><br><span class="line">    <span class="attribute">height</span>: <span class="number">60px</span></span><br><span class="line">    <span class="attribute">background-image</span>: <span class="string">&#x27;linear-gradient(-180deg, rgba(255,255,255,0) 0%, %s 65%)&#x27;</span> % highlight-background;</span><br><span class="line">    <span class="attribute">text-align</span>: center</span><br><span class="line">  <span class="selector-class">.show-btn</span></span><br><span class="line">    <span class="attribute">font-size</span>: <span class="number">12px</span></span><br><span class="line">    <span class="attribute">color</span>: <span class="number">#fff</span></span><br><span class="line">    <span class="attribute">position</span>: absolute</span><br><span class="line">    <span class="attribute">left</span>: <span class="number">50%</span></span><br><span class="line">    <span class="attribute">transform</span>: <span class="built_in">translateX</span>(-<span class="number">50%</span>)</span><br><span class="line">    <span class="attribute">bottom</span>: <span class="number">0</span></span><br><span class="line">    <span class="attribute">line-height</span>: <span class="number">2em</span></span><br><span class="line">    <span class="attribute">text-decoration</span>: none</span><br><span class="line">    <span class="attribute">padding</span>: <span class="number">0</span> <span class="number">0.8em</span></span><br><span class="line">    <span class="attribute">text-align</span>: center</span><br><span class="line">    <span class="attribute">border-radius</span>: <span class="number">4px</span> <span class="number">4px</span> <span class="number">0</span></span><br><span class="line">    <span class="selector-pseudo">&amp;:hover</span></span><br><span class="line">      <span class="attribute">text-decoration</span>: none</span><br><span class="line">  <span class="selector-class">.hide-btn</span></span><br><span class="line">    <span class="attribute">color</span>: <span class="number">#fff</span></span><br><span class="line">    <span class="attribute">font-size</span>: <span class="number">12px</span></span><br><span class="line">    <span class="attribute">width</span>: <span class="number">22px</span></span><br><span class="line">    <span class="attribute">position</span>: absolute</span><br><span class="line">    <span class="attribute">left</span>: -<span class="number">21px</span></span><br><span class="line">    <span class="attribute">top</span>: <span class="number">0</span></span><br><span class="line">    <span class="attribute">line-height</span>: <span class="number">1em</span></span><br><span class="line">    <span class="attribute">text-decoration</span>: none</span><br><span class="line">    <span class="attribute">text-align</span>: center</span><br><span class="line">    <span class="attribute">display</span>: none</span><br><span class="line">    <span class="attribute">flex-direction</span>: column</span><br><span class="line">    <span class="attribute">background-color</span>: highlight-background</span><br><span class="line">    <span class="attribute">border-radius</span>: <span class="number">4px</span> <span class="number">0</span> <span class="number">0</span> <span class="number">4px</span></span><br><span class="line">    <span class="attribute">padding</span>: <span class="number">0.1em</span> <span class="number">0</span> <span class="number">0.6em</span></span><br><span class="line">    <span class="attribute">transition</span>: top ease <span class="number">0.35s</span></span><br><span class="line">  <span class="selector-class">.fa-angle-up</span>,</span><br><span class="line">  <span class="selector-class">.fa-angle-down</span></span><br><span class="line">    <span class="attribute">font-style</span>: normal</span><br><span class="line">    <span class="attribute">color</span>: <span class="number">#fff</span></span><br><span class="line">  <span class="selector-class">.fa-angle-up</span><span class="selector-pseudo">:before</span></span><br><span class="line">    <span class="attribute">content</span>:<span class="string">&quot;\f106&quot;</span></span><br><span class="line">  <span class="selector-class">.fa-angle-down</span><span class="selector-pseudo">:before</span></span><br><span class="line">    <span class="attribute">content</span>:<span class="string">&quot;\f107&quot;</span></span><br><span class="line">    <span class="attribute">margin-left</span>: <span class="number">0.5em</span></span><br><span class="line">  <span class="selector-class">.js_unfold_code_btn</span>, <span class="selector-class">.js_retract_code_btn</span></span><br><span class="line">    <span class="attribute">background</span>: <span class="built_in">rgba</span>(<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0.5</span>)</span><br><span class="line">    <span class="attribute">border-bottom</span>: none <span class="meta">!important</span></span><br><span class="line">    <span class="selector-pseudo">&amp;:hover</span></span><br><span class="line">      <span class="attribute">border-bottom-color</span>: none <span class="meta">!important</span></span><br></pre></td></tr></table></figure><h3 id="引用样式"><a href="#引用样式" class="headerlink" title="引用样式"></a>引用样式</h3><p>找到文件<code>themes/next/source/css/_common/components/index.styl</code>：</p><figure class="highlight stylus"><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="keyword">@import</span> <span class="string">&#x27;post&#x27;</span>;</span><br><span class="line"><span class="keyword">@import</span> <span class="string">&#x27;pages&#x27;</span>;</span><br><span class="line"><span class="keyword">@import</span> <span class="string">&#x27;third-party&#x27;</span>;</span><br><span class="line"><span class="comment">// 添加这一行</span></span><br><span class="line"><span class="keyword">@import</span> <span class="string">&#x27;highlight&#x27;</span></span><br></pre></td></tr></table></figure><p>至此，我们就完成了hexo博客长代码折叠功能。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;在Heox中即使再长的代码，默认发布出来很长，也不会进行折叠。这怎么能忍？本文介绍如何在Hexo中实现类型代码折叠功能，让文章看起来更加清爽&lt;/p&gt;</summary>
    
    
    
    <category term="hexo博客进阶玩法" scheme="https://www.toimc.com/categories/hexo%E5%8D%9A%E5%AE%A2%E8%BF%9B%E9%98%B6%E7%8E%A9%E6%B3%95/"/>
    
    
    <category term="node.js" scheme="https://www.toimc.com/tags/node-js/"/>
    
    <category term="hexo" scheme="https://www.toimc.com/tags/hexo/"/>
    
    <category term="next" scheme="https://www.toimc.com/tags/next/"/>
    
    <category term="前端" scheme="https://www.toimc.com/tags/%E5%89%8D%E7%AB%AF/"/>
    
  </entry>
  
  <entry>
    <title>hexo博客next主题添加夜间模式（Dark Mode）</title>
    <link href="https://www.toimc.com/hexo-usage-1/"/>
    <id>https://www.toimc.com/hexo-usage-1/</id>
    <published>2021-03-19T05:35:09.000Z</published>
    <updated>2025-10-06T05:13:13.973Z</updated>
    
    <content type="html"><![CDATA[<p>本文主要介绍为hexo next主题添加一个可以切换的黑色&#x2F;夜间模式，虽然next 8.0 主题已经支持原生黑色模式，但是这个黑色模式是不可以切换的。本文介绍如何实现按钮来切换，包括如何在代码部分也切换夜间模式。</p><span id="more"></span><h2 id="效果"><a href="#效果" class="headerlink" title="效果"></a>效果</h2><p>费话不多说，先上效果</p><p><img src="https://static.www.toimc.com/blog/img/2021/hexo-usage-1/dark.gif?watermark/2/text/VG9pbWPlh7rlk4E=/fontsize/200"></p><p>特点：</p><ul><li>代码部分进行Dark&#x2F;Light模式切换</li><li>切换有动画</li><li>对<code>code</code>部分进行优化</li></ul><h2 id="前题"><a href="#前题" class="headerlink" title="前题"></a>前题</h2><p>首先，你要确定自己的Hexo与next的版本：</p><p>Hexo在这里：<a href="https://github.com/hexojs/hexo">hexojs&#x2F;hexo</a>，我们使用的版本是：<code>5.4.0</code></p><p>Next主题在这里：<a href="https://github.com/next-theme/hexo-theme-next">next-theme&#x2F;hexo-theme-next</a>，我们使用的版本是：<code>8.2.2</code></p><p>虽然next 8.0 主题已经支持原生黑色模式，只需要在<code>_config.yml</code>文件中，将相应开关打开<code>darkmode: true</code>即可。</p><p>但是这个黑色模式是不可以切换的，本文将介绍如何实现一个按钮来切换黑&#x2F;白模式。</p><p>我们使用的主题是：<code>scheme: Mist</code>，其他类似思路进行调整。</p><h2 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h2><p>思路：</p><ol><li>使用第三方的包实现切换darkmode主题</li><li>切用next主题已经有的dark模式，为代码高亮部分添加样式</li></ol><h3 id="了解darkmode-js"><a href="#了解darkmode-js" class="headerlink" title="了解darkmode.js"></a>了解darkmode.js</h3><p>我们主要使用到了这个库<a href="https://github.com/sandoche/Darkmode.js">Darkmode.js</a> 来实现整体切换效果，在官方的github上有<a href="https://github.com/sandoche/Darkmode.js#-how-to-use">使用方式</a>的介绍。</p><h3 id="下载并配置next主题"><a href="#下载并配置next主题" class="headerlink" title="下载并配置next主题"></a>下载并配置next主题</h3><p>我们要集成到hexo中时，需要考虑是使用本地的js还是cdn的js，我的方式是<a href="https://github.com/sandoche/Darkmode.js/blob/master/lib/darkmode-js.min.js">下载</a>到lib目录中，使用本地的，以免出现后续的依赖问题。</p><p>在 <code>themes/next/_vendors.yml</code> 中指定<code>darkmode-js</code>的文件：</p><figure class="highlight dts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="symbol">darkmode_js:</span></span><br><span class="line"><span class="symbol">  name:</span> darkmode-js</span><br><span class="line"><span class="symbol">  version:</span> <span class="number">1.5</span><span class="number">.7</span></span><br><span class="line"><span class="symbol">  file:</span> lib/darkmode-js.min.js</span><br></pre></td></tr></table></figure><p>在<code>next</code>主题的的 <code>_config.yml</code>文件中，添加<code>darkmode_js</code>的相关开关：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">vendors:</span></span><br><span class="line">  <span class="comment"># 使用CDN可选，darkmode_js: https://cdn.jsdelivr.net/npm/darkmode-js/lib/darkmode-js.min.js</span></span><br><span class="line">  <span class="attr">darkmode_js:</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># darkmode.js</span></span><br><span class="line"><span class="attr">darkmode_js:</span> </span><br><span class="line">  <span class="attr">enable:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><blockquote><p>注意缩进！第一个darkmode_js是在vendors栏目下，第二个darkmode_js是一个单独的栏目。</p></blockquote><h3 id="修改主题模板"><a href="#修改主题模板" class="headerlink" title="修改主题模板"></a>修改主题模板</h3><p>打开 <code>themes/next/layout/_scripts/vendors.njk</code>，配置以下代码：</p><figure class="highlight js"><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></pre></td><td class="code"><pre><span class="line">&#123;%- <span class="keyword">if</span> theme.<span class="property">canvas_ribbon</span>.<span class="property">enable</span> %&#125;</span><br><span class="line">  &lt;script size=<span class="string">&quot;&#123;&#123; theme.canvas_ribbon.size &#125;&#125;&quot;</span> alpha=<span class="string">&quot;&#123;&#123; theme.canvas_ribbon.alpha &#125;&#125;&quot;</span> zIndex=<span class="string">&quot;&#123;&#123; theme.canvas_ribbon.zIndex &#125;&#125;&quot;</span> src=<span class="string">&quot;&#123;&#123; theme.vendors.canvas_ribbon &#125;&#125;&quot;</span>&gt;&lt;/script&gt;</span><br><span class="line">&#123;%- endif %&#125;</span><br><span class="line"></span><br><span class="line">&#123;# <span class="title class_">Customize</span> darkmode.<span class="property">js</span> - <span class="title class_">Declaration</span> #&#125;</span><br><span class="line">&#123;%- <span class="keyword">if</span> theme.<span class="property">darkmode_js</span>.<span class="property">enable</span> %&#125;</span><br><span class="line">  &lt;script src=<span class="string">&quot;&#123;&#123; theme.vendors.darkmode_js &#125;&#125;&quot;</span>&gt;&lt;/script&gt;</span><br><span class="line">&#123;%- endif %&#125;</span><br><span class="line"></span><br><span class="line">&#123;%- <span class="keyword">for</span> name <span class="keyword">in</span> <span class="title function_">js_vendors</span>() %&#125;</span><br><span class="line">  &lt;script src=<span class="string">&quot;&#123;&#123; url_for(theme.vendors[name]) &#125;&#125;&quot;</span>&gt;&lt;/script&gt;</span><br><span class="line">&#123;%- endfor %&#125;</span><br><span class="line"></span><br><span class="line">&#123;# <span class="title class_">Customize</span> darkmode.<span class="property">js</span> - <span class="title class_">Invokation</span> #&#125;</span><br><span class="line">&#123;%- <span class="keyword">if</span> theme.<span class="property">darkmode_js</span>.<span class="property">enable</span> %&#125;</span><br><span class="line">&lt;script&gt;</span><br><span class="line"><span class="keyword">var</span> options = &#123;</span><br><span class="line">  <span class="attr">bottom</span>: <span class="string">&#x27;64px&#x27;</span>, <span class="comment">// default: &#x27;32px&#x27;</span></span><br><span class="line">  <span class="attr">right</span>: <span class="string">&#x27;unset&#x27;</span>, <span class="comment">// default: &#x27;32px&#x27;</span></span><br><span class="line">  <span class="attr">left</span>: <span class="string">&#x27;32px&#x27;</span>, <span class="comment">// default: &#x27;unset&#x27;</span></span><br><span class="line">  <span class="attr">time</span>: <span class="string">&#x27;0.5s&#x27;</span>, <span class="comment">// default: &#x27;0.3s&#x27;</span></span><br><span class="line">  <span class="attr">mixColor</span>: <span class="string">&#x27;#fff&#x27;</span>, <span class="comment">// default: &#x27;#fff&#x27;</span></span><br><span class="line">  <span class="attr">backgroundColor</span>: <span class="string">&#x27;#fff&#x27;</span>,  <span class="comment">// default: &#x27;#fff&#x27;</span></span><br><span class="line">  <span class="attr">buttonColorDark</span>: <span class="string">&#x27;#100f2c&#x27;</span>,  <span class="comment">// default: &#x27;#100f2c&#x27;</span></span><br><span class="line">  <span class="attr">buttonColorLight</span>: <span class="string">&#x27;#fff&#x27;</span>, <span class="comment">// default: &#x27;#fff&#x27;</span></span><br><span class="line">  <span class="attr">saveInCookies</span>: <span class="literal">true</span>, <span class="comment">// default: true,</span></span><br><span class="line">  <span class="attr">label</span>: <span class="string">&#x27;🌓&#x27;</span>, <span class="comment">// default: &#x27;&#x27;</span></span><br><span class="line">  <span class="attr">autoMatchOsTheme</span>: <span class="literal">true</span> <span class="comment">// default: true</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">const</span> darkmode = <span class="keyword">new</span> <span class="title class_">Darkmode</span>(options);</span><br><span class="line">darkmode.<span class="title function_">showWidget</span>();</span><br><span class="line">&lt;/script&gt;</span><br><span class="line">&#123;%- endif %&#125;</span><br></pre></td></tr></table></figure><p>这里主要是在全局加入了一段初始化<code>darkmode-js</code>的代码。</p><p>重新生成即可开启:</p><figure class="highlight 1c"><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 clean <span class="meta">&amp;&amp; hexo g</span></span><br><span class="line">hexo s</span><br></pre></td></tr></table></figure><p>初步效果</p><p><img src="https://static.www.toimc.com/blog/img/2021/hexo-usage-1/dark-1.gif?watermark/2/text/VG9pbWPlh7rlk4E=/fontsize/200"></p><p>致命问题，没有对代码块<code>highlight</code>部分生效。</p><h3 id="代码Dark主题"><a href="#代码Dark主题" class="headerlink" title="代码Dark主题"></a>代码Dark主题</h3><p>从<code>next</code>的8.0开始，已经有了dark主题，直接可以<code>_config.xml</code>，参考：<a href="https://github.com/theme-next/hexo-theme-next/issues/1395">issues#1395</a></p><p>但是，按照官方的设置了之后，无法动态的切换了。</p><p>继续深挖<a href="https://github.com/next-theme/hexo-theme-next/commit/03e50d01ac59d136d8d9ccda187d898c0e424332">官方的代码</a>，有一个非常重要的文件<code>source/css/_colors.styl</code>：</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-pseudo">:root</span> &#123;</span><br><span class="line">  <span class="attr">--body-bg-color</span>: $body-bg-color;</span><br><span class="line">  <span class="attr">--content-bg-color</span>: $content-bg-color;</span><br><span class="line">  <span class="attr">--card-bg-color</span>: $card-bg-color;</span><br><span class="line">  <span class="attr">--text-color</span>: $text-color;</span><br><span class="line">  <span class="attr">--blockquote-color</span>: $blockquote-color;</span><br><span class="line">  <span class="attr">--link-color</span>: $link-color;</span><br><span class="line">  <span class="attr">--link-hover-color</span>: $link-hover-color;</span><br><span class="line">  <span class="attr">--brand-color</span>: $brand-color;</span><br><span class="line">  <span class="attr">--brand-hover-color</span>: $brand-hover-color;</span><br><span class="line">  <span class="attr">--table-row-odd-bg-color</span>: $table-row-odd-bg-color;</span><br><span class="line">  <span class="attr">--table-row-hover-bg-color</span>: $table-row-hover-bg-color;</span><br><span class="line">  <span class="attr">--menu-item-bg-color</span>: $menu-item-bg-color;</span><br><span class="line"></span><br><span class="line">  <span class="attr">--btn-default-bg</span>: $btn-default-bg;</span><br><span class="line">  <span class="attr">--btn-default-color</span>: $btn-default-color;</span><br><span class="line">  <span class="attr">--btn-default-border-color</span>: $btn-default-border-color;</span><br><span class="line">  <span class="attr">--btn-default-hover-bg</span>: $btn-default-hover-bg;</span><br><span class="line">  <span class="attr">--btn-default-hover-color</span>: $btn-default-hover-color;</span><br><span class="line">  <span class="attr">--btn-default-hover-border-color</span>: $btn-default-hover-border-color;</span><br><span class="line"></span><br><span class="line">  <span class="attr">--highlight-background</span>: $highlight-background;</span><br><span class="line">  <span class="attr">--highlight-foreground</span>: $highlight-foreground;</span><br><span class="line">  <span class="attr">--highlight-gutter-background</span>: $highlight-gutter-background;</span><br><span class="line">  <span class="attr">--highlight-gutter-foreground</span>: $highlight-gutter-foreground;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">if (hexo-config(&#x27;darkmode&#x27;)) &#123;</span><br><span class="line">  <span class="keyword">@media</span> (<span class="attribute">prefers-color-scheme</span>: dark) &#123;</span><br><span class="line">    <span class="selector-pseudo">:root</span> &#123;</span><br><span class="line">      <span class="attr">--body-bg-color</span>: $body-bg-color-dark;</span><br><span class="line">      <span class="attr">--content-bg-color</span>: $content-bg-color-dark;</span><br><span class="line">      <span class="attr">--card-bg-color</span>: $card-bg-color-dark;</span><br><span class="line">      <span class="attr">--text-color</span>: $text-color-dark;</span><br><span class="line">      <span class="attr">--blockquote-color</span>: $blockquote-color-dark;</span><br><span class="line">      <span class="attr">--link-color</span>: $link-color-dark;</span><br><span class="line">      <span class="attr">--link-hover-color</span>: $link-hover-color-dark;</span><br><span class="line">      <span class="attr">--brand-color</span>: $brand-color-dark;</span><br><span class="line">      <span class="attr">--brand-hover-color</span>: $brand-hover-color-dark;</span><br><span class="line">      <span class="attr">--table-row-odd-bg-color</span>: $table-row-odd-bg-color-dark;</span><br><span class="line">      <span class="attr">--table-row-hover-bg-color</span>: $table-row-hover-bg-color-dark;</span><br><span class="line">      <span class="attr">--menu-item-bg-color</span>: $menu-item-bg-color-dark;</span><br><span class="line"></span><br><span class="line">      <span class="attr">--btn-default-bg</span>: $btn-default-bg-dark;</span><br><span class="line">      <span class="attr">--btn-default-color</span>: $btn-default-color-dark;</span><br><span class="line">      <span class="attr">--btn-default-border-color</span>: $btn-default-border-color-dark;</span><br><span class="line">      <span class="attr">--btn-default-hover-bg</span>: $btn-default-hover-bg-dark;</span><br><span class="line">      <span class="attr">--btn-default-hover-color</span>: $btn-default-hover-color-dark;</span><br><span class="line">      <span class="attr">--btn-default-hover-border-color</span>: $btn-default-hover-border-color-dark;</span><br><span class="line"></span><br><span class="line">      <span class="attr">--highlight-background</span>: $highlight-background-dark;</span><br><span class="line">      <span class="attr">--highlight-foreground</span>: $highlight-foreground-dark;</span><br><span class="line">      <span class="attr">--highlight-gutter-background</span>: $highlight-gutter-background-dark;</span><br><span class="line">      <span class="attr">--highlight-gutter-foreground</span>: $highlight-gutter-foreground-dark;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="selector-tag">img</span> &#123;</span><br><span class="line">      <span class="attribute">opacity</span>: .<span class="number">75</span>;</span><br><span class="line"></span><br><span class="line">      &amp;<span class="selector-pseudo">:hover</span> &#123;</span><br><span class="line">        <span class="attribute">opacity</span>: .<span class="number">9</span>;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>原来切换深色与否是切换了基础的变量，那么怎么与darkmode-js&#96;结合呢？</p><blockquote><p>A CSS class <code>darkmode--activated</code> is added to the body tag when the darkmode is activated. You can take advantage of it to override the style and have a custom style</p></blockquote><p>在<code>darkmode</code>被激活的时候，会在body上添加一个<code>darkmode--activated</code>的类，那么如果我们把上面的样式添加到它的下面，是不是就实现了dark模式的切换？</p><p>首先，修改文件<code>next/source/css/_colors.styl</code>，把<code>:root</code>修改成<code>body</code>：</p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">body</span> &#123;</span><br><span class="line">  <span class="attr">--body-bg-color</span>: <span class="variable">$body</span>-bg-<span class="attribute">color</span>;</span><br><span class="line">  <span class="attr">--content-bg-color</span>: <span class="variable">$content</span>-bg-<span class="attribute">color</span>;</span><br><span class="line"><span class="comment">// ..</span></span><br></pre></td></tr></table></figure><p>然后添加文件<code>next/source/css/_common/scaffolding/darkmode.styl</code>：</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.darkmode--activated</span>&#123;</span><br><span class="line">  <span class="attr">--body-bg-color</span>: $body-bg-color-dark;</span><br><span class="line">  <span class="attr">--content-bg-color</span>: $content-bg-color-dark;</span><br><span class="line">  <span class="attr">--card-bg-color</span>: $card-bg-color-dark;</span><br><span class="line">  <span class="attr">--text-color</span>: $text-color-dark;</span><br><span class="line">  <span class="attr">--blockquote-color</span>: $blockquote-color-dark;</span><br><span class="line">  <span class="attr">--link-color</span>: $link-color-dark;</span><br><span class="line">  <span class="attr">--link-hover-color</span>: $link-hover-color-dark;</span><br><span class="line">  <span class="attr">--brand-color</span>: $brand-color-dark;</span><br><span class="line">  <span class="attr">--brand-hover-color</span>: $brand-hover-color-dark;</span><br><span class="line">  <span class="attr">--table-row-odd-bg-color</span>: $table-row-odd-bg-color-dark;</span><br><span class="line">  <span class="attr">--table-row-hover-bg-color</span>: $table-row-hover-bg-color-dark;</span><br><span class="line">  <span class="attr">--menu-item-bg-color</span>: $menu-item-bg-color-dark;</span><br><span class="line"></span><br><span class="line">  <span class="attr">--btn-default-bg</span>: $btn-default-bg-dark;</span><br><span class="line">  <span class="attr">--btn-default-color</span>: $btn-default-color-dark;</span><br><span class="line">  <span class="attr">--btn-default-border-color</span>: $btn-default-border-color-dark;</span><br><span class="line">  <span class="attr">--btn-default-hover-bg</span>: $btn-default-hover-bg-dark;</span><br><span class="line">  <span class="attr">--btn-default-hover-color</span>: $btn-default-hover-color-dark;</span><br><span class="line">  <span class="attr">--btn-default-hover-border-color</span>: $btn-default-hover-border-color-dark;</span><br><span class="line"></span><br><span class="line">  <span class="attr">--highlight-background</span>: $highlight-background-dark;</span><br><span class="line">  <span class="attr">--highlight-foreground</span>: $highlight-foreground-dark;</span><br><span class="line">  <span class="attr">--highlight-gutter-background</span>: $highlight-gutter-background-dark;</span><br><span class="line">  <span class="attr">--highlight-gutter-foreground</span>: $highlight-gutter-foreground-dark;</span><br><span class="line"></span><br><span class="line">  <span class="selector-tag">img</span> &#123;</span><br><span class="line">    <span class="attribute">opacity</span>: .<span class="number">75</span>;</span><br><span class="line"></span><br><span class="line">    &amp;<span class="selector-pseudo">:hover</span> &#123;</span><br><span class="line">      <span class="attribute">opacity</span>: .<span class="number">9</span>;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;    </span><br></pre></td></tr></table></figure><p>最后就是引入<code>next/source/css/_common/scaffolding/index.styl</code>：</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">//</span><br><span class="line">// Scaffolding</span><br><span class="line">// ==================================================</span><br><span class="line"><span class="keyword">@import</span> <span class="string">&#x27;normalize&#x27;</span>;</span><br><span class="line"><span class="keyword">@import</span> <span class="string">&#x27;base&#x27;</span>;</span><br><span class="line"><span class="keyword">@import</span> <span class="string">&#x27;tables&#x27;</span>;</span><br><span class="line"><span class="keyword">@import</span> <span class="string">&#x27;buttons&#x27;</span>;</span><br><span class="line"><span class="keyword">@import</span> <span class="string">&#x27;toggles&#x27;</span>;</span><br><span class="line"><span class="keyword">@import</span> <span class="string">&#x27;highlight&#x27;</span>;</span><br><span class="line"><span class="keyword">@import</span> <span class="string">&#x27;tags&#x27;</span>;</span><br><span class="line"><span class="keyword">@import</span> <span class="string">&#x27;pagination&#x27;</span>;</span><br><span class="line"><span class="keyword">@import</span> <span class="string">&#x27;comments&#x27;</span>;</span><br><span class="line">// 这里引入</span><br><span class="line"><span class="keyword">@import</span> <span class="string">&#x27;darkmode&#x27;</span>;</span><br></pre></td></tr></table></figure><p>实现效果：</p><p><img src="https://static.www.toimc.com/blog/img/2021/hexo-usage-1/dark.jpg?watermark/2/text/VG9pbWPlh7rlk4E=/fontsize/200"></p><h3 id="优化"><a href="#优化" class="headerlink" title="优化"></a>优化</h3><p>上面，显示的代码块<code>code</code>部分，不太完美，而且，对于头部也不太完美，所以我们进行样式上的调整：</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.darkmode--activated</span>&#123;</span><br><span class="line">  // ..</span><br><span class="line"></span><br><span class="line">// 优化内容</span><br><span class="line">  <span class="selector-tag">code</span> &#123;</span><br><span class="line">    <span class="attribute">color</span>: <span class="number">#69dbdc</span>;</span><br><span class="line">    <span class="attribute">background</span>: transparent;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="selector-class">.header</span> &#123;</span><br><span class="line">    <span class="attribute">background</span>: <span class="number">#fff</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;    </span><br></pre></td></tr></table></figure><p>本文完。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;本文主要介绍为hexo next主题添加一个可以切换的黑色&amp;#x2F;夜间模式，虽然next 8.0 主题已经支持原生黑色模式，但是这个黑色模式是不可以切换的。本文介绍如何实现按钮来切换，包括如何在代码部分也切换夜间模式。&lt;/p&gt;</summary>
    
    
    
    <category term="hexo博客进阶玩法" scheme="https://www.toimc.com/categories/hexo%E5%8D%9A%E5%AE%A2%E8%BF%9B%E9%98%B6%E7%8E%A9%E6%B3%95/"/>
    
    
    <category term="node.js" scheme="https://www.toimc.com/tags/node-js/"/>
    
    <category term="hexo" scheme="https://www.toimc.com/tags/hexo/"/>
    
    <category term="next" scheme="https://www.toimc.com/tags/next/"/>
    
    <category term="前端" scheme="https://www.toimc.com/tags/%E5%89%8D%E7%AB%AF/"/>
    
  </entry>
  
  <entry>
    <title>nestjs搭建通用业务框架（5）：数据库+配置</title>
    <link href="https://www.toimc.com/nestjs-example-project-5/"/>
    <id>https://www.toimc.com/nestjs-example-project-5/</id>
    <published>2021-03-17T00:06:28.000Z</published>
    <updated>2025-10-06T05:13:13.973Z</updated>
    
    <content type="html"><![CDATA[<p>这是《nestjs搭建通用业务框架》系列的第5篇，进入开发具体的功能之前，学习nest框架本身提供的CLI工具与规划合理的工程目录，对于要实现的内容进行架构与计划，这是实现健壮高可用的框架的前提。</p><span id="more"></span><p>技术整合会从三个层次进行介绍：</p><ul><li>数据库 -&gt; 配置(多环境) -&gt; 配置验证 -&gt; 系统日志（本篇）</li><li>跨域 -&gt; 错误拦截器 -&gt; 缓存Redis</li><li>数据校验 -&gt; 日志拦截 -&gt; 鉴权</li></ul><h2 id="数据库"><a href="#数据库" class="headerlink" title="数据库"></a>数据库</h2><h3 id="ORM工具库"><a href="#ORM工具库" class="headerlink" title="ORM工具库"></a>ORM工具库</h3><p>通过数据库集成库或 <code>ORM</code> ，例如 <a href="https://www.npmjs.com/package/sequelize">Sequelize (recipe)</a>和 <a href="https://github.com/typeorm/typeorm">TypeORM</a> ，以在更高的抽象级别上进行操作。</p><blockquote><p>ORM：对象关系映射（英语：Object Relational Mapping）是一种程序设计技术，用于实现面向对象编程语言里不同类型系统的数据之间的转换。 从效果上说，它其实是创建了一个可在编程语言里使用的“虚拟对象数据库”。</p></blockquote><p>应用场景：</p><ul><li>SQL -&gt; DB：我们写一套配置，针对不同的数据库，都可以方便的接入</li><li>DB -&gt; SQL：针对不同的数据库，都可以通过抽象层进行联接</li></ul><blockquote><p><code>Nest</code> 还提供了与现成的 <code>TypeORM</code> 与 <code>@nestjs/typeorm</code> 的紧密集成，我们将在本章中对此进行介绍，而与 <code>@nestjs/mongoose</code> 的紧密集成将在官方的<a href="https://docs.nestjs.cn/7/techniques?id=mongo">这一章</a>中介绍</p></blockquote><p>目前主要的ORM工具库与特点：</p><ul><li><a href="https://www.npmjs.com/package/typeorm">typeorm</a>：跨库查询，事务、TS支持，支持数据库：MySQL, MariaDB, Postgres, CockroachDB, SQLite, MSSQL, Oracle, SAPHana, sql.js, MongoDB</li><li><a href="https://www.npmjs.com/package/objection">objection</a>： TS、事务、饥饿加载、数据效验，基于<a href="http://knexjs.org/">knexjs</a>，支持数据库：Postgres**, <strong>MSSQL</strong>, <strong>MySQL</strong>, <strong>MariaDB</strong>, <strong>SQLite3</strong>, **Oracle, Amazon Redshift</li><li><a href="https://www.npmjs.com/package/sequelize">sequelize</a>： 有非官方的中文文档，目前缺少核心的维护与开发。支持：PostgreSQL, MySQL, MariaDB, SQLite, MSSQL</li><li><a href="https://www.npmjs.com/package/prisma">prisma</a>：后起之秀(官方文档写的很不错)，SQL自动合并，对接GraphQL，客户端、服务端+数据管理GUI，支持：PostgreSQL, MSSQL, MySQL, SQLite</li></ul><p>通过上面的简单对比，目前来看TypeORM是<code>nest</code>官方支持且推荐的，可以来<a href="https://typeorm.biunav.com/zh/#%E5%AE%89%E8%A3%85">这里</a>看看它的特性。</p><h3 id="集成Postgre-MySQL"><a href="#集成Postgre-MySQL" class="headerlink" title="集成Postgre(MySQL)"></a>集成<code>Postgre(MySQL)</code></h3><p>步骤：</p><ul><li>安装<code>@nestjs/typeorm</code>，<code>typeorm</code></li><li>安装nodejs侧的数据库驱动程序，如<code>mysql</code>，<code>pg</code></li><li>新建数据库配置文件，配置数据库</li><li>在<code>app.module.ts</code>引入数据库的配置文件，调用<code>TypeOrmModule.forRoot</code>方法</li><li>启动程序，进行测试</li></ul><p>安装依赖（跳过数据库安装过程）：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install --save @nestjs/typeorm typeorm pg</span><br></pre></td></tr></table></figure><p>按照上面的步骤，创建文件<code>src/config/database.config.ts</code>：</p><p>数据库配置信息：</p><figure class="highlight typescript"><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="keyword">import</span> &#123; <span class="title class_">TypeOrmModuleOptions</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/typeorm&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> <span class="attr">typeOrmConfig</span>: <span class="title class_">TypeOrmModuleOptions</span> = &#123;</span><br><span class="line">  <span class="attr">type</span>: <span class="string">&#x27;postgres&#x27;</span>,</span><br><span class="line">  <span class="attr">host</span>: <span class="string">&#x27;localhost&#x27;</span>,</span><br><span class="line">  <span class="attr">port</span>: <span class="number">5432</span>,</span><br><span class="line">  <span class="attr">username</span>: <span class="string">&#x27;postgres&#x27;</span>,</span><br><span class="line">  <span class="attr">password</span>: <span class="string">&#x27;changeme&#x27;</span>,</span><br><span class="line">  <span class="attr">database</span>: <span class="string">&#x27;demo-db&#x27;</span>,</span><br><span class="line">  <span class="attr">entities</span>: [<span class="string">`<span class="subst">$&#123;__dirname&#125;</span>/../entity/**/*.&#123;js,ts&#125;`</span>],</span><br><span class="line">  <span class="attr">synchronize</span>: <span class="literal">false</span>,</span><br><span class="line">  <span class="attr">logging</span>: [<span class="string">&quot;error&quot;</span>],</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在<code>src/app.module.ts</code>中添加<code>TypeOrm</code>配置：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">Module</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/common&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">TypeOrmModule</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/typeorm&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; typeOrmConfig &#125; <span class="keyword">from</span> <span class="string">&#x27;./config/typeorm.config&#x27;</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@Module</span>(&#123;</span><br><span class="line">  <span class="attr">imports</span>: [</span><br><span class="line">    <span class="title class_">TypeOrmModule</span>.<span class="title function_">forRoot</span>(typeOrmConfig)</span><br><span class="line">  ],</span><br><span class="line">  <span class="attr">providers</span>: [],</span><br><span class="line">&#125;)</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">AppModule</span> &#123;&#125;</span><br></pre></td></tr></table></figure><p>然后就可以使用<code>npm run start:dev</code>来进行调试了。</p><h3 id="集成MongoDB"><a href="#集成MongoDB" class="headerlink" title="集成MongoDB"></a>集成<code>MongoDB</code></h3><p>非关系型数据库<code>MongoDB</code>同样可以使用<code>TypeORM</code>，官方提供了<code>@nestjs/mongoose</code>包，所以，我们来介绍两种集成方法：</p><h4 id="官方-nestjs-mongoose"><a href="#官方-nestjs-mongoose" class="headerlink" title="官方@nestjs/mongoose"></a>官方<code>@nestjs/mongoose</code></h4><p>安装依赖：</p><figure class="highlight coffeescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">npm</span> install --save @nestjs/mongoose mongoose</span><br></pre></td></tr></table></figure><p>配置<code>app.module.ts</code>：</p><figure class="highlight typescript"><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="keyword">import</span> &#123; <span class="title class_">Module</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/common&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">MongooseModule</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/mongoose&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Module</span>(&#123;</span><br><span class="line">  <span class="attr">imports</span>: [<span class="title class_">MongooseModule</span>.<span class="title function_">forRoot</span>(<span class="string">&#x27;mongodb://localhost:27017/test&#x27;</span>)],</span><br><span class="line">&#125;)</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">AppModule</span> &#123;&#125;</span><br></pre></td></tr></table></figure><h4 id="使用mongoose库"><a href="#使用mongoose库" class="headerlink" title="使用mongoose库"></a>使用<code>mongoose</code>库</h4><p>安装依赖：</p><figure class="highlight ada"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install <span class="comment">--save mongoose</span></span><br></pre></td></tr></table></figure><p>新建<code>src/database/database.providers.ts</code></p><figure class="highlight typescript"><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="keyword">import</span> * <span class="keyword">as</span> mongoose <span class="keyword">from</span> <span class="string">&#x27;mongoose&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> databaseProviders = [</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="attr">provide</span>: <span class="string">&#x27;DATABASE_CONNECTION&#x27;</span>,</span><br><span class="line">    <span class="attr">useFactory</span>: <span class="keyword">async</span> (): <span class="title class_">Promise</span>&lt;<span class="keyword">typeof</span> mongoose&gt; =&gt;</span><br><span class="line">      <span class="keyword">await</span> mongoose.<span class="title function_">connect</span>(<span class="string">&#x27;mongodb://localhost:27017/test&#x27;</span>),</span><br><span class="line">  &#125;,</span><br><span class="line">];</span><br></pre></td></tr></table></figure><p>新建<code>src/database/database.module.ts</code>：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">import &#123; Module &#125; from &#x27;@nestjs/common&#x27;;</span><br><span class="line">import &#123; databaseProviders &#125; from &#x27;./database.providers&#x27;;</span><br><span class="line"></span><br><span class="line">@Module(&#123;</span><br><span class="line">  providers: [...databaseProviders],</span><br><span class="line">  exports: [...databaseProviders],</span><br><span class="line">&#125;)</span><br><span class="line">export class DatabaseModule &#123;&#125;</span><br></pre></td></tr></table></figure><p>配置<code>app.module.ts</code>：</p><figure class="highlight typescript"><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="keyword">import</span> &#123; <span class="title class_">Module</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/common&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">DatabaseModule</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./database/database.module.ts&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Module</span>(&#123;</span><br><span class="line">  <span class="attr">imports</span>: [</span><br><span class="line">  <span class="title class_">DatabaseModule</span>,</span><br><span class="line">  ],</span><br><span class="line">&#125;)</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">AppModule</span> &#123;&#125;</span><br></pre></td></tr></table></figure><h2 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h2><p>应用程序通常在不同的<strong>环境</strong>中运行，根据环境（Development，Production）的不同，应该使用不同的配置设置。</p><p>两种方法：</p><ul><li>使用<code>@nestjs/config</code>来实现对<code>.env</code>的<code>key=value</code>对进行解析</li><li>使用<code>config</code>库解析<code>yaml</code>格式的文件</li></ul><h3 id="官方-nestjs-config"><a href="#官方-nestjs-config" class="headerlink" title="官方@nestjs/config"></a>官方<code>@nestjs/config</code></h3><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></pre></td><td class="code"><pre><span class="line">npm i --save @nestjs/config</span><br></pre></td></tr></table></figure><p>配置<code>src/app.module.ts</code>：</p><figure class="highlight typescript"><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="keyword">import</span> &#123; <span class="title class_">Module</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/common&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">AppController</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./app.controller&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">AppService</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./app.service&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">ConfigModule</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/config&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Module</span>(&#123;</span><br><span class="line">  <span class="attr">imports</span>: [</span><br><span class="line">    <span class="title class_">ConfigModule</span>.<span class="title function_">forRoot</span>(),</span><br><span class="line">  ],</span><br><span class="line">  <span class="attr">controllers</span>: [<span class="title class_">AppController</span>],</span><br><span class="line">  <span class="attr">providers</span>: [<span class="title class_">AppService</span>],</span><br><span class="line">&#125;)</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">AppModule</span> &#123;&#125;</span><br></pre></td></tr></table></figure><p>然后创建：<code>.env</code>文件：</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">DATABASE_USER</span>=test</span><br><span class="line"><span class="attr">DATABASE_PASSWORD</span>=test123</span><br></pre></td></tr></table></figure><p>下面来使用<code>src/app.controller.ts</code>中使用：</p><figure class="highlight typescript"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">Controller</span>, <span class="title class_">Get</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/common&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">ConfigService</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/config&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">AppService</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./app.service&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Controller</span>()</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">AppController</span> &#123;</span><br><span class="line">  <span class="title function_">constructor</span>(<span class="params"></span></span><br><span class="line"><span class="params">    <span class="keyword">private</span> <span class="keyword">readonly</span> appService: AppService,</span></span><br><span class="line"><span class="params">    <span class="keyword">private</span> configService: ConfigService,</span></span><br><span class="line"><span class="params">  </span>) &#123;&#125;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Get</span>()</span><br><span class="line">  <span class="title function_">getHello</span>(): <span class="built_in">string</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> dbUser = <span class="variable language_">this</span>.<span class="property">configService</span>.<span class="property">get</span>&lt;<span class="built_in">string</span>&gt;(<span class="string">&#x27;DATABASE_USER&#x27;</span>);</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(dbUser); <span class="comment">// 这里来测试</span></span><br><span class="line">    <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">appService</span>.<span class="title function_">getHello</span>();</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果访问<code>localhost:3000</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></pre></td><td class="code"><pre><span class="line">[Nest] 14039   - 2021/03/13 下午9:43:54   [NestFactory] Starting Nest application...</span><br><span class="line">[Nest] 14039   - 2021/03/13 下午9:43:54   [InstanceLoader] ConfigHostModule dependencies initialized +95ms</span><br><span class="line">[Nest] 14039   - 2021/03/13 下午9:43:54   [InstanceLoader] ConfigModule dependencies initialized +0ms</span><br><span class="line">[Nest] 14039   - 2021/03/13 下午9:43:54   [InstanceLoader] AppModule dependencies initialized +1ms</span><br><span class="line">[Nest] 14039   - 2021/03/13 下午9:43:54   [RoutesResolver] AppController &#123;&#125;: +7ms</span><br><span class="line">[Nest] 14039   - 2021/03/13 下午9:43:54   [RouterExplorer] Mapped &#123;, GET&#125; route +3ms</span><br><span class="line">[Nest] 14039   - 2021/03/13 下午9:43:54   [NestApplication] Nest application successfully started +2ms</span><br><span class="line"><span class="built_in">test</span></span><br></pre></td></tr></table></figure><h4 id="进阶玩法"><a href="#进阶玩法" class="headerlink" title="进阶玩法"></a>进阶玩法</h4><p><img src="https://static.www.toimc.com/blog/img/2021/nestjs-example-project-4/nestjs-1.jpg?watermark/2/text/VG9pbWPlh7rlk4E=/fontsize/200"></p><p>从这里点进去，我们发现<code>ConfigModuleOptions</code>：</p><figure class="highlight typescript"><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="keyword">import</span> &#123; <span class="title class_">ConfigFactory</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./config-factory.interface&#x27;</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">interface</span> <span class="title class_">ConfigModuleOptions</span> &#123;</span><br><span class="line">    cache?: <span class="built_in">boolean</span>;</span><br><span class="line">    isGlobal?: <span class="built_in">boolean</span>;</span><br><span class="line">    ignoreEnvFile?: <span class="built_in">boolean</span>;</span><br><span class="line">    ignoreEnvVars?: <span class="built_in">boolean</span>;</span><br><span class="line">    envFilePath?: <span class="built_in">string</span> | <span class="built_in">string</span>[];</span><br><span class="line">    encoding?: <span class="built_in">string</span>;</span><br><span class="line">    validate?: <span class="function">(<span class="params">config: Record&lt;<span class="built_in">string</span>, <span class="built_in">any</span>&gt;</span>) =&gt;</span> <span class="title class_">Record</span>&lt;<span class="built_in">string</span>, <span class="built_in">any</span>&gt;;</span><br><span class="line">    validationSchema?: <span class="built_in">any</span>;</span><br><span class="line">    validationOptions?: <span class="title class_">Record</span>&lt;<span class="built_in">string</span>, <span class="built_in">any</span>&gt;;</span><br><span class="line">    load?: <span class="title class_">Array</span>&lt;<span class="title class_">ConfigFactory</span>&gt;;</span><br><span class="line">    expandVariables?: <span class="built_in">boolean</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>所支持的参数。</p><p>我们可以利用<code>envFilePath</code>配合<code>NODE_ENV</code>来，在不同的启动命令的时候使用不同的配置。</p><figure class="highlight mel"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm i <span class="keyword">cross</span>-<span class="keyword">env</span></span><br></pre></td></tr></table></figure><p>然后添加两个文件：<code>.env.development</code>与<code>.env.production</code>，比如<code>.env.production</code>：</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">DATABASE_USER</span>=test1</span><br><span class="line"><span class="attr">DATABASE_PASSWORD</span>=test123321</span><br></pre></td></tr></table></figure><p>下面修改<code>scripts</code>：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">&quot;start:prod&quot;</span><span class="punctuation">:</span> <span class="string">&quot;cross-env NODE_ENV=production node dist/main&quot;</span><span class="punctuation">,</span></span><br></pre></td></tr></table></figure><p>可以设置<code>app.module.ts</code>中默认是<code>development</code>：</p><figure class="highlight typescript"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">Module</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/common&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">AppController</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./app.controller&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">AppService</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./app.service&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">ConfigModule</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/config&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> envPath = <span class="string">`.env.<span class="subst">$&#123;process.env.NODE_ENV || <span class="string">&#x27;development&#x27;</span>&#125;</span>`</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;🚀 ~ file: app.module.ts ~ line 7 ~ envPath&#x27;</span>, envPath);</span><br><span class="line"></span><br><span class="line"><span class="meta">@Module</span>(&#123;</span><br><span class="line">  <span class="attr">imports</span>: [</span><br><span class="line">    <span class="title class_">ConfigModule</span>.<span class="title function_">forRoot</span>(&#123;</span><br><span class="line">      <span class="attr">envFilePath</span>: envPath,</span><br><span class="line">    &#125;),</span><br><span class="line">  ],</span><br><span class="line">  <span class="attr">controllers</span>: [<span class="title class_">AppController</span>],</span><br><span class="line">  <span class="attr">providers</span>: [<span class="title class_">AppService</span>],</span><br><span class="line">&#125;)</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">AppModule</span> &#123;&#125;</span><br></pre></td></tr></table></figure><p>同样，大家可以启动了测试一下。</p><figure class="highlight gradle"><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">➜ npm run start:prod</span><br><span class="line"></span><br><span class="line">&gt; nestjs-common-template@<span class="number">0.0</span>.<span class="number">1</span> start:prod <span class="regexp">/Users/m</span>acos<span class="regexp">/Projects/</span>nestjs/nestjs-common-template</span><br><span class="line">&gt; cross-env NODE_ENV=production node dist/main</span><br><span class="line"></span><br><span class="line">🚀 ~ <span class="keyword">file</span>: app.module.ts ~ line <span class="number">7</span> ~ envPath .env.production</span><br><span class="line">[Nest] <span class="number">14977</span>   - <span class="number">2021</span><span class="regexp">/03/</span><span class="number">13</span> 下午<span class="number">11</span>:<span class="number">10</span>:<span class="number">13</span>   [NestFactory] Starting Nest application...</span><br><span class="line">[Nest] <span class="number">14977</span>   - <span class="number">2021</span><span class="regexp">/03/</span><span class="number">13</span> 下午<span class="number">11</span>:<span class="number">10</span>:<span class="number">13</span>   [InstanceLoader] ConfigHostModule <span class="keyword">dependencies</span> initialized +<span class="number">34</span>ms</span><br><span class="line">[Nest] <span class="number">14977</span>   - <span class="number">2021</span><span class="regexp">/03/</span><span class="number">13</span> 下午<span class="number">11</span>:<span class="number">10</span>:<span class="number">13</span>   [InstanceLoader] ConfigModule <span class="keyword">dependencies</span> initialized +<span class="number">1</span>ms</span><br><span class="line">[Nest] <span class="number">14977</span>   - <span class="number">2021</span><span class="regexp">/03/</span><span class="number">13</span> 下午<span class="number">11</span>:<span class="number">10</span>:<span class="number">13</span>   [InstanceLoader] AppModule <span class="keyword">dependencies</span> initialized +<span class="number">1</span>ms</span><br><span class="line">[Nest] <span class="number">14977</span>   - <span class="number">2021</span><span class="regexp">/03/</span><span class="number">13</span> 下午<span class="number">11</span>:<span class="number">10</span>:<span class="number">13</span>   [RoutesResolver] AppController &#123;&#125;: +<span class="number">6</span>ms</span><br><span class="line">[Nest] <span class="number">14977</span>   - <span class="number">2021</span><span class="regexp">/03/</span><span class="number">13</span> 下午<span class="number">11</span>:<span class="number">10</span>:<span class="number">13</span>   [RouterExplorer] Mapped &#123;, GET&#125; route +<span class="number">3</span>ms</span><br><span class="line">[Nest] <span class="number">14977</span>   - <span class="number">2021</span><span class="regexp">/03/</span><span class="number">13</span> 下午<span class="number">11</span>:<span class="number">10</span>:<span class="number">13</span>   [NestApplication] Nest application successfully started +<span class="number">3</span>ms</span><br><span class="line">test1</span><br></pre></td></tr></table></figure><p>上面打印的<code>test1</code>正是我们设置在<code>.env.production</code>中的内容。</p><h4 id="解析yaml格式的配置"><a href="#解析yaml格式的配置" class="headerlink" title="解析yaml格式的配置"></a>解析<code>yaml</code>格式的配置</h4><p>步骤：</p><ul><li><p>下载<code>js-yaml</code>与<code>@types/js-yaml</code></p><figure class="highlight coffeescript"><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">npm</span> i js-yaml</span><br><span class="line"><span class="built_in">npm</span> i -D @types/js-yaml</span><br></pre></td></tr></table></figure></li><li><p>创建配置：<code>config.yml</code></p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">http:</span></span><br><span class="line">  <span class="attr">host:</span> <span class="string">&#x27;localhost&#x27;</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">8080</span></span><br><span class="line"></span><br><span class="line"><span class="attr">db:</span></span><br><span class="line">  <span class="attr">postgres:</span></span><br><span class="line">    <span class="attr">url:</span> <span class="string">&#x27;localhost&#x27;</span></span><br><span class="line">    <span class="attr">port:</span> <span class="number">5432</span></span><br><span class="line">    <span class="attr">database:</span> <span class="string">&#x27;yaml-db&#x27;</span></span><br><span class="line"></span><br><span class="line">  <span class="attr">sqlite:</span></span><br><span class="line">    <span class="attr">database:</span> <span class="string">&#x27;sqlite.db&#x27;</span></span><br></pre></td></tr></table></figure></li><li><p>配置自定义文件<code>configuration.ts</code></p><figure class="highlight typescript"><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="keyword">import</span> &#123; readFileSync &#125; <span class="keyword">from</span> <span class="string">&#x27;fs&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> yaml <span class="keyword">from</span> <span class="string">&#x27;js-yaml&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; join &#125; <span class="keyword">from</span> <span class="string">&#x27;path&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">YAML_CONFIG_FILENAME</span> = <span class="string">&#x27;config.yml&#x27;</span>;</span><br><span class="line"><span class="keyword">const</span> filePath = <span class="title function_">join</span>(__dirname, <span class="variable constant_">YAML_CONFIG_FILENAME</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> () =&gt; &#123;</span><br><span class="line">  <span class="keyword">return</span> yaml.<span class="title function_">load</span>(<span class="title function_">readFileSync</span>(filePath, <span class="string">&#x27;utf8&#x27;</span>));</span><br><span class="line">&#125;;</span><br><span class="line"></span><br></pre></td></tr></table></figure></li><li><p>调用<code>forRoot</code>中的load方法</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">Module</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/common&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">AppController</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./app.controller&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">AppService</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./app.service&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">ConfigModule</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/config&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="title class_">Configuration</span> <span class="keyword">from</span> <span class="string">&#x27;./config/configuration&#x27;</span>; <span class="comment">// 这里调整</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Module</span>(&#123;</span><br><span class="line">  <span class="attr">imports</span>: [</span><br><span class="line">    <span class="title class_">ConfigModule</span>.<span class="title function_">forRoot</span>(&#123;</span><br><span class="line">      <span class="attr">load</span>: [<span class="title class_">Configuration</span>], <span class="comment">// load方法</span></span><br><span class="line">    &#125;),</span><br><span class="line">  ],</span><br><span class="line">  <span class="attr">controllers</span>: [<span class="title class_">AppController</span>],</span><br><span class="line">  <span class="attr">providers</span>: [<span class="title class_">AppService</span>],</span><br><span class="line">&#125;)</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">AppModule</span> &#123;&#125;</span><br></pre></td></tr></table></figure></li><li><p>修改<code>app.controller.ts</code>中的代码：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">Controller</span>, <span class="title class_">Get</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/common&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">ConfigService</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/config&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">AppService</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./app.service&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">DatabaseConfig</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./interface&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Controller</span>()</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">AppController</span> &#123;</span><br><span class="line">  <span class="title function_">constructor</span>(<span class="params"></span></span><br><span class="line"><span class="params">    <span class="keyword">private</span> <span class="keyword">readonly</span> appService: AppService,</span></span><br><span class="line"><span class="params">    <span class="keyword">private</span> configService: ConfigService,</span></span><br><span class="line"><span class="params">  </span>) &#123;&#125;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Get</span>()</span><br><span class="line">  <span class="title function_">getHello</span>(): <span class="built_in">string</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> db = <span class="variable language_">this</span>.<span class="property">configService</span>.<span class="property">get</span>&lt;<span class="title class_">DatabaseConfig</span>&gt;(<span class="string">&#x27;db&#x27;</span>);</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(db);</span><br><span class="line">    <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">appService</span>.<span class="title function_">getHello</span>();</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>定义<code>src/interface.ts</code>：</p><figure class="highlight typescript"><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="keyword">export</span> <span class="keyword">interface</span> <span class="title class_">DatabaseConfig</span> &#123;</span><br><span class="line">  <span class="attr">postgres</span>: <span class="title class_">PostgresConfig</span>;</span><br><span class="line">  <span class="attr">sqlite</span>: <span class="title class_">SqliteConfig</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">interface</span> <span class="title class_">PostgresConfig</span> &#123;</span><br><span class="line">  <span class="attr">url</span>: <span class="built_in">string</span>;</span><br><span class="line">  <span class="attr">port</span>: <span class="built_in">number</span>;</span><br><span class="line">  <span class="attr">database</span>: <span class="built_in">string</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">interface</span> <span class="title class_">SqliteConfig</span> &#123;</span><br><span class="line">  <span class="attr">database</span>: <span class="built_in">string</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></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">[Nest] 16960   - 2021/03/13 下午11:34:00   [NestFactory] Starting Nest application...</span><br><span class="line">[Nest] 16960   - 2021/03/13 下午11:34:00   [InstanceLoader] ConfigHostModule dependencies initialized +30ms</span><br><span class="line">[Nest] 16960   - 2021/03/13 下午11:34:00   [InstanceLoader] ConfigModule dependencies initialized +0ms</span><br><span class="line">[Nest] 16960   - 2021/03/13 下午11:34:00   [InstanceLoader] AppModule dependencies initialized +0ms</span><br><span class="line">[Nest] 16960   - 2021/03/13 下午11:34:00   [RoutesResolver] AppController &#123;&#125;: +4ms</span><br><span class="line">[Nest] 16960   - 2021/03/13 下午11:34:00   [RouterExplorer] Mapped &#123;, GET&#125; route +3ms</span><br><span class="line">[Nest] 16960   - 2021/03/13 下午11:34:00   [NestApplication] Nest application successfully started +1ms</span><br><span class="line">&#123;</span><br><span class="line">  postgres: &#123; url: <span class="string">&#x27;localhost&#x27;</span>, port: 5432, database: <span class="string">&#x27;yaml-db&#x27;</span> &#125;,</span><br><span class="line">  sqlite: &#123; database: <span class="string">&#x27;sqlite.db&#x27;</span> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>写到这里，应该够用了，代码可以查看<a href="https://github.com/toimc/nestjs-example-template/tree/48c0d75e807faa1da7de53de26a5b1512a6584aa">本次提交</a>。</p><h3 id="使用config库解析"><a href="#使用config库解析" class="headerlink" title="使用config库解析"></a>使用<code>config</code>库解析</h3><p>步骤：</p><ul><li><p>安装第三方包<code>config</code></p><figure class="highlight coffeescript"><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">npm</span> i config -S</span><br><span class="line"><span class="built_in">npm</span> i cross-env -D</span><br></pre></td></tr></table></figure></li><li><p>新建 配置文件<code>config/default.json</code>，同样还可以建立<code>development.json</code>, <code>production.json</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;server&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;happy&quot;</span><span class="punctuation">:</span> <span class="string">&quot;my default value&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><p><code>development.json</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;server&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;port&quot;</span><span class="punctuation">:</span> <span class="number">3001</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;host&quot;</span><span class="punctuation">:</span> <span class="string">&quot;localhost&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;username&quot;</span><span class="punctuation">:</span> <span class="string">&quot;test&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;password&quot;</span><span class="punctuation">:</span> <span class="string">&quot;test&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><p><code>production.json</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;server&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;port&quot;</span><span class="punctuation">:</span> <span class="number">3002</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;host&quot;</span><span class="punctuation">:</span> <span class="string">&quot;localhost&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;username&quot;</span><span class="punctuation">:</span> <span class="string">&quot;prod&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;password&quot;</span><span class="punctuation">:</span> <span class="string">&quot;prod&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></li><li><p>在<code>app.controller.ts</code>中使用：</p><figure class="highlight typescript"><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="keyword">import</span> &#123; <span class="title class_">Controller</span>, <span class="title class_">Get</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/common&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">AppService</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./app.service&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> config <span class="keyword">from</span> <span class="string">&#x27;config&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Controller</span>()</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">AppController</span> &#123;</span><br><span class="line">  <span class="title function_">constructor</span>(<span class="params"><span class="keyword">private</span> <span class="keyword">readonly</span> appService: AppService</span>) &#123;&#125;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Get</span>()</span><br><span class="line">  <span class="title function_">getHello</span>(): <span class="built_in">string</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> server = config.<span class="title function_">get</span>(<span class="string">&#x27;server&#x27;</span>);</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(server);</span><br><span class="line">    <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">appService</span>.<span class="title function_">getHello</span>();</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>配置脚本：</p><figure class="highlight json"><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">&quot;start:dev&quot;</span><span class="punctuation">:</span> <span class="string">&quot;cross-env NODE_ENV=development nest start --watch&quot;</span><span class="punctuation">,</span></span><br><span class="line"><span class="attr">&quot;start:prod&quot;</span><span class="punctuation">:</span> <span class="string">&quot;cross-env NODE_ENV=production node dist/main&quot;</span><span class="punctuation">,</span></span><br></pre></td></tr></table></figure></li><li><p>运行结果：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">➜ npm run start:dev</span><br><span class="line">[Nest] 34580   - 2021/03/14 上午12:50:42   [NestFactory] Starting Nest application...</span><br><span class="line">[Nest] 34580   - 2021/03/14 上午12:50:42   [InstanceLoader] AppModule dependencies initialized +34ms</span><br><span class="line">[Nest] 34580   - 2021/03/14 上午12:50:42   [RoutesResolver] AppController &#123;&#125;: +6ms</span><br><span class="line">[Nest] 34580   - 2021/03/14 上午12:50:42   [RouterExplorer] Mapped &#123;, GET&#125; route +3ms</span><br><span class="line">[Nest] 34580   - 2021/03/14 上午12:50:42   [NestApplication] Nest application successfully started +2ms</span><br><span class="line">&#123;</span><br><span class="line">  happy: <span class="string">&#x27;my default value&#x27;</span>,</span><br><span class="line">  port: 3001,</span><br><span class="line">  host: <span class="string">&#x27;localhost&#x27;</span>,</span><br><span class="line">  username: <span class="string">&#x27;test&#x27;</span>,</span><br><span class="line">  password: <span class="string">&#x27;test&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">➜ npm run start:prod</span><br><span class="line"></span><br><span class="line">&gt; nestjs-common-template@0.0.1 start:prod /Users/macos/Projects/nestjs/nestjs-common-template</span><br><span class="line">&gt; cross-env NODE_ENV=production node dist/main</span><br><span class="line"></span><br><span class="line">[Nest] 34400   - 2021/03/14 上午12:50:03   [NestFactory] Starting Nest application...</span><br><span class="line">[Nest] 34400   - 2021/03/14 上午12:50:03   [InstanceLoader] AppModule dependencies initialized +71ms</span><br><span class="line">[Nest] 34400   - 2021/03/14 上午12:50:03   [RoutesResolver] AppController &#123;&#125;: +6ms</span><br><span class="line">[Nest] 34400   - 2021/03/14 上午12:50:03   [RouterExplorer] Mapped &#123;, GET&#125; route +2ms</span><br><span class="line">[Nest] 34400   - 2021/03/14 上午12:50:03   [NestApplication] Nest application successfully started +2ms</span><br><span class="line">&#123;</span><br><span class="line">  happy: <span class="string">&#x27;my default value&#x27;</span>,</span><br><span class="line">  port: 3002,</span><br><span class="line">  host: <span class="string">&#x27;localhost&#x27;</span>,</span><br><span class="line">  username: <span class="string">&#x27;prod&#x27;</span>,</span><br><span class="line">  password: <span class="string">&#x27;prod&#x27;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>附上：<a href="https://github.com/toimc/nestjs-example-template/tree/50858c28746323ee69a4dc65c6624f7d7c0937be">代码地址</a></p></li></ul><h3 id="配置验证"><a href="#配置验证" class="headerlink" title="配置验证"></a>配置验证</h3><p>配置验证，主要是指在应用程序启动时，如果没有提供所需的环境变量或不符合某些验证规则，就会抛出一个异常。<code>@nestjs/config</code>包实现了两种不同的方式来实现这一点。</p><ul><li><code>Joi</code>内置验证器。通过<a href="https://www.npmjs.com/package/joi">Joi</a>，你可以定义一个对象模式，并根据它验证JavaScript对象</li><li>一个自定义的<code>validate()</code>函数，它将环境变量作为输入</li></ul><h4 id="Joi用法"><a href="#Joi用法" class="headerlink" title="Joi用法"></a>Joi用法</h4><p>特别说明：</p><ul><li>最新版本的<code>joi</code>需要你运行Node v12或更高版本。旧版本的node请安装<code>v16.1.8</code>。这主要是因为在<code>v17.0.2</code>发布后，在构建的时候会出现错误。更多信息请参考其17.0.0发布说明，<a href="https://joi.dev/resources/changelog/">点击这里</a>。</li><li>joi最好配合官方的<code>@nestjs/config</code>进行使用</li></ul><p>步骤：</p><ul><li><p>安装依赖</p><figure class="highlight ada"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install <span class="comment">--save joi</span></span><br></pre></td></tr></table></figure></li><li><p>定义验证Schema：</p><figure class="highlight typescript"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">Module</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/common&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">AppController</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./app.controller&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">AppService</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./app.service&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> <span class="title class_">Joi</span> <span class="keyword">from</span> <span class="string">&#x27;joi&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">ConfigModule</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/config&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> envPath = <span class="string">`.env.<span class="subst">$&#123;process.env.NODE_ENV || <span class="string">&#x27;development&#x27;</span>&#125;</span>`</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Module</span>(&#123;</span><br><span class="line">  <span class="attr">imports</span>: [</span><br><span class="line">    <span class="title class_">ConfigModule</span>.<span class="title function_">forRoot</span>(&#123;</span><br><span class="line">      <span class="attr">envFilePath</span>: envPath,</span><br><span class="line">      <span class="comment">// 这里多了一个属性：validationSchema</span></span><br><span class="line">      <span class="attr">validationSchema</span>: <span class="title class_">Joi</span>.<span class="title function_">object</span>(&#123;</span><br><span class="line">        <span class="attr">NODE_ENV</span>: <span class="title class_">Joi</span>.<span class="title function_">string</span>()</span><br><span class="line">          .<span class="title function_">valid</span>(<span class="string">&#x27;development&#x27;</span>, <span class="string">&#x27;production&#x27;</span>, <span class="string">&#x27;test&#x27;</span>, <span class="string">&#x27;provision&#x27;</span>)</span><br><span class="line">          .<span class="title function_">default</span>(<span class="string">&#x27;development&#x27;</span>),</span><br><span class="line">        <span class="attr">PORT</span>: <span class="title class_">Joi</span>.<span class="title function_">number</span>().<span class="title function_">default</span>(<span class="number">3000</span>),</span><br><span class="line">        <span class="attr">DATABASE_USER</span>: <span class="title class_">Joi</span>.<span class="title function_">string</span>().required()</span><br><span class="line">      &#125;),</span><br><span class="line">    &#125;),</span><br><span class="line">  ],</span><br><span class="line">  <span class="attr">controllers</span>: [<span class="title class_">AppController</span>],</span><br><span class="line">  <span class="attr">providers</span>: [<span class="title class_">AppService</span>],</span><br><span class="line">&#125;)</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">AppModule</span> &#123;&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></li><li><p>验证测试</p><p>配置<code>错误</code>脚本：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">&quot;start:dev&quot;</span><span class="punctuation">:</span> <span class="string">&quot;cross-env NODE_ENV=development PORT=toimc nest start --watch&quot;</span><span class="punctuation">,</span></span><br></pre></td></tr></table></figure><p>配置正确的脚本：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">&quot;start:dev&quot;</span><span class="punctuation">:</span> <span class="string">&quot;cross-env NODE_ENV=development PORT=3000 nest start --watch&quot;</span><span class="punctuation">,</span></span><br></pre></td></tr></table></figure><p>测试命令</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm <span class="built_in">run</span> start:dev</span><br></pre></td></tr></table></figure><p>错误的提示：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">[下午7:33:38] Found 0 errors. Watching <span class="keyword">for</span> file changes.</span><br><span class="line"></span><br><span class="line">/Users/macos/Projects/nestjs/nestjs-common-template/node_modules/_@nestjs_config@0.6.3@@nestjs/config/dist/config.module.js:61</span><br><span class="line">                throw new Error(`Config validation error: <span class="variable">$&#123;error.message&#125;</span>`);</span><br><span class="line">                ^</span><br><span class="line"></span><br><span class="line">Error: Config validation error: <span class="string">&quot;PORT&quot;</span> must be a number</span><br><span class="line">    at Function.forRoot (/Users/macos/Projects/nestjs/nestjs-common-template/node_modules/_@nestjs_config@0.6.3@@nestjs/config/dist/config.module.js:61:23)</span><br><span class="line">    at Object.&lt;anonymous&gt; (/Users/macos/Projects/nestjs/nestjs-common-template/dist/app.module.js:21:35)</span><br><span class="line">    at Module._compile (internal/modules/cjs/loader.js:1063:30)</span><br><span class="line">    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)</span><br><span class="line">    at Module.load (internal/modules/cjs/loader.js:928:32)</span><br><span class="line">    at Function.Module._load (internal/modules/cjs/loader.js:769:14)</span><br><span class="line">    at Module.require (internal/modules/cjs/loader.js:952:19)</span><br><span class="line">    at require (internal/modules/cjs/helpers.js:88:18)</span><br><span class="line">    at Object.&lt;anonymous&gt; (/Users/macos/Projects/nestjs/nestjs-common-template/dist/main.js:4:22)</span><br><span class="line">    at Module._compile (internal/modules/cjs/loader.js:1063:30)</span><br></pre></td></tr></table></figure><p>或者修改<code>.env.development</code>中的配置信息：</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">DATABASE_USER</span>=</span><br><span class="line"><span class="attr">DATABASE_PASSWORD</span>=test123</span><br></pre></td></tr></table></figure><p>错误提示：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">/Users/macos/Projects/nestjs/nestjs-common-template/node_modules/_@nestjs_config@0.6.3@@nestjs/config/dist/config.module.js:61</span><br><span class="line">                throw new Error(`Config validation error: <span class="variable">$&#123;error.message&#125;</span>`);</span><br><span class="line">                ^</span><br><span class="line"></span><br><span class="line">Error: Config validation error: <span class="string">&quot;DATABASE_USER&quot;</span> is not allowed to be empty</span><br><span class="line">    at Function.forRoot (/Users/macos/Projects/nestjs/nestjs-common-template/node_modules/_@nestjs_config@0.6.3@@nestjs/config/dist/config.module.js:61:23)</span><br><span class="line">    at Object.&lt;anonymous&gt; (/Users/macos/Projects/nestjs/nestjs-common-template/dist/app.module.js:21:35)</span><br><span class="line">    at Module._compile (internal/modules/cjs/loader.js:1063:30)</span><br><span class="line">    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)</span><br><span class="line">    at Module.load (internal/modules/cjs/loader.js:928:32)</span><br><span class="line">    at Function.Module._load (internal/modules/cjs/loader.js:769:14)</span><br><span class="line">    at Module.require (internal/modules/cjs/loader.js:952:19)</span><br><span class="line">    at require (internal/modules/cjs/helpers.js:88:18)</span><br><span class="line">    at Object.&lt;anonymous&gt; (/Users/macos/Projects/nestjs/nestjs-common-template/dist/main.js:4:22)</span><br><span class="line">    at Module._compile (internal/modules/cjs/loader.js:1063:30)</span><br></pre></td></tr></table></figure></li></ul><p>结论：使用<code>Joi</code>可以很方便对传入应用程序的参数进行验证，可以限制传入的数据类型。</p><p>除了上面写的验证以外，还可以加入以下属性来验证输入的<strong>命令参数</strong>：</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Module</span>(&#123;</span><br><span class="line">  <span class="attr">imports</span>: [</span><br><span class="line">    <span class="title class_">ConfigModule</span>.<span class="title function_">forRoot</span>(&#123;</span><br><span class="line">      <span class="attr">envFilePath</span>: envPath,</span><br><span class="line">      <span class="attr">validationSchema</span>: <span class="title class_">Joi</span>.<span class="title function_">object</span>(&#123;</span><br><span class="line">        <span class="attr">NODE_ENV</span>: <span class="title class_">Joi</span>.<span class="title function_">string</span>()</span><br><span class="line">          .<span class="title function_">valid</span>(<span class="string">&#x27;development&#x27;</span>, <span class="string">&#x27;production&#x27;</span>, <span class="string">&#x27;test&#x27;</span>, <span class="string">&#x27;provision&#x27;</span>)</span><br><span class="line">          .<span class="title function_">default</span>(<span class="string">&#x27;development&#x27;</span>),</span><br><span class="line">        <span class="attr">PORT</span>: <span class="title class_">Joi</span>.<span class="title function_">number</span>().<span class="title function_">default</span>(<span class="number">3000</span>),</span><br><span class="line">        <span class="attr">DATABASE_USER</span>: <span class="title class_">Joi</span>.<span class="title function_">string</span>().required()</span><br><span class="line">      &#125;),</span><br><span class="line">      <span class="attr">validationOptions</span>: &#123; <span class="comment">// 这里加</span></span><br><span class="line">        <span class="attr">allowUnknown</span>: <span class="literal">false</span>,</span><br><span class="line">        <span class="attr">abortEarly</span>: <span class="literal">true</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="attr">controllers</span>: [<span class="title class_">AppController</span>],</span><br><span class="line">  <span class="attr">providers</span>: [<span class="title class_">AppService</span>],</span><br><span class="line">&#125;)</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">AppModule</span> &#123;&#125;</span><br></pre></td></tr></table></figure><p><code>@nestjs/config</code>包使用的默认设置是：</p><ul><li><code>allowUnknown</code>：控制是否允许在环境变量中使用未知键。默认为true</li><li><code>abortEarly</code>：如果为true，则在第一个错误时停止验证；如果为false，则返回所有错误。默认值为false。</li></ul><p>注意上面的Joi的用法：</p><ul><li>主要是校验<code>process.env</code>传入的参数</li><li>主要是校验<code>envFilePath</code>初次加载的时候的参数</li></ul><h4 id="使用class-validator"><a href="#使用class-validator" class="headerlink" title="使用class-validator"></a>使用<code>class-validator</code></h4><p>步骤：</p><ul><li><p>安装依赖<code>class-validator</code>与<code>class-transformer</code></p><figure class="highlight coffeescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">npm</span> i <span class="keyword">class</span>-validator <span class="keyword">class</span>-transformer</span><br></pre></td></tr></table></figure></li><li><p>配置效验文件<code>src/env.validation.ts</code></p><figure class="highlight typescript"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; plainToClass &#125; <span class="keyword">from</span> <span class="string">&#x27;class-transformer&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">IsEnum</span>, <span class="title class_">IsNumber</span>, validateSync &#125; <span class="keyword">from</span> <span class="string">&#x27;class-validator&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">enum</span> <span class="title class_">Environment</span> &#123;</span><br><span class="line">  <span class="title class_">Development</span> = <span class="string">&quot;development&quot;</span>,</span><br><span class="line">  <span class="title class_">Production</span> = <span class="string">&quot;production&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">EnvironmentVariables</span> &#123;</span><br><span class="line">  <span class="meta">@IsEnum</span>(<span class="title class_">Environment</span>)</span><br><span class="line">  <span class="attr">NODE_ENV</span>: <span class="title class_">Environment</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@IsNumber</span>()</span><br><span class="line">  <span class="attr">PORT</span>: <span class="built_in">number</span>;</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_">validate</span>(<span class="params">config: Record&lt;<span class="built_in">string</span>, <span class="built_in">unknown</span>&gt;</span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> validatedConfig = <span class="title function_">plainToClass</span>(</span><br><span class="line">    <span class="title class_">EnvironmentVariables</span>,</span><br><span class="line">    config,</span><br><span class="line">    &#123; <span class="attr">enableImplicitConversion</span>: <span class="literal">true</span> &#125;,</span><br><span class="line">  );</span><br><span class="line">  <span class="keyword">const</span> errors = <span class="title function_">validateSync</span>(validatedConfig, &#123; <span class="attr">skipMissingProperties</span>: <span class="literal">false</span> &#125;);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (errors.<span class="property">length</span> &gt; <span class="number">0</span>) &#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(errors.<span class="title function_">toString</span>());</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> validatedConfig;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>调整<code>app.module.ts</code>文件</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">Module</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/common&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">AppController</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./app.controller&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">AppService</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./app.service&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">ConfigModule</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/config&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; validate &#125; <span class="keyword">from</span> <span class="string">&#x27;./env.validation&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> envPath = <span class="string">`.env.<span class="subst">$&#123;process.env.NODE_ENV || <span class="string">&#x27;development&#x27;</span>&#125;</span>`</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Module</span>(&#123;</span><br><span class="line">  <span class="attr">imports</span>: [</span><br><span class="line">    <span class="title class_">ConfigModule</span>.<span class="title function_">forRoot</span>(&#123;</span><br><span class="line">      <span class="attr">envFilePath</span>: envPath,</span><br><span class="line">      validate,</span><br><span class="line">    &#125;),</span><br><span class="line">  ],</span><br><span class="line">  <span class="attr">controllers</span>: [<span class="title class_">AppController</span>],</span><br><span class="line">  <span class="attr">providers</span>: [<span class="title class_">AppService</span>],</span><br><span class="line">&#125;)</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">AppModule</span> &#123;&#125;</span><br></pre></td></tr></table></figure></li></ul><p>与使用<code>Joi</code>验证结果一致。</p><h3 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h3><ul><li><p>使用第三方的包<code>config</code>，可以方便的读取配置信息，但是校验却需要在读取的位置来加，对于不需要验证，而需要全局使用的配置项可以使用这种方式；</p></li><li><p>官方的<code>@nestjs/config</code>可以方便的导入<code>.env</code>的文件，同时结合<code>js-yaml</code>也可以导入<code>yaml</code>格式的配置。</p><p>配置灵活，而且可以配合验证工具<code>Joi</code>进行参数的验证（推荐）</p><p>自定义的校验第三方包<code>class-validator</code>这里只是冰山一角，后面在学习数据验证的时候还会使用到它；</p></li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;这是《nestjs搭建通用业务框架》系列的第5篇，进入开发具体的功能之前，学习nest框架本身提供的CLI工具与规划合理的工程目录，对于要实现的内容进行架构与计划，这是实现健壮高可用的框架的前提。&lt;/p&gt;</summary>
    
    
    
    <category term="nestjs搭建通用业务框架" scheme="https://www.toimc.com/categories/nestjs%E6%90%AD%E5%BB%BA%E9%80%9A%E7%94%A8%E4%B8%9A%E5%8A%A1%E6%A1%86%E6%9E%B6/"/>
    
    
    <category term="node.js" scheme="https://www.toimc.com/tags/node-js/"/>
    
    <category term="nestjs" scheme="https://www.toimc.com/tags/nestjs/"/>
    
    <category term="web框架" scheme="https://www.toimc.com/tags/web%E6%A1%86%E6%9E%B6/"/>
    
  </entry>
  
  <entry>
    <title>nestjs搭建通用业务框架（4）：工程目录与代码规范</title>
    <link href="https://www.toimc.com/nestjs-example-project-4/"/>
    <id>https://www.toimc.com/nestjs-example-project-4/</id>
    <published>2021-03-13T03:02:33.000Z</published>
    <updated>2025-10-06T05:13:13.973Z</updated>
    
    <content type="html"><![CDATA[<p>这是《nestjs搭建通用业务框架》系列的第4篇，进入开发具体的功能之前，养成良好的工程目录与代码风格的习惯，目的构建大型复杂项目，提高代码易维护性。</p><span id="more"></span><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>大多数前端同学拿到一个新的任务的时候，或者要做一个新的技术设计的时候，往往无从下手。知乎、掘金上问人可能是一种方案，还可以找一个社交渠道(推特、电报、微博、朋友圈、学校论坛)，通过别人的现实例子来进行架构的设计是一个很好切入点。其实，大家可能忽视了以下的渠道：技术框架的官方+示例、公司&amp;团队的历史项目库、找比较厉害的同事取经和发有偿技术咨询的单（程序员各种接单平台）等。</p><p>那么，对于nestjs，它的官方提供了很多现成的技术解决方案，所以我们可以借鉴(拿来即用)。</p><h2 id="认识CLI"><a href="#认识CLI" class="headerlink" title="认识CLI"></a>认识CLI</h2><p>先从官方的CLI开始：</p><blockquote><p><a href="https://github.com/nestjs/nest-cli">Nest CLI</a>是一个命令行界面工具，以帮助您初始化、开发和维护 <code>Nest</code> 应用程序。它以多种方式提供帮助，包括搭建项目、以开发模式为其提供服务，以及为生产分发构建和打包应用程序。它体现了最佳实践的架构模式，以构建良好的应用程序。</p></blockquote><p>大多命令行工具可以使用<code>--help</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></pre></td><td class="code"><pre><span class="line">➜ nest --<span class="built_in">help</span></span><br><span class="line">Usage: nest &lt;<span class="built_in">command</span>&gt; [options]</span><br><span class="line"></span><br><span class="line">Options:</span><br><span class="line">  -v, --version                                   Output the current version.</span><br><span class="line">  -h, --<span class="built_in">help</span>                                      Output usage information.</span><br><span class="line"></span><br><span class="line">Commands:</span><br><span class="line">  new|n [options] [name]                          Generate Nest application.</span><br><span class="line">  build [options] [app]                           Build Nest application.</span><br><span class="line">  start [options] [app]                           Run Nest application.</span><br><span class="line">  info|i                                          Display Nest project details.</span><br><span class="line">  update|u [options]                              Update Nest dependencies.</span><br><span class="line">  add [options] &lt;library&gt;                         Adds support <span class="keyword">for</span> an external library to your project.</span><br><span class="line">  generate|g [options] &lt;schematic&gt; [name] [path]  Generate a Nest element.</span><br><span class="line">    Available schematics:</span><br><span class="line">      ┌───────────────┬─────────────┬──────────────────────────────────────────────┐</span><br><span class="line">      │ name          │ <span class="built_in">alias</span>       │ description                                  │</span><br><span class="line">      │ application   │ application │ Generate a new application workspace         │</span><br><span class="line">      │ class         │ cl          │ Generate a new class                         │</span><br><span class="line">      │ configuration │ config      │ Generate a CLI configuration file            │</span><br><span class="line">      │ controller    │ co          │ Generate a controller declaration            │</span><br><span class="line">      │ decorator     │ d           │ Generate a custom decorator                  │</span><br><span class="line">      │ filter        │ f           │ Generate a filter declaration                │</span><br><span class="line">      │ gateway       │ ga          │ Generate a gateway declaration               │</span><br><span class="line">      │ guard         │ gu          │ Generate a guard declaration                 │</span><br><span class="line">      │ interceptor   │ <span class="keyword">in</span>          │ Generate an interceptor declaration          │</span><br><span class="line">      │ interface     │ interface   │ Generate an interface                        │</span><br><span class="line">      │ middleware    │ mi          │ Generate a middleware declaration            │</span><br><span class="line">      │ module        │ mo          │ Generate a module declaration                │</span><br><span class="line">      │ pipe          │ pi          │ Generate a pipe declaration                  │</span><br><span class="line">      │ provider      │ <span class="built_in">pr</span>          │ Generate a provider declaration              │</span><br><span class="line">      │ resolver      │ r           │ Generate a GraphQL resolver declaration      │</span><br><span class="line">      │ service       │ s           │ Generate a service declaration               │</span><br><span class="line">      │ library       │ lib         │ Generate a new library within a monorepo     │</span><br><span class="line">      │ sub-app       │ app         │ Generate a new application within a monorepo │</span><br><span class="line">      │ resource      │ res         │ Generate a new CRUD resource                 │</span><br><span class="line">      └───────────────┴─────────────┴──────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><p>然后，如果想知道其中某一个子命令的用法，可以使用<code>nest &lt;command&gt; --help</code>的形式来进行查看：</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">➜ nest generate --<span class="built_in">help</span></span><br></pre></td></tr></table></figure><p>特别说明：</p><table><thead><tr><th>名称</th><th>缩写</th><th>描述</th></tr></thead><tbody><tr><td>application</td><td>application</td><td>生成一个新的应用工作区</td></tr><tr><td>class</td><td>cl</td><td>生成一个新的class</td></tr><tr><td>configuration</td><td>config</td><td>生成 CLI 配置文件</td></tr><tr><td>controller</td><td>co</td><td>生成一个控制器声明</td></tr><tr><td>decorator</td><td>d</td><td>生成一个自定义的装饰者</td></tr><tr><td>filter</td><td>f</td><td>生成一个过滤器声明</td></tr><tr><td>gateway</td><td>ga</td><td>生成网关</td></tr><tr><td>guard</td><td>gu</td><td>生成守卫</td></tr><tr><td>interceptor</td><td>in</td><td>生成拦截器</td></tr><tr><td>interface</td><td>interface</td><td>生成接口声明</td></tr><tr><td>middleware</td><td>mi</td><td>生成中间件声明</td></tr><tr><td>module</td><td>mo</td><td>生成一个模块声明</td></tr><tr><td>pipe</td><td>pi</td><td>生成管道声明</td></tr><tr><td>provider</td><td>pr</td><td>生成提供者声明</td></tr><tr><td>resolver</td><td>r</td><td>生成GraphQL resolver声明</td></tr><tr><td>service</td><td>s</td><td>生成服务</td></tr><tr><td>library</td><td>lib</td><td>生成一个monorepo库</td></tr><tr><td>sub-app</td><td>App</td><td>生成一个monorepo的应用</td></tr><tr><td>resource</td><td>Res</td><td>生成一个新的CURD资源</td></tr></tbody></table><p>我们最开始使用了一个<code>new</code>命令，后面最常用的即是<code>generator</code>或<code>g</code>（简写）命令，可以对照着上表进行熟悉。</p><h2 id="合理的工程目录"><a href="#合理的工程目录" class="headerlink" title="合理的工程目录"></a>合理的工程目录</h2><p>为了去理解Python的语言设计之美，其实更要理解这样的一句话“约定大于配置”，好的工程化目录（约定）能够很好的提升项目的可维护性。</p><h3 id="作者推荐"><a href="#作者推荐" class="headerlink" title="作者推荐"></a>作者推荐</h3><p>在官方的issues中，我们可以找到一些提示：<a href="https://github.com/nestjs/nest/issues/2249#issuecomment-494734673">Best scalable project structure #2249</a> 这里有作者的回复。</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></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> src</span><br><span class="line"><span class="bullet">  -</span> core</span><br><span class="line"><span class="bullet">  -</span> common</span><br><span class="line"><span class="bullet">    -</span> middleware</span><br><span class="line"><span class="bullet">    -</span> interceptors</span><br><span class="line"><span class="bullet">    -</span> guards</span><br><span class="line"><span class="bullet">  -</span> user</span><br><span class="line"><span class="bullet">      -</span> interceptors (scoped interceptors)</span><br><span class="line"><span class="bullet">    -</span> user.controller.ts</span><br><span class="line"><span class="bullet">    -</span> user.model.ts</span><br><span class="line"><span class="bullet">  -</span> store</span><br><span class="line"><span class="bullet">    -</span> store.controller.ts</span><br><span class="line"><span class="bullet">    -</span> store.model.ts</span><br></pre></td></tr></table></figure><ul><li><p>可以使用monorepo的方法——在一个repo中创建两个项目，并在它们之间共享共同的东西，如库&#x2F;包。</p></li><li><p>没有模块目录，按照功能进行划分。</p></li><li><p>把通用&#x2F;核心的东西归为单独的目录：common，比如：拦截器&#x2F;守卫&#x2F;管道</p></li></ul><h3 id="参考项目"><a href="#参考项目" class="headerlink" title="参考项目"></a>参考项目</h3><p><strong>第一个参考项目</strong></p><p>技术栈：Nest + sequelize-typescript + JWT + Jest + Swagger</p><p>项目地址：<a href="https://github.com/kentloog/nestjs-sequelize-typescript">kentloog&#x2F;nestjs-sequelize-typescript</a></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><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line">.</span><br><span class="line">├── README.md</span><br><span class="line">├── assets</span><br><span class="line">│   └── logo.png</span><br><span class="line">├── config</span><br><span class="line">│   ├── config.development.ts</span><br><span class="line">│   └── config.production.ts</span><br><span class="line">├── config.ts</span><br><span class="line">├── db</span><br><span class="line">│   ├── config.ts</span><br><span class="line">│   ├── migrations</span><br><span class="line">│   │   └── 20190128160000-create-table-user.js</span><br><span class="line">│   └── seeders-dev</span><br><span class="line">│       └── 20190129093300-test-data-users.js</span><br><span class="line">├── nest-cli.json</span><br><span class="line">├── nodemon-debug.json</span><br><span class="line">├── nodemon.json</span><br><span class="line">├── package-lock.json</span><br><span class="line">├── package.json</span><br><span class="line">├── src</span><br><span class="line">│   ├── app.module.ts</span><br><span class="line">│   ├── database</span><br><span class="line">│   │   ├── database.module.ts</span><br><span class="line">│   │   └── database.providers.ts</span><br><span class="line">│   ├── main.ts</span><br><span class="line">│   ├── posts</span><br><span class="line">│   │   ├── dto</span><br><span class="line">│   │   │   ├── create-post.dto.ts</span><br><span class="line">│   │   │   ├── post.dto.ts</span><br><span class="line">│   │   │   └── update-post.dto.ts</span><br><span class="line">│   │   ├── post.entity.ts</span><br><span class="line">│   │   ├── posts.controller.ts</span><br><span class="line">│   │   ├── posts.module.ts</span><br><span class="line">│   │   ├── posts.providers.ts</span><br><span class="line">│   │   └── posts.service.ts</span><br><span class="line">│   ├── shared</span><br><span class="line">│   │   ├── config</span><br><span class="line">│   │   │   └── config.service.ts</span><br><span class="line">│   │   ├── enum</span><br><span class="line">│   │   │   └── gender.ts</span><br><span class="line">│   │   └── shared.module.ts</span><br><span class="line">│   ├── swagger.ts</span><br><span class="line">│   └── <span class="built_in">users</span></span><br><span class="line">│       ├── auth</span><br><span class="line">│       │   ├── jwt-payload.model.ts</span><br><span class="line">│       │   └── jwt-strategy.ts</span><br><span class="line">│       ├── dto</span><br><span class="line">│       │   ├── create-user.dto.ts</span><br><span class="line">│       │   ├── update-user.dto.ts</span><br><span class="line">│       │   ├── user-login-request.dto.ts</span><br><span class="line">│       │   ├── user-login-response.dto.ts</span><br><span class="line">│       │   └── user.dto.ts</span><br><span class="line">│       ├── user.entity.ts</span><br><span class="line">│       ├── users.controller.ts</span><br><span class="line">│       ├── users.module.ts</span><br><span class="line">│       ├── users.providers.ts</span><br><span class="line">│       └── users.service.ts</span><br><span class="line">├── <span class="built_in">test</span></span><br><span class="line">│   ├── app.e2e-spec.ts</span><br><span class="line">│   ├── jest-e2e.json</span><br><span class="line">│   └── test-data.ts</span><br><span class="line">├── tsconfig.build.json</span><br><span class="line">├── tsconfig.json</span><br><span class="line">└── tslint.json</span><br></pre></td></tr></table></figure><p>特点：</p><ul><li>项目文档及相关的资源在根目录</li><li>数据库及项目配置会放在根目录（细节：数据库升级文件）</li><li><code>src</code>中会对功能进行划分建不同的文件夹<code>users</code>、<code>posts</code></li><li>单个功能文件夹中，会包括一个完整CURD的相关文件(dto&#x2F;controller&#x2F;module&#x2F;providers&#x2F;service)</li><li>抽离公共配置到<code>shared</code>文件夹</li></ul><p><strong>第二个参考项目</strong></p><p>技术栈：具有AWS Lambda，DynamoDB，DynamoDB Streams的完全无服务器生产应用程序</p><p>项目地址：<a href="https://github.com/International-Slackline-Association/Rankings-Backend">International-Slackline-Association&#x2F;Rankings-Backend</a></p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br></pre></td><td class="code"><pre><span class="line">.</span><br><span class="line">├── LICENSE</span><br><span class="line">├── README<span class="selector-class">.md</span></span><br><span class="line">├── docs</span><br><span class="line">│   ├── AWS_Architecture<span class="selector-class">.png</span></span><br><span class="line">│   ├── Development\ Notes<span class="selector-class">.md</span></span><br><span class="line">│   └── GourceOutput<span class="selector-class">.png</span></span><br><span class="line">├── jest<span class="selector-class">.config</span><span class="selector-class">.js</span></span><br><span class="line">├── package-lock<span class="selector-class">.json</span></span><br><span class="line">├── package<span class="selector-class">.json</span></span><br><span class="line">├── serverless</span><br><span class="line">│   ├── environment<span class="selector-class">.yml</span></span><br><span class="line">│   └── secrets<span class="selector-class">.example</span><span class="selector-class">.yml</span></span><br><span class="line">├── serverless<span class="selector-class">.yml</span></span><br><span class="line">├── <span class="attribute">src</span></span><br><span class="line">│   ├── api</span><br><span class="line">│   │   ├── admin</span><br><span class="line">│   │   │   ├── api<span class="selector-class">.module</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   │   ├── athlete</span><br><span class="line">│   │   │   ├── contest</span><br><span class="line">│   │   │   ├── database<span class="selector-class">.module</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   │   ├── index<span class="selector-class">.ts</span></span><br><span class="line">│   │   │   ├── results</span><br><span class="line">│   │   │   └── submit</span><br><span class="line">│   │   └── webapp</span><br><span class="line">│   │       ├── api<span class="selector-class">.module</span><span class="selector-class">.ts</span></span><br><span class="line">│   │       ├── athlete</span><br><span class="line">│   │       ├── contest</span><br><span class="line">│   │       ├── country</span><br><span class="line">│   │       ├── database<span class="selector-class">.module</span><span class="selector-class">.ts</span></span><br><span class="line">│   │       ├── index<span class="selector-class">.ts</span></span><br><span class="line">│   │       ├── nestjsTest<span class="selector-class">.controller</span><span class="selector-class">.ts</span></span><br><span class="line">│   │       └── rankings</span><br><span class="line">│   ├── core</span><br><span class="line">│   │   ├── athlete</span><br><span class="line">│   │   │   ├── athlete<span class="selector-class">.service</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   │   ├── entity</span><br><span class="line">│   │   │   ├── interfaces</span><br><span class="line">│   │   │   └── rankings<span class="selector-class">.service</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   ├── aws</span><br><span class="line">│   │   │   ├── aws<span class="selector-class">.module</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   │   ├── aws<span class="selector-class">.services</span><span class="selector-class">.interface</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   │   └── aws<span class="selector-class">.services</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   ├── category</span><br><span class="line">│   │   │   └── categories<span class="selector-class">.service</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   ├── contest</span><br><span class="line">│   │   │   ├── contest<span class="selector-class">.service</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   │   ├── entity</span><br><span class="line">│   │   │   └── points-calculator<span class="selector-class">.service</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   └── database</span><br><span class="line">│   │       ├── database<span class="selector-class">.module</span><span class="selector-class">.ts</span></span><br><span class="line">│   │       ├── database<span class="selector-class">.service</span><span class="selector-class">.ts</span></span><br><span class="line">│   │       ├── dynamodb</span><br><span class="line">│   │       ├── redis</span><br><span class="line">│   │       └── test</span><br><span class="line">│   ├── cron-job</span><br><span class="line">│   │   ├── cron-job<span class="selector-class">.module</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   ├── cron-job<span class="selector-class">.service</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   ├── cron-job<span class="selector-class">.spec</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   ├── database<span class="selector-class">.module</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   └── index<span class="selector-class">.ts</span></span><br><span class="line">│   ├── dynamodb-streams</span><br><span class="line">│   │   ├── athlete</span><br><span class="line">│   │   │   ├── athlete-contest-record<span class="selector-class">.service</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   │   ├── athlete-details-record<span class="selector-class">.service</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   │   └── athlete-records<span class="selector-class">.module</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   ├── contest</span><br><span class="line">│   │   │   ├── contest-record<span class="selector-class">.service</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   │   └── contest-records<span class="selector-class">.module</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   ├── database<span class="selector-class">.module</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   ├── dynamodb-streams<span class="selector-class">.module</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   ├── dynamodb-streams<span class="selector-class">.service</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   ├── index<span class="selector-class">.ts</span></span><br><span class="line">│   │   ├── test</span><br><span class="line">│   │   │   ├── contest-modifications<span class="selector-class">.spec</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   │   └── lambda-trigger<span class="selector-class">.ts</span></span><br><span class="line">│   │   └── utils<span class="selector-class">.ts</span></span><br><span class="line">│   ├── image-resizer</span><br><span class="line">│   │   ├── S3Events<span class="selector-class">.module</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   ├── S3Events<span class="selector-class">.service</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   ├── database<span class="selector-class">.module</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   ├── index<span class="selector-class">.ts</span></span><br><span class="line">│   │   ├── test</span><br><span class="line">│   │   │   ├── lambda-trigger<span class="selector-class">.ts</span></span><br><span class="line">│   │   │   └── s3-image-put<span class="selector-class">.spec</span><span class="selector-class">.ts</span></span><br><span class="line">│   │   └── thumbnail-creator</span><br><span class="line">│   │       ├── imagemagick<span class="selector-class">.ts</span></span><br><span class="line">│   │       ├── s3<span class="selector-class">.service</span><span class="selector-class">.ts</span></span><br><span class="line">│   │       ├── thumbnail-creator<span class="selector-class">.module</span><span class="selector-class">.ts</span></span><br><span class="line">│   │       └── thumbnail-creator<span class="selector-class">.service</span><span class="selector-class">.ts</span></span><br><span class="line">│   └── shared</span><br><span class="line">│       ├── constants<span class="selector-class">.ts</span></span><br><span class="line">│       ├── decorators</span><br><span class="line">│       │   └── roles<span class="selector-class">.decorator</span><span class="selector-class">.ts</span></span><br><span class="line">│       ├── enums</span><br><span class="line">│       │   ├── contestType-utility<span class="selector-class">.ts</span></span><br><span class="line">│       │   ├── discipline-utility<span class="selector-class">.ts</span></span><br><span class="line">│       │   ├── enums-utility<span class="selector-class">.ts</span></span><br><span class="line">│       │   └── index<span class="selector-class">.ts</span></span><br><span class="line">│       ├── env_variables<span class="selector-class">.ts</span></span><br><span class="line">│       ├── exceptions</span><br><span class="line">│       │   ├── api<span class="selector-class">.error</span><span class="selector-class">.ts</span></span><br><span class="line">│       │   └── api<span class="selector-class">.exceptions</span><span class="selector-class">.ts</span></span><br><span class="line">│       ├── extensions<span class="selector-class">.ts</span></span><br><span class="line">│       ├── filters</span><br><span class="line">│       │   └── exception<span class="selector-class">.filter</span><span class="selector-class">.ts</span></span><br><span class="line">│       ├── generators</span><br><span class="line">│       │   └── id<span class="selector-class">.generator</span><span class="selector-class">.ts</span></span><br><span class="line">│       ├── guards</span><br><span class="line">│       │   └── roles<span class="selector-class">.guard</span><span class="selector-class">.ts</span></span><br><span class="line">│       ├── index<span class="selector-class">.ts</span></span><br><span class="line">│       ├── logger<span class="selector-class">.ts</span></span><br><span class="line">│       ├── pipes</span><br><span class="line">│       │   └── JoiValidation<span class="selector-class">.pipe</span><span class="selector-class">.ts</span></span><br><span class="line">│       ├── types</span><br><span class="line">│       │   ├── express<span class="selector-class">.d</span><span class="selector-class">.ts</span></span><br><span class="line">│       │   ├── extensions<span class="selector-class">.d</span><span class="selector-class">.ts</span></span><br><span class="line">│       │   └── shared<span class="selector-class">.d</span><span class="selector-class">.ts</span></span><br><span class="line">│       └── utils<span class="selector-class">.ts</span></span><br><span class="line">├── test</span><br><span class="line">│   ├── jest-e2e<span class="selector-class">.json</span></span><br><span class="line">│   └── test-setup<span class="selector-class">.ts</span></span><br><span class="line">├── tsconfig<span class="selector-class">.json</span></span><br><span class="line">├── tslint<span class="selector-class">.json</span></span><br><span class="line">└── webpack</span><br><span class="line">    ├── webpack<span class="selector-class">.config</span><span class="selector-class">.Dev</span><span class="selector-class">.js</span></span><br><span class="line">    ├── webpack<span class="selector-class">.config</span><span class="selector-class">.Prod</span><span class="selector-class">.js</span></span><br><span class="line">    ├── webpack<span class="selector-class">.config</span><span class="selector-class">.Test</span><span class="selector-class">.js</span></span><br><span class="line">    └── webpack<span class="selector-class">.config</span><span class="selector-class">.base</span>.js</span><br></pre></td></tr></table></figure><p>特点：</p><ul><li>根目录中存放webpack、微服务配置 + 项目文档</li><li><code>src</code>中会对功能进行划分建不同的文件夹： <code>api</code>、<code>core</code>、<code>dynamodb-stream</code>、<code>image-resizer</code></li><li>在核心模块中，按照功能模块进划分，与之相关的entity、service放置在同一文件夹中</li><li>抽离公共配置到<code>shared</code>文件夹：常量、自定义的装饰器、统一错误处理、过滤器、生成器、守卫、日志服务</li></ul><p>第三个参考项目：</p><p>技术栈：使用 NestJS 的 Blog&#x2F;CMS， RESTful API 服务端应用</p><p>项目地址：<a href="https://github.com/surmon-china/nodepress">surmon-china&#x2F;nodepressTemplate</a></p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br></pre></td><td class="code"><pre><span class="line">.</span><br><span class="line">├── API_DOC.md</span><br><span class="line">├── CHANGELOG.md</span><br><span class="line">├── LICENSE</span><br><span class="line">├── README.md</span><br><span class="line">├── classified</span><br><span class="line">├── cspell.json</span><br><span class="line">├── dbbackup</span><br><span class="line">├── logo.png</span><br><span class="line">├── logo.psd</span><br><span class="line">├── nest-cli.json</span><br><span class="line">├── package.json</span><br><span class="line">├── scripts</span><br><span class="line">│   ├── README.md</span><br><span class="line">│   ├── dbbackup.sh</span><br><span class="line">│   ├── dbrecover.sh</span><br><span class="line">│   └── deploy.sh</span><br><span class="line">├── src</span><br><span class="line">│   ├── app.config.ts</span><br><span class="line">│   ├── app.controller.spec.ts</span><br><span class="line">│   ├── app.controller.ts</span><br><span class="line">│   ├── app.environment.ts</span><br><span class="line">│   ├── app.module.ts</span><br><span class="line">│   ├── constants</span><br><span class="line">│   │   ├── cache.<span class="keyword">constant</span>.ts</span><br><span class="line">│   │   ├── meta.<span class="keyword">constant</span>.ts</span><br><span class="line">│   │   ├── <span class="keyword">system</span>.<span class="keyword">constant</span>.ts</span><br><span class="line">│   │   └── <span class="type">text</span>.<span class="keyword">constant</span>.ts</span><br><span class="line">│   ├── decorators</span><br><span class="line">│   │   ├── cache.decorator.ts</span><br><span class="line">│   │   ├── http.decorator.ts</span><br><span class="line">│   │   └── query-params.decorator.ts</span><br><span class="line">│   ├── errors</span><br><span class="line">│   │   ├── bad-request.error.ts</span><br><span class="line">│   │   ├── custom.error.ts</span><br><span class="line">│   │   ├── forbidden.error.ts</span><br><span class="line">│   │   ├── unauthorized.error.ts</span><br><span class="line">│   │   └── validation.error.ts</span><br><span class="line">│   ├── filters</span><br><span class="line">│   │   └── error.<span class="keyword">filter</span>.ts</span><br><span class="line">│   ├── guards</span><br><span class="line">│   │   ├── auth.guard.ts</span><br><span class="line">│   │   └── humanized-auth.guard.ts</span><br><span class="line">│   ├── interceptors</span><br><span class="line">│   │   ├── cache.interceptor.ts</span><br><span class="line">│   │   ├── error.interceptor.ts</span><br><span class="line">│   │   ├── logging.interceptor.ts</span><br><span class="line">│   │   └── <span class="keyword">transform</span>.interceptor.ts</span><br><span class="line">│   ├── interfaces</span><br><span class="line">│   │   ├── http.interface.ts</span><br><span class="line">│   │   ├── mongoose.interface.ts</span><br><span class="line">│   │   └── state.interface.ts</span><br><span class="line">│   ├── main.ts</span><br><span class="line">│   ├── middlewares</span><br><span class="line">│   │   ├── cors.middleware.ts</span><br><span class="line">│   │   └── origin.middleware.ts</span><br><span class="line">│   ├── models</span><br><span class="line">│   │   └── extend.model.ts</span><br><span class="line">│   ├── modules</span><br><span class="line">│   │   ├── announcement</span><br><span class="line">│   │   │   ├── announcement.controller.ts</span><br><span class="line">│   │   │   ├── announcement.model.ts</span><br><span class="line">│   │   │   ├── announcement.module.ts</span><br><span class="line">│   │   │   └── announcement.service.ts</span><br><span class="line">│   │   ├── article</span><br><span class="line">│   │   │   ├── article.controller.ts</span><br><span class="line">│   │   │   ├── article.model.ts</span><br><span class="line">│   │   │   ├── article.module.ts</span><br><span class="line">│   │   │   └── article.service.ts</span><br><span class="line">│   │   ├── auth</span><br><span class="line">│   │   │   ├── auth.controller.ts</span><br><span class="line">│   │   │   ├── auth.interface.ts</span><br><span class="line">│   │   │   ├── auth.model.ts</span><br><span class="line">│   │   │   ├── auth.module.ts</span><br><span class="line">│   │   │   ├── auth.service.ts</span><br><span class="line">│   │   │   └── jwt.strategy.ts</span><br><span class="line">│   │   ├── category</span><br><span class="line">│   │   │   ├── category.controller.ts</span><br><span class="line">│   │   │   ├── category.model.ts</span><br><span class="line">│   │   │   ├── category.module.ts</span><br><span class="line">│   │   │   └── category.service.ts</span><br><span class="line">│   │   ├── <span class="keyword">comment</span></span><br><span class="line">│   │   │   ├── <span class="keyword">comment</span>.controller.ts</span><br><span class="line">│   │   │   ├── <span class="keyword">comment</span>.model.ts</span><br><span class="line">│   │   │   ├── <span class="keyword">comment</span>.module.ts</span><br><span class="line">│   │   │   └── <span class="keyword">comment</span>.service.ts</span><br><span class="line">│   │   ├── expansion</span><br><span class="line">│   │   │   ├── expansion.controller.ts</span><br><span class="line">│   │   │   ├── expansion.module.ts</span><br><span class="line">│   │   │   ├── expansion.service.dbbackup.ts</span><br><span class="line">│   │   │   └── expansion.service.statistic.ts</span><br><span class="line">│   │   ├── <span class="keyword">like</span></span><br><span class="line">│   │   │   ├── <span class="keyword">like</span>.controller.ts</span><br><span class="line">│   │   │   ├── <span class="keyword">like</span>.module.ts</span><br><span class="line">│   │   │   └── <span class="keyword">like</span>.service.ts</span><br><span class="line">│   │   ├── <span class="keyword">option</span></span><br><span class="line">│   │   │   ├── <span class="keyword">option</span>.controller.ts</span><br><span class="line">│   │   │   ├── <span class="keyword">option</span>.model.ts</span><br><span class="line">│   │   │   ├── <span class="keyword">option</span>.module.ts</span><br><span class="line">│   │   │   └── <span class="keyword">option</span>.service.ts</span><br><span class="line">│   │   ├── syndication</span><br><span class="line">│   │   │   ├── syndication.controller.ts</span><br><span class="line">│   │   │   ├── syndication.module.ts</span><br><span class="line">│   │   │   └── syndication.service.ts</span><br><span class="line">│   │   └── tag</span><br><span class="line">│   │       ├── tag.controller.ts</span><br><span class="line">│   │       ├── tag.model.ts</span><br><span class="line">│   │       ├── tag.module.ts</span><br><span class="line">│   │       └── tag.service.ts</span><br><span class="line">│   ├── pipes</span><br><span class="line">│   │   └── validation.pipe.ts</span><br><span class="line">│   ├── processors</span><br><span class="line">│   │   ├── <span class="keyword">cache</span></span><br><span class="line">│   │   │   ├── <span class="keyword">cache</span>.config.service.ts</span><br><span class="line">│   │   │   ├── <span class="keyword">cache</span>.module.ts</span><br><span class="line">│   │   │   └── <span class="keyword">cache</span>.service.ts</span><br><span class="line">│   │   ├── database</span><br><span class="line">│   │   │   ├── database.module.ts</span><br><span class="line">│   │   │   └── database.provider.ts</span><br><span class="line">│   │   └── helper</span><br><span class="line">│   │       ├── helper.module.ts</span><br><span class="line">│   │       ├── helper.service.akismet.ts</span><br><span class="line">│   │       ├── helper.service.cs.ts</span><br><span class="line">│   │       ├── helper.service.email.ts</span><br><span class="line">│   │       ├── helper.service.google.ts</span><br><span class="line">│   │       ├── helper.service.ip.ts</span><br><span class="line">│   │       └── helper.service.seo.ts</span><br><span class="line">│   └── transformers</span><br><span class="line">│       ├── codec.transformer.ts</span><br><span class="line">│       ├── error.transformer.ts</span><br><span class="line">│       ├── model.transformer.ts</span><br><span class="line">│       ├── mongoose.transformer.ts</span><br><span class="line">│       └── urlmap.transformer.ts</span><br><span class="line">├── test</span><br><span class="line">│   ├── app.e2e-spec.ts</span><br><span class="line">│   └── jest-e2e.json</span><br><span class="line">├── tsconfig.build.json</span><br><span class="line">├── tsconfig.json</span><br><span class="line">├── tsconfig.spec.json</span><br><span class="line">└── yarn.lock</span><br></pre></td></tr></table></figure><p>特点：</p><ul><li>项目文档及相关的资源在根目录</li><li><code>src</code>中<code>modules</code>会对功能进行划分建不同的文件夹</li><li>单个功能文件夹中，会包括一个完整CURD的相关文件(model&#x2F;controller&#x2F;module&#x2F;service)</li><li>把公共的代码（按照nestjs逻辑分层）拆成单独的文件夹<code>guards</code>、<code>filters</code>、<code>decorators</code>、<code>interceptors</code>、<code>interfaces</code>、<code>errors</code></li></ul><h3 id="最佳实践"><a href="#最佳实践" class="headerlink" title="最佳实践"></a>最佳实践</h3><p>项目：<a href="https://github.com/CatsMiaow/node-nestjs-structure">CatsMiaow&#x2F;node-nestjs-structure</a></p><p>下面的项目结构：</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">+-- bin                  <span class="regexp">//</span> Custom tasks</span><br><span class="line">+-- dist                 <span class="regexp">//</span> Source build</span><br><span class="line">+-- public               <span class="regexp">//</span> Static Files</span><br><span class="line">+-- src</span><br><span class="line">|   +-- config           <span class="regexp">//</span> Environment Configuration</span><br><span class="line">|   +-- entity           <span class="regexp">//</span> TypeORM Entities generated by `typeorm-model-generator` module</span><br><span class="line">|   +-- auth             <span class="regexp">//</span> Authentication</span><br><span class="line">|   +-- common           <span class="regexp">//</span> Global Nest Module</span><br><span class="line">|   |   +-- constants    <span class="regexp">//</span> Constant value and Enum</span><br><span class="line">|   |   +-- controllers  <span class="regexp">//</span> Nest Controllers</span><br><span class="line">|   |   +-- decorators   <span class="regexp">//</span> Nest Decorators</span><br><span class="line">|   |   +-- dto          <span class="regexp">//</span> DTO (Data Transfer Object) Schema, Validation</span><br><span class="line">|   |   +-- filters      <span class="regexp">//</span> Nest Filters</span><br><span class="line">|   |   +-- guards       <span class="regexp">//</span> Nest Guards</span><br><span class="line">|   |   +-- interceptors <span class="regexp">//</span> Nest Interceptors</span><br><span class="line">|   |   +-- interfaces   <span class="regexp">//</span> TypeScript Interfaces</span><br><span class="line">|   |   +-- middleware   <span class="regexp">//</span> Nest Middleware</span><br><span class="line">|   |   +-- pipes        <span class="regexp">//</span> Nest Pipes</span><br><span class="line">|   |   +-- providers    <span class="regexp">//</span> Nest Providers</span><br><span class="line">|   |   +-- *            <span class="regexp">//</span> models, repositories, services...</span><br><span class="line">|   +-- shared           <span class="regexp">//</span> Shared Nest Modules</span><br><span class="line">|   +-- gql              <span class="regexp">//</span> GraphQL Structure Sample</span><br><span class="line">|   +-- *                <span class="regexp">//</span> Other Nest Modules, non-global, same as common structure above</span><br><span class="line">+-- test                 <span class="regexp">//</span> Jest testing</span><br><span class="line">+-- typings              <span class="regexp">//</span> Modules and global type definitions</span><br></pre></td></tr></table></figure><p>如果是功能模块：</p><figure class="highlight sql"><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="operator">/</span><span class="operator">/</span> <span class="keyword">Module</span> structure</span><br><span class="line"><span class="operator">/</span><span class="operator">/</span> <span class="keyword">Add</span> folders according <span class="keyword">to</span> <span class="keyword">module</span> scale. If it<span class="string">&#x27;s small, you don&#x27;</span>t need <span class="keyword">to</span> <span class="keyword">add</span> folders.</span><br><span class="line"><span class="operator">+</span><span class="comment">-- src/greeter</span></span><br><span class="line"><span class="operator">|</span>   <span class="operator">+</span><span class="comment">-- *                // folders</span></span><br><span class="line"><span class="operator">|</span>   <span class="operator">+</span><span class="comment">-- greeter.constant.ts</span></span><br><span class="line"><span class="operator">|</span>   <span class="operator">+</span><span class="comment">-- greeter.controller.ts</span></span><br><span class="line"><span class="operator">|</span>   <span class="operator">+</span><span class="comment">-- greeter.service.ts</span></span><br><span class="line"><span class="operator">|</span>   <span class="operator">+</span><span class="comment">-- greeter.module.ts</span></span><br><span class="line"><span class="operator">|</span>   <span class="operator">+</span><span class="comment">-- greeter.*.ts</span></span><br><span class="line"><span class="operator">|</span>   <span class="operator">+</span><span class="comment">-- index.ts</span></span><br></pre></td></tr></table></figure><p>特点：</p><ul><li>项目文档及相关的资源在根目录，包括<code>typings</code>、<code>test</code>、<code>bin</code></li><li><code>src</code>中会对功能进行划分建不同的文件夹</li><li>抽离公共代码到<code>common</code>文件夹，配置文件放在<code>config</code>文件夹，实体类放置在<code>entity</code>中</li><li>鉴权相关的逻辑放在<code>auth</code></li><li>把同类的<code>guards</code>、<code>filters</code>、<code>decorators</code>、<code>interceptors</code>、<code>interfaces</code>、<code>errors</code>存放在<code>common</code>文件夹中</li></ul><h2 id="代码规范-风格指南"><a href="#代码规范-风格指南" class="headerlink" title="代码规范(风格指南)"></a>代码规范(风格指南)</h2><p>我们对Angular风格指南进行了摘抄，如下：</p><blockquote><p>参考：<a href="https://angular.cn/guide/styleguide">Angular风格指南</a></p></blockquote><h3 id="总则"><a href="#总则" class="headerlink" title="总则"></a>总则</h3><p><strong>坚持</strong>每个文件只定义一样东西（例如服务或组件）</p><p><strong>考虑</strong>把文件大小限制在 400 行代码以内</p><p><strong>坚持</strong>定义简单函数</p><p><strong>考虑</strong>限制在 75 行之内</p><h3 id="命名"><a href="#命名" class="headerlink" title="命名"></a>命名</h3><p><strong>坚持</strong>所有符号使用一致的命名规则</p><p><strong>坚持</strong>遵循同一个模式来描述符号的特性和类型</p><h4 id="使用点和横杠来分隔文件名"><a href="#使用点和横杠来分隔文件名" class="headerlink" title="使用点和横杠来分隔文件名"></a>使用点和横杠来分隔文件名</h4><p><strong>坚持</strong> 在描述性名字中，用横杠来分隔单词。</p><p><strong>坚持</strong>使用点来分隔描述性名字和类型。</p><p><strong>坚持</strong>遵循先描述组件特性，再描述它的类型的模式，对所有组件使用一致的类型命名规则。推荐的模式为 <code>feature.type.ts</code>。</p><p><strong>坚持</strong>使用惯用的后缀来描述类型，包括 <code>*.service</code>、<code>*.component</code>、<code>*.pipe</code>、<code>.module</code>、<code>.directive</code>。 必要时可以创建更多类型名，但必须注意，不要创建太多。</p><h4 id="符号名与文件名"><a href="#符号名与文件名" class="headerlink" title="符号名与文件名"></a>符号名与文件名</h4><p><strong>坚持</strong>为所有东西使用一致的命名约定，以它们所代表的东西命名。</p><p><strong>坚持</strong>使用<strong>大写驼峰命名法来命名类</strong></p><p><strong>坚持</strong>匹配符号名与它所在的文件名</p><p><strong>坚持</strong>在符号名后面追加约定的类型后缀（例如 <code>Component</code>、<code>Directive</code>、<code>Module</code>、<code>Pipe</code>、<code>Service</code>）。</p><p><strong>坚持</strong>在文件名后面追加约定的类型后缀（例如 <code>.component.ts</code>、<code>.directive.ts</code>、<code>.module.ts</code>、<code>.pipe.ts</code>、<code>.service.ts</code>）</p><p><strong>坚持</strong>使用<em>中线命名法（dashed-case）</em>或叫<em>烤串命名法（kebab-case）</em>来命名组件的元素选择器。</p><h4 id="服务名-amp-管道名"><a href="#服务名-amp-管道名" class="headerlink" title="服务名&amp;管道名"></a>服务名&amp;管道名</h4><p><strong>坚持</strong>使用一致的规则命名服务，以它们的特性来命名</p><p><strong>坚持</strong>为服务的类名加上 <code>Service</code> 后缀。 例如，获取数据或英雄列表的服务应该命名为 <code>DataService</code> 或 <code>HeroService</code></p><p><strong>坚持</strong>为所有管道使用一致的命名约定，用它们的特性来命名。 管道类名应该使用 <a href="https://angular.cn/guide/glossary#case-types">UpperCamelCase</a>（类名的通用约定），而相应的 <code>name</code> 字符串应该使用 lowerCamelCase。 <code>name</code> 字符串中不应该使用中线（“中线格式”或“烤串格式”）。例如：</p><p><code>ellipsis.pipe.ts</code></p><figure class="highlight typescript"><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="meta">@Pipe</span>(&#123; <span class="attr">name</span>: <span class="string">&#x27;ellipsis&#x27;</span> &#125;)</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">EllipsisPipe</span> <span class="keyword">implements</span> <span class="title class_">PipeTransform</span> &#123; &#125;</span><br></pre></td></tr></table></figure><p>和</p><p><code>init-caps.pipe.ts</code></p><figure class="highlight typescript"><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="meta">@Pipe</span>(&#123; <span class="attr">name</span>: <span class="string">&#x27;initCaps&#x27;</span> &#125;)</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">InitCapsPipe</span> <span class="keyword">implements</span> <span class="title class_">PipeTransform</span> &#123; &#125;</span><br></pre></td></tr></table></figure><p><strong>坚持</strong>在模块中只包含模块间的依赖关系，所有其它逻辑都应该放到服务中</p><p><strong>坚持</strong>把可复用的逻辑放到服务中，保持组件简单，聚焦于它们预期目的</p><p><strong>坚持</strong>在同一个注入器内，把服务当做单例使用，用它们来共享数据和功能</p><p><strong>坚持</strong>创建封装在上下文中的单一职责的服务</p><p><strong>坚持</strong>当服务成长到超出单一用途时，创建一个新服务</p><p><strong>坚持</strong>把数据操作和与数据交互的逻辑重构到服务里。</p><h4 id="引导"><a href="#引导" class="headerlink" title="引导"></a>引导</h4><p><strong>坚持</strong>把应用的引导程序和平台相关的逻辑放到名为 <code>main.ts</code> 的文件里</p><p><strong>坚持</strong>在引导逻辑中包含错误处理代码</p><p><strong>避免</strong>把应用逻辑放在 <code>main.ts</code> 中，而应放在组件或服务里</p><h4 id="测试文件名"><a href="#测试文件名" class="headerlink" title="测试文件名"></a>测试文件名</h4><p><strong>单元测试：</strong></p><p><strong>坚持</strong>测试规格文件名与被测试组件文件名相同</p><p><strong>坚持</strong>测试规格文件名添加 <code>.spec</code> 后缀</p><p><strong>端到端的测试：</strong></p><p><strong>坚持</strong>端到端测试规格文件和它们所测试的特性同名，添加 <code>.e2e-spec</code> 后缀，或者放在特定的文件夹中。</p><h3 id="其他原则"><a href="#其他原则" class="headerlink" title="其他原则"></a>其他原则</h3><ul><li><p>定位：</p><p><strong>坚持</strong>直观、简单和快速地定位代码。</p></li><li><p>识别：</p><p><strong>坚持</strong>命名文件到这个程度：看到名字立刻知道它包含了什么，代表了什么。</p><p><strong>坚持</strong>文件名要具有说明性，确保文件中只包含一个组件。</p><p><strong>避免</strong>创建包含多个组件、服务或者混合体的文件。</p></li><li><p>扁平</p><p><strong>坚持</strong>尽可能保持扁平的目录结构。</p><p><strong>考虑</strong>当同一目录下达到 7 个或更多个文件时创建子目录。</p><p><strong>考虑</strong>配置 IDE，以隐藏无关的文件，例如生成出来的 <code>.js</code> 文件和 <code>.js.map</code> 文件等。</p></li><li><p>T-DRY</p><p><strong>坚持</strong> DRY（Don’t Repeat Yourself，不重复自己）。</p><p><strong>避免</strong>过度 DRY，以致牺牲了阅读性</p></li><li><p>代码结构</p><p><strong>坚持</strong>从零开始，但要考虑应用程序接下来的路往哪儿走</p><p><strong>坚持</strong>有一个近期实施方案和一个长期的愿景</p><p><strong>坚持</strong>把所有源代码都放到名为 <code>src</code> 的目录里</p><p><strong>坚持</strong>如果组件具有多个伴生文件 (<code>.ts</code>、<code>.html</code>、<code>.css</code> 和 <code>.spec</code>)，就为它创建一个文件夹</p></li><li><p>ESLint</p><p><strong>坚持</strong>使用VSCode等IDE、配合ESLint + Prettier等工具来整理代码格式、检查代码风格问题。</p></li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;这是《nestjs搭建通用业务框架》系列的第4篇，进入开发具体的功能之前，养成良好的工程目录与代码风格的习惯，目的构建大型复杂项目，提高代码易维护性。&lt;/p&gt;</summary>
    
    
    
    <category term="nestjs搭建通用业务框架" scheme="https://www.toimc.com/categories/nestjs%E6%90%AD%E5%BB%BA%E9%80%9A%E7%94%A8%E4%B8%9A%E5%8A%A1%E6%A1%86%E6%9E%B6/"/>
    
    
    <category term="node.js" scheme="https://www.toimc.com/tags/node-js/"/>
    
    <category term="nestjs" scheme="https://www.toimc.com/tags/nestjs/"/>
    
    <category term="web框架" scheme="https://www.toimc.com/tags/web%E6%A1%86%E6%9E%B6/"/>
    
  </entry>
  
  <entry>
    <title>什么是依赖注入?</title>
    <link href="https://www.toimc.com/what-is-dependency-injection/"/>
    <id>https://www.toimc.com/what-is-dependency-injection/</id>
    <published>2021-03-12T02:39:50.000Z</published>
    <updated>2025-10-06T05:13:13.973Z</updated>
    
    <content type="html"><![CDATA[<p>本篇是nestjs系列文章的番外篇，主要是介绍控制反转Invention of Control(IoC)和依赖注入Dependecy Injection(DI)，作为nestjs和angular的核心概念，学习它们有利于更好的进行框架的学习。</p><span id="more"></span><h2 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h2><p><strong>控制反转（Inversion of Control）</strong>是一种是面向对象编程中的一种设计原则，用来减低计算机代码之间的耦合度。其基本思想是：借助于“第三方”实现具有依赖关系的对象之间的解耦。</p><p><strong>依赖注入</strong>是一种用于实现IoC的设计模式，它允许在类外创建依赖对象，并通过不同的方式将这些对象提供给类。使用DI，我们将依赖对象的创建和绑定移到依赖它们的类之外。具体的做法，比如：将实例变量传入到一个对象中去(Dependency injection means giving an object its instance variables)</p><p>简单来说它和依赖注入间的区别就是：</p><ul><li><strong>控制反转是一种设计思想</strong></li><li><strong>依赖注入是一种编程技巧</strong></li></ul><h2 id="作用"><a href="#作用" class="headerlink" title="作用"></a>作用</h2><p>先看个例子：我们希望在通知组件(NotificationComponent)中通过消息服务(MessageService)发送一条消息。</p><p>如果不使用依赖注入的话，我们的代码大概长这样：</p><figure class="highlight js"><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="keyword">class</span> <span class="title class_">NotificationComponent</span> &#123;</span><br><span class="line">  <span class="attr">msg</span>: <span class="title class_">MessageService</span>;</span><br><span class="line">  <span class="title function_">constructor</span>(<span class="params"></span>) &#123;</span><br><span class="line">     <span class="variable language_">this</span>.<span class="property">msg</span> = <span class="keyword">new</span> <span class="title class_">MessageService</span>();</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="title function_">sendMsg</span>(<span class="params">msgType: string, info: string</span>) &#123;</span><br><span class="line">     <span class="variable language_">this</span>.<span class="property">msg</span>.<span class="title function_">send</span>(msgType, info);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>使用依赖注入时：</p><figure class="highlight js"><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="keyword">class</span> <span class="title class_">NotificationComponent</span> &#123;</span><br><span class="line">  <span class="title function_">constructor</span>(<span class="params">msg: MessageService</span>) &#123;&#125;  <span class="comment">// Angular 中注入依赖的方式</span></span><br><span class="line">  <span class="title function_">sendMsg</span>(<span class="params">msgType: string, info: string</span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">msg</span>.<span class="title function_">send</span>(msgType, info);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>经过对比，可以看到使用依赖注入有两个很显然的优点：</p><ul><li>代码的行数变少了</li><li><code>NotificationComponent</code> 与 <code>MessageService</code> 间的耦合性降低了</li></ul><h2 id="结论"><a href="#结论" class="headerlink" title="结论"></a>结论</h2><p>在控制反转中，”控制“是指对程序流程的控制，”反转“则是将控制权从程序员的手里反转到了外层框架。</p><p>控制反转是一种在软件工程中解耦合的思想，调用类只依赖接口，而不依赖具体的实现类，减少了耦合。控制权交给了容器，在运行的时候才由容器决定将具体的实现动态的“注入”到调用类的对象中。</p><p>既然控制反转是一种设计思想，那么作为相应实现方式之一的依赖注入（模板模式也是种实现方式）必然也遵循此思想。</p><p>依赖注入是一种设计模式，可以作为控制反转的一种实现方式。依赖注入就是将实例变量传入到一个对象中去(Dependency injection means giving an object its instance variables)。</p><p>通过IoC框架，类A依赖类B的强耦合关系可以在运行时通过容器建立，也就是说把创建B实例的工作移交给容器，类A只管使用就可以。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;本篇是nestjs系列文章的番外篇，主要是介绍控制反转Invention of Control(IoC)和依赖注入Dependecy Injection(DI)，作为nestjs和angular的核心概念，学习它们有利于更好的进行框架的学习。&lt;/p&gt;</summary>
    
    
    
    
    <category term="Dependecy Injection(DI)" scheme="https://www.toimc.com/tags/Dependecy-Injection-DI/"/>
    
    <category term="Invention of Control(IoC)" scheme="https://www.toimc.com/tags/Invention-of-Control-IoC/"/>
    
    <category term="依赖注入" scheme="https://www.toimc.com/tags/%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5/"/>
    
    <category term="架构思维" scheme="https://www.toimc.com/tags/%E6%9E%B6%E6%9E%84%E6%80%9D%E7%BB%B4/"/>
    
  </entry>
  
  <entry>
    <title>nestjs搭建通用业务框架（3）：核心概念</title>
    <link href="https://www.toimc.com/nestjs-example-project-3/"/>
    <id>https://www.toimc.com/nestjs-example-project-3/</id>
    <published>2021-03-11T01:30:45.000Z</published>
    <updated>2025-10-06T05:13:13.973Z</updated>
    
    <content type="html"><![CDATA[<p>这是《nestjs搭建通用业务框架》系列的第3篇，主要是介绍nestjs中的核心概念，理解这些概念与使用方法是今后学习上的垫脚石。PS: 对于本篇的学习，你需要有一定的TS的基础，理解起来才会比较顺畅。</p><span id="more"></span><blockquote><p>本章的内容不是随意的复制、粘贴官方的译本，这样没有任何学习的价值。所有的内容经过个人的整理，也算是知识的输出，大家且学且手下留情。</p><p>其中比较重要的内容：装饰器、装饰器 - 依赖注入，在依赖注入部分因为概念比较难理解，所以后面打算单独再写一篇关于依赖注入（设计模式）的博文！欢迎订阅RSS！</p></blockquote><h2 id="总览概念"><a href="#总览概念" class="headerlink" title="总览概念"></a>总览概念</h2><p>先不废话，给大家总结了一张基础概念的思维导图：</p><p><img src="https://static.www.toimc.com/blog/img/2021/nestjs-example-project-3/nestjs%EF%BC%88TypeScript%EF%BC%89%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5.jpg?watermark/2/text/VG9pbWPlh7rlk4E=/fontsize/200" alt="核心概念"></p><p>大家可以把上面的图，当成是平时查阅概念的一个索引，我们把相关的内容进行了连接与分层，方便大家学习。</p><h2 id="装饰器"><a href="#装饰器" class="headerlink" title="装饰器"></a>装饰器</h2><p><strong>这是nestjs（Angular）学习中非常！非常！非常重要的一部分！</strong>可以说是nestjs借用ES高级特性的一个灵魂~，我们将会从以下几个概念出发来进行学习。</p><p><u>在下面的学习中，不乏会有一些代码的内容，看不懂没有关系</u>，先读每一部分的前置的介绍，然后TS代码的内容，主要是了解基础的应用与该装饰器的作用即可。</p><h3 id="控制器"><a href="#控制器" class="headerlink" title="控制器"></a>控制器</h3><p>控制器负责处理传入的 <strong>请求</strong> 和向客户端返回 <strong>响应</strong> 。</p><figure class="highlight typescript"><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="keyword">import</span> &#123; <span class="title class_">Controller</span>, <span class="title class_">Get</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;@nestjs/common&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Controller</span>(<span class="string">&#x27;cats&#x27;</span>) <span class="comment">// Controller - 控制器的装饰器， cats - 路由，或者说是请求的一个前缀，类似于 /api/user /api/test ... 中的/api</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">CatsController</span> &#123;</span><br><span class="line">  <span class="meta">@Get</span>(<span class="string">&#x27;ab*cd&#x27;</span>) <span class="comment">// Get - 请求的类型的装饰器</span></span><br><span class="line">  <span class="title function_">findAll</span>(<span class="meta">@Req</span>() <span class="attr">request</span>: <span class="title class_">Request</span>): <span class="built_in">string</span> &#123; <span class="comment">// 具体对应上面请求的响应函数</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">&#x27;This action returns all cats&#x27;</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="meta">@Post</span>()</span><br><span class="line">  <span class="meta">@HttpCode</span>(<span class="number">204</span>) <span class="comment">// http状态码</span></span><br><span class="line">  <span class="meta">@Header</span>(<span class="string">&#x27;Cache-Control&#x27;</span>, <span class="string">&#x27;none&#x27;</span>) <span class="comment">// 响应头</span></span><br><span class="line">  <span class="title function_">create</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&#x27;This action adds a new cat&#x27;</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="meta">@Get</span>(<span class="string">&#x27;docs&#x27;</span>)</span><br><span class="line">  <span class="meta">@Redirect</span>(<span class="string">&#x27;https://docs.nestjs.com&#x27;</span>, <span class="number">302</span>) <span class="comment">// 重定向</span></span><br><span class="line">  <span class="title function_">getDocs</span>(<span class="params"><span class="meta">@Query</span>(<span class="string">&#x27;version&#x27;</span>) version</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (version &amp;&amp; version === <span class="string">&#x27;5&#x27;</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> &#123; <span class="attr">url</span>: <span class="string">&#x27;https://docs.nestjs.com/v5/&#x27;</span> &#125;;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="meta">@Get</span>(<span class="string">&#x27;:id&#x27;</span>) <span class="comment">// 参数  - 路径传参使用Param，url传参使用Query，post传参使用Body</span></span><br><span class="line">  <span class="title function_">findOne</span>(<span class="meta">@Param</span>() params): <span class="built_in">string</span> &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(params.<span class="property">id</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">`This action returns a #<span class="subst">$&#123;params.id&#125;</span> cat`</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>使用如下命令：</p><figure class="highlight delphi"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nest g controller [<span class="keyword">file</span>-<span class="keyword">name</span>]</span><br></pre></td></tr></table></figure><p>来创建一个标准的控制器文件。</p><ul><li><p>路由 - <code>@Controller</code></p></li><li><p>Request - 如下表：</p><p>了解Express的同学应该知道，请求对象<code>req</code>与响应对象<code>res</code>：</p><table><thead><tr><th>装饰器</th><th>对应的Express中的对象或者用法</th></tr></thead><tbody><tr><td><code>@Request()</code></td><td><code>req</code></td></tr><tr><td><code>@Response() @Res()*</code></td><td><code>res</code></td></tr><tr><td><code>@Next()</code></td><td><code>next</code></td></tr><tr><td><code>@Session()</code></td><td><code>req.session</code></td></tr><tr><td><code>@Param(key?: string)</code></td><td><code>req.params</code> &#x2F; <code>req.params[key]</code></td></tr><tr><td><code>@Body(key?: string)</code></td><td><code>req.body</code> &#x2F; <code>req.body[key]</code></td></tr><tr><td><code>@Query(key?: string)</code></td><td><code>req.query</code> &#x2F; <code>req.query[key]</code></td></tr><tr><td><code>@Headers(name?: string)</code></td><td><code>req.headers</code> &#x2F; <code>req.headers[name]</code></td></tr><tr><td><code>@Ip()</code></td><td><code>req.ip</code></td></tr></tbody></table></li><li><p>资源（请求方法）- <code>@Put()</code> 、 <code>@Delete()</code>、 <code>@Patch()</code>、 <code>@Options()</code>、 <code>@Head()</code>和 <code>@All()</code>。这些表示各自的 <code>HTTP</code>请求方法。</p></li><li><p>路由通配符 - 设置在上面的资源中的正则表达式，字符 <code>?</code> 、 <code>+</code> 、 <code>*</code> 以及 <code>()</code> 是它们的正则表达式对应项的子集。连字符 (<code>-</code>) 和点 (<code>.</code>) 按字符串路径解析。如：<code>@Get(&#39;ab*cd&#39;)</code>会匹配成<code>abcd</code>， <code>ab_cd</code>，<code>abecd</code>等。</p></li><li><p>状态码 - <code>@HttpCode()</code></p></li><li><p>响应头 - <code>@Header</code></p></li></ul><h3 id="提供者"><a href="#提供者" class="headerlink" title="提供者"></a>提供者</h3><p>提供者是<code>nest</code>借用<code>Angular</code>的设计模式引入的一个概念。</p><blockquote><p>Providers 是 <code>Nest</code> 的一个基本概念，许多基本的 <code>Nest</code> 类可能被视为 provider - <code>service</code>,<code> repository</code>, <code>factory</code>, <code>helper</code> 等等，他们都可以通过 <code>constructor</code> <strong>注入</strong>依赖关系。</p></blockquote><p><code>providers</code>就是在代码组织上抽象的一层存放公共代码的部分，在程序内部交由框架来处理依赖关系，通过<code>providers</code>和<code>@Injectable()</code>注解可以自由的使用这些公共的代码中的逻辑。</p><h4 id="服务"><a href="#服务" class="headerlink" title="服务"></a>服务</h4><p>这张图很好的解释了<code>@Injectable()</code>的作用：</p><p><img src="https://static.www.toimc.com/blog/img/2021/nestjs-example-project-3/injector-injects.png?watermark/2/text/VG9pbWPlh7rlk4E=/fontsize/200"></p><p>解释：</p><p>一个<code>ServiceA</code>被加上<code>@Injectable()</code>的注解之后，就变成了一个可以被其他<code>Component</code>在构造器部分引用的参数，在<code>nest</code>的提供者<code>providers:[]</code>中需要添加对应的<code>ServiceA</code>，如：<code>providers: [ ServiceA ]</code>，以便框架自动来处理依赖关系，并进行<u>必要的</u>实例化。</p><p>用法：</p><figure class="highlight verilog"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nest <span class="keyword">generate</span> service [service-name]</span><br></pre></td></tr></table></figure><h4 id="依赖注入-重要"><a href="#依赖注入-重要" class="headerlink" title="依赖注入(重要)"></a>依赖注入(重要)</h4><p>主要有三类具体的应用：</p><ul><li>管道 - 实现数据的转换与验证</li><li>守卫 - 实现流程控制，鉴权管理</li><li>拦截器 - 绑定函数执行前后的处理函数，如：请求&amp;响应拦截器、错误统一处理、缓存等</li></ul><blockquote><p>这三类全是<code>@Injectable()</code>修饰的，官方在架构上提供的现成的工具类，管道应实现 <code>PipeTransform</code> 接口，守卫应该实现 <code>CanActivate</code> 接口，拦截器应该实现 <code>NestInterceptor</code> 接口。</p></blockquote><p>要理解依赖注入，要先看个例子：我们希望在通知组件(NotificationComponent)中通过消息服务(MessageService)发送一条消息。</p><p>如果不使用依赖注入的话，我们的代码大概长这样：</p><figure class="highlight js"><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="keyword">class</span> <span class="title class_">NotificationComponent</span> &#123;</span><br><span class="line">  <span class="attr">msg</span>: <span class="title class_">MessageService</span>;</span><br><span class="line">  <span class="title function_">constructor</span>(<span class="params"></span>) &#123;</span><br><span class="line">     <span class="variable language_">this</span>.<span class="property">msg</span> = <span class="keyword">new</span> <span class="title class_">MessageService</span>();</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="title function_">sendMsg</span>(<span class="params">msgType: string, info: string</span>) &#123;</span><br><span class="line">     <span class="variable language_">this</span>.<span class="property">msg</span>.<span class="title function_">send</span>(msgType, info);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>使用依赖注入时：</p><figure class="highlight js"><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="keyword">class</span> <span class="title class_">NotificationComponent</span> &#123;</span><br><span class="line">  <span class="title function_">constructor</span>(<span class="params">msg: MessageService</span>) &#123;&#125;  <span class="comment">// Angular 中注入依赖的方式</span></span><br><span class="line">  <span class="title function_">sendMsg</span>(<span class="params">msgType: string, info: string</span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">msg</span>.<span class="title function_">send</span>(msgType, info);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>经过对比，可以看到使用依赖注入有两个很显然的优点：</p><ul><li>代码的行数变少了</li><li>NotificationComponent 与 MessageService 间的耦合性降低了</li></ul><p>大家先可以去了解一下IoC（Inversion of Control）控制反转，简单来说它和依赖注入（Dependency Injection）间的区别就是：</p><ul><li><strong>依赖注入是一种编程技巧</strong></li><li><strong>控制反转是一种设计思想</strong></li></ul><p><strong>依赖注入</strong>就是不通过 new 这种方式来在类（NotificationComponent）的内部创建所依赖类（MessageService）的对象，而是在外部创建好需要依赖的类对象之后通过构造函数等方式注入进来就可以了。</p><p>在<strong>控制反转</strong>中，”控制“是指对程序流程的控制，”反转“则是将控制权从程序员的手里反转到了外层框架。</p><ol><li><p>管道(Pipe)</p><p>管道有两个作用:</p><ul><li><strong>转换</strong>：管道将输入数据转换为所需的数据输出</li><li><strong>验证</strong>：对输入数据进行验证，如果验证成功继续传递; 验证失败则抛出异常;</li></ul><p>按照作用分的类型：内置管道、结构验证、类验证、转换管道</p><p><img src="https://static.www.toimc.com/blog/img/2021/nestjs-example-project-3/Pipe_1.png?watermark/2/text/VG9pbWPlh7rlk4E=/fontsize/200"></p></li><li><p>守卫(Guard)</p><p>守卫有一个单独的责任，它们根据运行时出现的某些条件（例如权限，角色，访问控制列表等）来确定给定的请求是否由路由处理程序处理。 这通常称为授权。</p><p><img src="https://static.www.toimc.com/blog/img/2021/nestjs-example-project-3/Guards_1.png?watermark/2/text/VG9pbWPlh7rlk4E=/fontsize/200"></p></li><li><p>拦截器(Interceptor)</p><p>拦截器具有一系列有用的功能，这些功能受面向切面编程（AOP）技术的启发。它们可以：</p><ul><li>在函数执行之前&#x2F;之后绑定<strong>额外的逻辑</strong></li><li>转换从函数返回的结果</li><li><strong>转换</strong>从函数抛出的异常</li><li>扩展基本函数行为</li><li>根据所选条件完全重写函数 (例如, 缓存目的)</li></ul><p><img src="https://static.www.toimc.com/blog/img/2021/nestjs-example-project-3/Interceptors_1.png?watermark/2/text/VG9pbWPlh7rlk4E=/fontsize/200"></p></li></ol><h3 id="模块"><a href="#模块" class="headerlink" title="模块"></a>模块</h3><p>模块化的概念无处不在，在<code>nest</code>中，模块是应用程序的基础的组成部分，如下图：</p><p><img src="https://static.www.toimc.com/blog/img/2021/nestjs-example-project-3/Modules_1.png?watermark/2/text/VG9pbWPlh7rlk4E=/fontsize/200"></p><p>具体特点：</p><ul><li><p>模块是具有 <code>@Module()</code> 装饰器的类。</p></li><li><p>每个 Nest 应用程序至少有一个模块，即根模块。根模块可能是应用程序中唯一的模块，特别是当应用程序很小时，但是对于大型程序来说这是没有意义的。</p></li><li><p>在大多数情况下，您将拥有多个模块，每个模块都有一组紧密相关的属性，如下表：</p></li></ul><p><code>@module()</code> 装饰器接受一个描述模块属性的对象：</p><table><thead><tr><th>属性名</th><th>解释</th></tr></thead><tbody><tr><td>providers</td><td>由 Nest 注入器实例化的提供者，并且可以至少在整个模块中共享</td></tr><tr><td>controllers</td><td>必须创建的一组控制器</td></tr><tr><td>imports</td><td>导入模块的列表，这些模块导出了此模块中所需提供者</td></tr><tr><td>exports</td><td>由本模块提供并应在其他模块中可用的提供者的子集。</td></tr></tbody></table><h2 id="中间件"><a href="#中间件" class="headerlink" title="中间件"></a>中间件</h2><p>中间件是在路由处理程序 <strong>之前</strong> 调用的函数，实现 <code>NestMiddleware</code> 接口。</p><p>Nest 中间件实际上等价于 <a href="http://expressjs.com/en/guide/using-middleware.html">express</a> 中间件。 下面是Express官方文档中所述的中间件功能：</p><p>中间件函数可以执行以下任务:</p><ul><li>执行任何代码。</li><li>对请求和响应对象进行更改。</li><li>结束请求-响应周期。</li><li>调用堆栈中的下一个中间件函数。</li><li>如果当前的中间件函数没有结束请求-响应周期, 它必须调用 <code>next()</code> 将控制传递给下一个中间件函数。否则, 请求将被挂起。</li></ul><p><img src="https://static.www.toimc.com/blog/img/2021/nestjs-example-project-3/Middlewares_1.png?watermark/2/text/VG9pbWPlh7rlk4E=/fontsize/200"></p><h2 id="异常过滤器"><a href="#异常过滤器" class="headerlink" title="异常过滤器"></a>异常过滤器</h2><p>内置的<strong>异常层</strong>负责处理整个应用程序中的所有抛出的异常。当捕获到未处理的异常时，最终用户将收到友好的响应。</p><p>开箱即用，此操作由内置的全局异常过滤器执行，该过滤器处理类型 <code>HttpException</code>（及其子类）的异常。每个发生的异常都由全局异常过滤器处理, 当这个异常<strong>无法被识别</strong>时 (既不是 <code>HttpException</code> 也不是继承的类 <code>HttpException</code> ) , 用户将收到以下 <code>JSON</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;statusCode&quot;</span><span class="punctuation">:</span> <span class="number">500</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;message&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Internal server error&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>通过本篇的介绍，大家应该有如下的感受：</p><ul><li><code>nest</code>大量使用了注解来简化逻辑</li><li><code>nest</code>的程序设计（架构）很丰富，解耦&amp;易用。解耦不用说，从上面的分层就能看出来；易用，主要体现在语义化关键词、强大的CLI命令。</li></ul><p>核心的概念：模块 -&gt; 控制器 -&gt; 服务、管道、守卫、拦截器 -&gt; 中间件 -&gt; 异常过滤器。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p>推荐一些非官方的，用于去理解“依赖注入”的资料：</p><ul><li><a href="https://zhuanlan.zhihu.com/p/113299696">详解Angular依赖注入</a>：国人写的案例，还比较有意思，解释的比较清楚的了，本篇引用了部分内容</li><li><a href="https://angular.cn/guide/architecture-services#dependency-injection-di">Angular - 服务与依赖注入简介</a>：这个是Angular的官方解读，也有配图，我感觉还是比较容易搞清楚的</li><li><a href="https://www.tutorialsteacher.com/ioc/dependency-injection">Dependency Injection</a>：虽然是英文，但是非常推荐去读一读</li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;这是《nestjs搭建通用业务框架》系列的第3篇，主要是介绍nestjs中的核心概念，理解这些概念与使用方法是今后学习上的垫脚石。PS: 对于本篇的学习，你需要有一定的TS的基础，理解起来才会比较顺畅。&lt;/p&gt;</summary>
    
    
    
    <category term="nestjs搭建通用业务框架" scheme="https://www.toimc.com/categories/nestjs%E6%90%AD%E5%BB%BA%E9%80%9A%E7%94%A8%E4%B8%9A%E5%8A%A1%E6%A1%86%E6%9E%B6/"/>
    
    
    <category term="node.js" scheme="https://www.toimc.com/tags/node-js/"/>
    
    <category term="nestjs" scheme="https://www.toimc.com/tags/nestjs/"/>
    
    <category term="web框架" scheme="https://www.toimc.com/tags/web%E6%A1%86%E6%9E%B6/"/>
    
  </entry>
  
</feed>
