<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Hexo</title>
  
  
  <link href="http://example.com/atom.xml" rel="self"/>
  
  <link href="http://example.com/"/>
  <updated>2022-08-05T07:24:03.574Z</updated>
  <id>http://example.com/</id>
  
  <author>
    <name>Abu</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>设计模式-如何理解工厂模式?</title>
    <link href="http://example.com/2022/08/08/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.%E5%A6%82%E4%BD%95%E7%90%86%E8%A7%A3%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F_/"/>
    <id>http://example.com/2022/08/08/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.%E5%A6%82%E4%BD%95%E7%90%86%E8%A7%A3%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F_/</id>
    <published>2022-08-08T08:49:23.175Z</published>
    <updated>2022-08-05T07:24:03.574Z</updated>
    
    <content type="html"><![CDATA[<h1 id="设计模式-如何理解工厂模式"><a href="#设计模式-如何理解工厂模式" class="headerlink" title="设计模式-如何理解工厂模式?"></a>设计模式-如何理解工厂模式?</h1><p>设计模式-如何理解工厂模式?</p><blockquote><p>定义: 创建对象的接口，让子类决定实例化哪个类。工厂方法将类的实例化延迟到子类,而子类可以重写接口方法以便创建的时候指定自己的对象类型。</p></blockquote><h5 id="适用场景"><a href="#适用场景" class="headerlink" title="适用场景"></a>适用场景</h5><p>需要根据不同参数产生不同实例，这些实例都有相同的行为,这时候我们可以使用工厂模式，简化实现的过程，同时也可以减少每种对象所需的代码量。工厂模式有利于消除对象间的耦合，提供更大的灵活性</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="comment">// 下方就是一个典型的工厂模式// 首先创建对象的接口</span></span><br><span class="line"><span class="keyword">const</span> productManager = &#123;&#125;;</span><br><span class="line">productManager.<span class="property">createProductA</span> = <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;ProductA&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line">productManager.<span class="property">createProductB</span> = <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;ProductB&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line">productManager.<span class="property">factory</span> = <span class="keyword">function</span> (<span class="params">type</span>) &#123;    <span class="comment">// 工厂方法将类的实例化延迟到子类    return new productManager[type];&#125;// 让子类决定实例化哪个类productManager.factory(&quot;createProductA&quot;);</span></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><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"><span class="keyword">var</span> page = page || &#123;&#125;;</span><br><span class="line">page.<span class="property">dom</span> = page.<span class="property">dom</span> || &#123;&#125;;<span class="comment">//子函数1：处理文本</span></span><br><span class="line">page.<span class="property">dom</span>.<span class="property">Text</span> = <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">insert</span> = <span class="keyword">function</span> (<span class="params">where</span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> txt = <span class="variable language_">document</span>.<span class="title function_">createTextNode</span>(<span class="variable language_">this</span>.<span class="property">url</span>);</span><br><span class="line">    where.<span class="title function_">appendChild</span>(txt);</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">//子函数2：处理链接</span></span><br><span class="line">page.<span class="property">dom</span>.<span class="property">Link</span> = <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">insert</span> = <span class="keyword">function</span> (<span class="params">where</span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> link = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&#x27;a&#x27;</span>);</span><br><span class="line">    link.<span class="property">href</span> = <span class="variable language_">this</span>.<span class="property">url</span>;</span><br><span class="line">    link.<span class="title function_">appendChild</span>(<span class="variable language_">document</span>.<span class="title function_">createTextNode</span>(<span class="variable language_">this</span>.<span class="property">url</span>));</span><br><span class="line">    where.<span class="title function_">appendChild</span>(link);</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">//子函数3：处理图片</span></span><br><span class="line">page.<span class="property">dom</span>.<span class="property">Image</span> = <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">insert</span> = <span class="keyword">function</span> (<span class="params">where</span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> im = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&#x27;img&#x27;</span>);</span><br><span class="line">    im.<span class="property">src</span> = <span class="variable language_">this</span>.<span class="property">url</span>;</span><br><span class="line">    where.<span class="title function_">appendChild</span>(im);</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></pre></td><td class="code"><pre><span class="line">page.<span class="property">dom</span>.<span class="property">factory</span> = <span class="keyword">function</span> (<span class="params">type</span>) &#123;    </span><br><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> page.<span class="property">dom</span>[type];</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> o = page.<span class="property">dom</span>.<span class="title function_">factory</span>(<span class="string">&#x27;Link&#x27;</span>);</span><br><span class="line">o.<span class="property">url</span> = <span class="string">&#x27;http://www.cnblogs.com&#x27;</span>;</span><br><span class="line">o.<span class="title function_">insert</span>(<span class="variable language_">document</span>.<span class="property">body</span>);</span><br></pre></td></tr></table></figure><h5 id="熟悉的jquery"><a href="#熟悉的jquery" class="headerlink" title="熟悉的jquery"></a>熟悉的jquery</h5><p>jQuery中的$()其实就是一个工厂函数，它根据传入参数的不同创建元素或者去寻找上下文中的元素，创建成相应的jQuery对象</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// https://github.com/jquery/jquery/blob/master/src/core/init.js</span></span><br><span class="line">init = jQuery.<span class="property">fn</span>.<span class="property">init</span> = <span class="keyword">function</span> (<span class="params">selector, context, root</span>) &#123;</span><br><span class="line">  <span class="keyword">var</span> match, elem;</span><br><span class="line">  <span class="comment">// HANDLE: $(&quot;&quot;), $(null), $(undefined), $(false)    </span></span><br><span class="line">  <span class="keyword">if</span> (!selector) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="variable language_">this</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// Method init() accepts an alternate rootjQuery    </span></span><br><span class="line">  <span class="comment">// so migrate can support jQuery.sub (gh-2101)    </span></span><br><span class="line">  root = root || rootjQuery;</span><br><span class="line">  <span class="comment">// Handle HTML strings    </span></span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">typeof</span> selector === <span class="string">&quot;string&quot;</span>) &#123;</span><br><span class="line">    <span class="comment">//...    </span></span><br><span class="line">    <span class="comment">// HANDLE: $(DOMElement)    </span></span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (selector.<span class="property">nodeType</span>) &#123;</span><br><span class="line">    <span class="comment">//....    </span></span><br><span class="line">    <span class="comment">// HANDLE: $(function)    </span></span><br><span class="line">    <span class="comment">// Shortcut for document ready    </span></span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (jQuery.<span class="title function_">isFunction</span>(selector)) &#123;</span><br><span class="line">    <span class="comment">//....    </span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> jQuery.<span class="title function_">makeArray</span>(selector, <span class="variable language_">this</span>);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><p>优点</p><ul><li>工厂类含有必要的判断逻辑, 实现了对责任的分割，它提供了专门的工厂类用于创建对象</li><li>用户只需要关心所需产品对应的工厂，无须关心创建细节</li><li>在系统中加入新产品时，无须修改抽象工厂和抽象产品提供的接口,符合“开闭原则”</li></ul><p>缺点</p><ul><li>添加新产品时，需要编写新的具体产品类,一定程度上增加了系统的复杂度</li><li>考虑到系统的可扩展性，需要引入抽象层，在客户端代码中均使用抽象层进行定义，增加了系统的抽象性和理解难度</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;设计模式-如何理解工厂模式&quot;&gt;&lt;a href=&quot;#设计模式-如何理解工厂模式&quot; class=&quot;headerlink&quot; title=&quot;设计模式-如何理解工厂模式?&quot;&gt;&lt;/a&gt;设计模式-如何理解工厂模式?&lt;/h1&gt;&lt;p&gt;设计模式-如何理解工厂模式?&lt;/p&gt;
&lt;bloc</summary>
      
    
    
    
    <category term="设计模式" scheme="http://example.com/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    
    
  </entry>
  
  <entry>
    <title>说说你对集合的理解？常见的操作有哪些？</title>
    <link href="http://example.com/2022/08/08/%E7%AE%97%E6%B3%95.%E8%AF%B4%E8%AF%B4%E4%BD%A0%E5%AF%B9%E9%9B%86%E5%90%88%E7%9A%84%E7%90%86%E8%A7%A3%EF%BC%9F%E5%B8%B8%E8%A7%81%E7%9A%84%E6%93%8D%E4%BD%9C%E6%9C%89%E5%93%AA%E4%BA%9B%EF%BC%9F/"/>
    <id>http://example.com/2022/08/08/%E7%AE%97%E6%B3%95.%E8%AF%B4%E8%AF%B4%E4%BD%A0%E5%AF%B9%E9%9B%86%E5%90%88%E7%9A%84%E7%90%86%E8%A7%A3%EF%BC%9F%E5%B8%B8%E8%A7%81%E7%9A%84%E6%93%8D%E4%BD%9C%E6%9C%89%E5%93%AA%E4%BA%9B%EF%BC%9F/</id>
    <published>2022-08-08T08:49:23.175Z</published>
    <updated>2022-08-05T07:24:08.055Z</updated>
    
    <content type="html"><![CDATA[<h1 id="说说你对集合的理解？常见的操作有哪些？"><a href="#说说你对集合的理解？常见的操作有哪些？" class="headerlink" title="说说你对集合的理解？常见的操作有哪些？"></a>说说你对集合的理解？常见的操作有哪些？</h1><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928215808109" alt="图片"></p><h2 id="一、是什么"><a href="#一、是什么" class="headerlink" title="一、是什么"></a>一、是什么</h2><p>集合（Set），指具有某种特定性质的事物的总体，里面的每一项内容称作元素</p><p>在数学中，我们经常会遇到集合的概念：</p><ul><li>有限集合：例如一个班级所有的同学构成的集合</li><li>无限集合：例如全体自然数集合</li></ul><p>在计算机中集合道理也基本一致，具有三大特性：</p><ul><li>确定性：于一个给定的集合，集合中的元素是确定的。即一个元素，或者属于该集合，或者不属于该集合，两者必居其一</li><li>无序性：在一个集合中，不考虑元素之间的顺序，只要元素完全相同，就认为是同一个集合</li><li>互异性：集合中任意两个元素都是不同的</li></ul><h2 id="二、操作"><a href="#二、操作" class="headerlink" title="二、操作"></a>二、操作</h2><p>在<code>ES6</code>中，集合本身是一个构建函数<code>Set</code>，用来生成 <code>Set</code> 数据结构，如下：</p><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> s = <span class="keyword">new</span> <span class="title class_">Set</span>();</span><br></pre></td></tr></table></figure><p>关于集合常见的方法有：</p><ul><li>add()：增</li><li>delete()：删</li><li>has()：改</li><li>clear()：查</li></ul><h3 id="add"><a href="#add" class="headerlink" title="add()"></a>add()</h3><p>添加某个值，返回 <code>Set</code> 结构本身</p><p>当添加实例中已经存在的元素，<code>set</code>不会进行处理添加</p><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">s.<span class="title function_">add</span>(<span class="number">1</span>).<span class="title function_">add</span>(<span class="number">2</span>).<span class="title function_">add</span>(<span class="number">2</span>); <span class="comment">// 2只被添加了一次</span></span><br></pre></td></tr></table></figure><p>体现了集合的互异性特性</p><h3 id="delete"><a href="#delete" class="headerlink" title="delete()"></a>delete()</h3><p>删除某个值，返回一个布尔值，表示删除是否成功</p><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">s.<span class="title function_">delete</span>(<span class="number">1</span>)</span><br></pre></td></tr></table></figure><h3 id="has"><a href="#has" class="headerlink" title="has()"></a>has()</h3><p>返回一个布尔值，判断该值是否为<code>Set</code>的成员</p><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">s.<span class="title function_">has</span>(<span class="number">2</span>)</span><br></pre></td></tr></table></figure><h3 id="clear"><a href="#clear" class="headerlink" title="clear()"></a>clear()</h3><p>清除所有成员，没有返回值</p><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">s.<span class="title function_">clear</span>()</span><br></pre></td></tr></table></figure><p>关于多个集合常见的操作有：</p><ul><li>并集</li><li>交集</li><li>差集</li></ul><h3 id="并集"><a href="#并集" class="headerlink" title="并集"></a>并集</h3><p>两个集合的共同元素，如下图所示：</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928215803821" alt="图片"></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> a = <span class="keyword">new</span> <span class="title class_">Set</span>([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]);</span><br><span class="line"><span class="keyword">let</span> b = <span class="keyword">new</span> <span class="title class_">Set</span>([<span class="number">4</span>, <span class="number">3</span>, <span class="number">2</span>]);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 并集</span></span><br><span class="line"><span class="keyword">let</span> union = <span class="keyword">new</span> <span class="title class_">Set</span>([...a, ...b]);</span><br><span class="line"><span class="comment">// Set &#123;1, 2, 3, 4&#125;</span></span><br></pre></td></tr></table></figure><h3 id="交集"><a href="#交集" class="headerlink" title="交集"></a>交集</h3><p>两个集合<code>A</code> 和 <code>B</code>，即属于<code>A</code>又属于<code>B</code>的元素，如下图所示：</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928215800454" alt="图片"></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> a = <span class="keyword">new</span> <span class="title class_">Set</span>([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]);</span><br><span class="line"><span class="keyword">let</span> b = <span class="keyword">new</span> <span class="title class_">Set</span>([<span class="number">4</span>, <span class="number">3</span>, <span class="number">2</span>]);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 交集</span></span><br><span class="line"><span class="keyword">let</span> intersect = <span class="keyword">new</span> <span class="title class_">Set</span>([...a].<span class="title function_">filter</span>(<span class="function"><span class="params">x</span> =&gt;</span> b.<span class="title function_">has</span>(x)));</span><br><span class="line"><span class="comment">// set &#123;2, 3&#125;</span></span><br></pre></td></tr></table></figure><h3 id="差集"><a href="#差集" class="headerlink" title="差集"></a>差集</h3><p>两个集合<code>A</code> 和 <code>B</code>，属于<code>A</code>的元素但不属于<code>B</code>的元素称为<code>A</code>相对于<code>B</code>的差集，如下图所示：</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928215757849" alt="图片"></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> a = <span class="keyword">new</span> <span class="title class_">Set</span>([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]);</span><br><span class="line"><span class="keyword">let</span> b = <span class="keyword">new</span> <span class="title class_">Set</span>([<span class="number">4</span>, <span class="number">3</span>, <span class="number">2</span>]);</span><br><span class="line"></span><br><span class="line"><span class="comment">// （a 相对于 b 的）差集</span></span><br><span class="line"><span class="keyword">let</span> difference = <span class="keyword">new</span> <span class="title class_">Set</span>([...a].<span class="title function_">filter</span>(<span class="function"><span class="params">x</span> =&gt;</span> !b.<span class="title function_">has</span>(x)));</span><br><span class="line"><span class="comment">// Set &#123;1&#125;</span></span><br></pre></td></tr></table></figure><h2 id="三、应用场景"><a href="#三、应用场景" class="headerlink" title="三、应用场景"></a>三、应用场景</h2><p>一般情况下，使用数组的概率会比集合概率高很多</p><p>使用<code>set</code>集合的场景一般是借助其确定性，其本身只包含不同的元素</p><p>所以，可以利用<code>Set</code>的一些原生方法轻松的完成数组去重，查找数组公共元素及不同元素等操作</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;说说你对集合的理解？常见的操作有哪些？&quot;&gt;&lt;a href=&quot;#说说你对集合的理解？常见的操作有哪些？&quot; class=&quot;headerlink&quot; title=&quot;说说你对集合的理解？常见的操作有哪些？&quot;&gt;&lt;/a&gt;说说你对集合的理解？常见的操作有哪些？&lt;/h1&gt;&lt;p&gt;&lt;i</summary>
      
    
    
    
    <category term="数据结构与算法" scheme="http://example.com/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
    
    
  </entry>
  
  <entry>
    <title>说说你对算法的理解？应用场景？</title>
    <link href="http://example.com/2022/08/08/%E7%AE%97%E6%B3%95.%E8%AF%B4%E8%AF%B4%E4%BD%A0%E5%AF%B9%E7%AE%97%E6%B3%95%E7%9A%84%E7%90%86%E8%A7%A3%EF%BC%9F%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF%EF%BC%9F/"/>
    <id>http://example.com/2022/08/08/%E7%AE%97%E6%B3%95.%E8%AF%B4%E8%AF%B4%E4%BD%A0%E5%AF%B9%E7%AE%97%E6%B3%95%E7%9A%84%E7%90%86%E8%A7%A3%EF%BC%9F%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF%EF%BC%9F/</id>
    <published>2022-08-08T08:49:23.174Z</published>
    <updated>2022-08-05T07:24:18.355Z</updated>
    
    <content type="html"><![CDATA[<h1 id="说说你对算法的理解？应用场景？"><a href="#说说你对算法的理解？应用场景？" class="headerlink" title="说说你对算法的理解？应用场景？"></a>说说你对算法的理解？应用场景？</h1><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928215248590" alt="图片"></p><h2 id="一、是什么"><a href="#一、是什么" class="headerlink" title="一、是什么"></a>一、是什么</h2><p>算法（Algorithm）是指解题方案的准确而完整的描述，是一系列解决问题的清晰指令，算法代表着用系统的方法描述解决问题的策略机制</p><p>也就是说，能够对一定规范的输入，在有限时间内获得所要求的输出</p><p>如果一个算法有缺陷，或不适合于某个问题，执行这个算法将不会解决这个问题</p><p>一个程序=算法+数据结构，数据结构是算法实现的基础，算法总是要依赖于某种数据结构来实现的，两者不可分割</p><p>因此，算法的设计和选择要同时结合数据结构，简单地说数据结构的设计就是选择存储方式，如确定问题中的信息是用数组存储还是用普通的变量存储或其他更加复杂的数据结构</p><p>针对上述，可以得出一个总结：不同的算法可能用不同的时间、空间或效率来完成同样的任务</p><h2 id="二、特性"><a href="#二、特性" class="headerlink" title="二、特性"></a>二、特性</h2><p>关于算法的五大特性，有如下：</p><ul><li>有限性（Finiteness）：一个算法必须保证执行有限步之后结束</li><li>确切性（Definiteness）：一个算法的每一步骤必须有确切的定义</li><li>输入（Input）：一个算法有零个或多个输入，以刻画运算对象的初始情况，所谓零个输入是指算法本身给定了初始条件</li><li>输出（Output）：一个算法有一个或多个输出。没有输出的算法毫无意义</li><li>可行性（Effectiveness）：算法中执行的任何计算步骤都是可以被分解为基本的可执行的操作步骤，即每个计算步骤都可以在有限时间内完成（也称之为有效性）</li></ul><h2 id="三、应用场景"><a href="#三、应用场景" class="headerlink" title="三、应用场景"></a>三、应用场景</h2><p>在前端领域中，数据结构与算法无法不在，例如现在的<code>vue</code>或者<code>react</code>项目，实现虚拟<code>DOM</code>或者<code>Fiber</code>结构，本质就是一种数据结构，如下一个简单的虚拟<code>DOM</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></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">type</span>: <span class="string">&#x27;div&#x27;</span>,</span><br><span class="line">    <span class="attr">props</span>: &#123;</span><br><span class="line">      <span class="attr">name</span>: <span class="string">&#x27;lucifer&#x27;</span></span><br><span class="line">    &#125;,</span><br><span class="line">      <span class="attr">children</span>: [&#123;</span><br><span class="line">        <span class="attr">type</span>: <span class="string">&#x27;span&#x27;</span>,</span><br><span class="line">        <span class="attr">props</span>: &#123;&#125;,</span><br><span class="line">        <span class="attr">children</span>: []</span><br><span class="line">      &#125;]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>vue</code>与<code>react</code>都能基于基于对应的数据结构实现<code>diff</code>算法，提高了整个框架的性能以及拓展性</p><p>包括在前端<code>javascript</code>编译的时候，都会生成对应的抽象语法树<code>AST</code>，其本身不涉及到任何语法，因此你只要编写相应的转义规则，就可以将任何语法转义到任何语法，也是<code>babel</code>， <code>PostCSS</code>, <code>prettier</code>， <code>typescript</code></p><p>除了这些框架或者工具底层用到算法与数据结构之外，日常业务也无处不在，例如实现一个输入框携带联想功能，如下：</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928215242342" alt="图片"></p><p>如果我们要实现这个功能， 则可以使用前缀树，如下：</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928215238758" alt="图片"></p><p>包括前端可能会做一些对字符串进行相似度检测，例如”每日一题”和”js每日一题”两个字符串进行相似度对比，这种情况可以通过“最小编辑距离”算法，如果<code>a</code>和<code>b</code>的编辑距离越小，我们认为越相似</p><p>日常在编写任何代码的都需要一个良好的算法思维，选择好的算法或者数据结构，能让整个程序效率更高</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;说说你对算法的理解？应用场景？&quot;&gt;&lt;a href=&quot;#说说你对算法的理解？应用场景？&quot; class=&quot;headerlink&quot; title=&quot;说说你对算法的理解？应用场景？&quot;&gt;&lt;/a&gt;说说你对算法的理解？应用场景？&lt;/h1&gt;&lt;p&gt;&lt;img src=&quot;https://</summary>
      
    
    
    
    <category term="数据结构与算法" scheme="http://example.com/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
    
    
  </entry>
  
  <entry>
    <title>说说你对链表的理解？常见的操作有哪些？</title>
    <link href="http://example.com/2022/08/08/%E7%AE%97%E6%B3%95.%E8%AF%B4%E8%AF%B4%E4%BD%A0%E5%AF%B9%E9%93%BE%E8%A1%A8%E7%9A%84%E7%90%86%E8%A7%A3%EF%BC%9F%E5%B8%B8%E8%A7%81%E7%9A%84%E6%93%8D%E4%BD%9C%E6%9C%89%E5%93%AA%E4%BA%9B%EF%BC%9F/"/>
    <id>http://example.com/2022/08/08/%E7%AE%97%E6%B3%95.%E8%AF%B4%E8%AF%B4%E4%BD%A0%E5%AF%B9%E9%93%BE%E8%A1%A8%E7%9A%84%E7%90%86%E8%A7%A3%EF%BC%9F%E5%B8%B8%E8%A7%81%E7%9A%84%E6%93%8D%E4%BD%9C%E6%9C%89%E5%93%AA%E4%BA%9B%EF%BC%9F/</id>
    <published>2022-08-08T08:49:23.174Z</published>
    <updated>2022-08-05T07:24:10.604Z</updated>
    
    <content type="html"><![CDATA[<h1 id="说说你对链表的理解？常见的操作有哪些？"><a href="#说说你对链表的理解？常见的操作有哪些？" class="headerlink" title="说说你对链表的理解？常见的操作有哪些？"></a>说说你对链表的理解？常见的操作有哪些？</h1><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928215647320" alt="图片"></p><h2 id="一、是什么"><a href="#一、是什么" class="headerlink" title="一、是什么"></a>一、是什么</h2><p>链表（Linked List）是一种物理存储单元上非连续、非顺序的存储结构，数据元素的逻辑顺序是通过链表中的指针链接次序实现的，由一系列结点（链表中每一个元素称为结点）组成</p><p>每个结点包括两个部分：一个是存储数据元素的数据域，另一个是存储下一个结点地址的指针域</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928215652334" alt="图片"></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Node</span> &#123;</span><br><span class="line">  <span class="title function_">constructor</span>(<span class="params">val</span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">val</span> = val;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">next</span> = <span class="literal">null</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>data 表示节点存放的数据</li><li>next 表示下一个节点指向的内存空间</li></ul><p>相比于线性表顺序结构，操作复杂。由于不必须按顺序存储，链表在插入的时候可以达到<code>O(1)</code>的复杂度，比另一种线性表顺序表快得多，但是查找一个节点或者访问特定编号的节点则需要O(n)的时间，而线性表和顺序表相应的时间复杂度分别是<code>O(logn)</code>和<code>O(1)</code></p><p>链表的结构也十分多，常见的有四种形式：</p><ul><li>单链表：拥有两个特殊节点，头节点和尾节点，每个节点包含一个后继指针</li><li>循环链表：跟单链表唯一的区别就在于它的尾结点又指回了链表的头结点，首尾相连，形成了一个环</li><li>双向链表：每个结点具有两个方向指针，后继指针(next)指向后面的结点，前驱指针(prev)指向前面的结点，其中节点的前驱指针和尾结点的后继指针均指向空地址NULL</li><li>双向循环链表：跟双向链表基本一致，不过头节点前驱指针指向尾迹诶单和尾节点的后继指针指向头节点</li></ul><h2 id="二、操作"><a href="#二、操作" class="headerlink" title="二、操作"></a>二、操作</h2><p>关于链表的操作可以主要分成如下：</p><ul><li>遍历</li><li>插入</li><li>删除</li></ul><h3 id="遍历"><a href="#遍历" class="headerlink" title="遍历"></a>遍历</h3><p>遍历很好理解，就是根据<code>next</code>指针遍历下去，直到为<code>null</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> current = head</span><br><span class="line"><span class="keyword">while</span>(current)&#123;</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(current.<span class="property">val</span>)</span><br><span class="line">  current = current.<span class="property">next</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="插入"><a href="#插入" class="headerlink" title="插入"></a>插入</h3><p>向链表中间插入一个元素，可以如下图所示：</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928215658532" alt="图片"></p><p>可以看到，插入节点可以分成如下步骤：</p><ul><li>存储插入位置的前一个节点</li><li>存储插入位置的后一个节点</li><li>将插入位置的前一个节点的 next 指向插入节点</li><li>将插入节点的 next 指向前面存储的 next 节点</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> current = head</span><br><span class="line"><span class="keyword">while</span> (current &lt; position)&#123;</span><br><span class="line">  pervious = current;</span><br><span class="line">  current = current.<span class="property">next</span>;</span><br><span class="line">&#125;</span><br><span class="line">pervious.<span class="property">next</span> = node;</span><br><span class="line">node.<span class="property">next</span> = current;</span><br></pre></td></tr></table></figure><p>如果在头节点进行插入操作的时候，会实现<code>previousNode</code>节点为<code>undefined</code>，不适合上述方式</p><p>解放方式可以是在头节点前面添加一个虚拟头节点，保证插入行为一致</p><h3 id="删除"><a href="#删除" class="headerlink" title="删除"></a>删除</h3><p>向链表任意位置删除节点，如下图操作：</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928215703878" alt="图片"></p><p>从上图可以看到删除节点的步骤为如下：</p><ul><li>获取删除节点的前一个节点</li><li>获取删除节点的后一个节点</li><li>将前一个节点的 next 指向后一个节点</li><li>向删除节点的 next 指向为null</li></ul><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">while</span> (current != node)&#123;</span><br><span class="line">  pervious = current;</span><br><span class="line">  current = current.<span class="property">next</span>;</span><br><span class="line">  nextNode = current.<span class="property">next</span>;</span><br><span class="line">&#125;</span><br><span class="line">pervious.<span class="property">next</span> = nextNode</span><br></pre></td></tr></table></figure><p>同样如何希望删除节点处理行为一致，可以在头节点前面添加一个虚拟头节点</p><h2 id="三、应用场景"><a href="#三、应用场景" class="headerlink" title="三、应用场景"></a>三、应用场景</h2><p>缓存是一种提高数据读取性能的技术，在硬件设计、软件开发中都有着非常广泛的应用，比如常见的<code>CPU</code>缓存、数据库缓存、浏览器缓存等等</p><p>当缓存空间被用满时，我们可能会使用<code>LRU</code>最近最好使用策略去清楚，而实现<code>LRU</code>算法的数据结构是链表，思路如下：</p><p>维护一个有序单链表，越靠近链表尾部的结点是越早之前访问的。当有一个新的数据被访问时，我们从链表头部开始顺序遍历链表</p><ul><li><p>如果此数据之前已经被缓存在链表中了，我们遍历得到这个数据的对应结点，并将其从原来的位置删除，并插入到链表头部</p></li><li><p>如果此数据没在缓存链表中</p></li><li><ul><li>如果此时缓存未满，可直接在链表头部插入新节点存储此数据</li><li>如果此时缓存已满，则删除链表尾部节点，再在链表头部插入新节点</li></ul></li></ul><p>由于链表插入删除效率极高，达到O(1)。对于不需要搜索但变动频繁且无法预知数量上限的数据的情况的时候，都可以使用链表</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;说说你对链表的理解？常见的操作有哪些？&quot;&gt;&lt;a href=&quot;#说说你对链表的理解？常见的操作有哪些？&quot; class=&quot;headerlink&quot; title=&quot;说说你对链表的理解？常见的操作有哪些？&quot;&gt;&lt;/a&gt;说说你对链表的理解？常见的操作有哪些？&lt;/h1&gt;&lt;p&gt;&lt;i</summary>
      
    
    
    
    <category term="数据结构与算法" scheme="http://example.com/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
    
    
  </entry>
  
  <entry>
    <title>说说你对算法中时间复杂度，空间复杂度的理解？如何计算？</title>
    <link href="http://example.com/2022/08/08/%E7%AE%97%E6%B3%95.%E8%AF%B4%E8%AF%B4%E4%BD%A0%E5%AF%B9%E7%AE%97%E6%B3%95%E4%B8%AD%E6%97%B6%E9%97%B4%E5%A4%8D%E6%9D%82%E5%BA%A6%EF%BC%8C%E7%A9%BA%E9%97%B4%E5%A4%8D%E6%9D%82%E5%BA%A6%E7%9A%84%E7%90%86%E8%A7%A3%EF%BC%9F%E5%A6%82%E4%BD%95%E8%AE%A1%E7%AE%97%EF%BC%9F/"/>
    <id>http://example.com/2022/08/08/%E7%AE%97%E6%B3%95.%E8%AF%B4%E8%AF%B4%E4%BD%A0%E5%AF%B9%E7%AE%97%E6%B3%95%E4%B8%AD%E6%97%B6%E9%97%B4%E5%A4%8D%E6%9D%82%E5%BA%A6%EF%BC%8C%E7%A9%BA%E9%97%B4%E5%A4%8D%E6%9D%82%E5%BA%A6%E7%9A%84%E7%90%86%E8%A7%A3%EF%BC%9F%E5%A6%82%E4%BD%95%E8%AE%A1%E7%AE%97%EF%BC%9F/</id>
    <published>2022-08-08T08:49:23.173Z</published>
    <updated>2022-08-05T07:24:21.311Z</updated>
    
    <content type="html"><![CDATA[<h1 id="说说你对算法中时间复杂度，空间复杂度的理解？如何计算？"><a href="#说说你对算法中时间复杂度，空间复杂度的理解？如何计算？" class="headerlink" title="说说你对算法中时间复杂度，空间复杂度的理解？如何计算？"></a>说说你对算法中时间复杂度，空间复杂度的理解？如何计算？</h1><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928215422399" alt="图片"></p><h2 id="一、前言"><a href="#一、前言" class="headerlink" title="一、前言"></a>一、前言</h2><p>算法（Algorithm）是指用来操作数据、解决程序问题的一组方法。对于同一个问题，使用不同的算法，也许最终得到的结果是一样的，但在过程中消耗的资源和时间却会有很大的区别</p><p>衡量不同算法之间的优劣主要是通过「时间」和「空间」两个维度去考量：</p><ul><li>时间维度：是指执行当前算法所消耗的时间，我们通常用「时间复杂度」来描述。</li><li>空间维度：是指执行当前算法需要占用多少内存空间，我们通常用「空间复杂度」来描述</li></ul><p>通常会遇到一种情况，时间和空间维度不能够兼顾，需要在两者之间取得一个平衡点是我们需要考虑的</p><p>一个算法通常存在最好、平均、最坏三种情况，我们一般关注的是最坏情况</p><p>最坏情况是算法运行时间的上界，对于某些算法来说，最坏情况出现的比较频繁，也意味着平均情况和最坏情况一样差</p><h2 id="二、时间复杂度"><a href="#二、时间复杂度" class="headerlink" title="二、时间复杂度"></a>二、时间复杂度</h2><p>时间复杂度是指执行这个算法所需要的计算工作量，其复杂度反映了程序执行时间「随输入规模增长而增长的量级」，在很大程度上能很好地反映出算法的优劣与否</p><p>一个算法花费的时间与算法中语句的「执行次数成正比」，执行次数越多，花费的时间就越多</p><p>算法的复杂度通常用大O符号表述，定义为<code>T(n) = O(f(n))</code>，常见的时间复杂度有：O(1)常数型、O(log n)对数型、O(n)线性型、O(nlogn)线性对数型、O(n^2)平方型、O(n^3)立方型、O(n^k)k次方型、O(2^n)指数型，如下图所示：</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928215431223" alt="图片"></p><p>从上述可以看到，随着问题规模<code>n</code>的不断增大，上述时间复杂度不断增大，算法的执行效率越低，由小到大排序如下：</p><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="number">1</span>)＜Ο(log n)＜Ο(n)＜Ο(nlog n)＜Ο(n2)＜Ο(n3)＜…＜Ο(<span class="number">2</span>^n)＜Ο(n!)</span><br></pre></td></tr></table></figure><p>注意的是，算法复杂度只是描述算法的增长趋势，并不能说一个算法一定比另外一个算法高效，如果常数项过大的时候也会导致算法的执行时间变长</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">function</span> <span class="title function_">process</span>(<span class="params">n</span>) &#123;</span><br><span class="line">  <span class="keyword">let</span> a = <span class="number">1</span></span><br><span class="line">  <span class="keyword">let</span> b = <span class="number">2</span></span><br><span class="line">  <span class="keyword">let</span> sum = a + b</span><br><span class="line">  <span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; n; i++) &#123;</span><br><span class="line">    sum += i</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> sum</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>该函数算法需要执行的运算次数用输入大小<code>n</code>的函数表示，即 <code>T(n) = 2 + n + 1</code>，那么时间复杂度为<code>O(n + 3)</code>，又因为时间复杂度只关注最高数量级，且与之系数也没有关系，因此上述的时间复杂度为<code>O(n)</code></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">process</span>(<span class="params">n</span>) &#123;</span><br><span class="line"> <span class="keyword">let</span> count = <span class="number">0</span></span><br><span class="line">  <span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; n; i++)&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; n; i++)&#123;</span><br><span class="line">      count += <span class="number">1</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>n</code>次，因此时间复杂度为 <code>O(n*n*1 + 2) = O(n^2)</code></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><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">function</span> <span class="title function_">process</span>(<span class="params">n</span>) &#123;</span><br><span class="line">  <span class="keyword">let</span> sum = <span class="number">0</span></span><br><span class="line">  <span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; n; i++) &#123;</span><br><span class="line">    sum += i</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; n; i++)&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; n; i++)&#123;</span><br><span class="line">      sum += <span class="number">1</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> sum</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上述第一部分复杂度为<code>O(n)</code>，第二部分复杂度为<code>O(n^2)</code>，总复杂度为<code>max(O(n^2), O(n)) = O(n^2)</code></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">process</span>(<span class="params">n</span>) &#123;</span><br><span class="line">  <span class="keyword">let</span> i = <span class="number">1</span>; <span class="comment">// ①</span></span><br><span class="line">  <span class="keyword">while</span> (i &lt;= n) &#123;</span><br><span class="line">     i = i * <span class="number">2</span>; <span class="comment">// ②</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>循环语句中以2的倍数来逼近<code>n</code>，每次都乘以2。如果用公式表示就是1 *  2 * 2 * 2 … * 2 &lt;=n，也就是说2的<code>x</code>次方小于等于<code>n</code>时会执行循环体，记作<code>2^x &lt;= n</code>，于是得出<code>x&lt;=logn</code></p><p>因此循环在执行<code>logn</code>次之后，便结束，因此时间复杂度为<code>O(logn)</code></p><p>同理，如果一个<code>O(n)</code>循环里面嵌套<code>O(logn)</code>的循环，则时间复杂度为<code>O(nlogn)</code>，像<code>O(n^3)</code>无非也就是嵌套了三层<code>O(n)</code>循环</p><h2 id="三、空间复杂度"><a href="#三、空间复杂度" class="headerlink" title="三、空间复杂度"></a>三、空间复杂度</h2><p>空间复杂度主要指执行算法所需内存的大小，用于对程序运行过程中所需要的临时存储空间的度量</p><p>除了需要存储空间、指令、常数、变量和输入数据外，还包括对数据进行操作的工作单元和存储计算所需信息的辅助空间</p><p>下面给出空间复杂度为<code>O(1)</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> a = <span class="number">1</span></span><br><span class="line"><span class="keyword">let</span> b = <span class="number">2</span></span><br><span class="line"><span class="keyword">let</span> c = <span class="number">3</span></span><br></pre></td></tr></table></figure><p>上述代码的临时空间不会随着<code>n</code>的变化而变化，因此空间复杂度为<code>O(1)</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arr []</span><br><span class="line"><span class="keyword">for</span>(i=<span class="number">1</span>; i&lt;=n; ++i)&#123;</span><br><span class="line">  arr.<span class="title function_">push</span>(i)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上述可以看到，随着<code>n</code>的增加，数组的占用的内存空间越大</p><p>通常来说，只要算法不涉及到动态分配的空间，以及递归、栈所需的空间，空间复杂度通常为<code>O(1)</code>，一个一维数组<code>a[n]</code>，空间复杂度<code>O(n)</code>，二维数组为<code>O(n^2)</code></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;说说你对算法中时间复杂度，空间复杂度的理解？如何计算？&quot;&gt;&lt;a href=&quot;#说说你对算法中时间复杂度，空间复杂度的理解？如何计算？&quot; class=&quot;headerlink&quot; title=&quot;说说你对算法中时间复杂度，空间复杂度的理解？如何计算？&quot;&gt;&lt;/a&gt;说说你对算</summary>
      
    
    
    
    <category term="数据结构与算法" scheme="http://example.com/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
    
    
  </entry>
  
  <entry>
    <title>说说你对树的理解？相关的操作有哪些？</title>
    <link href="http://example.com/2022/08/08/%E7%AE%97%E6%B3%95.%E8%AF%B4%E8%AF%B4%E4%BD%A0%E5%AF%B9%E6%A0%91%E7%9A%84%E7%90%86%E8%A7%A3%EF%BC%9F%E7%9B%B8%E5%85%B3%E7%9A%84%E6%93%8D%E4%BD%9C%E6%9C%89%E5%93%AA%E4%BA%9B%EF%BC%9F/"/>
    <id>http://example.com/2022/08/08/%E7%AE%97%E6%B3%95.%E8%AF%B4%E8%AF%B4%E4%BD%A0%E5%AF%B9%E6%A0%91%E7%9A%84%E7%90%86%E8%A7%A3%EF%BC%9F%E7%9B%B8%E5%85%B3%E7%9A%84%E6%93%8D%E4%BD%9C%E6%9C%89%E5%93%AA%E4%BA%9B%EF%BC%9F/</id>
    <published>2022-08-08T08:49:23.173Z</published>
    <updated>2022-08-05T07:24:13.342Z</updated>
    
    <content type="html"><![CDATA[<h1 id="说说你对树的理解？相关的操作有哪些？"><a href="#说说你对树的理解？相关的操作有哪些？" class="headerlink" title="说说你对树的理解？相关的操作有哪些？"></a>说说你对树的理解？相关的操作有哪些？</h1><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928215951875" alt="图片"></p><h2 id="一、是什么"><a href="#一、是什么" class="headerlink" title="一、是什么"></a>一、是什么</h2><p>在计算机领域，树形数据结构是一类重要的非线性数据结构，可以表示数据之间一对多的关系。以树与二叉树最为常用，直观看来，树是以分支关系定义的层次结构</p><p>二叉树满足以下两个条件：</p><ul><li>本身是有序树</li><li>树中包含的各个节点的度不能超过 2，即只能是 0、1 或者 2</li></ul><p>如下图，左侧的为二叉树，而右侧的因为头结点的子结点超过2，因此不属于二叉树：</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928215948785" alt="图片"></p><p>同时，二叉树可以继续进行分类，分成了满二叉树和完成二叉树：</p><ul><li>满二叉树：如果二叉树中除了叶子结点，每个结点的度都为 2</li></ul><p><img src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="图片"></p><ul><li>完成二叉树：如果二叉树中除去最后一层节点为满二叉树，且最后一层的结点依次从左到右分布</li></ul><p><img src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" alt="图片"></p><h2 id="二、操作"><a href="#二、操作" class="headerlink" title="二、操作"></a>二、操作</h2><p>关于二叉树的遍历，常见的有：</p><ul><li>前序遍历</li><li>中序遍历</li><li>后序遍历</li><li>层序遍历</li></ul><h3 id="前序遍历"><a href="#前序遍历" class="headerlink" title="前序遍历"></a>前序遍历</h3><p>前序遍历的实现思想是：</p><ul><li>访问根节点</li><li>访问当前节点的左子树</li><li>若当前节点无左子树，则访问当前节点的右子</li></ul><p>根据遍历特性，递归版本用代码表示则如下：</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><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">preOrder</span> = (<span class="params">root</span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">if</span>(!root)&#123; <span class="keyword">return</span> &#125;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(root)</span><br><span class="line">  <span class="title function_">preOrder</span>(root.<span class="property">left</span>)</span><br><span class="line">  <span class="title function_">preOrder</span>(root.<span class="property">right</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果不使用递归版本，可以借助栈先进后出的特性实现，先将根节点压入栈，再分别压入右节点和左节点，直到栈中没有元素，如下：</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><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">const</span> <span class="title function_">preOrder</span> = (<span class="params">root</span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">if</span>(!root)&#123; <span class="keyword">return</span> &#125;</span><br><span class="line">  <span class="keyword">const</span> stack = [root]</span><br><span class="line">  <span class="keyword">while</span> (stack.<span class="property">length</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> n = stack.<span class="title function_">pop</span>()</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(n.<span class="property">val</span>)</span><br><span class="line">    <span class="keyword">if</span> (n.<span class="property">right</span>) &#123;</span><br><span class="line">      stack.<span class="title function_">push</span>(n.<span class="property">right</span>)</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (n.<span class="property">left</span>) &#123;</span><br><span class="line">      stack.<span class="title function_">push</span>(n.<span class="property">left</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><h3 id="中序遍历"><a href="#中序遍历" class="headerlink" title="中序遍历"></a>中序遍历</h3><p>前序遍历的实现思想是：</p><ul><li>访问当前节点的左子树</li><li>访问根节点</li><li>访问当前节点的右子</li></ul><p>递归版本很好理解，用代码表示则如下：</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><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">inOrder</span> = (<span class="params">root</span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">if</span> (!root) &#123; <span class="keyword">return</span> &#125;</span><br><span class="line">  <span class="title function_">inOrder</span>(root.<span class="property">left</span>)</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(root.<span class="property">val</span>)</span><br><span class="line">  <span class="title function_">inOrder</span>(root.<span class="property">right</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>非递归版本也是借助栈先进后出的特性，可以一直首先一直压入节点的左元素，当左节点没有后，才开始进行出栈操作，压入右节点，然后有依次压入左节点，如下：</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><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">const</span> <span class="title function_">inOrder</span> = (<span class="params">root</span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">if</span> (!root) &#123; <span class="keyword">return</span> &#125;</span><br><span class="line">  <span class="keyword">const</span> stack = [root]</span><br><span class="line">  <span class="keyword">let</span> p = root</span><br><span class="line">  <span class="keyword">while</span>(stack.<span class="property">length</span> || p)&#123;</span><br><span class="line">    <span class="keyword">while</span> (p) &#123;</span><br><span class="line">      stack.<span class="title function_">push</span>(p)</span><br><span class="line">      p = p.<span class="property">left</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">const</span> n = stack.<span class="title function_">pop</span>()</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(n.<span class="property">val</span>)</span><br><span class="line">    p = n.<span class="property">right</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>后序遍历</h3><p>前序遍历的实现思想是：</p><ul><li>访问当前节点的左子树</li><li>访问当前节点的右子</li><li>访问根节点</li></ul><p>递归版本，用代码表示则如下：</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><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">postOrder</span> = (<span class="params">root</span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">if</span> (!root) &#123; <span class="keyword">return</span> &#125;</span><br><span class="line">  <span class="title function_">postOrder</span>(root.<span class="property">left</span>)</span><br><span class="line">  <span class="title function_">postOrder</span>(root.<span class="property">right</span>)</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(n.<span class="property">val</span>)</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><p>后序遍历非递归版本实际跟全序遍历是逆序关系，可以再多创建一个栈用来进行输出，如下：</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><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">const</span> <span class="title function_">preOrder</span> = (<span class="params">root</span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">if</span>(!root)&#123; <span class="keyword">return</span> &#125;</span><br><span class="line">  <span class="keyword">const</span> stack = [root]</span><br><span class="line">  <span class="keyword">const</span> outPut = []</span><br><span class="line">  <span class="keyword">while</span> (stack.<span class="property">length</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> n = stack.<span class="title function_">pop</span>()</span><br><span class="line">    outPut.<span class="title function_">push</span>(n.<span class="property">val</span>)</span><br><span class="line">    <span class="keyword">if</span> (n.<span class="property">right</span>) &#123;</span><br><span class="line">      stack.<span class="title function_">push</span>(n.<span class="property">right</span>)</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (n.<span class="property">left</span>) &#123;</span><br><span class="line">      stack.<span class="title function_">push</span>(n.<span class="property">left</span>)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">while</span> (outPut.<span class="property">length</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> n = outPut.<span class="title function_">pop</span>()</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(n.<span class="property">val</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>层序遍历</h3><p>按照二叉树中的层次从左到右依次遍历每层中的结点</p><p>借助队列先进先出的特性，从树的根结点开始，依次将其左孩子和右孩子入队。而后每次队列中一个结点出队，都将其左孩子和右孩子入队，直到树中所有结点都出队，出队结点的先后顺序就是层次遍历的最终结果</p><p>用代码表示则如下：</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><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="keyword">const</span> <span class="title function_">levelOrder</span> = (<span class="params">root</span>) =&gt; &#123;</span><br><span class="line">    <span class="keyword">if</span> (!root) &#123; <span class="keyword">return</span> [] &#125;</span><br><span class="line">    <span class="keyword">const</span> queue = [[root, <span class="number">0</span>]]</span><br><span class="line">    <span class="keyword">const</span> res = []</span><br><span class="line">    <span class="keyword">while</span> (queue.<span class="property">length</span>) &#123;</span><br><span class="line">        <span class="keyword">const</span> n = queue.<span class="title function_">shift</span>()</span><br><span class="line">        <span class="keyword">const</span> [node, leval] = n</span><br><span class="line">        <span class="keyword">if</span> (!res[leval]) &#123;</span><br><span class="line">            res[leval] = [node.<span class="property">val</span>]</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            res[leval].<span class="title function_">push</span>(node.<span class="property">val</span>)</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (node.<span class="property">left</span>) &#123; queue.<span class="title function_">push</span>([node.<span class="property">left</span>, leval + <span class="number">1</span>]) &#125;</span><br><span class="line">        <span class="keyword">if</span> (node.<span class="property">right</span>) &#123; queue.<span class="title function_">push</span>([node.<span class="property">right</span>, leval + <span class="number">1</span>]) &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> res</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h2 id="三、总结"><a href="#三、总结" class="headerlink" title="三、总结"></a>三、总结</h2><p>树是一个非常重要的非线性结构，其中二叉树以二叉树最常见，二叉树的遍历方式可以分成前序遍历、中序遍历、后序遍历</p><p>同时，二叉树又分成了完成二叉树和满二叉树</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;说说你对树的理解？相关的操作有哪些？&quot;&gt;&lt;a href=&quot;#说说你对树的理解？相关的操作有哪些？&quot; class=&quot;headerlink&quot; title=&quot;说说你对树的理解？相关的操作有哪些？&quot;&gt;&lt;/a&gt;说说你对树的理解？相关的操作有哪些？&lt;/h1&gt;&lt;p&gt;&lt;img s</summary>
      
    
    
    
    <category term="数据结构与算法" scheme="http://example.com/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
    
    
  </entry>
  
  <entry>
    <title>说说你对数据结构的理解？有哪些？区别？</title>
    <link href="http://example.com/2022/08/08/%E7%AE%97%E6%B3%95.%E8%AF%B4%E8%AF%B4%E4%BD%A0%E5%AF%B9%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E7%9A%84%E7%90%86%E8%A7%A3%EF%BC%9F%E6%9C%89%E5%93%AA%E4%BA%9B%EF%BC%9F%E5%8C%BA%E5%88%AB%EF%BC%9F/"/>
    <id>http://example.com/2022/08/08/%E7%AE%97%E6%B3%95.%E8%AF%B4%E8%AF%B4%E4%BD%A0%E5%AF%B9%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E7%9A%84%E7%90%86%E8%A7%A3%EF%BC%9F%E6%9C%89%E5%93%AA%E4%BA%9B%EF%BC%9F%E5%8C%BA%E5%88%AB%EF%BC%9F/</id>
    <published>2022-08-08T08:49:23.172Z</published>
    <updated>2022-08-05T07:24:15.481Z</updated>
    
    <content type="html"><![CDATA[<h1 id="说说你对数据结构的理解？有哪些？区别？"><a href="#说说你对数据结构的理解？有哪些？区别？" class="headerlink" title="说说你对数据结构的理解？有哪些？区别？"></a>说说你对数据结构的理解？有哪些？区别？</h1><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928215508529" alt="图片"></p><h2 id="一、是什么"><a href="#一、是什么" class="headerlink" title="一、是什么"></a>一、是什么</h2><p>数据结构是计算机存储、组织数据的方式，是指相互之间存在一种或多种特定关系的数据元素的集合</p><p>前面讲到，一个程序 = 算法 + 数据结构，数据结构是实现算法的基础，选择合适的数据结构可以带来更高的运行或者存储效率</p><p>数据元素相互之间的关系称为结构，根据数据元素之间关系的不同特性，通常有如下四类基本的结构：</p><ul><li>集合结构：该结构的数据元素间的关系是“属于同一个集合”</li><li>线性结构：该结构的数据元素之间存在着一对一的关系</li><li>树型结构：该结构的数据元素之间存在着一对多的关系</li><li>图形结构：该结构的数据元素之间存在着多对多的关系，也称网状结构</li></ul><p>由于数据结构种类太多，逻辑结构可以再分成为：</p><ul><li>线性结构：有序数据元素的集合，其中数据元素之间的关系是一对一的关系，除了第一个和最后一个数据元素之外，其它数据元素都是首尾相接的</li><li>非线性结构：各个数据元素不再保持在一个线性序列中，每个数据元素可能与零个或者多个其他数据元素发生关联</li></ul><h2 id="二、有哪些"><a href="#二、有哪些" class="headerlink" title="二、有哪些"></a>二、有哪些</h2><p>常见的数据结构有如下：</p><ul><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><p>在程序设计中，为了处理方便， 一般情况把具有相同类型的若干变量按有序的形式组织起来，这些按序排列的同类数据元素的集合称为数组</p><h3 id="栈"><a href="#栈" class="headerlink" title="栈"></a>栈</h3><p>一种特殊的线性表，只能在某一端插入和删除的特殊线性表，按照先进后出的特性存储数据</p><p>先进入的数据被压入栈底，最后的数据在栈顶，需要读数据的时候从栈顶开始弹出数据</p><h3 id="队列"><a href="#队列" class="headerlink" title="队列"></a>队列</h3><p>跟栈基本一致，也是一种特殊的线性表，其特性是先进先出，只允许在表的前端进行删除操作，而在表的后端进行插入操作</p><h3 id="链表"><a href="#链表" class="headerlink" title="链表"></a>链表</h3><p>是一种物理存储单元上非连续、非顺序的存储结构，数据元素的逻辑顺序是通过链表中的指针链接次序实现的</p><p>链表由一系列结点（链表中每一个元素称为结点）组成，结点可以在运行时动态生成</p><p>一般情况，每个结点包括两个部分：一个是存储数据元素的数据域，另一个是存储下一个结点地址的指针域</p><h3 id="树"><a href="#树" class="headerlink" title="树"></a>树</h3><p>树是典型的非线性结构，在树的结构中，有且仅有一个根结点，该结点没有前驱结点。在树结构中的其他结点都有且仅有一个前驱结点，而且可以有两个以上的后继结点</p><h3 id="图"><a href="#图" class="headerlink" title="图"></a>图</h3><p>一种非线性结构。在图形结构中，数据结点一般称为顶点，而边是顶点的有序偶对。如果两个顶点之间存在一条边，那么就表示这两个顶点具有相邻关系</p><h3 id="堆"><a href="#堆" class="headerlink" title="堆"></a>堆</h3><p>堆是一种特殊的树形数据结构，每个结点都有一个值，特点是根结点的值最小（或最大），且根结点的两个子树也是一个堆</p><h3 id="散列表"><a href="#散列表" class="headerlink" title="散列表"></a>散列表</h3><p>若结构中存在关键字和<code>K</code>相等的记录，则必定在<code>f(K)</code>的存储位置上，不需比较便可直接取得所查记录</p><h2 id="三、区别"><a href="#三、区别" class="headerlink" title="三、区别"></a>三、区别</h2><p>上述的数据结构，之前的区别可以分成线性结构和非线性结构：</p><ul><li>线性结构有：数组、栈、队列、链表等</li><li>非线性结构有：树、图、堆等</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;说说你对数据结构的理解？有哪些？区别？&quot;&gt;&lt;a href=&quot;#说说你对数据结构的理解？有哪些？区别？&quot; class=&quot;headerlink&quot; title=&quot;说说你对数据结构的理解？有哪些？区别？&quot;&gt;&lt;/a&gt;说说你对数据结构的理解？有哪些？区别？&lt;/h1&gt;&lt;p&gt;&lt;i</summary>
      
    
    
    
    <category term="数据结构与算法" scheme="http://example.com/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
    
    
  </entry>
  
  <entry>
    <title>说说你对堆的理解？如何实现？应用场景？</title>
    <link href="http://example.com/2022/08/08/%E7%AE%97%E6%B3%95.%E8%AF%B4%E8%AF%B4%E4%BD%A0%E5%AF%B9%E5%A0%86%E7%9A%84%E7%90%86%E8%A7%A3%EF%BC%9F%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%EF%BC%9F%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF%EF%BC%9F/"/>
    <id>http://example.com/2022/08/08/%E7%AE%97%E6%B3%95.%E8%AF%B4%E8%AF%B4%E4%BD%A0%E5%AF%B9%E5%A0%86%E7%9A%84%E7%90%86%E8%A7%A3%EF%BC%9F%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%EF%BC%9F%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF%EF%BC%9F/</id>
    <published>2022-08-08T08:49:23.171Z</published>
    <updated>2022-08-05T07:24:05.914Z</updated>
    
    <content type="html"><![CDATA[<h1 id="说说你对堆的理解？如何实现？应用场景？"><a href="#说说你对堆的理解？如何实现？应用场景？" class="headerlink" title="说说你对堆的理解？如何实现？应用场景？"></a>说说你对堆的理解？如何实现？应用场景？</h1><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928220110597" alt="图片"></p><h2 id="一、是什么"><a href="#一、是什么" class="headerlink" title="一、是什么"></a>一、是什么</h2><p>堆(Heap)是计算机科学中一类特殊的数据结构的统称</p><p>堆通常是一个可以被看做一棵完全二叉树的数组对象，如下图：</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928220104400" alt="图片"></p><p>总是满足下列性质：</p><ul><li>堆中某个结点的值总是不大于或不小于其父结点的值</li><li>堆总是一棵完全二叉树</li></ul><p>堆又可以分成最大堆和最小堆：</p><ul><li>最大堆：每个根结点，都有根结点的值大于两个孩子结点的值</li><li>最小堆：每个根结点，都有根结点的值小于孩子结点的值</li></ul><h2 id="二、操作"><a href="#二、操作" class="headerlink" title="二、操作"></a>二、操作</h2><p>堆的元素存储方式，按照完全二叉树的顺序存储方式存储在一个一维数组中，如下图：</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928220100557" alt="图片"></p><p>用一维数组存储则如下：</p><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="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>]</span><br></pre></td></tr></table></figure><p>根据完全二叉树的特性，可以得到如下特性：</p><ul><li>数组零坐标代码的是堆顶元素</li><li>一个节点的父亲节点的坐标等于其坐标除以2整数部分</li><li>一个节点的左节点等于其本身节点坐标 * 2 + 1</li><li>一个节点的右节点等于其本身节点坐标 * 2 + 2</li></ul><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MinHeap</span> &#123;</span><br><span class="line">  <span class="title function_">constructor</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="comment">// 存储堆元素</span></span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">heap</span> = []</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 获取父元素坐标</span></span><br><span class="line">  <span class="title function_">getParentIndex</span>(<span class="params">i</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> (i - <span class="number">1</span>) &gt;&gt; <span class="number">1</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="title function_">getLeftIndex</span>(<span class="params">i</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> i * <span class="number">2</span> + <span class="number">1</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="title function_">getRightIndex</span>(<span class="params">i</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> i * <span class="number">2</span> + <span class="number">2</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="title function_">swap</span>(<span class="params">i1, i2</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> temp = <span class="variable language_">this</span>.<span class="property">heap</span>[i1]</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">heap</span>[i1] = <span class="variable language_">this</span>.<span class="property">heap</span>[i2]</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">heap</span>[i2] = 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="title function_">peek</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">heap</span>[<span class="number">0</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="title function_">size</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">heap</span>.<span class="property">length</span></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>删除</li></ul><h3 id="插入"><a href="#插入" class="headerlink" title="插入"></a>插入</h3><p>将值插入堆的底部，即数组的尾部，当插入一个新的元素之后，堆的结构就会被破坏，因此需要堆中一个元素做上移操作</p><p>将这个值和它父节点进行交换，直到父节点小于等于这个插入的值，大小为<code>k</code>的堆中插入元素的时间复杂度为<code>O(logk)</code></p><p>如下图所示，22节点是新插入的元素，然后进行上移操作：</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928220055273" alt="图片"></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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 插入元素</span></span><br><span class="line"><span class="title function_">insert</span>(<span class="params">value</span>) &#123;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">heap</span>.<span class="title function_">push</span>(value)</span><br><span class="line">  <span class="variable language_">this</span>.<span class="title function_">shifUp</span>(<span class="variable language_">this</span>.<span class="property">heap</span>.<span class="property">length</span> - <span class="number">1</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="title function_">shiftUp</span>(<span class="params">index</span>) &#123;</span><br><span class="line">  <span class="keyword">if</span> (index === <span class="number">0</span>) &#123; <span class="keyword">return</span> &#125;</span><br><span class="line">  <span class="keyword">const</span> parentIndex = <span class="variable language_">this</span>.<span class="title function_">getParentIndex</span>(index)</span><br><span class="line">  <span class="keyword">if</span>(<span class="variable language_">this</span>.<span class="property">heap</span>[parentIndex] &gt; <span class="variable language_">this</span>.<span class="property">heap</span>[index])&#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">swap</span>(parentIndex, index)</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">shiftUp</span>(parentIndex)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="删除"><a href="#删除" class="headerlink" title="删除"></a>删除</h3><p>常见操作是用数组尾部元素替换堆顶，这里不直接删除堆顶，因为所有的元素会向前移动一位，会破坏了堆的结构</p><p>然后进行下移操作，将新的堆顶和它的子节点进行交换，直到子节点大于等于这个新的堆顶，删除堆顶的时间复杂度为<code>O(logk)</code></p><p>整体如下图操作：</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928220043262" alt="图片"></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><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="comment">// 删除元素</span></span><br><span class="line"><span class="title function_">pop</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">heap</span>[<span class="number">0</span>] = <span class="variable language_">this</span>.<span class="property">heap</span>.<span class="title function_">pop</span>()</span><br><span class="line">  <span class="variable language_">this</span>.<span class="title function_">shiftDown</span>(<span class="number">0</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="title function_">shiftDown</span>(<span class="params">index</span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> leftIndex = <span class="variable language_">this</span>.<span class="title function_">getLeftIndex</span>(index)</span><br><span class="line">  <span class="keyword">const</span> rightIndex = <span class="variable language_">this</span>.<span class="title function_">getRightIndex</span>(index)</span><br><span class="line">  <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">heap</span>[leftIndex] &lt; <span class="variable language_">this</span>.<span class="property">heap</span>[index])&#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">swap</span>(leftIndex, index)</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">shiftDown</span>(leftIndex)</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">heap</span>[rightIndex] &lt; <span class="variable language_">this</span>.<span class="property">heap</span>[index])&#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">swap</span>(rightIndex, index)</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">shiftDown</span>(rightIndex)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="时间复杂度"><a href="#时间复杂度" class="headerlink" title="时间复杂度"></a>时间复杂度</h3><p>关于堆的插入和删除时间复杂度都是<code>Olog(n)</code>，原因在于包含n个节点的完全二叉树，树的高度不会超过<code>log2n</code></p><p>堆化的过程是顺着节点所在路径比较交换的，所以堆化的时间复杂度跟树的高度成正比，也就是<code>Olog(n)</code>，插入数据和删除堆顶元素的主要逻辑就是堆化</p><h3 id="三、总结"><a href="#三、总结" class="headerlink" title="三、总结"></a>三、总结</h3><ul><li>堆是一个完全二叉树</li><li>堆中每一个节点的值都必须大于等于(或小于等于)其子树中每个节点的值</li><li>对于每个节点的值都大于等于子树中每个节点值的堆，叫作“大顶堆”</li><li>对于每个节点的值都小于等于子树中每个节点值的堆，叫作“小顶堆”</li><li>根据堆的特性，我们可以使用堆来进行排序操作，也可以使用其来求第几大或者第几小的值</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;说说你对堆的理解？如何实现？应用场景？&quot;&gt;&lt;a href=&quot;#说说你对堆的理解？如何实现？应用场景？&quot; class=&quot;headerlink&quot; title=&quot;说说你对堆的理解？如何实现？应用场景？&quot;&gt;&lt;/a&gt;说说你对堆的理解？如何实现？应用场景？&lt;/h1&gt;&lt;p&gt;&lt;i</summary>
      
    
    
    
    <category term="数据结构与算法" scheme="http://example.com/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
    
    
  </entry>
  
  <entry>
    <title>说说你对图的理解？相关操作有哪些？</title>
    <link href="http://example.com/2022/08/08/%E7%AE%97%E6%B3%95.%E8%AF%B4%E8%AF%B4%E4%BD%A0%E5%AF%B9%E5%9B%BE%E7%9A%84%E7%90%86%E8%A7%A3%EF%BC%9F%E7%9B%B8%E5%85%B3%E6%93%8D%E4%BD%9C%E6%9C%89%E5%93%AA%E4%BA%9B%EF%BC%9F/"/>
    <id>http://example.com/2022/08/08/%E7%AE%97%E6%B3%95.%E8%AF%B4%E8%AF%B4%E4%BD%A0%E5%AF%B9%E5%9B%BE%E7%9A%84%E7%90%86%E8%A7%A3%EF%BC%9F%E7%9B%B8%E5%85%B3%E6%93%8D%E4%BD%9C%E6%9C%89%E5%93%AA%E4%BA%9B%EF%BC%9F/</id>
    <published>2022-08-08T08:49:23.171Z</published>
    <updated>2022-08-05T07:24:24.294Z</updated>
    
    <content type="html"><![CDATA[<h1 id="说说你对图的理解？相关操作有哪些？"><a href="#说说你对图的理解？相关操作有哪些？" class="headerlink" title="说说你对图的理解？相关操作有哪些？"></a>说说你对图的理解？相关操作有哪些？</h1><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210929085030174" alt="图片"></p><h2 id="一、是什么"><a href="#一、是什么" class="headerlink" title="一、是什么"></a>一、是什么</h2><p>在计算机科学中，图是一种抽象的数据类型，在图中的数据元素通常称为结点，<code>V</code>是所有顶点的集合，<code>E</code>是所有边的集合</p><p>如果两个顶点<code>v</code>,<code>w</code>，只能由<code>v</code>向<code>w</code>，而不能由<code>w</code>向<code>v</code>，那么我们就把这种情况叫做一个从 <code>v</code> 到 <code>w</code> 的有向边。<code>v</code>也被称做初始点，<code>w</code>也被称为终点。这种图就被称做有向图</p><p>如果<code>v</code>和<code>w</code>是没有顺序的，从<code>v</code>到达<code>w</code>和从<code>w</code>到达<code>v</code>是完全相同的，这种图就被称为无向图</p><p>图的结构比较复杂，任意两个顶点之间都可能存在联系，因此无法以数据元素在存储区中的物理位置来表示元素之间的关系</p><p>常见表达图的方式有如下：</p><ul><li>邻接矩阵</li><li>邻接表</li></ul><h3 id="邻接矩阵"><a href="#邻接矩阵" class="headerlink" title="邻接矩阵"></a>邻接矩阵</h3><p>通过使用一个二维数组<code>G[N][N]</code>进行表示<code>N</code>个点到<code>N-1</code>编号，通过邻接矩阵可以立刻看出两顶点之间是否存在一条边，只需要检查邻接矩阵行<code>i</code>和列<code>j</code>是否是非零值，对于无向图，邻接矩阵是对称的</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210929085101984" alt="图片"></p><h3 id="邻接表"><a href="#邻接表" class="headerlink" title="邻接表"></a>邻接表</h3><p>存储方式如下图所示：</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210929085059531" alt="图片"></p><p>在<code>javascript</code>中，可以使用<code>Object</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> graph = &#123;</span><br><span class="line">  <span class="attr">A</span>: [<span class="number">2</span>, <span class="number">3</span>, <span class="number">5</span>],</span><br><span class="line">  <span class="attr">B</span>: [<span class="number">2</span>],</span><br><span class="line">  <span class="attr">C</span>: [<span class="number">0</span>, <span class="number">1</span>, <span class="number">3</span>],</span><br><span class="line">  <span class="attr">D</span>: [<span class="number">0</span>, <span class="number">2</span>],</span><br><span class="line">  <span class="attr">E</span>: [<span class="number">6</span>],</span><br><span class="line">  <span class="attr">F</span>: [<span class="number">0</span>, <span class="number">6</span>],</span><br><span class="line">  <span class="attr">G</span>: [<span class="number">4</span>, <span class="number">5</span>]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>图的数据结构还可能包含和每条边相关联的数值（edge value），例如一个标号或一个数值（即权重，weight；表示花费、容量、长度等）</p><h2 id="二、操作"><a href="#二、操作" class="headerlink" title="二、操作"></a>二、操作</h2><p>关于图的操作常见的有：</p><ul><li>深度优先遍历</li><li>广度优先遍历</li></ul><p>首先构建一个图的邻接矩阵表示，如下面的图：</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210929085056277" alt="图片"></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> graph = &#123;</span><br><span class="line">  <span class="number">0</span>: [<span class="number">1</span>, <span class="number">4</span>],</span><br><span class="line">  <span class="number">1</span>: [<span class="number">2</span>, <span class="number">4</span>],</span><br><span class="line">  <span class="number">2</span>: [<span class="number">2</span>, <span class="number">3</span>],</span><br><span class="line">  <span class="number">3</span>: [],</span><br><span class="line">  <span class="number">4</span>: [<span class="number">3</span>],</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="深度优先遍历"><a href="#深度优先遍历" class="headerlink" title="深度优先遍历"></a>深度优先遍历</h3><p>也就是尽可能的往深处的搜索图的分支</p><p>实现思路是，首先应该确定一个根节点，然后对根节点的没访问过的相邻节点进行深度优先遍历</p><p>确定以 0 为根节点，然后进行深度遍历，然后遍历1，接着遍历 2，然后3，此时完成一条分支<code>0 - 1- 2- 3</code>的遍历，换一条分支，也就是4，4后面因为3已经遍历过了，所以就不访问了</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><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> visited = <span class="keyword">new</span> <span class="title class_">Set</span>()</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">dfs</span> = (<span class="params">n</span>) =&gt; &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(n)</span><br><span class="line">  visited.<span class="title function_">add</span>(n) <span class="comment">// 访问过添加记录</span></span><br><span class="line">  graph[n].<span class="title function_">forEach</span>(<span class="function"><span class="params">c</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span>(!visited.<span class="title function_">has</span>(c))&#123; <span class="comment">// 判断是否访问呢过</span></span><br><span class="line">      <span class="title function_">dfs</span>(c)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="广度优先遍历"><a href="#广度优先遍历" class="headerlink" title="广度优先遍历"></a>广度优先遍历</h3><p>先访问离根节点最近的节点，然后进行入队操作，解决思路如下：</p><ul><li>新建一个队列，把根节点入队</li><li>把队头出队并访问</li><li>把队头的没访问过的相邻节点入队</li><li>重复二、三步骤，知道队列为空</li></ul><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><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">const</span> visited = <span class="keyword">new</span> <span class="title class_">Set</span>()</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">dfs</span> = (<span class="params">n</span>) =&gt; &#123;</span><br><span class="line">  visited.<span class="title function_">add</span>(n)</span><br><span class="line">  <span class="keyword">const</span> q = [n]</span><br><span class="line">  <span class="keyword">while</span>(q.<span class="property">length</span>)&#123;</span><br><span class="line">    <span class="keyword">const</span> n = q.<span class="title function_">shift</span>()</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(n)</span><br><span class="line">    graph[n].<span class="title function_">forEach</span>(<span class="function"><span class="params">c</span> =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">if</span>(!visited.<span class="title function_">has</span>(c))&#123;</span><br><span class="line">        q.<span class="title function_">push</span>(c)  </span><br><span class="line">        visited.<span class="title function_">add</span>(c)</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><h2 id="三、总结"><a href="#三、总结" class="headerlink" title="三、总结"></a>三、总结</h2><p>通过上面的初步了解，可以看到图就是由顶点的有穷非空集合和顶点之间的边组成的集合，分成了无向图与有向图</p><p>图的表达形式可以分成邻接矩阵和邻接表两种形式，在<code>javascript</code>中，则可以通过二维数组和对象的形式进行表达</p><p>图实际是很复杂的，后续还可以延伸出无向图和带权图，对应如下图所示：</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210929085051343" alt="图片"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;说说你对图的理解？相关操作有哪些？&quot;&gt;&lt;a href=&quot;#说说你对图的理解？相关操作有哪些？&quot; class=&quot;headerlink&quot; title=&quot;说说你对图的理解？相关操作有哪些？&quot;&gt;&lt;/a&gt;说说你对图的理解？相关操作有哪些？&lt;/h1&gt;&lt;p&gt;&lt;img src=&quot;</summary>
      
    
    
    
    <category term="数据结构与算法" scheme="http://example.com/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
    
    
  </entry>
  
  <entry>
    <title>说说你是如何利用Webpack来优化前端性能的？</title>
    <link href="http://example.com/2022/08/08/Webpack.%E8%AF%B4%E8%AF%B4%E4%BD%A0%E6%98%AF%E5%A6%82%E4%BD%95%E5%88%A9%E7%94%A8Webpack%E6%9D%A5%E4%BC%98%E5%8C%96%E5%89%8D%E7%AB%AF%E6%80%A7%E8%83%BD%E7%9A%84%EF%BC%9F/"/>
    <id>http://example.com/2022/08/08/Webpack.%E8%AF%B4%E8%AF%B4%E4%BD%A0%E6%98%AF%E5%A6%82%E4%BD%95%E5%88%A9%E7%94%A8Webpack%E6%9D%A5%E4%BC%98%E5%8C%96%E5%89%8D%E7%AB%AF%E6%80%A7%E8%83%BD%E7%9A%84%EF%BC%9F/</id>
    <published>2022-08-08T08:49:23.170Z</published>
    <updated>2022-08-05T06:15:03.590Z</updated>
    
    <content type="html"><![CDATA[<h1 id="说说你是如何利用Webpack来优化前端性能的？"><a href="#说说你是如何利用Webpack来优化前端性能的？" class="headerlink" title="说说你是如何利用Webpack来优化前端性能的？"></a>说说你是如何利用Webpack来优化前端性能的？</h1><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928151352193" alt="图片"></p><h2 id="一、背景"><a href="#一、背景" class="headerlink" title="一、背景"></a>一、背景</h2><p>随着前端的项目逐渐扩大，必然会带来的一个问题就是性能</p><p>尤其在大型复杂的项目中，前端业务可能因为一个小小的数据依赖，导致整个页面卡顿甚至奔溃</p><p>一般项目在完成后，会通过<code>webpack</code>进行打包，利用<code>webpack</code>对前端项目性能优化是一个十分重要的环节</p><h2 id="二、如何优化"><a href="#二、如何优化" class="headerlink" title="二、如何优化"></a>二、如何优化</h2><p>通过<code>webpack</code>优化前端的手段有：</p><ul><li>JS代码压缩</li><li>CSS代码压缩</li><li>Html文件代码压缩</li><li>文件大小压缩</li><li>图片压缩</li><li>Tree Shaking</li><li>代码分离</li><li>内联 chunk</li></ul><h3 id="JS代码压缩"><a href="#JS代码压缩" class="headerlink" title="JS代码压缩"></a>JS代码压缩</h3><p><code>terser</code>是一个<code>JavaScript</code>的解释、绞肉机、压缩机的工具集，可以帮助我们压缩、丑化我们的代码，让<code>bundle</code>更小</p><p>在<code>production</code>模式下，<code>webpack</code> 默认就是使用 <code>TerserPlugin</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title class_">TerserPlugin</span> = <span class="built_in">require</span>(<span class="string">&#x27;terser-webpack-plugin&#x27;</span>)</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="attr">optimization</span>: &#123;</span><br><span class="line">        <span class="attr">minimize</span>: <span class="literal">true</span>,</span><br><span class="line">        <span class="attr">minimizer</span>: [</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">TerserPlugin</span>(&#123;</span><br><span class="line">                <span class="attr">parallel</span>: <span class="literal">true</span> <span class="comment">// 电脑cpu核数-1</span></span><br><span class="line">            &#125;)</span><br><span class="line">        ]</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>属性介绍如下：</p><ul><li>extractComments：默认值为true，表示会将注释抽取到一个单独的文件中，开发阶段，我们可设置为 false ，不保留注释</li><li>parallel：使用多进程并发运行提高构建的速度，默认值是true，并发运行的默认数量：os.cpus().length - 1</li><li>terserOptions：设置我们的terser相关的配置：</li><li>compress：设置压缩相关的选项，mangle：设置丑化相关的选项，可以直接设置为true</li><li>mangle：设置丑化相关的选项，可以直接设置为true</li><li>toplevel：底层变量是否进行转换</li><li>keep_classnames：保留类的名称</li><li>keep_fnames：保留函数的名称</li></ul><h3 id="CSS代码压缩"><a href="#CSS代码压缩" class="headerlink" title="CSS代码压缩"></a>CSS代码压缩</h3><p><code>CSS</code>压缩通常是去除无用的空格等，因为很难去修改选择器、属性的名称、值等</p><p>CSS的压缩我们可以使用另外一个插件：<code>css-minimizer-webpack-plugin</code></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install css-minimizer-webpack-plugin -D</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><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">const</span> <span class="title class_">CssMinimizerPlugin</span> = <span class="built_in">require</span>(<span class="string">&#x27;css-minimizer-webpack-plugin&#x27;</span>)</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="attr">optimization</span>: &#123;</span><br><span class="line">        <span class="attr">minimize</span>: <span class="literal">true</span>,</span><br><span class="line">        <span class="attr">minimizer</span>: [</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">CssMinimizerPlugin</span>(&#123;</span><br><span class="line">                <span class="attr">parallel</span>: <span class="literal">true</span></span><br><span class="line">            &#125;)</span><br><span class="line">        ]</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Html文件代码压缩"><a href="#Html文件代码压缩" class="headerlink" title="Html文件代码压缩"></a>Html文件代码压缩</h3><p>使用<code>HtmlWebpackPlugin</code>插件来生成<code>HTML</code>的模板时候，通过配置属性<code>minify</code>进行<code>html</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></pre></td><td class="code"><pre><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="attr">plugin</span>:[</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">HtmlwebpackPlugin</span>(&#123;</span><br><span class="line">            ...</span><br><span class="line">            <span class="attr">minify</span>:&#123;</span><br><span class="line">                <span class="attr">minifyCSS</span>:<span class="literal">false</span>, <span class="comment">// 是否压缩css</span></span><br><span class="line">                <span class="attr">collapseWhitespace</span>:<span class="literal">false</span>, <span class="comment">// 是否折叠空格</span></span><br><span class="line">                <span class="attr">removeComments</span>:<span class="literal">true</span> <span class="comment">// 是否移除注释</span></span><br><span class="line">            &#125;</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>设置了<code>minify</code>，实际会使用另一个插件<code>html-minifier-terser</code></p><h3 id="文件大小压缩"><a href="#文件大小压缩" class="headerlink" title="文件大小压缩"></a>文件大小压缩</h3><p>对文件的大小进行压缩，减少<code>http</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></pre></td><td class="code"><pre><span class="line">npm install compression-webpack-plugin -D</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">ComepressionPlugin</span>(&#123;</span><br><span class="line">    <span class="attr">test</span>:<span class="regexp">/\.(css|js)$/</span>,  <span class="comment">// 哪些文件需要压缩</span></span><br><span class="line">    <span class="attr">threshold</span>:<span class="number">500</span>, <span class="comment">// 设置文件多大开始压缩</span></span><br><span class="line">    <span class="attr">minRatio</span>:<span class="number">0.7</span>, <span class="comment">// 至少压缩的比例</span></span><br><span class="line">    <span class="attr">algorithm</span>:<span class="string">&quot;gzip&quot;</span>, <span class="comment">// 采用的压缩算法</span></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><h3 id="图片压缩"><a href="#图片压缩" class="headerlink" title="图片压缩"></a>图片压缩</h3><p>一般来说在打包之后，一些图片文件的大小是远远要比 <code>js</code> 或者 <code>css</code> 文件要来的大，所以图片压缩较为重要</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><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></pre></td><td class="code"><pre><span class="line"><span class="attr">module</span>: &#123;</span><br><span class="line">  <span class="attr">rules</span>: [</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="attr">test</span>: <span class="regexp">/\.(png|jpg|gif)$/</span>,</span><br><span class="line">      <span class="attr">use</span>: [</span><br><span class="line">        &#123;</span><br><span class="line">          <span class="attr">loader</span>: <span class="string">&#x27;file-loader&#x27;</span>,</span><br><span class="line">          <span class="attr">options</span>: &#123;</span><br><span class="line">            <span class="attr">name</span>: <span class="string">&#x27;[name]_[hash].[ext]&#x27;</span>,</span><br><span class="line">            <span class="attr">outputPath</span>: <span class="string">&#x27;images/&#x27;</span>,</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        &#123;</span><br><span class="line">          <span class="attr">loader</span>: <span class="string">&#x27;image-webpack-loader&#x27;</span>,</span><br><span class="line">          <span class="attr">options</span>: &#123;</span><br><span class="line">            <span class="comment">// 压缩 jpeg 的配置</span></span><br><span class="line">            <span class="attr">mozjpeg</span>: &#123;</span><br><span class="line">              <span class="attr">progressive</span>: <span class="literal">true</span>,</span><br><span class="line">              <span class="attr">quality</span>: <span class="number">65</span></span><br><span class="line">            &#125;,</span><br><span class="line">            <span class="comment">// 使用 imagemin**-optipng 压缩 png，enable: false 为关闭</span></span><br><span class="line">            <span class="attr">optipng</span>: &#123;</span><br><span class="line">              <span class="attr">enabled</span>: <span class="literal">false</span>,</span><br><span class="line">            &#125;,</span><br><span class="line">            <span class="comment">// 使用 imagemin-pngquant 压缩 png</span></span><br><span class="line">            <span class="attr">pngquant</span>: &#123;</span><br><span class="line">              <span class="attr">quality</span>: <span class="string">&#x27;65-90&#x27;</span>,</span><br><span class="line">              <span class="attr">speed</span>: <span class="number">4</span></span><br><span class="line">            &#125;,</span><br><span class="line">            <span class="comment">// 压缩 gif 的配置</span></span><br><span class="line">            <span class="attr">gifsicle</span>: &#123;</span><br><span class="line">              <span class="attr">interlaced</span>: <span class="literal">false</span>,</span><br><span class="line">            &#125;,</span><br><span class="line">            <span class="comment">// 开启 webp，会把 jpg 和 png 图片压缩为 webp 格式</span></span><br><span class="line">            <span class="attr">webp</span>: &#123;</span><br><span class="line">              <span class="attr">quality</span>: <span class="number">75</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">    &#125;,</span><br><span class="line">  ]</span><br><span class="line">&#125; </span><br></pre></td></tr></table></figure><h3 id="Tree-Shaking"><a href="#Tree-Shaking" class="headerlink" title="Tree Shaking"></a>Tree Shaking</h3><p><code>Tree Shaking</code> 是一个术语，在计算机中表示消除死代码，依赖于<code>ES Module</code>的静态语法分析（不执行任何的代码，可以明确知道模块的依赖关系）</p><p>在<code>webpack</code>实现<code>Trss shaking</code>有两种不同的方案：</p><ul><li>usedExports：通过标记某些函数是否被使用，之后通过Terser来进行优化的</li><li>sideEffects：跳过整个模块/文件，直接查看该文件是否有副作用</li></ul><p>两种不同的配置方案， 有不同的效果</p><h4 id="usedExports"><a href="#usedExports" class="headerlink" title="usedExports"></a>usedExports</h4><p>配置方法也很简单，只需要将<code>usedExports</code>设为<code>true</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></pre></td><td class="code"><pre><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="attr">optimization</span>:&#123;</span><br><span class="line">        usedExports</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>使用之后，没被用上的代码在<code>webpack</code>打包中会加入<code>unused harmony export mul</code>注释，用来告知 <code>Terser</code> 在优化时，可以删除掉这段代码</p><p>如下面<code>sum</code>函数没被用到，<code>webpack</code>打包会添加注释，<code>terser</code>在优化时，则将该函数去掉</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928151400385" alt="图片"></p><h4 id="sideEffects"><a href="#sideEffects" class="headerlink" title="sideEffects"></a>sideEffects</h4><p><code>sideEffects</code>用于告知<code>webpack compiler</code>哪些模块时有副作用，配置方法是在<code>package.json</code>中设置<code>sideEffects</code>属性</p><p>如果<code>sideEffects</code>设置为false，就是告知<code>webpack</code>可以安全的删除未用到的<code>exports</code></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></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;sideEffecis&quot;</span>:[</span><br><span class="line">    <span class="string">&quot;./src/util/format.js&quot;</span>,</span><br><span class="line">    <span class="string">&quot;*.css&quot;</span> <span class="comment">// 所有的css文件</span></span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>上述都是关于<code>javascript</code>的<code>tree shaking</code>，<code>css</code>同样也能够实现<code>tree shaking</code></p><h4 id="css-tree-shaking"><a href="#css-tree-shaking" class="headerlink" title="css tree shaking"></a>css tree shaking</h4><p><code>css</code>进行<code>tree shaking</code>优化可以安装<code>PurgeCss</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></pre></td><td class="code"><pre><span class="line">npm install purgecss-plugin-webpack -D</span><br><span class="line"><span class="keyword">const</span> <span class="title class_">PurgeCssPlugin</span> = <span class="built_in">require</span>(<span class="string">&#x27;purgecss-webpack-plugin&#x27;</span>)</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="attr">plugins</span>:[</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">PurgeCssPlugin</span>(&#123;</span><br><span class="line">            <span class="attr">path</span>:glob.<span class="title function_">sync</span>(<span class="string">`<span class="subst">$&#123;path.resolve(<span class="string">&#x27;./src&#x27;</span>)&#125;</span>/**/*`</span>), &#123;<span class="attr">nodir</span>:<span class="literal">true</span>&#125;<span class="comment">// src里面的所有文件</span></span><br><span class="line">            <span class="attr">satelist</span>:<span class="keyword">function</span>(<span class="params"></span>)&#123;</span><br><span class="line">                <span class="keyword">return</span> &#123;</span><br><span class="line">                    <span class="attr">standard</span>:[<span class="string">&quot;html&quot;</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">&#125;</span><br></pre></td></tr></table></figure><ul><li>paths：表示要检测哪些目录下的内容需要被分析，配合使用glob</li><li>默认情况下，Purgecss会将我们的html标签的样式移除掉，如果我们希望保留，可以添加一个safelist的属性</li></ul><h3 id="代码分离"><a href="#代码分离" class="headerlink" title="代码分离"></a>代码分离</h3><p>将代码分离到不同的<code>bundle</code>中，之后我们可以按需加载，或者并行加载这些文件</p><p>默认情况下，所有的<code>JavaScript</code>代码（业务代码、第三方依赖、暂时没有用到的模块）在首页全部都加载，就会影响首页的加载速度</p><p>代码分离可以分出出更小的<code>bundle</code>，以及控制资源加载优先级，提供代码的加载性能</p><p>这里通过<code>splitChunksPlugin</code>来实现，该插件<code>webpack</code>已经默认安装和集成，只需要配置即可</p><p>默认配置中，chunks仅仅针对于异步（async）请求，我们可以设置为initial或者all</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="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="attr">optimization</span>:&#123;</span><br><span class="line">        <span class="attr">splitChunks</span>:&#123;</span><br><span class="line">            <span class="attr">chunks</span>:<span class="string">&quot;all&quot;</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>splitChunks</code>主要属性有如下：</p><ul><li>Chunks，对同步代码还是异步代码进行处理</li><li>minSize：拆分包的大小, 至少为minSize，如何包的大小不超过minSize，这个包不会拆分</li><li>maxSize：将大于maxSize的包，拆分为不小于minSize的包</li><li>minChunks：被引入的次数，默认是1</li></ul><h3 id="内联chunk"><a href="#内联chunk" class="headerlink" title="内联chunk"></a>内联chunk</h3><p>可以通过<code>InlineChunkHtmlPlugin</code>插件将一些<code>chunk</code>的模块内联到<code>html</code>，如<code>runtime</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title class_">InlineChunkHtmlPlugin</span> = <span class="built_in">require</span>(<span class="string">&#x27;react-dev-utils/InlineChunkHtmlPlugin&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> <span class="title class_">HtmlWebpackPlugin</span> = <span class="built_in">require</span>(<span class="string">&#x27;html-webpack-plugin&#x27;</span>)</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="attr">plugin</span>:[</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">InlineChunkHtmlPlugin</span>(<span class="title class_">HtmlWebpackPlugin</span>,[<span class="regexp">/runtime.+\.js/</span>]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="三、总结"><a href="#三、总结" class="headerlink" title="三、总结"></a>三、总结</h3><p>关于<code>webpack</code>对前端性能的优化，可以通过文件体积大小入手，其次还可通过分包的形式、减少http请求次数等方式，实现对前端性能的优化</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;说说你是如何利用Webpack来优化前端性能的？&quot;&gt;&lt;a href=&quot;#说说你是如何利用Webpack来优化前端性能的？&quot; class=&quot;headerlink&quot; title=&quot;说说你是如何利用Webpack来优化前端性能的？&quot;&gt;&lt;/a&gt;说说你是如何利用Webpac</summary>
      
    
    
    
    <category term="Webpack" scheme="http://example.com/categories/Webpack/"/>
    
    
  </entry>
  
  <entry>
    <title>说说提高webpack的构建速度的手段有哪些？</title>
    <link href="http://example.com/2022/08/08/Webpack.%E8%AF%B4%E8%AF%B4%E6%8F%90%E9%AB%98webpack%E7%9A%84%E6%9E%84%E5%BB%BA%E9%80%9F%E5%BA%A6%E7%9A%84%E6%89%8B%E6%AE%B5%E6%9C%89%E5%93%AA%E4%BA%9B%EF%BC%9F/"/>
    <id>http://example.com/2022/08/08/Webpack.%E8%AF%B4%E8%AF%B4%E6%8F%90%E9%AB%98webpack%E7%9A%84%E6%9E%84%E5%BB%BA%E9%80%9F%E5%BA%A6%E7%9A%84%E6%89%8B%E6%AE%B5%E6%9C%89%E5%93%AA%E4%BA%9B%EF%BC%9F/</id>
    <published>2022-08-08T08:49:23.170Z</published>
    <updated>2022-08-05T06:14:56.703Z</updated>
    
    <content type="html"><![CDATA[<h1 id="说说提高webpack的构建速度的手段有哪些？"><a href="#说说提高webpack的构建速度的手段有哪些？" class="headerlink" title="说说提高webpack的构建速度的手段有哪些？"></a>说说提高webpack的构建速度的手段有哪些？</h1><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928151505798" alt="图片"></p><h2 id="一、背景"><a href="#一、背景" class="headerlink" title="一、背景"></a>一、背景</h2><p>随着我们的项目涉及到页面越来越多，功能和业务代码也会随着越多，相应的 <code>webpack</code> 的构建时间也会越来越久</p><p>构建时间与我们日常开发效率密切相关，当我们本地开发启动 <code>devServer</code> 或者 <code>build</code> 的时候，如果时间过长，会大大降低我们的工作效率</p><p>所以，优化<code>webpack</code> 构建速度是十分重要的环节</p><h2 id="二、如何优化"><a href="#二、如何优化" class="headerlink" title="二、如何优化"></a>二、如何优化</h2><p>常见的提升构建速度的手段有如下：</p><ul><li>优化 loader 配置</li><li>合理使用 resolve.extensions</li><li>优化 resolve.modules</li><li>优化 resolve.alias</li><li>使用 DLLPlugin 插件</li><li>使用 cache-loader</li><li>terser 启动多线程</li><li>合理使用 sourceMap</li></ul><h3 id="优化loader配置"><a href="#优化loader配置" class="headerlink" title="优化loader配置"></a>优化loader配置</h3><p>在使用<code>loader</code>时，可以通过配置<code>include</code>、<code>exclude</code>、<code>test</code>属性来匹配文件，接触<code>include</code>、<code>exclude</code>规定哪些匹配应用<code>loader</code></p><p>如采用 ES6 的项目为例，在配置 <code>babel-loader</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></pre></td><td class="code"><pre><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">  <span class="attr">module</span>: &#123;</span><br><span class="line">    <span class="attr">rules</span>: [</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="comment">// 如果项目源码中只有 js 文件就不要写成 /\.jsx?$/，提升正则表达式性能</span></span><br><span class="line">        <span class="attr">test</span>: <span class="regexp">/\.js$/</span>,</span><br><span class="line">        <span class="comment">// babel-loader 支持缓存转换出的结果，通过 cacheDirectory 选项开启</span></span><br><span class="line">        <span class="attr">use</span>: [<span class="string">&#x27;babel-loader?cacheDirectory&#x27;</span>],</span><br><span class="line">        <span class="comment">// 只对项目根目录下的 src 目录中的文件采用 babel-loader</span></span><br><span class="line">        <span class="attr">include</span>: path.<span class="title function_">resolve</span>(__dirname, <span class="string">&#x27;src&#x27;</span>),</span><br><span class="line">      &#125;,</span><br><span class="line">    ]</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="合理使用-resolve-extensions"><a href="#合理使用-resolve-extensions" class="headerlink" title="合理使用 resolve.extensions"></a>合理使用 resolve.extensions</h3><p>在开发中我们会有各种各样的模块依赖，这些模块可能来自于自己编写的代码，也可能来自第三方库， <code>resolve</code>可以帮助<code>webpack</code>从每个 <code>require/import</code> 语句中，找到需要引入到合适的模块代码</p><p>通过<code>resolve.extensions</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></pre></td><td class="code"><pre><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="attr">extensions</span>:[<span class="string">&quot;.warm&quot;</span>,<span class="string">&quot;.mjs&quot;</span>,<span class="string">&quot;.js&quot;</span>,<span class="string">&quot;.json&quot;</span>]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>当我们引入文件的时候，若没有文件后缀名，则会根据数组内的值依次查找</p><p>当我们配置的时候，则不要随便把所有后缀都写在里面，这会调用多次文件的查找，这样就会减慢打包速度</p><h3 id="优化-resolve-modules"><a href="#优化-resolve-modules" class="headerlink" title="优化 resolve.modules"></a>优化 resolve.modules</h3><p><code>resolve.modules</code> 用于配置 <code>webpack</code> 去哪些目录下寻找第三方模块。默认值为<code>[&#39;node_modules&#39;]</code>，所以默认会从<code>node_modules</code>中查找文件 当安装的第三方模块都放在项目根目录下的 <code>./node_modules</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></pre></td><td class="code"><pre><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">  <span class="attr">resolve</span>: &#123;</span><br><span class="line">    <span class="comment">// 使用绝对路径指明第三方模块存放的位置，以减少搜索步骤</span></span><br><span class="line">    <span class="comment">// 其中 __dirname 表示当前工作目录，也就是项目根目录</span></span><br><span class="line">    <span class="attr">modules</span>: [path.<span class="title function_">resolve</span>(__dirname, <span class="string">&#x27;node_modules&#x27;</span>)]</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="优化-resolve-alias"><a href="#优化-resolve-alias" class="headerlink" title="优化 resolve.alias"></a>优化 resolve.alias</h3><p><code>alias</code>给一些常用的路径起一个别名，特别当我们的项目目录结构比较深的时候，一个文件的路径可能是<code>./../../</code>的形式</p><p>通过配置<code>alias</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></pre></td><td class="code"><pre><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="attr">resolve</span>:&#123;</span><br><span class="line">        <span class="attr">alias</span>:&#123;</span><br><span class="line">            <span class="string">&quot;@&quot;</span>:path.<span class="title function_">resolve</span>(__dirname,<span class="string">&#x27;./src&#x27;</span>)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="使用-DLLPlugin-插件"><a href="#使用-DLLPlugin-插件" class="headerlink" title="使用 DLLPlugin 插件"></a>使用 DLLPlugin 插件</h3><p><code>DLL</code>全称是 动态链接库，是为软件在winodw中实现共享函数库的一种实现方式，而Webpack也内置了DLL的功能，为的就是可以共享，不经常改变的代码，抽成一个共享的库。这个库在之后的编译过程中，会被引入到其他项目的代码中</p><p>使用步骤分成两部分：</p><ul><li>打包一个 DLL 库</li><li>引入 DLL 库</li></ul><h4 id="打包一个-DLL-库"><a href="#打包一个-DLL-库" class="headerlink" title="打包一个 DLL 库"></a>打包一个 DLL 库</h4><p><code>webpack</code>内置了一个<code>DllPlugin</code>可以帮助我们打包一个DLL的库文件</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="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="attr">plugins</span>:[</span><br><span class="line">        <span class="keyword">new</span> webpack.<span class="title class_">DllPlugin</span>(&#123;</span><br><span class="line">            <span class="attr">name</span>:<span class="string">&#x27;dll_[name]&#x27;</span>,</span><br><span class="line">            <span class="attr">path</span>:path.<span class="title function_">resolve</span>(__dirname,<span class="string">&quot;./dll/[name].mainfest.json&quot;</span>)</span><br><span class="line">        &#125;)</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="引入-DLL-库"><a href="#引入-DLL-库" class="headerlink" title="引入 DLL 库"></a>引入 DLL 库</h4><p>使用 <code>webpack</code> 自带的 <code>DllReferencePlugin</code> 插件对 <code>mainfest.json</code> 映射文件进行分析，获取要使用的<code>DLL</code>库</p><p>然后再通过<code>AddAssetHtmlPlugin</code>插件，将我们打包的<code>DLL</code>库引入到<code>Html</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></pre></td><td class="code"><pre><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">new</span> webpack.<span class="title class_">DllReferencePlugin</span>(&#123;</span><br><span class="line">        <span class="attr">context</span>:path.<span class="title function_">resolve</span>(__dirname,<span class="string">&quot;./dll/dll_react.js&quot;</span>),</span><br><span class="line">        <span class="attr">mainfest</span>:path.<span class="title function_">resolve</span>(__dirname,<span class="string">&quot;./dll/react.mainfest.json&quot;</span>)</span><br><span class="line">    &#125;),</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">AddAssetHtmlPlugin</span>(&#123;</span><br><span class="line">        <span class="attr">outputPath</span>:<span class="string">&quot;./auto&quot;</span>,</span><br><span class="line">        <span class="attr">filepath</span>:path.<span class="title function_">resolve</span>(__dirname,<span class="string">&quot;./dll/dll_react.js&quot;</span>)</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="使用-cache-loader"><a href="#使用-cache-loader" class="headerlink" title="使用 cache-loader"></a>使用 cache-loader</h3><p>在一些性能开销较大的 <code>loader</code>之前添加 <code>cache-loader</code>，以将结果缓存到磁盘里，显著提升二次构建速度</p><p>保存和读取这些缓存文件会有一些时间开销，所以请只对性能开销较大的 <code>loader</code>使用此<code>loader</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></pre></td><td class="code"><pre><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">    <span class="attr">module</span>: &#123;</span><br><span class="line">        <span class="attr">rules</span>: [</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="attr">test</span>: <span class="regexp">/\.ext$/</span>,</span><br><span class="line">                <span class="attr">use</span>: [<span class="string">&#x27;cache-loader&#x27;</span>, ...loaders],</span><br><span class="line">                <span class="attr">include</span>: path.<span class="title function_">resolve</span>(<span class="string">&#x27;src&#x27;</span>),</span><br><span class="line">            &#125;,</span><br><span class="line">        ],</span><br><span class="line">    &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="terser-启动多线程"><a href="#terser-启动多线程" class="headerlink" title="terser 启动多线程"></a>terser 启动多线程</h3><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="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">  <span class="attr">optimization</span>: &#123;</span><br><span class="line">    <span class="attr">minimizer</span>: [</span><br><span class="line">      <span class="keyword">new</span> <span class="title class_">TerserPlugin</span>(&#123;</span><br><span class="line">        <span class="attr">parallel</span>: <span class="literal">true</span>,</span><br><span class="line">      &#125;),</span><br><span class="line">    ],</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="合理使用-sourceMap"><a href="#合理使用-sourceMap" class="headerlink" title="合理使用 sourceMap"></a>合理使用 sourceMap</h3><p>打包生成  <code>sourceMap</code> 的时候，如果信息越详细，打包速度就会越慢。对应属性取值如下所示：</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928151514492" alt="图片"></p><h3 id="三、总结"><a href="#三、总结" class="headerlink" title="三、总结"></a>三、总结</h3><p>可以看到，优化<code>webpack</code>构建的方式有很多，主要可以从优化搜索时间、缩小文件搜索范围、减少不必要的编译等方面入手</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;说说提高webpack的构建速度的手段有哪些？&quot;&gt;&lt;a href=&quot;#说说提高webpack的构建速度的手段有哪些？&quot; class=&quot;headerlink&quot; title=&quot;说说提高webpack的构建速度的手段有哪些？&quot;&gt;&lt;/a&gt;说说提高webpack的构建速度的</summary>
      
    
    
    
    <category term="Webpack" scheme="http://example.com/categories/Webpack/"/>
    
    
  </entry>
  
  <entry>
    <title>说说Webpack的热更新是如何做到的？原理是什么？</title>
    <link href="http://example.com/2022/08/08/Webpack.%E8%AF%B4%E8%AF%B4Webpack%E7%9A%84%E7%83%AD%E6%9B%B4%E6%96%B0%E6%98%AF%E5%A6%82%E4%BD%95%E5%81%9A%E5%88%B0%E7%9A%84%EF%BC%9F%E5%8E%9F%E7%90%86%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F/"/>
    <id>http://example.com/2022/08/08/Webpack.%E8%AF%B4%E8%AF%B4Webpack%E7%9A%84%E7%83%AD%E6%9B%B4%E6%96%B0%E6%98%AF%E5%A6%82%E4%BD%95%E5%81%9A%E5%88%B0%E7%9A%84%EF%BC%9F%E5%8E%9F%E7%90%86%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F/</id>
    <published>2022-08-08T08:49:23.169Z</published>
    <updated>2022-08-05T06:14:40.884Z</updated>
    
    <content type="html"><![CDATA[<h1 id="说说Webpack的热更新是如何做到的？原理是什么？"><a href="#说说Webpack的热更新是如何做到的？原理是什么？" class="headerlink" title="说说Webpack的热更新是如何做到的？原理是什么？"></a>说说Webpack的热更新是如何做到的？原理是什么？</h1><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928151126266" alt="图片"></p><h2 id="一、是什么"><a href="#一、是什么" class="headerlink" title="一、是什么"></a>一、是什么</h2><p><code>HMR</code>全称 <code>Hot Module Replacement</code>，可以理解为模块热替换，指在应用程序运行过程中，替换、添加、删除模块，而无需重新刷新整个应用</p><p>例如，我们在应用运行过程中修改了某个模块，通过自动刷新会导致整个应用的整体刷新，那页面中的状态信息都会丢失</p><p>如果使用的是 <code>HMR</code>，就可以实现只将修改的模块实时替换至应用中，不必完全刷新整个应用</p><p>在<code>webpack</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> webpack = <span class="built_in">require</span>(<span class="string">&#x27;webpack&#x27;</span>)</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  <span class="attr">devServer</span>: &#123;</span><br><span class="line">    <span class="comment">// 开启 HMR 特性</span></span><br><span class="line">    <span class="attr">hot</span>: <span class="literal">true</span></span><br><span class="line">    <span class="comment">// hotOnly: true</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>通过上述这种配置，如果我们修改并保存<code>css</code>文件，确实能够以不刷新的形式更新到页面中</p><p>但是，当我们修改并保存<code>js</code>文件之后，页面依旧自动刷新了，这里并没有触发热模块</p><p>所以，<code>HMR</code>并不像 <code>Webpack</code> 的其他特性一样可以开箱即用，需要有一些额外的操作</p><p>我们需要去指定哪些模块发生更新时进行<code>HRM</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(<span class="variable language_">module</span>.<span class="property">hot</span>)&#123;</span><br><span class="line">    <span class="variable language_">module</span>.<span class="property">hot</span>.<span class="title function_">accept</span>(<span class="string">&#x27;./util.js&#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">&quot;util.js更新了&quot;</span>)</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="二、实现原理"><a href="#二、实现原理" class="headerlink" title="二、实现原理"></a>二、实现原理</h2><p>首先来看看一张图，如下：</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928151131791" alt="图片"></p><ul><li>Webpack Compile：将 JS 源代码编译成 bundle.js</li><li>HMR Server：用来将热更新的文件输出给 HMR Runtime</li><li>Bundle Server：静态资源文件服务器，提供文件访问路径</li><li>HMR Runtime：socket服务器，会被注入到浏览器，更新文件的变化</li><li>bundle.js：构建输出的文件</li><li>在HMR Runtime 和 HMR Server之间建立 websocket，即图上4号线，用于实时更新文件变化</li></ul><p>上面图中，可以分成两个阶段：</p><ul><li>启动阶段为上图 1 - 2 - A - B</li></ul><p>在编写未经过<code>webpack</code>打包的源代码后，<code>Webpack Compile</code> 将源代码和 <code>HMR Runtime</code> 一起编译成 <code>bundle</code>文件，传输给<code>Bundle Server</code> 静态资源服务器</p><ul><li>更新阶段为上图 1 - 2 - 3 - 4</li></ul><p>当某一个文件或者模块发生变化时，<code>webpack</code>监听到文件变化对文件重新编译打包，编译生成唯一的<code>hash</code>值，这个<code>hash</code>值用来作为下一次热更新的标识</p><p>根据变化的内容生成两个补丁文件：<code>manifest</code>（包含了 <code>hash</code> 和 <code>chundId</code>，用来说明变化的内容）和<code>chunk.js</code> 模块</p><p>由于<code>socket</code>服务器在<code>HMR Runtime</code> 和 <code>HMR Server</code>之间建立 <code>websocket</code>链接，当文件发生改动的时候，服务端会向浏览器推送一条消息，消息包含文件改动后生成的<code>hash</code>值，如下图的<code>h</code>属性，作为下一次热更新的标识</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928151136924" alt="图片"></p><p>在浏览器接受到这条消息之前，浏览器已经在上一次<code>socket</code> 消息中已经记住了此时的<code>hash</code> 标识，这时候我们会创建一个 <code>ajax</code> 去服务端请求获取到变化内容的 <code>manifest</code> 文件</p><p><code>mainfest</code>文件包含重新<code>build</code>生成的<code>hash</code>值，以及变化的模块，对应上图的<code>c</code>属性</p><p>浏览器根据 <code>manifest</code> 文件获取模块变化的内容，从而触发<code>render</code>流程，实现局部模块更新</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928151142425" alt="图片"></p><h2 id="三、总结"><a href="#三、总结" class="headerlink" title="三、总结"></a>三、总结</h2><p>关于<code>webpack</code>热模块更新的总结如下：</p><ul><li>通过<code>webpack-dev-server</code>创建两个服务器：提供静态资源的服务（express）和Socket服务</li><li>express server 负责直接提供静态资源的服务（打包后的资源直接被浏览器请求和解析）</li><li>socket server 是一个 websocket 的长连接，双方可以通信</li><li>当 socket server 监听到对应的模块发生变化时，会生成两个文件.json（manifest文件）和.js文件（update chunk）</li><li>通过长连接，socket server 可以直接将这两个文件主动发送给客户端（浏览器）</li><li>浏览器拿到两个新的文件后，通过HMR runtime机制，加载这两个文件，并且针对修改的模块进行更新</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;说说Webpack的热更新是如何做到的？原理是什么？&quot;&gt;&lt;a href=&quot;#说说Webpack的热更新是如何做到的？原理是什么？&quot; class=&quot;headerlink&quot; title=&quot;说说Webpack的热更新是如何做到的？原理是什么？&quot;&gt;&lt;/a&gt;说说Webpac</summary>
      
    
    
    
    <category term="Webpack" scheme="http://example.com/categories/Webpack/"/>
    
    
  </entry>
  
  <entry>
    <title>说说你对Webpack的理解？解决了什么问题？</title>
    <link href="http://example.com/2022/08/08/Webpack.%E8%AF%B4%E8%AF%B4%E4%BD%A0%E5%AF%B9Webpack%E7%9A%84%E7%90%86%E8%A7%A3%EF%BC%9F%E8%A7%A3%E5%86%B3%E4%BA%86%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98%EF%BC%9F/"/>
    <id>http://example.com/2022/08/08/Webpack.%E8%AF%B4%E8%AF%B4%E4%BD%A0%E5%AF%B9Webpack%E7%9A%84%E7%90%86%E8%A7%A3%EF%BC%9F%E8%A7%A3%E5%86%B3%E4%BA%86%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98%EF%BC%9F/</id>
    <published>2022-08-08T08:49:23.169Z</published>
    <updated>2022-08-05T06:13:16.086Z</updated>
    
    <content type="html"><![CDATA[<h1 id="说说你对Webpack的理解？解决了什么问题？"><a href="#说说你对Webpack的理解？解决了什么问题？" class="headerlink" title="说说你对Webpack的理解？解决了什么问题？"></a>说说你对Webpack的理解？解决了什么问题？</h1><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928145840391" alt="图片"></p><h2 id="一、背景"><a href="#一、背景" class="headerlink" title="一、背景"></a>一、背景</h2><p><code>Webpack</code> 最初的目标是实现前端项目的模块化，旨在更高效地管理和维护项目中的每一个资源</p><h4 id="模块化"><a href="#模块化" class="headerlink" title="模块化"></a>模块化</h4><p>最早的时候，我们会通过文件划分的形式实现模块化，也就是将每个功能及其相关状态数据各自单独放到不同的<code>JS</code> 文件中</p><p>约定每个文件是一个独立的模块，然后再将这些<code>js</code>文件引入到页面，一个<code>script</code>标签对应一个模块，然后调用模块化的成员</p><figure class="highlight html"><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="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;module-a.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;module-b.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><p>但这种模块弊端十分的明显，模块都是在全局中工作，大量模块成员污染了环境，模块与模块之间并没有依赖关系、维护困难、没有私有空间等问题</p><p>项目一旦变大，上述问题会尤其明显</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></pre></td><td class="code"><pre><span class="line"><span class="variable language_">window</span>.<span class="property">moduleA</span> = &#123;</span><br><span class="line">  <span class="attr">method1</span>: <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;moduleA#method1&#x27;</span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这种方式也并没有解决第一种方式的依赖等问题</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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// module-a.js</span></span><br><span class="line">(<span class="keyword">function</span> (<span class="params">$</span>) &#123;</span><br><span class="line">  <span class="keyword">var</span> name = <span class="string">&#x27;module-a&#x27;</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">function</span> <span class="title function_">method1</span> () &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(name + <span class="string">&#x27;#method1&#x27;</span>)</span><br><span class="line">    $(<span class="string">&#x27;body&#x27;</span>).<span class="title function_">animate</span>(&#123; <span class="attr">margin</span>: <span class="string">&#x27;200px&#x27;</span> &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">    </span><br><span class="line">  <span class="variable language_">window</span>.<span class="property">moduleA</span> = &#123;</span><br><span class="line">    <span class="attr">method1</span>: method1</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)(jQuery)</span><br></pre></td></tr></table></figure><p>上述的方式都是早期解决模块的方式，但是仍然存在一些没有解决的问题。例如，我们是用过<code>script</code>标签在页面引入这些模块的，这些模块的加载并不受代码的控制，时间一久维护起来也十分的麻烦</p><p>理想的解决方式是，在页面中引入一个<code>JS</code>入口文件，其余用到的模块可以通过代码控制，按需加载进来</p><p>除了模块加载的问题以外，还需要规定模块化的规范，如今流行的则是<code>CommonJS</code>、<code>ES Modules</code></p><h2 id="二、问题"><a href="#二、问题" class="headerlink" title="二、问题"></a>二、问题</h2><p>从后端渲染的<code>JSP</code>、<code>PHP</code>，到前端原生<code>JavaScript</code>，再到<code>jQuery</code>开发，再到目前的三大框架<code>Vue</code>、<code>React</code>、<code>Angular</code></p><p>开发方式，也从<code>javascript</code>到后面的<code>es5</code>、<code>es6、7、8、9、10</code>，再到<code>typescript</code>，包括编写<code>CSS</code>的预处理器<code>less</code>、<code>scss</code>等</p><p>现代前端开发已经变得十分的复杂，所以我们开发过程中会遇到如下的问题：</p><ul><li>需要通过模块化的方式来开发</li><li>使用一些高级的特性来加快我们的开发效率或者安全性，比如通过ES6+、TypeScript开发脚本逻辑，通过sass、less等方式来编写css样式代码</li><li>监听文件的变化来并且反映到浏览器上，提高开发的效率</li><li>JavaScript 代码需要模块化，HTML 和 CSS 这些资源文件也会面临需要被模块化的问题</li><li>开发完成后我们还需要将代码进行压缩、合并以及其他相关的优化</li></ul><p>而<code>webpack</code>恰巧可以解决以上问题</p><h2 id="三、是什么"><a href="#三、是什么" class="headerlink" title="三、是什么"></a>三、是什么</h2><p><code>webpack</code> 是一个用于现代<code>JavaScript</code>应用程序的静态模块打包工具</p><ul><li>静态模块</li></ul><p>这里的静态模块指的是开发阶段，可以被 <code>webpack</code> 直接引用的资源（可以直接被获取打包进<code>bundle.js</code>的资源）</p><p>当 <code>webpack</code>处理应用程序时，它会在内部构建一个依赖图，此依赖图对应映射到项目所需的每个模块（不再局限<code>js</code>文件），并生成一个或多个 <code>bundle</code></p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928145847460" alt="图片"></p><h4 id="webpack的能力："><a href="#webpack的能力：" class="headerlink" title="webpack的能力："></a><code>webpack</code>的能力：</h4><p>「编译代码能力」，提高效率，解决浏览器兼容问题<img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928145853678" alt="图片">「模块整合能力」，提高性能，可维护性，解决浏览器频繁请求文件的问题<img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928145900241" alt="图片">「万物皆可模块能力」，项目维护性增强，支持不同种类的前端模块类型，统一的模块化方案，所有资源文件的加载都可以通过代码控制<img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928145905918" alt="图片"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;说说你对Webpack的理解？解决了什么问题？&quot;&gt;&lt;a href=&quot;#说说你对Webpack的理解？解决了什么问题？&quot; class=&quot;headerlink&quot; title=&quot;说说你对Webpack的理解？解决了什么问题？&quot;&gt;&lt;/a&gt;说说你对Webpack的理解？解决</summary>
      
    
    
    
    <category term="Webpack" scheme="http://example.com/categories/Webpack/"/>
    
    
  </entry>
  
  <entry>
    <title>说说webpack中常见的Plugin？解决了什么问题？</title>
    <link href="http://example.com/2022/08/08/Webpack.%E8%AF%B4%E8%AF%B4webpack%E4%B8%AD%E5%B8%B8%E8%A7%81%E7%9A%84Plugin%EF%BC%9F%E8%A7%A3%E5%86%B3%E4%BA%86%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98%EF%BC%9F/"/>
    <id>http://example.com/2022/08/08/Webpack.%E8%AF%B4%E8%AF%B4webpack%E4%B8%AD%E5%B8%B8%E8%A7%81%E7%9A%84Plugin%EF%BC%9F%E8%A7%A3%E5%86%B3%E4%BA%86%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98%EF%BC%9F/</id>
    <published>2022-08-08T08:49:23.168Z</published>
    <updated>2022-08-05T06:14:30.086Z</updated>
    
    <content type="html"><![CDATA[<h1 id="说说webpack中常见的Plugin？解决了什么问题？"><a href="#说说webpack中常见的Plugin？解决了什么问题？" class="headerlink" title="说说webpack中常见的Plugin？解决了什么问题？"></a>说说webpack中常见的Plugin？解决了什么问题？</h1><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928150804618" alt="图片"></p><h2 id="一、是什么"><a href="#一、是什么" class="headerlink" title="一、是什么"></a>一、是什么</h2><p><code>Plugin</code>（Plug-in）是一种计算机应用程序，它和主应用程序互相交互，以提供特定的功能</p><p>是一种遵循一定规范的应用程序接口编写出来的程序，只能运行在程序规定的系统下，因为其需要调用原纯净系统提供的函数库或者数据</p><p><code>webpack</code>中的<code>plugin</code>也是如此，<code>plugin</code>赋予其各种灵活的功能，例如打包优化、资源管理、环境变量注入等，它们会运行在 <code>webpack</code> 的不同阶段（钩子 / 生命周期），贯穿了<code>webpack</code>整个编译周期</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928150813113" alt="图片"></p><p>目的在于解决<code>loader</code> 无法实现的其他事</p><h3 id="配置方式"><a href="#配置方式" class="headerlink" title="配置方式"></a>配置方式</h3><p>这里讲述文件的配置方式，一般情况，通过配置文件导出对象中<code>plugins</code>属性传入<code>new</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title class_">HtmlWebpackPlugin</span> = <span class="built_in">require</span>(<span class="string">&#x27;html-webpack-plugin&#x27;</span>); <span class="comment">// 通过 npm 安装</span></span><br><span class="line"><span class="keyword">const</span> webpack = <span class="built_in">require</span>(<span class="string">&#x27;webpack&#x27;</span>); <span class="comment">// 访问内置的插件</span></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">  ...</span><br><span class="line">  <span class="attr">plugins</span>: [</span><br><span class="line">    <span class="keyword">new</span> webpack.<span class="title class_">ProgressPlugin</span>(),</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">HtmlWebpackPlugin</span>(&#123; <span class="attr">template</span>: <span class="string">&#x27;./src/index.html&#x27;</span> &#125;),</span><br><span class="line">  ],</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h2 id="二、特性"><a href="#二、特性" class="headerlink" title="二、特性"></a>二、特性</h2><p>其本质是一个具有<code>apply</code>方法<code>javascript</code>对象</p><p><code>apply</code> 方法会被 <code>webpack compiler</code>调用，并且在整个编译生命周期都可以访问 <code>compiler</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> pluginName = <span class="string">&#x27;ConsoleLogOnBuildWebpackPlugin&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ConsoleLogOnBuildWebpackPlugin</span> &#123;</span><br><span class="line">  <span class="title function_">apply</span>(<span class="params">compiler</span>) &#123;</span><br><span class="line">    compiler.<span class="property">hooks</span>.<span class="property">run</span>.<span class="title function_">tap</span>(pluginName, <span class="function">(<span class="params">compilation</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;webpack 构建过程开始！&#x27;</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="variable language_">module</span>.<span class="property">exports</span> = <span class="title class_">ConsoleLogOnBuildWebpackPlugin</span>;</span><br></pre></td></tr></table></figure><p><code>compiler hook</code> 的 <code>tap</code>方法的第一个参数，应是驼峰式命名的插件名称</p><p>关于整个编译生命周期钩子，有如下：</p><ul><li>entry-option ：初始化 option</li><li>run</li><li>compile：真正开始的编译，在创建 compilation 对象之前</li><li>compilation ：生成好了 compilation 对象</li><li>make 从 entry 开始递归分析依赖，准备对每个模块进行 build</li><li>after-compile：编译 build 过程结束</li><li>emit ：在将内存中 assets 内容写到磁盘文件夹之前</li><li>after-emit ：在将内存中 assets 内容写到磁盘文件夹之后</li><li>done：完成所有的编译过程</li><li>failed：编译失败的时候</li></ul><h2 id="三、常见的Plugin"><a href="#三、常见的Plugin" class="headerlink" title="三、常见的Plugin"></a>三、常见的Plugin</h2><p>常见的<code>plugin</code>有如图所示：</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928150819292" alt="图片"></p><p>下面介绍几个常用的插件用法：</p><h3 id="HtmlWebpackPlugin"><a href="#HtmlWebpackPlugin" class="headerlink" title="HtmlWebpackPlugin"></a>HtmlWebpackPlugin</h3><p>在打包结束后，⾃动生成⼀个 <code>html</code> ⽂文件，并把打包生成的<code>js</code> 模块引⼊到该 <code>html</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></pre></td><td class="code"><pre><span class="line">npm install --save-dev html-webpack-plugin</span><br><span class="line"><span class="comment">// webpack.config.js</span></span><br><span class="line"><span class="keyword">const</span> <span class="title class_">HtmlWebpackPlugin</span> = <span class="built_in">require</span>(<span class="string">&quot;html-webpack-plugin&quot;</span>);</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line"> ...</span><br><span class="line">  <span class="attr">plugins</span>: [</span><br><span class="line">     <span class="keyword">new</span> <span class="title class_">HtmlWebpackPlugin</span>(&#123;</span><br><span class="line">       <span class="attr">title</span>: <span class="string">&quot;My App&quot;</span>,</span><br><span class="line">       <span class="attr">filename</span>: <span class="string">&quot;app.html&quot;</span>,</span><br><span class="line">       <span class="attr">template</span>: <span class="string">&quot;./src/html/index.html&quot;</span></span><br><span class="line">     &#125;) </span><br><span class="line">  ]</span><br><span class="line">&#125;;</span><br><span class="line">&lt;!--./src/html/index.<span class="property">html</span>--&gt;</span><br><span class="line">&lt;!<span class="variable constant_">DOCTYPE</span> html&gt;</span><br><span class="line">&lt;html lang=&quot;en&quot;&gt;</span><br><span class="line">&lt;head&gt;</span><br><span class="line">    &lt;meta charset=&quot;UTF-8&quot;&gt;</span><br><span class="line">    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;</span><br><span class="line">    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;ie=edge&quot;&gt;</span><br><span class="line">    &lt;title&gt;&lt;%=htmlWebpackPlugin.options.title%&gt;&lt;/title&gt;</span><br><span class="line">&lt;/head&gt;</span><br><span class="line">&lt;body&gt;</span><br><span class="line">    &lt;h1&gt;html-webpack-plugin&lt;/h1&gt;</span><br><span class="line">&lt;/body&gt;</span><br><span class="line">&lt;/html&gt;</span><br></pre></td></tr></table></figure><p>在 <code>html</code> 模板中，可以通过 <code>&lt;%=htmlWebpackPlugin.options.XXX%&gt;</code> 的方式获取配置的值</p><p>更多的配置可以自寻查找</p><h3 id="clean-webpack-plugin"><a href="#clean-webpack-plugin" class="headerlink" title="clean-webpack-plugin"></a>clean-webpack-plugin</h3><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><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">npm install --save-dev clean-webpack-plugin</span><br><span class="line"><span class="keyword">const</span> &#123;<span class="title class_">CleanWebpackPlugin</span>&#125; = <span class="built_in">require</span>(<span class="string">&#x27;clean-webpack-plugin&#x27;</span>);</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line"> ...</span><br><span class="line">  <span class="attr">plugins</span>: [</span><br><span class="line">    ...,</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">CleanWebpackPlugin</span>(),</span><br><span class="line">    ...</span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="mini-css-extract-plugin"><a href="#mini-css-extract-plugin" class="headerlink" title="mini-css-extract-plugin"></a>mini-css-extract-plugin</h3><p>提取 <code>CSS</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></pre></td><td class="code"><pre><span class="line">npm install --save-dev mini-css-extract-plugin</span><br><span class="line"><span class="keyword">const</span> <span class="title class_">MiniCssExtractPlugin</span> = <span class="built_in">require</span>(<span class="string">&#x27;mini-css-extract-plugin&#x27;</span>);</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line"> ...,</span><br><span class="line">  <span class="attr">module</span>: &#123;</span><br><span class="line">   <span class="attr">rules</span>: [</span><br><span class="line">    &#123;</span><br><span class="line">     <span class="attr">test</span>: <span class="regexp">/\.s[ac]ss$/</span>,</span><br><span class="line">     <span class="attr">use</span>: [</span><br><span class="line">      &#123;</span><br><span class="line">       <span class="attr">loader</span>: <span class="title class_">MiniCssExtractPlugin</span>.<span class="property">loader</span></span><br><span class="line">     &#125;,</span><br><span class="line">          <span class="string">&#x27;css-loader&#x27;</span>,</span><br><span class="line">          <span class="string">&#x27;sass-loader&#x27;</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><span class="line">  <span class="attr">plugins</span>: [</span><br><span class="line">    ...,</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">MiniCssExtractPlugin</span>(&#123;</span><br><span class="line">     <span class="attr">filename</span>: <span class="string">&#x27;[name].css&#x27;</span></span><br><span class="line">    &#125;),</span><br><span class="line">    ...</span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="DefinePlugin"><a href="#DefinePlugin" class="headerlink" title="DefinePlugin"></a>DefinePlugin</h3><p>允许在编译时创建配置的全局对象，是一个<code>webpack</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> &#123; <span class="title class_">DefinePlugun</span> &#125; = <span class="built_in">require</span>(<span class="string">&#x27;webpack&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line"> ...</span><br><span class="line">    <span class="attr">plugins</span>:[</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">DefinePlugin</span>(&#123;</span><br><span class="line">            <span class="attr">BASE_URL</span>:<span class="string">&#x27;&quot;./&quot;&#x27;</span></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>这时候编译<code>template</code>模块的时候，就能通过下述形式获取全局对象</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;icon&quot;</span> <span class="attr">href</span>=<span class="string">&quot;&lt;%= BASE_URL%&gt;favicon.ico&gt;&quot;</span></span></span><br></pre></td></tr></table></figure><h3 id="copy-webpack-plugin"><a href="#copy-webpack-plugin" class="headerlink" title="copy-webpack-plugin"></a>copy-webpack-plugin</h3><p>复制文件或目录到执行区域，如<code>vue</code>的打包过程中，如果我们将一些文件放到<code>public</code>的目录下，那么这个目录会被复制到<code>dist</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></pre></td><td class="code"><pre><span class="line">npm install copy-webpack-plugin -D</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">CopyWebpackPlugin</span>(&#123;</span><br><span class="line">    <span class="attr">parrerns</span>:[</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="attr">from</span>:<span class="string">&quot;public&quot;</span>,</span><br><span class="line">            <span class="attr">globOptions</span>:&#123;</span><br><span class="line">                <span class="attr">ignore</span>:[</span><br><span class="line">                    <span class="string">&#x27;**/index.html&#x27;</span></span><br><span class="line">                ]</span><br><span class="line">            &#125;</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>复制的规则在<code>patterns</code>属性中设置：</p><ul><li>from：设置从哪一个源中开始复制</li><li>to：复制到的位置，可以省略，会默认复制到打包的目录下</li><li>globOptions：设置一些额外的选项，其中可以编写需要忽略的文件</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;说说webpack中常见的Plugin？解决了什么问题？&quot;&gt;&lt;a href=&quot;#说说webpack中常见的Plugin？解决了什么问题？&quot; class=&quot;headerlink&quot; title=&quot;说说webpack中常见的Plugin？解决了什么问题？&quot;&gt;&lt;/a&gt;说说</summary>
      
    
    
    
    <category term="Webpack" scheme="http://example.com/categories/Webpack/"/>
    
    
  </entry>
  
  <entry>
    <title>说说webpack的构建流程?</title>
    <link href="http://example.com/2022/08/08/Webpack.%E8%AF%B4%E8%AF%B4webpack%E7%9A%84%E6%9E%84%E5%BB%BA%E6%B5%81%E7%A8%8B_/"/>
    <id>http://example.com/2022/08/08/Webpack.%E8%AF%B4%E8%AF%B4webpack%E7%9A%84%E6%9E%84%E5%BB%BA%E6%B5%81%E7%A8%8B_/</id>
    <published>2022-08-08T08:49:23.168Z</published>
    <updated>2022-08-05T06:14:45.730Z</updated>
    
    <content type="html"><![CDATA[<h1 id="说说webpack的构建流程"><a href="#说说webpack的构建流程" class="headerlink" title="说说webpack的构建流程?"></a>说说webpack的构建流程?</h1><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928150518815" alt="图片"></p><h2 id="一、运行流程"><a href="#一、运行流程" class="headerlink" title="一、运行流程"></a>一、运行流程</h2><p><code>webpack</code> 的运行流程是一个串行的过程，它的工作流程就是将各个插件串联起来</p><p>在运行过程中会广播事件，插件只需要监听它所关心的事件，就能加入到这条<code>webpack</code>机制中，去改变<code>webpack</code>的运作，使得整个系统扩展性良好</p><p>从启动到结束会依次执行以下三大步骤：</p><ul><li>初始化流程：从配置文件和 <code>Shell</code> 语句中读取与合并参数，并初始化需要使用的插件和配置插件等执行环境所需要的参数</li><li>编译构建流程：从 Entry 发出，针对每个 Module 串行调用对应的 Loader 去翻译文件内容，再找到该 Module 依赖的 Module，递归地进行编译处理</li><li>输出流程：对编译后的 Module 组合成 Chunk，把 Chunk 转换成文件，输出到文件系统</li></ul><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928150523962" alt="图片"></p><h3 id="初始化流程"><a href="#初始化流程" class="headerlink" title="初始化流程"></a>初始化流程</h3><p>从配置文件和 <code>Shell</code> 语句中读取与合并参数，得出最终的参数</p><p>配置文件默认下为<code>webpack.config.js</code>，也或者通过命令的形式指定配置文件，主要作用是用于激活<code>webpack</code>的加载项和插件</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> path = <span class="built_in">require</span>(<span class="string">&#x27;path&#x27;</span>);</span><br><span class="line"><span class="keyword">var</span> node_modules = path.<span class="title function_">resolve</span>(__dirname, <span class="string">&#x27;node_modules&#x27;</span>);</span><br><span class="line"><span class="keyword">var</span> pathToReact = path.<span class="title function_">resolve</span>(node_modules, <span class="string">&#x27;react/dist/react.min.js&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">  <span class="comment">// 入口文件，是模块构建的起点，同时每一个入口文件对应最后生成的一个 chunk。</span></span><br><span class="line">  <span class="attr">entry</span>: <span class="string">&#x27;./path/to/my/entry/file.js&#x27;</span>，</span><br><span class="line">  <span class="comment">// 文件路径指向(可加快打包过程)。</span></span><br><span class="line">  <span class="attr">resolve</span>: &#123;</span><br><span class="line">    <span class="attr">alias</span>: &#123;</span><br><span class="line">      <span class="string">&#x27;react&#x27;</span>: pathToReact</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="attr">output</span>: &#123;</span><br><span class="line">    <span class="attr">path</span>: path.<span class="title function_">resolve</span>(__dirname, <span class="string">&#x27;build&#x27;</span>),</span><br><span class="line">    <span class="attr">filename</span>: <span class="string">&#x27;[name].js&#x27;</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// 这里配置了处理各模块的 loader ，包括 css 预处理 loader ，es6 编译 loader，图片处理 loader。</span></span><br><span class="line">  <span class="attr">module</span>: &#123;</span><br><span class="line">    <span class="attr">loaders</span>: [</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="attr">test</span>: <span class="regexp">/\.js$/</span>,</span><br><span class="line">        <span class="attr">loader</span>: <span class="string">&#x27;babel&#x27;</span>,</span><br><span class="line">        <span class="attr">query</span>: &#123;</span><br><span class="line">          <span class="attr">presets</span>: [<span class="string">&#x27;es2015&#x27;</span>, <span class="string">&#x27;react&#x27;</span>]</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    ],</span><br><span class="line">    <span class="attr">noParse</span>: [pathToReact]</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// webpack 各插件对象，在 webpack 的事件流中执行对应的方法。</span></span><br><span class="line">  <span class="attr">plugins</span>: [</span><br><span class="line">    <span class="keyword">new</span> webpack.<span class="title class_">HotModuleReplacementPlugin</span>()</span><br><span class="line">  ]</span><br><span class="line">&#125;;</span><br><span class="line">webpack<span class="string">` 将 `</span>webpack.<span class="property">config</span>.<span class="property">js</span><span class="string">` 中的各个配置项拷贝到 `</span>options<span class="string">` 对象中，并加载用户配置的 `</span>plugins</span><br></pre></td></tr></table></figure><p>完成上述步骤之后，则开始初始化<code>Compiler</code>编译对象，该对象掌控者<code>webpack</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Compiler</span> <span class="keyword">extends</span> <span class="title class_ inherited__">Tapable</span> &#123;</span><br><span class="line">    <span class="title function_">constructor</span>(<span class="params">context</span>) &#123;</span><br><span class="line">        <span class="variable language_">super</span>();</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">hooks</span> = &#123;</span><br><span class="line">            <span class="attr">beforeCompile</span>: <span class="keyword">new</span> <span class="title class_">AsyncSeriesHook</span>([<span class="string">&quot;params&quot;</span>]),</span><br><span class="line">            <span class="attr">compile</span>: <span class="keyword">new</span> <span class="title class_">SyncHook</span>([<span class="string">&quot;params&quot;</span>]),</span><br><span class="line">            <span class="attr">afterCompile</span>: <span class="keyword">new</span> <span class="title class_">AsyncSeriesHook</span>([<span class="string">&quot;compilation&quot;</span>]),</span><br><span class="line">            <span class="attr">make</span>: <span class="keyword">new</span> <span class="title class_">AsyncParallelHook</span>([<span class="string">&quot;compilation&quot;</span>]),</span><br><span class="line">            <span class="attr">entryOption</span>: <span class="keyword">new</span> <span class="title class_">SyncBailHook</span>([<span class="string">&quot;context&quot;</span>, <span class="string">&quot;entry&quot;</span>])</span><br><span class="line">            <span class="comment">// 定义了很多不同类型的钩子</span></span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="comment">// ...</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="keyword">function</span> <span class="title function_">webpack</span>(<span class="params">options</span>) &#123;</span><br><span class="line">  <span class="keyword">var</span> compiler = <span class="keyword">new</span> <span class="title class_">Compiler</span>();</span><br><span class="line">  ...<span class="comment">// 检查options,若watch字段为true,则开启watch线程</span></span><br><span class="line">  <span class="keyword">return</span> compiler;</span><br><span class="line">&#125;</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p><code>Compiler</code> 对象继承自 <code>Tapable</code>，初始化时定义了很多钩子函数</p><h3 id="编译构建流程"><a href="#编译构建流程" class="headerlink" title="编译构建流程"></a>编译构建流程</h3><p>根据配置中的 <code>entry</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></pre></td><td class="code"><pre><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">  <span class="attr">entry</span>: <span class="string">&#x27;./src/file.js&#x27;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>初始化完成后会调用<code>Compiler</code>的<code>run</code>来真正启动<code>webpack</code>编译构建流程，主要流程如下：</p><ul><li><code>compile</code> 开始编译</li><li><code>make</code> 从入口点分析模块及其依赖的模块，创建这些模块对象</li><li><code>build-module</code> 构建模块</li><li><code>seal</code> 封装构建结果</li><li><code>emit</code> 把各个chunk输出到结果文件</li></ul><h3 id="compile-编译"><a href="#compile-编译" class="headerlink" title="compile 编译"></a><strong>compile 编译</strong></h3><p>执行了<code>run</code>方法后，首先会触发<code>compile</code>，主要是构建一个<code>Compilation</code>对象</p><p>该对象是编译阶段的主要执行者，主要会依次下述流程：执行模块创建、依赖收集、分块、打包等主要任务的对象</p><h4 id="make-编译模块"><a href="#make-编译模块" class="headerlink" title="make 编译模块"></a>make 编译模块</h4><p>当完成了上述的<code>compilation</code>对象后，就开始从<code>Entry</code>入口文件开始读取，主要执行<code>_addModuleChain()</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></pre></td><td class="code"><pre><span class="line"><span class="title function_">_addModuleChain</span>(<span class="params">context, dependency, onModule, callback</span>) &#123;</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 class_">Dep</span> = <span class="comment">/** <span class="doctag">@type</span> &#123;<span class="type">DepConstructor</span>&#125; */</span> (dependency.<span class="property">constructor</span>);</span><br><span class="line">   <span class="keyword">const</span> moduleFactory = <span class="variable language_">this</span>.<span class="property">dependencyFactories</span>.<span class="title function_">get</span>(<span class="title class_">Dep</span>);</span><br><span class="line">   </span><br><span class="line">   <span class="comment">// 调用工厂函数NormalModuleFactory的create来生成一个空的NormalModule对象</span></span><br><span class="line">   moduleFactory.<span class="title function_">create</span>(&#123;</span><br><span class="line">       <span class="attr">dependencies</span>: [dependency]</span><br><span class="line">       ...</span><br><span class="line">   &#125;, <span class="function">(<span class="params">err, <span class="variable language_">module</span></span>) =&gt;</span> &#123;</span><br><span class="line">       ...</span><br><span class="line">       <span class="keyword">const</span> <span class="title function_">afterBuild</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="title function_">processModuleDependencies</span>(<span class="variable language_">module</span>, <span class="function"><span class="params">err</span> =&gt;</span> &#123;</span><br><span class="line">         <span class="keyword">if</span> (err) <span class="keyword">return</span> <span class="title function_">callback</span>(err);</span><br><span class="line">         <span class="title function_">callback</span>(<span class="literal">null</span>, <span class="variable language_">module</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="variable language_">this</span>.<span class="title function_">buildModule</span>(<span class="variable language_">module</span>, <span class="literal">false</span>, <span class="literal">null</span>, <span class="literal">null</span>, <span class="function"><span class="params">err</span> =&gt;</span> &#123;</span><br><span class="line">           ...</span><br><span class="line">           <span class="title function_">afterBuild</span>();</span><br><span class="line">       &#125;)</span><br><span class="line">   &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>过程如下：</p><p><code>_addModuleChain</code>中接收参数<code>dependency</code>传入的入口依赖，使用对应的工厂函数<code>NormalModuleFactory.create</code>方法生成一个空的<code>module</code>对象</p><p>回调中会把此<code>module</code>存入<code>compilation.modules</code>对象和<code>dependencies.module</code>对象中，由于是入口文件，也会存入<code>compilation.entries</code>中</p><p>随后执行<code>buildModule</code>进入真正的构建模块<code>module</code>内容的过程</p><h4 id="build-module-完成模块编译"><a href="#build-module-完成模块编译" class="headerlink" title="build module 完成模块编译"></a>build module 完成模块编译</h4><p>这里主要调用配置的<code>loaders</code>，将我们的模块转成标准的<code>JS</code>模块</p><p>在用<code>Loader</code> 对一个模块转换完后，使用 <code>acorn</code> 解析转换后的内容，输出对应的抽象语法树（<code>AST</code>），以方便 <code>Webpack</code>后面对代码的分析</p><p>从配置的入口模块开始，分析其 <code>AST</code>，当遇到<code>require</code>等导入其它模块语句时，便将其加入到依赖的模块列表，同时对新找出的依赖模块递归分析，最终搞清所有模块的依赖关系</p><h3 id="输出流程"><a href="#输出流程" class="headerlink" title="输出流程"></a>输出流程</h3><h4 id="seal-输出资源"><a href="#seal-输出资源" class="headerlink" title="seal 输出资源"></a>seal 输出资源</h4><p><code>seal</code>方法主要是要生成<code>chunks</code>，对<code>chunks</code>进行一系列的优化操作，并生成要输出的代码</p><p><code>webpack</code> 中的 <code>chunk</code> ，可以理解为配置在 <code>entry</code> 中的模块，或者是动态引入的模块</p><p>根据入口和模块之间的依赖关系，组装成一个个包含多个模块的 <code>Chunk</code>，再把每个 <code>Chunk</code> 转换成一个单独的文件加入到输出列表</p><h4 id="emit-输出完成"><a href="#emit-输出完成" class="headerlink" title="emit 输出完成"></a>emit 输出完成</h4><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></pre></td><td class="code"><pre><span class="line"><span class="attr">output</span>: &#123;</span><br><span class="line">    <span class="attr">path</span>: path.<span class="title function_">resolve</span>(__dirname, <span class="string">&#x27;build&#x27;</span>),</span><br><span class="line">        <span class="attr">filename</span>: <span class="string">&#x27;[name].js&#x27;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在 <code>Compiler</code> 开始生成文件前，钩子 <code>emit</code> 会被执行，这是我们修改最终文件的最后一个机会</p><p>从而<code>webpack</code>整个打包过程则结束了</p><h3 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h3><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928150532825" alt="图片"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;说说webpack的构建流程&quot;&gt;&lt;a href=&quot;#说说webpack的构建流程&quot; class=&quot;headerlink&quot; title=&quot;说说webpack的构建流程?&quot;&gt;&lt;/a&gt;说说webpack的构建流程?&lt;/h1&gt;&lt;p&gt;&lt;img src=&quot;https://cd</summary>
      
    
    
    
    <category term="Webpack" scheme="http://example.com/categories/Webpack/"/>
    
    
  </entry>
  
  <entry>
    <title>说说Webpack中常见的Loader？解决了什么问题？</title>
    <link href="http://example.com/2022/08/08/Webpack.%E8%AF%B4%E8%AF%B4Webpack%E4%B8%AD%E5%B8%B8%E8%A7%81%E7%9A%84Loader%EF%BC%9F%E8%A7%A3%E5%86%B3%E4%BA%86%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98%EF%BC%9F/"/>
    <id>http://example.com/2022/08/08/Webpack.%E8%AF%B4%E8%AF%B4Webpack%E4%B8%AD%E5%B8%B8%E8%A7%81%E7%9A%84Loader%EF%BC%9F%E8%A7%A3%E5%86%B3%E4%BA%86%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98%EF%BC%9F/</id>
    <published>2022-08-08T08:49:23.167Z</published>
    <updated>2022-08-05T06:14:35.976Z</updated>
    
    <content type="html"><![CDATA[<h1 id="说说Webpack中常见的Loader？解决了什么问题？"><a href="#说说Webpack中常见的Loader？解决了什么问题？" class="headerlink" title="说说Webpack中常见的Loader？解决了什么问题？"></a>说说Webpack中常见的Loader？解决了什么问题？</h1><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928150638050" alt="图片"></p><h2 id="一、是什么"><a href="#一、是什么" class="headerlink" title="一、是什么"></a>一、是什么</h2><p><code>loader</code> 用于对模块的源代码进行转换，在 <code>import</code> 或”加载”模块时预处理文件</p><p><code>webpack</code>做的事情，仅仅是分析出各种模块的依赖关系，然后形成资源列表，最终打包生成到指定的文件中。如下图所示：</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928150646220" alt="图片"></p><p>在<code>webpack</code>内部中，任何文件都是模块，不仅仅只是<code>js</code>文件</p><p>默认情况下，在遇到<code>import</code>或者<code>load</code>加载模块的时候，<code>webpack</code>只支持对<code>js</code>文件打包</p><p>像<code>css</code>、<code>sass</code>、<code>png</code>等这些类型的文件的时候，<code>webpack</code>则无能为力，这时候就需要配置对应的<code>loader</code>进行文件内容的解析</p><p>在加载模块的时候，执行顺序如下：</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928150653978" alt="图片"></p><p>当 <code>webpack</code> 碰到不识别的模块的时候，<code>webpack</code> 会在配置的中查找该文件解析规则</p><p>关于配置<code>loader</code>的方式有三种：</p><ul><li>配置方式（推荐）：在 webpack.config.js文件中指定 loader</li><li>内联方式：在每个 import 语句中显式指定 loader</li><li>CLI 方式：在 shell 命令中指定它们</li></ul><h3 id="配置方式"><a href="#配置方式" class="headerlink" title="配置方式"></a>配置方式</h3><p>关于<code>loader</code>的配置，我们是写在<code>module.rules</code>属性中，属性介绍如下：</p><ul><li><code>rules</code>是一个数组的形式，因此我们可以配置很多个<code>loader</code></li><li>每一个<code>loader</code>对应一个对象的形式，对象属性<code>test</code> 为匹配的规则，一般情况为正则表达式</li><li>属性<code>use</code>针对匹配到文件类型，调用对应的 <code>loader</code> 进行处理</li></ul><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><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="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">  <span class="attr">module</span>: &#123;</span><br><span class="line">    <span class="attr">rules</span>: [</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="attr">test</span>: <span class="regexp">/\.css$/</span>,</span><br><span class="line">        <span class="attr">use</span>: [</span><br><span class="line">          &#123; <span class="attr">loader</span>: <span class="string">&#x27;style-loader&#x27;</span> &#125;,</span><br><span class="line">          &#123;</span><br><span class="line">            <span class="attr">loader</span>: <span class="string">&#x27;css-loader&#x27;</span>,</span><br><span class="line">            <span class="attr">options</span>: &#123;</span><br><span class="line">              <span class="attr">modules</span>: <span class="literal">true</span></span><br><span class="line">            &#125;</span><br><span class="line">          &#125;,</span><br><span class="line">          &#123; <span class="attr">loader</span>: <span class="string">&#x27;sass-loader&#x27;</span> &#125;</span><br><span class="line">        ]</span><br><span class="line">      &#125;</span><br><span class="line">    ]</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h2 id="二、特性"><a href="#二、特性" class="headerlink" title="二、特性"></a>二、特性</h2><p>这里继续拿上述代码，来讲讲<code>loader</code>的特性</p><p>从上述代码可以看到，在处理<code>css</code>模块的时候，<code>use</code>属性中配置了三个<code>loader</code>分别处理<code>css</code>文件</p><p>因为<code>loader</code>支持链式调用，链中的每个<code>loader</code>会处理之前已处理过的资源，最终变为<code>js</code>代码。顺序为相反的顺序执行，即上述执行方式为<code>sass-loader</code>、<code>css-loader</code>、<code>style-loader</code></p><p>除此之外，<code>loader</code>的特性还有如下：</p><ul><li>loader 可以是同步的，也可以是异步的</li><li>loader 运行在 Node.js 中，并且能够执行任何操作</li><li>除了常见的通过 <code>package.json</code> 的 <code>main</code> 来将一个 npm 模块导出为 loader，还可以在 module.rules 中使用 <code>loader</code> 字段直接引用一个模块</li><li>插件(plugin)可以为 loader 带来更多特性</li><li>loader 能够产生额外的任意文件</li></ul><p>可以通过 loader 的预处理函数，为 JavaScript 生态系统提供更多能力。用户现在可以更加灵活地引入细粒度逻辑，例如：压缩、打包、语言翻译和更多其他特性</p><h2 id="三、常见的loader"><a href="#三、常见的loader" class="headerlink" title="三、常见的loader"></a>三、常见的loader</h2><p>在页面开发过程中，我们经常性加载除了<code>js</code>文件以外的内容，这时候我们就需要配置响应的<code>loader</code>进行加载</p><p>常见的<code>loader</code>如下：</p><ul><li>style-loader: 将css添加到DOM的内联样式标签style里</li><li>css-loader :允许将css文件通过require的方式引入，并返回css代码</li><li>less-loader: 处理less</li><li>sass-loader: 处理sass</li><li>postcss-loader: 用postcss来处理CSS</li><li>autoprefixer-loader: 处理CSS3属性前缀，已被弃用，建议直接使用postcss</li><li>file-loader: 分发文件到output目录并返回相对路径</li><li>url-loader: 和file-loader类似，但是当文件小于设定的limit时可以返回一个Data Url</li><li>html-minify-loader: 压缩HTML</li><li>babel-loader :用babel来转换ES6文件到ES</li></ul><p>下面给出一些常见的<code>loader</code>的使用：</p><h3 id="css-loader"><a href="#css-loader" class="headerlink" title="css-loader"></a>css-loader</h3><p>分析 <code>css</code> 模块之间的关系，并合成⼀个 <code>css</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></pre></td><td class="code"><pre><span class="line">npm install --save-dev css-loader</span><br><span class="line"><span class="attr">rules</span>: [</span><br><span class="line">  ...,</span><br><span class="line"> &#123;</span><br><span class="line">  <span class="attr">test</span>: <span class="regexp">/\.css$/</span>,</span><br><span class="line">    <span class="attr">use</span>: &#123;</span><br><span class="line">      <span class="attr">loader</span>: <span class="string">&quot;css-loader&quot;</span>,</span><br><span class="line">      <span class="attr">options</span>: &#123;</span><br><span class="line">     <span class="comment">// 启用/禁用 url() 处理</span></span><br><span class="line">     <span class="attr">url</span>: <span class="literal">true</span>,</span><br><span class="line">     <span class="comment">// 启用/禁用 @import 处理</span></span><br><span class="line">     <span class="attr">import</span>: <span class="literal">true</span>,</span><br><span class="line">        <span class="comment">// 启用/禁用 Sourcemap</span></span><br><span class="line">        <span class="attr">sourceMap</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></pre></td></tr></table></figure><p>如果只通过<code>css-loader</code>加载文件，这时候页面代码设置的样式并没有生效</p><p>原因在于，<code>css-loader</code>只是负责将<code>.css</code>文件进行一个解析，而并不会将解析后的<code>css</code>插入到页面中</p><p>如果我们希望再完成插入<code>style</code>的操作，那么我们还需要另外一个<code>loader</code>，就是<code>style-loader</code></p><h3 id="style-loader"><a href="#style-loader" class="headerlink" title="style-loader"></a>style-loader</h3><p>把 <code>css-loader</code> 生成的内容，用 <code>style</code> 标签挂载到页面的 <code>head</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></pre></td><td class="code"><pre><span class="line">npm install --save-dev style-loader</span><br><span class="line"><span class="attr">rules</span>: [</span><br><span class="line">  ...,</span><br><span class="line"> &#123;</span><br><span class="line">  <span class="attr">test</span>: <span class="regexp">/\.css$/</span>,</span><br><span class="line">    <span class="attr">use</span>: [<span class="string">&quot;style-loader&quot;</span>, <span class="string">&quot;css-loader&quot;</span>]</span><br><span class="line"> &#125;</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>同一个任务的 <code>loader</code> 可以同时挂载多个，处理顺序为：从右到左，从下往上</p><h3 id="less-loader"><a href="#less-loader" class="headerlink" title="less-loader"></a>less-loader</h3><p>开发中，我们也常常会使用<code>less</code>、<code>sass</code>、<code>stylus</code>预处理器编写<code>css</code>样式，使开发效率提高，这里需要使用<code>less-loader</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></pre></td><td class="code"><pre><span class="line">npm install less-loader -D</span><br><span class="line"><span class="attr">rules</span>: [</span><br><span class="line">  ...,</span><br><span class="line"> &#123;</span><br><span class="line">  <span class="attr">test</span>: <span class="regexp">/\.css$/</span>,</span><br><span class="line">    <span class="attr">use</span>: [<span class="string">&quot;style-loader&quot;</span>, <span class="string">&quot;css-loader&quot;</span>,<span class="string">&quot;less-loader&quot;</span>]</span><br><span class="line"> &#125;</span><br><span class="line">]</span><br></pre></td></tr></table></figure><h3 id="raw-loader"><a href="#raw-loader" class="headerlink" title="raw-loader"></a>raw-loader</h3><p>在 <code>webpack</code>中通过 <code>import</code>方式导入文件内容，该<code>loader</code>并不是内置的，所以首先要安装</p><figure class="highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install --save-dev raw-loader</span><br></pre></td></tr></table></figure><p>然后在 webpack.config.js 中进行配置</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">module.exports = &#123;</span><br><span class="line">  ...,</span><br><span class="line">  module: &#123;</span><br><span class="line">      rules: [</span><br><span class="line">      &#123;</span><br><span class="line">        test: /\.(txt|md)$/,</span><br><span class="line">        use: &#x27;raw-loader&#x27;</span><br><span class="line">     &#125;</span><br><span class="line">    ]</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="jsfile-loader"><a href="#jsfile-loader" class="headerlink" title="jsfile-loader"></a>jsfile-loader</h3><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><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">npm install --save-dev file-loader</span><br><span class="line"><span class="attr">rules</span>: [</span><br><span class="line">  ...,</span><br><span class="line"> &#123;</span><br><span class="line">  <span class="attr">test</span>: <span class="regexp">/\.(png|jpe?g|gif)$/</span>,</span><br><span class="line">    <span class="attr">use</span>: &#123;</span><br><span class="line">      <span class="attr">loader</span>: <span class="string">&quot;file-loader&quot;</span>,</span><br><span class="line">      <span class="attr">options</span>: &#123;</span><br><span class="line">        <span class="comment">// placeholder 占位符 [name] 源资源模块的名称</span></span><br><span class="line">        <span class="comment">// [ext] 源资源模块的后缀</span></span><br><span class="line">        <span class="attr">name</span>: <span class="string">&quot;[name]_[hash].[ext]&quot;</span>,</span><br><span class="line">        <span class="comment">//打包后的存放位置</span></span><br><span class="line">        <span class="attr">outputPath</span>: <span class="string">&quot;./images&quot;</span>,</span><br><span class="line">        <span class="comment">// 打包后文件的 url</span></span><br><span class="line">        <span class="attr">publicPath</span>: <span class="string">&#x27;./images&#x27;</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></pre></td></tr></table></figure><h3 id="url-loader"><a href="#url-loader" class="headerlink" title="url-loader"></a>url-loader</h3><p>可以处理理 <code>file-loader</code> 所有的事情，但是遇到图片格式的模块，可以选择性的把图片转成 <code>base64</code> 格式的字符串，并打包到 <code>js</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></pre></td><td class="code"><pre><span class="line">npm install --save-dev url-loader</span><br><span class="line"><span class="attr">rules</span>: [</span><br><span class="line">  ...,</span><br><span class="line"> &#123;</span><br><span class="line">  <span class="attr">test</span>: <span class="regexp">/\.(png|jpe?g|gif)$/</span>,</span><br><span class="line">    <span class="attr">use</span>: &#123;</span><br><span class="line">      <span class="attr">loader</span>: <span class="string">&quot;url-loader&quot;</span>,</span><br><span class="line">      <span class="attr">options</span>: &#123;</span><br><span class="line">        <span class="comment">// placeholder 占位符 [name] 源资源模块的名称</span></span><br><span class="line">        <span class="comment">// [ext] 源资源模块的后缀</span></span><br><span class="line">        <span class="attr">name</span>: <span class="string">&quot;[name]_[hash].[ext]&quot;</span>,</span><br><span class="line">        <span class="comment">//打包后的存放位置</span></span><br><span class="line">        <span class="attr">outputPath</span>: <span class="string">&quot;./images&quot;</span></span><br><span class="line">        <span class="comment">// 打包后文件的 url</span></span><br><span class="line">        <span class="attr">publicPath</span>: <span class="string">&#x27;./images&#x27;</span>,</span><br><span class="line">        <span class="comment">// 小于 100 字节转成 base64 格式</span></span><br><span class="line">        <span class="attr">limit</span>: <span class="number">100</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></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;说说Webpack中常见的Loader？解决了什么问题？&quot;&gt;&lt;a href=&quot;#说说Webpack中常见的Loader？解决了什么问题？&quot; class=&quot;headerlink&quot; title=&quot;说说Webpack中常见的Loader？解决了什么问题？&quot;&gt;&lt;/a&gt;说说</summary>
      
    
    
    
    <category term="Webpack" scheme="http://example.com/categories/Webpack/"/>
    
    
  </entry>
  
  <entry>
    <title>说说Webpack中Loader和Plugin的区别？编写Loader，Plugin的思路？</title>
    <link href="http://example.com/2022/08/08/Webpack.%E8%AF%B4%E8%AF%B4Webpack%E4%B8%ADLoader%E5%92%8CPlugin%E7%9A%84%E5%8C%BA%E5%88%AB%EF%BC%9F%E7%BC%96%E5%86%99Loader%EF%BC%8CPlugin%E7%9A%84%E6%80%9D%E8%B7%AF%EF%BC%9F/"/>
    <id>http://example.com/2022/08/08/Webpack.%E8%AF%B4%E8%AF%B4Webpack%E4%B8%ADLoader%E5%92%8CPlugin%E7%9A%84%E5%8C%BA%E5%88%AB%EF%BC%9F%E7%BC%96%E5%86%99Loader%EF%BC%8CPlugin%E7%9A%84%E6%80%9D%E8%B7%AF%EF%BC%9F/</id>
    <published>2022-08-08T08:49:23.166Z</published>
    <updated>2022-08-05T06:14:21.593Z</updated>
    
    <content type="html"><![CDATA[<h1 id="说说Webpack中Loader和Plugin的区别？编写Loader，Plugin的思路？"><a href="#说说Webpack中Loader和Plugin的区别？编写Loader，Plugin的思路？" class="headerlink" title="说说Webpack中Loader和Plugin的区别？编写Loader，Plugin的思路？"></a>说说Webpack中Loader和Plugin的区别？编写Loader，Plugin的思路？</h1><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928151007348" alt="图片"></p><h2 id="一、区别"><a href="#一、区别" class="headerlink" title="一、区别"></a>一、区别</h2><p>前面两节我们有提到<code>Loader</code>与<code>Plugin</code>对应的概念，先来回顾下</p><ul><li>loader 是文件加载器，能够加载资源文件，并对这些文件进行一些处理，诸如编译、压缩等，最终一起打包到指定的文件中</li><li>plugin 赋予了 webpack 各种灵活的功能，例如打包优化、资源管理、环境变量注入等，目的是解决 loader 无法实现的其他事</li></ul><p>从整个运行时机上来看，如下图所示：</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928151012114" alt="图片"></p><p>可以看到，两者在运行时机上的区别：</p><ul><li>loader 运行在打包文件之前</li><li>plugins 在整个编译周期都起作用</li></ul><p>在<code>Webpack</code> 运行的生命周期中会广播出许多事件，<code>Plugin</code> 可以监听这些事件，在合适的时机通过<code>Webpack</code>提供的 <code>API</code>改变输出结果</p><p>对于<code>loader</code>，实质是一个转换器，将A文件进行编译形成B文件，操作的是文件，比如将<code>A.scss</code>或<code>A.less</code>转变为<code>B.css</code>，单纯的文件转换过程</p><h2 id="二、编写loader"><a href="#二、编写loader" class="headerlink" title="二、编写loader"></a>二、编写loader</h2><p>在编写 <code>loader</code> 前，我们首先需要了解 <code>loader</code> 的本质</p><p>其本质为函数，函数中的 <code>this</code> 作为上下文会被 <code>webpack</code> 填充，因此我们不能将 <code>loader</code>设为一个箭头函数</p><p>函数接受一个参数，为 <code>webpack</code> 传递给 <code>loader</code> 的文件源内容</p><p>函数中 <code>this</code> 是由 <code>webpack</code> 提供的对象，能够获取当前 <code>loader</code> 所需要的各种信息</p><p>函数中有异步操作或同步操作，异步操作通过 <code>this.callback</code> 返回，返回值要求为 <code>string</code> 或者 <code>Buffer</code></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><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="comment">// 导出一个函数，source为webpack传递给loader的文件源内容</span></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="keyword">function</span>(<span class="params">source</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> content = <span class="title function_">doSomeThing2JsString</span>(source);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 如果 loader 配置了 options 对象，那么this.query将指向 options</span></span><br><span class="line">    <span class="keyword">const</span> options = <span class="variable language_">this</span>.<span class="property">query</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 可以用作解析其他模块路径的上下文</span></span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;this.context&#x27;</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * this.callback 参数：</span></span><br><span class="line"><span class="comment">     * error：Error | null，当 loader 出错时向外抛出一个 error</span></span><br><span class="line"><span class="comment">     * content：String | Buffer，经过 loader 编译后需要导出的内容</span></span><br><span class="line"><span class="comment">     * sourceMap：为方便调试生成的编译后内容的 source map</span></span><br><span class="line"><span class="comment">     * ast：本次编译生成的 AST 静态语法树，之后执行的 loader 可以直接使用这个 AST，进而省去重复生成 AST 的过程</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">callback</span>(<span class="literal">null</span>, content); <span class="comment">// 异步</span></span><br><span class="line">    <span class="keyword">return</span> content; <span class="comment">// 同步</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>一般在编写<code>loader</code>的过程中，保持功能单一，避免做多种功能</p><p>如<code>less</code>文件转换成 <code>css</code>文件也不是一步到位，而是 <code>less-loader</code>、<code>css-loader</code>、<code>style-loader</code>几个 <code>loader</code>的链式调用才能完成转换</p><h2 id="三、编写plugin"><a href="#三、编写plugin" class="headerlink" title="三、编写plugin"></a>三、编写plugin</h2><p>由于<code>webpack</code>基于发布订阅模式，在运行的生命周期中会广播出许多事件，插件通过监听这些事件，就可以在特定的阶段执行自己的插件任务</p><p>在之前也了解过，<code>webpack</code>编译会创建两个核心对象：</p><ul><li>compiler：包含了 webpack 环境的所有的配置信息，包括 options，loader 和 plugin，和 webpack 整个生命周期相关的钩子</li><li>compilation：作为 plugin 内置事件回调函数的参数，包含了当前的模块资源、编译生成资源、变化的文件以及被跟踪依赖的状态信息。当检测到一个文件变化，一次新的 Compilation 将被创建</li></ul><p>如果自己要实现<code>plugin</code>，也需要遵循一定的规范：</p><ul><li>插件必须是一个函数或者是一个包含 <code>apply</code> 方法的对象，这样才能访问<code>compiler</code>实例</li><li>传给每个插件的 <code>compiler</code> 和 <code>compilation</code> 对象都是同一个引用，因此不建议修改</li><li>异步的事件需要在插件处理完任务时调用回调函数通知 <code>Webpack</code> 进入下一个流程，不然会卡住</li></ul><p>实现<code>plugin</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MyPlugin</span> &#123;</span><br><span class="line">    <span class="comment">// Webpack 会调用 MyPlugin 实例的 apply 方法给插件实例传入 compiler 对象</span></span><br><span class="line">  apply (compiler) &#123;</span><br><span class="line">    <span class="comment">// 找到合适的事件钩子，实现自己的插件功能</span></span><br><span class="line">    compiler.<span class="property">hooks</span>.<span class="property">emit</span>.<span class="title function_">tap</span>(<span class="string">&#x27;MyPlugin&#x27;</span>, <span class="function"><span class="params">compilation</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="comment">// compilation: 当前打包构建流程的上下文</span></span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(compilation);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// do something...</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>emit</code> 事件发生时，代表源文件的转换和组装已经完成，可以读取到最终将输出的资源、代码块、模块及其依赖，并且可以修改输出资源的内容</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;说说Webpack中Loader和Plugin的区别？编写Loader，Plugin的思路？&quot;&gt;&lt;a href=&quot;#说说Webpack中Loader和Plugin的区别？编写Loader，Plugin的思路？&quot; class=&quot;headerlink&quot; title=&quot;</summary>
      
    
    
    
    <category term="Webpack" scheme="http://example.com/categories/Webpack/"/>
    
    
  </entry>
  
  <entry>
    <title>说说Webpack Proxy工作原理？为什么能解决跨域?</title>
    <link href="http://example.com/2022/08/08/Webpack.%E8%AF%B4%E8%AF%B4Webpack%20Proxy%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%EF%BC%9F%E4%B8%BA%E4%BB%80%E4%B9%88%E8%83%BD%E8%A7%A3%E5%86%B3%E8%B7%A8%E5%9F%9F_/"/>
    <id>http://example.com/2022/08/08/Webpack.%E8%AF%B4%E8%AF%B4Webpack%20Proxy%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%EF%BC%9F%E4%B8%BA%E4%BB%80%E4%B9%88%E8%83%BD%E8%A7%A3%E5%86%B3%E8%B7%A8%E5%9F%9F_/</id>
    <published>2022-08-08T08:49:23.166Z</published>
    <updated>2022-08-05T06:14:50.616Z</updated>
    
    <content type="html"><![CDATA[<h1 id="说说Webpack-Proxy工作原理？为什么能解决跨域"><a href="#说说Webpack-Proxy工作原理？为什么能解决跨域" class="headerlink" title="说说Webpack Proxy工作原理？为什么能解决跨域?"></a>说说Webpack Proxy工作原理？为什么能解决跨域?</h1><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928151240370" alt="图片"></p><h2 id="一、是什么"><a href="#一、是什么" class="headerlink" title="一、是什么"></a>一、是什么</h2><p><code>webpack proxy</code>，即<code>webpack</code>提供的代理服务</p><p>基本行为就是接收客户端发送的请求后转发给其他服务器</p><p>其目的是为了便于开发者在开发模式下解决跨域问题（浏览器安全策略限制）</p><p>想要实现代理首先需要一个中间服务器，<code>webpack</code>中提供服务器的工具为<code>webpack-dev-server</code></p><h4 id="webpack-dev-server"><a href="#webpack-dev-server" class="headerlink" title="webpack-dev-server"></a>webpack-dev-server</h4><p><code>webpack-dev-server</code>是 <code>webpack</code> 官方推出的一款开发工具，将自动编译和自动刷新浏览器等一系列对开发友好的功能全部集成在了一起</p><p>目的是为了提高开发者日常的开发效率，「只适用在开发阶段」</p><p>关于配置方面，在<code>webpack</code>配置对象属性中通过<code>devServer</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// ./webpack.config.js</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><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="attr">devServer</span>: &#123;</span><br><span class="line">        <span class="attr">contentBase</span>: path.<span class="title function_">join</span>(__dirname, <span class="string">&#x27;dist&#x27;</span>),</span><br><span class="line">        <span class="attr">compress</span>: <span class="literal">true</span>,</span><br><span class="line">        <span class="attr">port</span>: <span class="number">9000</span>,</span><br><span class="line">        <span class="attr">proxy</span>: &#123;</span><br><span class="line">            <span class="string">&#x27;/api&#x27;</span>: &#123;</span><br><span class="line">                <span class="attr">target</span>: <span class="string">&#x27;https://api.github.com&#x27;</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">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>devServetr</code>里面<code>proxy</code>则是关于代理的配置，该属性为对象的形式，对象中每一个属性就是一个代理的规则匹配</p><p>属性的名称是需要被代理的请求路径前缀，一般为了辨别都会设置前缀为<code>/api</code>，值为对应的代理匹配规则，对应如下：</p><ul><li>target：表示的是代理到的目标地址</li><li>pathRewrite：默认情况下，我们的 /api-hy 也会被写入到URL中，如果希望删除，可以使用pathRewrite</li><li>secure：默认情况下不接收转发到https的服务器上，如果希望支持，可以设置为false</li><li>changeOrigin：它表示是否更新代理后请求的 headers 中host地址</li></ul><h2 id="二、工作原理"><a href="#二、工作原理" class="headerlink" title="二、工作原理"></a>二、工作原理</h2><p><code>proxy</code>工作原理实质上是利用<code>http-proxy-middleware</code> 这个<code>http</code>代理中间件，实现请求转发给其他服务器</p><p>举个例子：</p><p>在开发阶段，本地地址为<code>http://localhost:3000</code>，该浏览器发送一个前缀带有<code>/api</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> express = <span class="built_in">require</span>(<span class="string">&#x27;express&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> proxy = <span class="built_in">require</span>(<span class="string">&#x27;http-proxy-middleware&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> app = <span class="title function_">express</span>();</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">use</span>(<span class="string">&#x27;/api&#x27;</span>, <span class="title function_">proxy</span>(&#123;<span class="attr">target</span>: <span class="string">&#x27;http://www.example.org&#x27;</span>, <span class="attr">changeOrigin</span>: <span class="literal">true</span>&#125;));</span><br><span class="line">app.<span class="title function_">listen</span>(<span class="number">3000</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// http://localhost:3000/api/foo/bar -&gt; http://www.example.org/api/foo/bar</span></span><br></pre></td></tr></table></figure><h2 id="三、跨域"><a href="#三、跨域" class="headerlink" title="三、跨域"></a>三、跨域</h2><p>在开发阶段， <code>webpack-dev-server</code> 会启动一个本地开发服务器，所以我们的应用在开发阶段是独立运行在 <code>localhost</code>的一个端口上，而后端服务又是运行在另外一个地址上</p><p>所以在开发阶段中，由于浏览器同源策略的原因，当本地访问后端就会出现跨域请求的问题</p><p>通过设置<code>webpack proxy</code>实现代理请求后，相当于浏览器与服务端中添加一个代理者</p><p>当本地发送请求的时候，代理服务器响应该请求，并将请求转发到目标服务器，目标服务器响应数据后再将数据返回给代理服务器，最终再由代理服务器将数据响应给本地</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928151247971" alt="图片"></p><p>在代理服务器传递数据给本地浏览器的过程中，两者同源，并不存在跨域行为，这时候浏览器就能正常接收数据</p><p>注意：「服务器与服务器之间请求数据并不会存在跨域行为，跨域行为是浏览器安全策略限制」</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;说说Webpack-Proxy工作原理？为什么能解决跨域&quot;&gt;&lt;a href=&quot;#说说Webpack-Proxy工作原理？为什么能解决跨域&quot; class=&quot;headerlink&quot; title=&quot;说说Webpack Proxy工作原理？为什么能解决跨域?&quot;&gt;&lt;/a&gt;说</summary>
      
    
    
    
    <category term="Webpack" scheme="http://example.com/categories/Webpack/"/>
    
    
  </entry>
  
  <entry>
    <title>与Webpack类似的工具还有哪些？区别？</title>
    <link href="http://example.com/2022/08/08/Webpack.%E4%B8%8EWebpack%E7%B1%BB%E4%BC%BC%E7%9A%84%E5%B7%A5%E5%85%B7%E8%BF%98%E6%9C%89%E5%93%AA%E4%BA%9B%EF%BC%9F%E5%8C%BA%E5%88%AB%EF%BC%9F/"/>
    <id>http://example.com/2022/08/08/Webpack.%E4%B8%8EWebpack%E7%B1%BB%E4%BC%BC%E7%9A%84%E5%B7%A5%E5%85%B7%E8%BF%98%E6%9C%89%E5%93%AA%E4%BA%9B%EF%BC%9F%E5%8C%BA%E5%88%AB%EF%BC%9F/</id>
    <published>2022-08-08T08:49:23.165Z</published>
    <updated>2022-08-05T06:14:16.760Z</updated>
    
    <content type="html"><![CDATA[<h1 id="与Webpack类似的工具还有哪些？区别？"><a href="#与Webpack类似的工具还有哪些？区别？" class="headerlink" title="与Webpack类似的工具还有哪些？区别？"></a>与Webpack类似的工具还有哪些？区别？</h1><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928151622372" alt="图片"></p><h2 id="一、模块化工具"><a href="#一、模块化工具" class="headerlink" title="一、模块化工具"></a>一、模块化工具</h2><p>模块化是一种处理复杂系统分解为更好的可管理模块的方式</p><p>可以用来分割，组织和打包应用。每个模块完成一个特定的子功能，所有的模块按某种方法组装起来，成为一个整体(<code>bundle</code>)</p><p>在前端领域中，并非只有<code>webpack</code>这一款优秀的模块打包工具，还有其他类似的工具，例如<code>Rollup</code>、<code>Parcel</code>、<code>snowpack</code>，以及最近风头无两的<code>Vite</code></p><p>通过这些模块打包工具，能够提高我们的开发效率，减少开发成本</p><p>这里没有提及<code>gulp</code>、<code>grunt</code>是因为它们只是定义为构建工具，不能类比</p><h3 id="Rollup"><a href="#Rollup" class="headerlink" title="Rollup"></a>Rollup</h3><p><code>Rollup</code> 是一款 <code>ES Modules</code> 打包器，从作用上来看，<code>Rollup</code> 与 <code>Webpack</code>非常类似。不过相比于 <code>Webpack</code>，<code>Rollup</code>要小巧的多</p><p>现在很多我们熟知的库都都使用它进行打包，比如：<code>Vue</code>、<code>React</code>和<code>three.js</code>等</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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ./src/messages.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">  <span class="attr">hi</span>: <span class="string">&#x27;Hey Guys, I am zce~&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ./src/logger.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> <span class="title function_">log</span> = msg =&gt; &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;---------- INFO ----------&#x27;</span>)</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(msg)</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">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> <span class="title function_">error</span> = msg =&gt; &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">&#x27;---------- ERROR ----------&#x27;</span>)</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">error</span>(msg)</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">&#x27;---------------------------&#x27;</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ./src/index.js</span></span><br><span class="line"><span class="keyword">import</span> &#123; log &#125; <span class="keyword">from</span> <span class="string">&#x27;./logger&#x27;</span></span><br><span class="line"><span class="keyword">import</span> messages <span class="keyword">from</span> <span class="string">&#x27;./messages&#x27;</span></span><br><span class="line"><span class="title function_">log</span>(messages.<span class="property">hi</span>)</span><br></pre></td></tr></table></figure><p>然后通过<code>rollup</code>进行打包</p><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">$ npx rollup ./src/index.<span class="property">js</span> --file ./dist/bundle.<span class="property">js</span></span><br></pre></td></tr></table></figure><p>打包结果如下图<img src="https://mmbiz.qpic.cn/mmbiz_png/gH31uF9VIibSp5L5l3omqclCLN32IwOoye5SlXD0N4vhUoXEAynxic3XFNicU0U6SdztGCVA29s2r5HONTujdrkow/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" alt="图片"></p><p>可以看到，代码非常简洁，完成不像<code>webpack</code>那样存在大量引导代码和模块函数</p><p>并且<code>error</code>方法由于没有被使用，输出的结果中并无<code>error</code>方法，可以看到，<code>rollup</code>默认开始<code>Tree-shaking</code> 优化输出结果</p><p>因此，可以看到<code>Rollup</code>的优点：</p><ul><li>代码效率更简洁、效率更高</li><li>默认支持 Tree-shaking</li></ul><p>但缺点也十分明显，加载其他类型的资源文件或者支持导入 <code>CommonJS</code> 模块，又或是编译 <code>ES</code> 新特性，这些额外的需求 <code>Rollup</code>需要使用插件去完成</p><p>综合来看，<code>rollup</code>并不适合开发应用使用，因为需要使用第三方模块，而目前第三方模块大多数使用<code>CommonJs</code>方式导出成员，并且<code>rollup</code>不支持<code>HMR</code>，使开发效率降低</p><p>但是在用于打包<code>JavaScript</code> 库时，<code>rollup</code>比 <code>webpack</code> 更有优势，因为其打包出来的代码更小、更快，其存在的缺点可以忽略</p><h3 id="Parcel"><a href="#Parcel" class="headerlink" title="Parcel"></a>Parcel</h3><p>Parcel ，是一款完全零配置的前端打包器，它提供了 “傻瓜式” 的使用体验，只需了解简单的命令，就能构建前端应用程序</p><p><code>Parcel</code> 跟 <code>Webpack</code> 一样都支持以任意类型文件作为打包入口，但建议使用<code>HTML</code>文件作为入口，该<code>HTML</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></pre></td><td class="code"><pre><span class="line">&lt;!-- ./src/index.<span class="property">html</span> --&gt;</span><br><span class="line">&lt;!<span class="variable constant_">DOCTYPE</span> html&gt;</span><br><span class="line"><span class="language-xml"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">&quot;en&quot;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">  <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">&quot;UTF-8&quot;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">  <span class="tag">&lt;<span class="name">title</span>&gt;</span>Parcel Tutorials<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span></span><br><span class="line"><span class="language-xml"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span></span><br><span class="line"><span class="language-xml"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">  <span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;main.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span></span><br><span class="line"><span class="language-xml"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span></span><br><span class="line"><span class="language-xml"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span></span><br></pre></td></tr></table></figure><p>main.js文件通过<code>ES Moudle</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// ./src/main.js</span></span><br><span class="line"><span class="keyword">import</span> &#123; log &#125; <span class="keyword">from</span> <span class="string">&#x27;./logger&#x27;</span></span><br><span class="line"><span class="title function_">log</span>(<span class="string">&#x27;hello parcel&#x27;</span>)</span><br><span class="line"><span class="comment">// ./src/logger.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> <span class="title function_">log</span> = msg =&gt; &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;---------- INFO ----------&#x27;</span>)</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(msg)</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></pre></td><td class="code"><pre><span class="line">npx parcel src/index.<span class="property">html</span></span><br></pre></td></tr></table></figure><p>执行命令后，<code>Parcel</code>不仅打包了应用，同时也启动了一个开发服务器，跟<code>webpack Dev Server</code>一样</p><p>跟<code>webpack</code>类似，也支持模块热替换，但用法更简单</p><p>同时，<code>Parcel</code>有个十分好用的功能：支持自动安装依赖，像<code>webpack</code>开发阶段突然使用安装某个第三方依赖，必然会终止<code>dev server</code>然后安装再启动。而<code>Parcel</code>则免了这繁琐的工作流程</p><p>同时，<code>Parcel</code>能够零配置加载其他类型的资源文件，无须像<code>webpack</code>那样配置对应的<code>loader</code></p><p>打包命令如下：</p><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">npx parcel src/index.<span class="property">html</span></span><br></pre></td></tr></table></figure><p>由于打包过程是多进程同时工作，构建速度会比<code>Webpack</code> 快，输出文件也会被压缩，并且样式代码也会被单独提取到单个文件中</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928151629904" alt="图片"></p><p>可以感受到，<code>Parcel</code>给开发者一种很大的自由度，只管去实现业务代码，其他事情用<code>Parcel</code>解决</p><h3 id="Snowpack"><a href="#Snowpack" class="headerlink" title="Snowpack"></a>Snowpack</h3><p>Snowpack，是一种闪电般快速的前端构建工具，专为现代<code>Web</code>设计，较复杂的打包工具（如<code>Webpack</code>或<code>Parcel</code>）的替代方案，利用<code>JavaScript</code>的本机模块系统，避免不必要的工作并保持流畅的开发体验</p><p>开发阶段，每次保存单个文件时，<code>Webpack</code>和<code>Parcel</code>都需要重新构建和重新打包应用程序的整个<code>bundle</code>。而<code>Snowpack</code>为你的应用程序每个文件构建一次，就可以永久缓存，文件更改时，<code>Snowpack</code>会重新构建该单个文件</p><p>下图给出<code>webpack</code>与<code>snowpack</code>打包区别：</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928151634138" alt="图片"></p><p>在重新构建每次变更时没有任何的时间浪费，只需要在浏览器中进行HMR更新</p><h3 id="Vite"><a href="#Vite" class="headerlink" title="Vite"></a>Vite</h3><p>vite ，是一种新型前端构建工具，能够显著提升前端开发体验</p><p>它主要由两部分组成：</p><ul><li>一个开发服务器，它基于 原生 ES 模块 提供了丰富的内建功能，如速度快到惊人的 [模块热更新HMR</li><li>一套构建指令，它使用 Rollup打包你的代码，并且它是预配置的，可以输出用于生产环境的优化过的静态资源</li></ul><p>其作用类似<code>webpack</code>+ <code>webpack-dev-server</code>，其特点如下：</p><ul><li>快速的冷启动</li><li>即时的模块热更新</li><li>真正的按需编译</li></ul><p><code>vite</code>会直接启动开发服务器，不需要进行打包操作，也就意味着不需要分析模块的依赖、不需要编译，因此启动速度非常快</p><p>利用现代浏览器支持<code>ES Module</code>的特性，当浏览器请求某个模块的时候，再根据需要对模块的内容进行编译，这种方式大大缩短了编译时间</p><p>原理图如下所示：</p><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928151646428" alt="图片"></p><p>在热模块<code>HMR</code>方面，当修改一个模块的时候，仅需让浏览器重新请求该模块即可，无须像<code>webpack</code>那样需要把该模块的相关依赖模块全部编译一次，效率更高</p><h3 id="webpack"><a href="#webpack" class="headerlink" title="webpack"></a>webpack</h3><p>相比上述的模块化工具，<code>webpack</code>大而全，很多常用的功能做到开箱即用。有两大最核心的特点：「一切皆模块」和「按需加载」</p><p>与其他构建工具相比，有如下优势：</p><ul><li>智能解析：对 CommonJS 、 AMD 、ES6 的语法做了兼容</li><li>万物模块：对 js、css、图片等资源文件都支持打包</li><li>开箱即用：HRM、Tree-shaking等功能</li><li>代码分割：可以将代码切割成不同的 chunk，实现按需加载，降低了初始化时间</li><li>插件系统，具有强大的 Plugin 接口，具有更好的灵活性和扩展性</li><li>易于调试：支持 SourceUrls 和 SourceMaps</li><li>快速运行：webpack 使用异步 IO 并具有多级缓存，这使得 webpack 很快且在增量编译上更加快</li><li>生态环境好：社区更丰富，出现的问题更容易解决</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;与Webpack类似的工具还有哪些？区别？&quot;&gt;&lt;a href=&quot;#与Webpack类似的工具还有哪些？区别？&quot; class=&quot;headerlink&quot; title=&quot;与Webpack类似的工具还有哪些？区别？&quot;&gt;&lt;/a&gt;与Webpack类似的工具还有哪些？区别？&lt;/</summary>
      
    
    
    
    <category term="Webpack" scheme="http://example.com/categories/Webpack/"/>
    
    
  </entry>
  
  <entry>
    <title>说说对observable的理解</title>
    <link href="http://example.com/2022/08/08/vue.%E8%AF%B4%E8%AF%B4%E5%AF%B9observable%E7%9A%84%E7%90%86%E8%A7%A3/"/>
    <id>http://example.com/2022/08/08/vue.%E8%AF%B4%E8%AF%B4%E5%AF%B9observable%E7%9A%84%E7%90%86%E8%A7%A3/</id>
    <published>2022-08-08T08:49:23.164Z</published>
    <updated>2022-08-05T02:33:15.797Z</updated>
    
    <content type="html"><![CDATA[<h1 id="说说对observable的理解"><a href="#说说对observable的理解" class="headerlink" title="说说对observable的理解"></a>说说对observable的理解</h1><p><img src="https://cdn.jsdelivr.net/gh/IceRain-mvc/cdn/img/640-20210928222821689" alt="图片"></p><h2 id="一、Observable-是什么"><a href="#一、Observable-是什么" class="headerlink" title="一、Observable 是什么"></a>一、Observable 是什么</h2><p><code>Observable</code> 翻译过来我们可以理解成<strong>可观察的</strong></p><p>我们先来看一下其在<code>Vue</code>中的定义</p><blockquote><p><code>Vue.observable</code>，让一个对象变成响应式数据。<code>Vue</code> 内部会用它来处理 <code>data</code> 函数返回的对象</p></blockquote><p>返回的对象可以直接用于渲染函数和计算属性内，并且会在发生变更时触发相应的更新。也可以作为最小化的跨组件状态存储器</p><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="title class_">Vue</span>.<span class="title function_">observable</span>(&#123; count : <span class="number">1</span>&#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></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> <span class="title function_">vue</span>(&#123; count : <span class="number">1</span>&#125;)</span><br></pre></td></tr></table></figure><p>在 <code>Vue 2.x</code> 中，被传入的对象会直接被 <code>Vue.observable</code> 变更，它和被返回的对象是同一个对象</p><p>在 <code>Vue 3.x</code> 中，则会返回一个可响应的代理，而对源对象直接进行变更仍然是不可响应的</p><h2 id="二、使用场景"><a href="#二、使用场景" class="headerlink" title="二、使用场景"></a>二、使用场景</h2><p>在非父子组件通信时，可以使用通常的<code>bus</code>或者使用<code>vuex</code>，但是实现的功能不是太复杂，而使用上面两个又有点繁琐。这时，<code>observable</code>就是一个很好的选择</p><p>创建一个<code>js</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 引入vue</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">Vue</span> <span class="keyword">from</span> <span class="string">&#x27;vue</span></span><br><span class="line"><span class="string">// 创建state对象，使用observable让state对象可响应</span></span><br><span class="line"><span class="string">export let state = Vue.observable(&#123;</span></span><br><span class="line"><span class="string">  name: &#x27;</span>张三<span class="string">&#x27;,</span></span><br><span class="line"><span class="string">  &#x27;</span>age<span class="string">&#x27;: 38</span></span><br><span class="line"><span class="string">&#125;)</span></span><br><span class="line"><span class="string">// 创建对应的方法</span></span><br><span class="line"><span class="string">export let mutations = &#123;</span></span><br><span class="line"><span class="string">  changeName(name) &#123;</span></span><br><span class="line"><span class="string">    state.name = name</span></span><br><span class="line"><span class="string">  &#125;,</span></span><br><span class="line"><span class="string">  setAge(age) &#123;</span></span><br><span class="line"><span class="string">    state.age = age</span></span><br><span class="line"><span class="string">  &#125;</span></span><br><span class="line"><span class="string">&#125;</span></span><br></pre></td></tr></table></figure><p>在<code>.vue</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></pre></td><td class="code"><pre><span class="line">&lt;template&gt;</span><br><span class="line">  <span class="language-xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">    姓名：&#123;&#123; name &#125;&#125;</span></span><br><span class="line"><span class="language-xml">    年龄：&#123;&#123; age &#125;&#125;</span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;<span class="name">button</span> @<span class="attr">click</span>=<span class="string">&quot;changeName(&#x27;李四&#x27;)&quot;</span>&gt;</span>改变姓名<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;<span class="name">button</span> @<span class="attr">click</span>=<span class="string">&quot;setAge(18)&quot;</span>&gt;</span>改变年龄<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">&lt;/template&gt;</span><br><span class="line"><span class="keyword">import</span> &#123; state, mutations &#125; <span class="keyword">from</span> <span class="string">&#x27;@/store</span></span><br><span class="line"><span class="string">export default &#123;</span></span><br><span class="line"><span class="string">  // 在计算属性中拿到值</span></span><br><span class="line"><span class="string">  computed: &#123;</span></span><br><span class="line"><span class="string">    name() &#123;</span></span><br><span class="line"><span class="string">      return state.name</span></span><br><span class="line"><span class="string">    &#125;,</span></span><br><span class="line"><span class="string">    age() &#123;</span></span><br><span class="line"><span class="string">      return state.age</span></span><br><span class="line"><span class="string">    &#125;</span></span><br><span class="line"><span class="string">  &#125;,</span></span><br><span class="line"><span class="string">  // 调用mutations里面的方法，更新数据</span></span><br><span class="line"><span class="string">  methods: &#123;</span></span><br><span class="line"><span class="string">    changeName: mutations.changeName,</span></span><br><span class="line"><span class="string">    setAge: mutations.setAge</span></span><br><span class="line"><span class="string">  &#125;</span></span><br><span class="line"><span class="string">&#125;</span></span><br></pre></td></tr></table></figure><h2 id="三、原理分析"><a href="#三、原理分析" class="headerlink" title="三、原理分析"></a>三、原理分析</h2><p>源码位置：src\core\observer\index.js</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">observe</span> (<span class="attr">value</span>: any, <span class="attr">asRootData</span>: ?boolean): <span class="title class_">Observer</span> | <span class="keyword">void</span> &#123;</span><br><span class="line">  <span class="keyword">if</span> (!<span class="title function_">isObject</span>(value) || value <span class="keyword">instanceof</span> <span class="title class_">VNode</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">let</span> <span class="attr">ob</span>: <span class="title class_">Observer</span> | <span class="keyword">void</span></span><br><span class="line">  <span class="comment">// 判断是否存在__ob__响应式属性</span></span><br><span class="line">  <span class="keyword">if</span> (<span class="title function_">hasOwn</span>(value, <span class="string">&#x27;__ob__&#x27;</span>) &amp;&amp; value.<span class="property">__ob__</span> <span class="keyword">instanceof</span> <span class="title class_">Observer</span>) &#123;</span><br><span class="line">    ob = value.<span class="property">__ob__</span></span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (</span><br><span class="line">    shouldObserve &amp;&amp;</span><br><span class="line">    !<span class="title function_">isServerRendering</span>() &amp;&amp;</span><br><span class="line">    (<span class="title class_">Array</span>.<span class="title function_">isArray</span>(value) || <span class="title function_">isPlainObject</span>(value)) &amp;&amp;</span><br><span class="line">    <span class="title class_">Object</span>.<span class="title function_">isExtensible</span>(value) &amp;&amp;</span><br><span class="line">    !value.<span class="property">_isVue</span></span><br><span class="line">  ) &#123;</span><br><span class="line">    <span class="comment">// 实例化Observer响应式对象</span></span><br><span class="line">    ob = <span class="keyword">new</span> <span class="title class_">Observer</span>(value)</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (asRootData &amp;&amp; ob) &#123;</span><br><span class="line">    ob.<span class="property">vmCount</span>++</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> ob</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>Observer</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">Observer</span> &#123;</span><br><span class="line">    <span class="attr">value</span>: any;</span><br><span class="line">    <span class="attr">dep</span>: <span class="title class_">Dep</span>;</span><br><span class="line">    <span class="attr">vmCount</span>: number; <span class="comment">// number of vms that have this object as root $data</span></span><br><span class="line"></span><br><span class="line">    <span class="title function_">constructor</span> (<span class="attr">value</span>: any) &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">value</span> = value</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">dep</span> = <span class="keyword">new</span> <span class="title class_">Dep</span>()</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">vmCount</span> = <span class="number">0</span></span><br><span class="line">        <span class="title function_">def</span>(value, <span class="string">&#x27;__ob__&#x27;</span>, <span class="variable language_">this</span>)</span><br><span class="line">        <span class="keyword">if</span> (<span class="title class_">Array</span>.<span class="title function_">isArray</span>(value)) &#123;</span><br><span class="line">            <span class="keyword">if</span> (hasProto) &#123;</span><br><span class="line">                <span class="title function_">protoAugment</span>(value, arrayMethods)</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="title function_">copyAugment</span>(value, arrayMethods, arrayKeys)</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="title function_">observeArray</span>(value)</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 实例化对象是一个对象，进入walk方法</span></span><br><span class="line">            <span class="variable language_">this</span>.<span class="title function_">walk</span>(value)</span><br><span class="line">        &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>walk</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></pre></td><td class="code"><pre><span class="line">walk (<span class="attr">obj</span>: <span class="title class_">Object</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> keys = <span class="title class_">Object</span>.<span class="title function_">keys</span>(obj)</span><br><span class="line">    <span class="comment">// 遍历key，通过defineReactive创建响应式对象</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; keys.<span class="property">length</span>; i++) &#123;</span><br><span class="line">        <span class="title function_">defineReactive</span>(obj, keys[i])</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>defineReactive</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">defineReactive</span> (</span><br><span class="line">  <span class="attr">obj</span>: <span class="title class_">Object</span>,</span><br><span class="line">  <span class="attr">key</span>: string,</span><br><span class="line">  <span class="attr">val</span>: any,</span><br><span class="line">  customSetter?: ?<span class="title class_">Function</span>,</span><br><span class="line">  shallow?: boolean</span><br><span class="line">) &#123;</span><br><span class="line">  <span class="keyword">const</span> dep = <span class="keyword">new</span> <span class="title class_">Dep</span>()</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> property = <span class="title class_">Object</span>.<span class="title function_">getOwnPropertyDescriptor</span>(obj, key)</span><br><span class="line">  <span class="keyword">if</span> (property &amp;&amp; property.<span class="property">configurable</span> === <span class="literal">false</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// cater for pre-defined getter/setters</span></span><br><span class="line">  <span class="keyword">const</span> getter = property &amp;&amp; property.<span class="property">get</span></span><br><span class="line">  <span class="keyword">const</span> setter = property &amp;&amp; property.<span class="property">set</span></span><br><span class="line">  <span class="keyword">if</span> ((!getter || setter) &amp;&amp; <span class="variable language_">arguments</span>.<span class="property">length</span> === <span class="number">2</span>) &#123;</span><br><span class="line">    val = obj[key]</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">let</span> childOb = !shallow &amp;&amp; <span class="title function_">observe</span>(val)</span><br><span class="line">  <span class="comment">// 接下来调用Object.defineProperty()给对象定义响应式属性</span></span><br><span class="line">  <span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(obj, key, &#123;</span><br><span class="line">    <span class="attr">enumerable</span>: <span class="literal">true</span>,</span><br><span class="line">    <span class="attr">configurable</span>: <span class="literal">true</span>,</span><br><span class="line">    <span class="attr">get</span>: <span class="keyword">function</span> <span class="title function_">reactiveGetter</span> () &#123;</span><br><span class="line">      <span class="keyword">const</span> value = getter ? getter.<span class="title function_">call</span>(obj) : val</span><br><span class="line">      <span class="keyword">if</span> (<span class="title class_">Dep</span>.<span class="property">target</span>) &#123;</span><br><span class="line">        dep.<span class="title function_">depend</span>()</span><br><span class="line">        <span class="keyword">if</span> (childOb) &#123;</span><br><span class="line">          childOb.<span class="property">dep</span>.<span class="title function_">depend</span>()</span><br><span class="line">          <span class="keyword">if</span> (<span class="title class_">Array</span>.<span class="title function_">isArray</span>(value)) &#123;</span><br><span class="line">            <span class="title function_">dependArray</span>(value)</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">return</span> value</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="attr">set</span>: <span class="keyword">function</span> <span class="title function_">reactiveSetter</span> (newVal) &#123;</span><br><span class="line">      <span class="keyword">const</span> value = getter ? getter.<span class="title function_">call</span>(obj) : val</span><br><span class="line">      <span class="comment">/* eslint-disable no-self-compare */</span></span><br><span class="line">      <span class="keyword">if</span> (newVal === value || (newVal !== newVal &amp;&amp; value !== value)) &#123;</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line">      &#125;</span><br><span class="line">      <span class="comment">/* eslint-enable no-self-compare */</span></span><br><span class="line">      <span class="keyword">if</span> (process.<span class="property">env</span>.<span class="property">NODE_ENV</span> !== <span class="string">&#x27;production&#x27;</span> &amp;&amp; customSetter) &#123;</span><br><span class="line">        <span class="title function_">customSetter</span>()</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="comment">// #7981: for accessor properties without setter</span></span><br><span class="line">      <span class="keyword">if</span> (getter &amp;&amp; !setter) <span class="keyword">return</span></span><br><span class="line">      <span class="keyword">if</span> (setter) &#123;</span><br><span class="line">        setter.<span class="title function_">call</span>(obj, newVal)</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        val = newVal</span><br><span class="line">      &#125;</span><br><span class="line">      childOb = !shallow &amp;&amp; <span class="title function_">observe</span>(newVal)</span><br><span class="line">      <span class="comment">// 对观察者watchers进行通知,state就成了全局响应式对象</span></span><br><span class="line">      dep.<span class="title function_">notify</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>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;说说对observable的理解&quot;&gt;&lt;a href=&quot;#说说对observable的理解&quot; class=&quot;headerlink&quot; title=&quot;说说对observable的理解&quot;&gt;&lt;/a&gt;说说对observable的理解&lt;/h1&gt;&lt;p&gt;&lt;img src=&quot;http</summary>
      
    
    
    
    <category term="Vue" scheme="http://example.com/categories/Vue/"/>
    
    
  </entry>
  
</feed>
