<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Cheese</title>
  
  
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://aartemida.github.io/"/>
  <updated>2022-08-25T14:29:49.128Z</updated>
  <id>https://aartemida.github.io/</id>
  
  <author>
    <name>咕噜咕噜</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>《故国的吃食》摘记</title>
    <link href="https://aartemida.github.io/2022/08/22/%E3%80%8A%E6%95%85%E5%9B%BD%E7%9A%84%E5%90%83%E9%A3%9F%E3%80%8B%E6%91%98%E8%AE%B0/"/>
    <id>https://aartemida.github.io/2022/08/22/《故国的吃食》摘记/</id>
    <published>2022-08-22T07:47:04.000Z</published>
    <updated>2022-08-25T14:29:49.128Z</updated>
    
    <content type="html"><![CDATA[<h4 id="【姜家羊肉汤】"><a href="#【姜家羊肉汤】" class="headerlink" title="【姜家羊肉汤】"></a>【姜家羊肉汤】</h4><p>羊肉汤是满满的一粗瓷大碗，上面撒满了芫荽、葱花和几丝细细的姜丝。用筷子翻了，大片的熟羊肉片，溜薄，挑开往里一淹，将芫荽往浓香肥腻的汤里一浸，舀一勺羊油熟的辣椒块，往碗里一搅，拿烧饼一块儿一块儿撕了，就着羊肉汤吃喝了。</p><a id="more"></a><h4 id="【奎楼街的鱼汤】"><a href="#【奎楼街的鱼汤】" class="headerlink" title="【奎楼街的鱼汤】"></a>【奎楼街的鱼汤】</h4><p>鱼是沙河里的那种细极细极的长条鱼，用菜花油炸了，焦黄里嫩，汤便是那沙河泉水和着各种作料熬煮的，盛汤的更是本地神垕产的青瓷细花碗。都说只有用那种碗盛，浸出的鱼汤才别样的美嫩。微风起时，独木招牌上的小红灯笼就一晃一晃的，模糊的柳影便在乌亮的方桌上渐渐移动。</p><h4 id="【茭白烩肥鸭】"><a href="#【茭白烩肥鸭】" class="headerlink" title="【茭白烩肥鸭】"></a>【茭白烩肥鸭】</h4><p>若是酷暑，又很口馋，便做一道茭白烩肥鸭来吃。茭白消暑气，去烦热；鸭子滋补，振食欲。</p><p>先将鸭子清水洗净，取一二盅白酒倾入冷水，搅匀，将鸭子放入水中冷镇。半小时左右，将鸭子取出，控去酒水，利刃旋片。茭白洗净，也切成圆片。坐炒锅，打火，倾坚果油若干。油热时，爆炒鸭肉，候变色，着三两盅白酒淋入，翻炒一下，盖锅焖煮。待鸭肉酒香溢出，揭盖将茭白片倾入，撒盐若干，翻炒三四下，此菜即成。</p><h4 id="【吃野菜】"><a href="#【吃野菜】" class="headerlink" title="【吃野菜】"></a>【吃野菜】</h4><p>寨外两个沟，沟里有星星点点杨树，若遇潮湿落雨天，这一处那一片勃长出许多“地曲莲”。“地曲莲”是乡间的一种俗称。一小瓣一小瓣褐叶子，薄薄的，采回来，拿井水淘净了，类似于县城集市上卖的黑木耳，但总体要比木耳小很多。油锅坐上，先磕几枚鸡蛋进去炒。待鸡蛋快熟了，将“地曲莲”搓进菜锅内，与鸡蛋翻炒几下，一盘香喷喷的菜便完成。</p><h4 id="【火烧夹肉】"><a href="#【火烧夹肉】" class="headerlink" title="【火烧夹肉】"></a>【火烧夹肉】</h4><p>火烧夹牛肉要好吃，火烧必炕的外焦里嫩，牛肉必煮的五香筋烂，二者缺一不可。火烧炕好了，案板上一扔，“啪”，圆圆整整，有弹性。一手压了一手便拿刀去片，“噗”——散出一股热气，里头的瓤如蜂窝，然后去切牛肉。牛肉必是五香的好吃，有嚼头，还要烂熟，切成溜薄的片，往火烧里一夹，满满一兜。</p><h4 id="【鲫鱼包韭菜】"><a href="#【鲫鱼包韭菜】" class="headerlink" title="【鲫鱼包韭菜】"></a>【鲫鱼包韭菜】</h4><p>若要讲究，做好鲫鱼包韭菜，性情不得急躁。</p><p>必提前半晌就要购回鲜活鲫鱼一二条来，逗进泉水或井水瓷缸里，为的是将鲫鱼内里的脏水排净。然后换水，依然还得用泉水或井水，倾入少许酒，鱼养进去。酒水养鱼，目的有二：一是为了好杀，二是酒浸鱼内，鱼味异常鲜美。这边就要准备春韭去。最好取一些肥肥的韭菜，洗净，手掐成长短齐整的段来。切忌不可刀切，铁刀夺味。鱼饮酒后，便倦了，拿竹刀杀之。然后剔鳞、剖肚，一边淘洗干净，大料、姜丝、酱油腌好，韭菜成段填进鱼肚，撒少量盐，文火蒸吃，味道清雅不俗；或者裹好、系紧，炭火烤吃。最妙处，是郊外野柴烤吃，美味自不必多言。</p><h4 id="【吃黏转儿】"><a href="#【吃黏转儿】" class="headerlink" title="【吃黏转儿】"></a>【吃黏转儿】</h4><p>小水渠沟里有积水，一汪一汪倒映着一小片一小片蓝天，天上一絮朵一絮朵镶了金边的白云，动动，就如优游水中的小金鱼。而沟垅上的草和野花，皆睡醒了，眨动晶晶亮的小眼睛看我们。我和妹妹尽量斜了身子，沿水渠走，怕碰疼他们。我们已经碰疼她们了，走过去，裤脚都被小小的泪水打湿。</p><h4 id="【香椿翅】"><a href="#【香椿翅】" class="headerlink" title="【香椿翅】"></a>【香椿翅】</h4><p>起风了，小小的风，像无数细碎风铃，满树满枝挂满了，脆生生乱响。我爬进风声中间，香椿叶子晃动我、拍打我，宛如肥嫩嫩的小巴掌。太阳光一耀，穿亮了青涩涩的香气，在灿灿亮光与风里面，我一叶一叶摘着，像摘下一片片小月亮、小银盘，放进筐里去。满筐都是银灿灿的。然而，一会儿，他们纷纷转变颜色——浅绿了，又似满筐的翠鸟。我一胳膊挎着满筐小鸟，一胳膊搂着树干，滑下来。地上的风与香椿树影，宛若明灭的花朵。</p>]]></content>
    
    <summary type="html">
    
      &lt;h4 id=&quot;【姜家羊肉汤】&quot;&gt;&lt;a href=&quot;#【姜家羊肉汤】&quot; class=&quot;headerlink&quot; title=&quot;【姜家羊肉汤】&quot;&gt;&lt;/a&gt;【姜家羊肉汤】&lt;/h4&gt;&lt;p&gt;羊肉汤是满满的一粗瓷大碗，上面撒满了芫荽、葱花和几丝细细的姜丝。用筷子翻了，大片的熟羊肉片，溜薄，挑开往里一淹，将芫荽往浓香肥腻的汤里一浸，舀一勺羊油熟的辣椒块，往碗里一搅，拿烧饼一块儿一块儿撕了，就着羊肉汤吃喝了。&lt;/p&gt;
    
    </summary>
    
      <category term="生活" scheme="https://aartemida.github.io/categories/%E7%94%9F%E6%B4%BB/"/>
    
    
  </entry>
  
  <entry>
    <title>前端高频面试题收录</title>
    <link href="https://aartemida.github.io/2022/08/16/%E5%89%8D%E7%AB%AF%E9%AB%98%E9%A2%91%E9%9D%A2%E8%AF%95%E9%A2%98%E6%94%B6%E5%BD%95/"/>
    <id>https://aartemida.github.io/2022/08/16/前端高频面试题收录/</id>
    <published>2022-08-16T11:06:19.000Z</published>
    <updated>2022-09-26T07:04:58.917Z</updated>
    
    <content type="html"><![CDATA[<h2 id="JavaScript"><a href="#JavaScript" class="headerlink" title="JavaScript"></a>JavaScript</h2><h4 id="1-深拷贝和浅拷贝-递归调用死循环问题如何解决-star"><a href="#1-深拷贝和浅拷贝-递归调用死循环问题如何解决-star" class="headerlink" title="1. 深拷贝和浅拷贝(递归调用死循环问题如何解决) :star:"></a>1. 深拷贝和浅拷贝(递归调用死循环问题如何解决) :star:</h4><blockquote><p>基本类型–名值都存储在栈内存中。<br>引用数据类型–存储的是地址（指针），数据存储在堆上（栈内存会提供一个引用的地址指向堆内存中的值）</p></blockquote><p>基本类型赋值时，赋的是值，所以不存在深浅拷贝问题。</p><p>引用类型赋值时，复制的是原本变量的引用地址（指针），浅拷贝就是新值和旧值变量指向同一个内存地址，当地址中的值改变时，他们都会同时变化。</p><p>深拷贝就是实现新旧值互不影响，拷贝的过程中，独立地开辟了一个空间，这个对象指向这个地址，与原来的对象互不干扰。深拷贝也被称为值拷贝。</p><a id="more"></a><p>对象实现深拷贝有<code>JSON.parse(JSON.stringify())</code>，<code>Object.assign()</code>。数组实现深拷贝有<code>slice</code>，<code>concat</code>。</p><blockquote><p>用扩展运算符对数组或者对象进行拷贝时，只能扩展和深拷贝第一层的值，对于第二层及其以后的值，扩展运算符将不能对其进行打散扩展，也不能对其进行深拷贝，即拷贝后和拷贝前第二层中的对象或者数组仍然引用的是同一个地址，其中一方改变，另一方也跟着改变。</p></blockquote><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 实现深拷贝</span></span><br><span class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">deepClone</span>(<span class="params">obj, hash = new WeakMap(</span>)) </span>&#123;</span><br><span class="line">  <span class="comment">// 处理null或者undefined</span></span><br><span class="line">  <span class="keyword">if</span> (obj === <span class="literal">null</span>) <span class="keyword">return</span> obj;</span><br><span class="line">  <span class="comment">// 处理日期类型</span></span><br><span class="line">  <span class="keyword">if</span> (obj <span class="keyword">instanceof</span> <span class="built_in">Date</span>) <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Date</span>(obj);</span><br><span class="line">  <span class="comment">// 处理正则类型</span></span><br><span class="line">  <span class="keyword">if</span> (obj <span class="keyword">instanceof</span> <span class="built_in">RegExp</span>) <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">RegExp</span>(obj);</span><br><span class="line">  <span class="comment">// 普通值或函数不需要深拷贝</span></span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">typeof</span> obj !== <span class="string">"object"</span>) <span class="keyword">return</span> obj;</span><br><span class="line">  <span class="comment">// 对象进行深拷贝(解决循环引用)</span></span><br><span class="line">  <span class="keyword">if</span> (hash.get(obj)) <span class="keyword">return</span> hash.get(obj);</span><br><span class="line">  <span class="keyword">let</span> cloneObj = <span class="keyword">new</span> obj.constructor();</span><br><span class="line">  <span class="comment">// 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身</span></span><br><span class="line">  hash.set(obj, cloneObj);</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> key <span class="keyword">in</span> obj) &#123;</span><br><span class="line">    <span class="comment">// 检测属性是否为对象的自有属性</span></span><br><span class="line">    <span class="keyword">if</span> (obj.hasOwnProperty(key)) &#123;</span><br><span class="line">      <span class="comment">// 实现一个递归拷贝</span></span><br><span class="line">      cloneObj[key] = deepClone(obj[key], hash);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> cloneObj;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><a href="https://www.cnblogs.com/echolun/p/16157161.html" target="_blank" rel="noopener">JS 从零手写一个深拷贝</a></p><h5 id="JSON-parse-JSON-stringify-obj-实现深拷贝存在的缺陷"><a href="#JSON-parse-JSON-stringify-obj-实现深拷贝存在的缺陷" class="headerlink" title="JSON.parse(JSON.stringify(obj)) 实现深拷贝存在的缺陷"></a>JSON.parse(JSON.stringify(obj)) 实现深拷贝存在的缺陷</h5><ul><li>如果obj里面存在时间对象,JSON.parse(JSON.stringify(obj))之后，时间对象变成了字符串。</li><li>如果obj里有RegExp、Error对象，则序列化的结果将只得到空对象。</li><li>如果obj里有函数，undefined，则序列化的结果会把函数， undefined丢失。</li><li>如果obj里有NaN、Infinity和-Infinity，则序列化的结果会变成null。</li><li>JSON.stringify()只能序列化对象的可枚举的自有属性。如果obj中的对象是有构造函数生成的，则使用JSON.parse(JSON.stringify(obj))深拷贝后，会丢弃对象的constructor。</li><li>如果对象中存在循环引用的情况也无法正确实现深拷贝。</li></ul><h5 id="JSON-stringify的参数"><a href="#JSON-stringify的参数" class="headerlink" title="JSON.stringify的参数"></a>JSON.stringify的参数</h5><p>JSON.stringify(value[, replacer [, space]])</p><ul><li>replacer：处理序列化中的每个属性的函数方法</li><li>space：指定缩进用的字符串，用于美化输出</li></ul><h5 id="手写一个JSON-stringify"><a href="#手写一个JSON-stringify" class="headerlink" title="手写一个JSON.stringify"></a>手写一个JSON.stringify</h5><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">myStringify</span>(<span class="params">obj</span>)</span>&#123;</span><br><span class="line">  <span class="comment">//忽略undefind和function 先置为undefined 组合的时候忽略</span></span><br><span class="line">  <span class="keyword">if</span>(obj === <span class="literal">undefined</span> || <span class="keyword">typeof</span>(obj) === <span class="string">'function'</span>)&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">undefined</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">//null或者NaN Infinity置为null</span></span><br><span class="line">  <span class="keyword">if</span>(obj === <span class="literal">null</span>)&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 时间对象转为字符串</span></span><br><span class="line">  <span class="keyword">if</span>(obj <span class="keyword">instanceof</span> <span class="built_in">Date</span>)&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">`"<span class="subst">$&#123;obj.toJSON()&#125;</span>"`</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">//regExp输出&#123;&#125;</span></span><br><span class="line">  <span class="keyword">if</span>(obj <span class="keyword">instanceof</span> <span class="built_in">RegExp</span>)&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">`&#123;&#125;`</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">//字符串 置为“spring” 布尔 数字原样输出</span></span><br><span class="line">  <span class="keyword">if</span>(<span class="keyword">typeof</span>(obj)!== <span class="string">'object'</span>)&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">typeof</span>(obj) === <span class="string">'string'</span> ? <span class="string">`"<span class="subst">$&#123;obj&#125;</span>"`</span>:obj</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">//数组</span></span><br><span class="line">  <span class="keyword">if</span>(<span class="built_in">Array</span>.isArray(obj))&#123;</span><br><span class="line">    <span class="keyword">let</span> arrStr = obj.map(<span class="function"><span class="params">item</span> =&gt;</span> <span class="string">`<span class="subst">$&#123;myStringify(item)&#125;</span>`</span> )</span><br><span class="line">   <span class="keyword">return</span> <span class="string">`[<span class="subst">$&#123;arrStr.join(<span class="string">','</span>)&#125;</span>]`</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">//采用Object.getOwnPropertyNames直接过滤掉symbol</span></span><br><span class="line">  <span class="keyword">let</span> keyNames = <span class="built_in">Object</span>.getOwnPropertyNames(obj);</span><br><span class="line">  <span class="keyword">const</span> arrObj = keyNames.map(<span class="function">(<span class="params">item</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">`<span class="subst">$&#123;myStringify(obj[item]) !== <span class="literal">undefined</span> &amp;&amp; <span class="string">`"<span class="subst">$&#123;item&#125;</span>":<span class="subst">$&#123;myStringify(obj[item])&#125;</span>`</span>&#125;</span>`</span></span><br><span class="line">  &#125;)</span><br><span class="line">  <span class="keyword">return</span> <span class="string">`&#123;<span class="subst">$&#123;arrObj.join(<span class="string">','</span>)&#125;</span>&#125;`</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="2-防抖和节流-star"><a href="#2-防抖和节流-star" class="headerlink" title="2. 防抖和节流 :star:"></a>2. 防抖和节流 :star:</h4><p><code>防抖</code>（debounce）是当事件被触发后，延迟n秒后再执行回调，如果在这n秒内事件又被触发，则重新计时<code>（多次触发，只执行最后一次）</code>。</p><p>作用： 高频率触发的事件，在指定的单位时间内，只响应最后一次，如果在指定的时间内再次触发，则重新计算时间。</p><p>应用场景：按钮多次点击，resize 多次触发、搜索框输入查询等</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 防抖函数</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">debounce</span> (<span class="params">f, wait</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> timer;  <span class="comment">// 创建一个标记用来存放定时器的返回值</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> <span class="function">(<span class="params">...args</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// 每当用户输入的时候把前一个 setTimeout clear 掉</span></span><br><span class="line">    clearTimeout(timer)</span><br><span class="line">    <span class="comment">// 然后又创建一个新的 setTimeout, 这样就能保证interval 间隔内如果时间持续触发，就不会执行 fn 函数</span></span><br><span class="line">    timer = setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">      f(...args)</span><br><span class="line">    &#125;, wait)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>节流</code>（throttle），控制事件发生的频率，如控制为1s发生一次，甚至1分钟发生一次。类似<code>（规定时间内，只触发一次）</code>。</p><p>作用： 高频率触发的事件,在指定的单位时间内，只响应第一次。</p><p>应用场景：监听滚动事件，播放事件等</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 节流函数</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">throttle</span> (<span class="params">f, wait</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> timer</span><br><span class="line">  <span class="keyword">return</span> <span class="function">(<span class="params">...args</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (timer) &#123; <span class="keyword">return</span> &#125;</span><br><span class="line">    timer = setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">      f(...args)</span><br><span class="line">      timer = <span class="literal">null</span></span><br><span class="line">    &#125;, wait)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="3-async-x2F-await-和-Promise、JS事件循环机制-star"><a href="#3-async-x2F-await-和-Promise、JS事件循环机制-star" class="headerlink" title="3. async&#x2F;await 和 Promise、JS事件循环机制 :star:"></a>3. async&#x2F;await 和 Promise、JS事件循环机制 :star:</h4><p>async&#x2F;await 是参照 Generator 封装的一套异步处理方案，可以理解为 Generator 的语法糖。</p><p>async&#x2F;await原理：将 Generator 函数和自动执行器，包装在一个函数里，不用手动调用Generator.next。一般返回一个promise对象。</p><p><a href="/2022/04/19/%E5%86%99%E7%BB%99%E8%87%AA%E5%B7%B1%E7%9A%84%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E6%8C%87%E5%8D%97/">写给自己的前端面试指南</a></p><h4 id="4-for-in-和-for-of"><a href="#4-for-in-和-for-of" class="headerlink" title="4. for in 和 for of"></a>4. for in 和 for of</h4><ul><li><p><code>for in</code>：主要用来遍历对象（for keys in obj）</p><p>遍历自身和继承的可枚举属性（延续原型链遍历出对象的原型属性）</p><p>有什么问题：要使用 hasOwnProperty 判断，只处理自身的，不处理继承的</p></li><li><p><code>for of</code>： ES6 新增的，遍历所有数据结构的统一的方法</p><p>只要部署了 Symbol.iterator 属性，就被视为具有 iterator 接口，就可以用 for…of 循环遍历它的成员</p><p>包括数组、Set 和 Map 结构、某些类似数组的对象（比如 arguments 对象、DOM NodeList 对象）、Generator 对象，以及字符串</p></li></ul><h4 id="5-函数式编程：柯里化-curry-；组合函数-compose-；管道函数-pipe"><a href="#5-函数式编程：柯里化-curry-；组合函数-compose-；管道函数-pipe" class="headerlink" title="5. 函数式编程：柯里化(curry)；组合函数(compose)；管道函数(pipe)"></a>5. 函数式编程：柯里化(curry)；组合函数(compose)；管道函数(pipe)</h4><p>高阶函数：一个函数接收的参数是一个函数，或者调用的返回值是一个函数。</p><ul><li>函数柯里化(curry)：把接收多个参数的函数，转成接收单一参数的函数，它返回一个新的函数, 这个新函数去处理剩余的参数；</li><li>函数组合（pipe&amp;compose）：函数组合可以让我们把多个函数组合成一个新的函数，然后在执行的过程中，我们可以把参数输入给第一个函数，当它执行完成以后会返回一个中间结果。并且把这个中间结果交接下一个函数去处理，当最后一个函数执行完毕之后，我们会把最终结果返回</li></ul><h4 id="6-slice和splice的区别"><a href="#6-slice和splice的区别" class="headerlink" title="6. slice和splice的区别"></a>6. slice和splice的区别</h4><p>两者都是数组删除的方法</p><ul><li>splice改变原数组，slice不改变原数组;</li><li>slice会返回一个新的数组，可用于截取数组;</li><li>splice返回被删除项组成的的新数组，除了可以删除之外，还可以替换，添加数组;</li><li>传参：splice(index, number, …newItems)，slice(start, end)</li></ul><h4 id="7-substr和substring的区别"><a href="#7-substr和substring的区别" class="headerlink" title="7. substr和substring的区别"></a>7. substr和substring的区别</h4><p>两者的作用都是截取字符串的。</p><ul><li>substr(start, length)是从起始索引开始提取指定长度的字符串</li><li>substring(start, stop)是提取字符串中两个指定索引之间的字符</li></ul><h4 id="8-let-const-var区别-star"><a href="#8-let-const-var区别-star" class="headerlink" title="8. let const var区别 :star:"></a>8. let const var区别 :star:</h4><p>和var的区别：</p><ul><li>重复声明：let和const不允许在相同作用域内，重复声明同一个变量</li><li>变量提升：let不像var那样，会发生“变量提升”现象。（var会提升变量的声明到作用域的顶部）</li><li>暂时性死区：只要作用域内存在let、const，它们所声明的变量或常量就会自动“绑定”这个区域，不再受外部作用域的影响</li><li>块级作用域：var没有块级作用域(var 声明的变量的作用域只能是全局或者整个函数块的,作用域是它当前的执行上下文)，let和const有块级作用域</li><li>全局作用域中：let和const声明的变量内存空间不挂在于window上（let、const声明的全局变量在window对象上看不到，在script中形成了一个块级作用域，这样在全局就可以访问到），而var声明的变量是挂载到window上</li></ul><p>const声明常量，声明之后常量的值不会改变。</p><h4 id="9-箭头函数与普通函数的区别"><a href="#9-箭头函数与普通函数的区别" class="headerlink" title="9. 箭头函数与普通函数的区别"></a>9. 箭头函数与普通函数的区别</h4><ul><li>箭头函数是匿名函数，而普通函数可以匿名也可以不匿名</li><li>本身没有 this，内部this是上层作用域决定</li><li>没有原型，没有自己的<code>arguments</code></li><li>不能作为构造函数</li></ul><h4 id="10-Map和WeakMap的区别-Set"><a href="#10-Map和WeakMap的区别-Set" class="headerlink" title="10. Map和WeakMap的区别, Set"></a>10. Map和WeakMap的区别, Set</h4><ul><li>Map可以接受任何类型作为key，WeakMap只接受Object作为key；Map 通过两个数组分别存放键和值，容易导致内存泄漏。</li><li>WeakMap 对键是弱引用，有垃圾回收机制，不可枚举。</li><li>Set: Set 对象允许你存储任何类型的值，无论是原始值或者是对象引用。它类似于数组，但是成员的值都是唯一的，没有重复的值。（has、add、delete、size）</li></ul><h4 id="11-requestAnimationFrame"><a href="#11-requestAnimationFrame" class="headerlink" title="11. requestAnimationFrame"></a>11. requestAnimationFrame</h4><p>requestAnimationFrame 自带函数节流功能，基本可以保证在 16.6 毫秒内只执行一次（不掉帧的情况下），并且该函数的延时效果是精确的，没有其他定时器时间不准的问题。</p><h4 id="12-class继承的优点"><a href="#12-class继承的优点" class="headerlink" title="12. class继承的优点"></a>12. class继承的优点</h4><p>简化了原型链继承的写法。</p><h4 id="13-js实现私有属性和方法"><a href="#13-js实现私有属性和方法" class="headerlink" title="13. js实现私有属性和方法"></a>13. js实现私有属性和方法</h4><p>在一个构造函数里面定义的function，只有父类可以访问的方法和属性，就是一个私有方法。</p><ul><li><p>基于编码规范约定实现方式: this._name</p></li><li><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="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params">name</span>)</span>&#123;</span><br><span class="line">  <span class="keyword">var</span> _name = name;</span><br><span class="line">  <span class="keyword">this</span>.getName = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> _name;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>使用Proxy 拦截访问私有变量_prop</p></li><li><p>Es6：通过 # 的方式来标识私有属性和方法 #prop</p></li></ul><h4 id="14-js为什么是单线程"><a href="#14-js为什么是单线程" class="headerlink" title="14. js为什么是单线程"></a>14. js为什么是单线程</h4><p>js是作为浏览器的脚本语言，主要是实现用户与浏览器的交互，以及操作dom。如果多线程同时操作dom会混乱（比如删除和监听点击事件同时被触发）。</p><p>js的异步：js主线程虽然是单线程，但是底层还是有其他线程的，比如事件执行线程等，异步任务就会放到事件队列中执行，执行完返回给主线程。</p><h4 id="16-dom事件模型"><a href="#16-dom事件模型" class="headerlink" title="16. dom事件模型"></a>16. dom事件模型</h4><p>事件冒泡和事件捕获。事件捕获 ——&gt; 目标 ——&gt; 冒泡</p><h5 id="IE和DOM事件流的区别"><a href="#IE和DOM事件流的区别" class="headerlink" title="IE和DOM事件流的区别"></a>IE和DOM事件流的区别</h5><p>事件冒泡（IE事件流）：从目标元素冒泡到body（绑定事件attachEvent）</p><p>事件捕获： 从body到目标元素</p><p>DOM事件模型： 先捕获后冒泡（绑定事件addEventListener）</p><h4 id="17-null，undefined的区别"><a href="#17-null，undefined的区别" class="headerlink" title="17. null，undefined的区别"></a>17. null，undefined的区别</h4><p>null 表示空对象，转换数值会变成0；undefined表示“无”的原始值，转换数值是NaN</p><p>null表示一个对象被定义了，但存放了空指针，转换为数值时为0。<br>undefined表示声明的变量未初始化，转换为数值时为NAN。</p><h4 id="18-栈和堆的区别？"><a href="#18-栈和堆的区别？" class="headerlink" title="18. 栈和堆的区别？"></a>18. 栈和堆的区别？</h4><p>栈（stack）：由程序自动分配释放，存放函数的<code>参数值</code>，<code>局部变量</code>等；<br>堆（heap）：一般由程序员分配释放，若程序员不释放，程序结束时可能由操作系统释放。</p><h4 id="19-JS延迟加载的方式"><a href="#19-JS延迟加载的方式" class="headerlink" title="19. JS延迟加载的方式"></a>19. JS延迟加载的方式</h4><ul><li>defer：延迟脚本。立即下载(与html解析并行)，但延迟执行（等整个页面都解析完再运行），按照脚本出现的先后顺序执行;</li><li>async: 异步脚本。下载(与html解析并行)完立即执行(可能会阻塞html解析)，但不保证按照脚本出现的先后顺序执行;</li><li>动态创建dom方式、</li><li>使用jquery的getScript方法、</li><li>使用setTimeout延迟方法、让js最后加载</li></ul><p>PS：同步和异步：同步强调的是顺序性，按顺序执行</p><h4 id="20-Symbol和bigint"><a href="#20-Symbol和bigint" class="headerlink" title="20. Symbol和bigint"></a>20. Symbol和bigint</h4><p>symbol作为对象的属性是不能用for in 和Object.keys()来枚举的。JSON.stringify()将对象转换成JSON字符串的时候，Symbol属性也会被排除在输出内容之外。<br>使用场景：</p><ul><li>使用Symbol来作为对象属性名，利用该特性，把一些不需要对外操作和访问的属性使用Symbol来定义</li><li>使用Symbol定义类的私有属性和方法</li><li>由于在 Number 与 BigInt 之间进行转换会损失精度，因而建议仅在值可能大于253 时使用 BigInt 类型，并且不在两种类型之间进行相互转换。</li></ul><h4 id="21-prefetch和preload"><a href="#21-prefetch和preload" class="headerlink" title="21. prefetch和preload"></a>21. prefetch和preload</h4><p>preload与prefetch同属于浏览器的Resource-Hints，用于辅助浏览器进行资源优化。prefetch通常翻译为预提取，preload则翻译为预加载。</p><ul><li>preload和prefetch的本质都是预加载，即先加载、后执行，加载与执行解耦。</li><li>preload和prefetch不会阻塞页面的onload。</li><li>preload用来声明当前页面的关键资源，强制浏览器尽快加载；而prefetch用来声明将来可能用到的资源，在浏览器空闲时进行加载。</li><li>关于preload和prefetch资源的缓存，它被存储在HTTP缓存（也就是disk cache）中，可以被现在或将来的任务使用；如果资源不能被缓存在HTTP缓存中，作为代替，它被放在内存缓存中直到被使用。</li></ul><h4 id="22-BOM（浏览器对象模型）和DOM（文档对象模型）"><a href="#22-BOM（浏览器对象模型）和DOM（文档对象模型）" class="headerlink" title="22. BOM（浏览器对象模型）和DOM（文档对象模型）"></a>22. BOM（浏览器对象模型）和DOM（文档对象模型）</h4><h4 id="23-Ajax、Axios、Fetch有啥区别？"><a href="#23-Ajax、Axios、Fetch有啥区别？" class="headerlink" title="23. Ajax、Axios、Fetch有啥区别？"></a>23. Ajax、Axios、Fetch有啥区别？</h4><p>Ajax：是对XMLHttpRequest(XHR)的封装<br>Axios：是基于Promise对XHR对象的封装<br>Fetch：是window的一个方法，基于Promise，与XHR无关，不兼容IE</p><h4 id="24-load、-document-ready、DOMContentLoaded的区别？"><a href="#24-load、-document-ready、DOMContentLoaded的区别？" class="headerlink" title="24. load、$(document).ready、DOMContentLoaded的区别？"></a>24. load、$(document).ready、DOMContentLoaded的区别？</h4><p><code>$(document).ready</code>、DOMContentLoaded：DOM树构建完毕，但还没有请求静态资源<br>load：静态资源请求完毕, 页面加载完成后</p><h4 id="25-includes-比-indexOf好在哪"><a href="#25-includes-比-indexOf好在哪" class="headerlink" title="25. includes 比 indexOf好在哪"></a>25. includes 比 indexOf好在哪</h4><p>includes可以检测NaN，indexOf不能检测NaN，includes内部使用了Number.isNaN对NaN进行了匹配</p><h2 id="Vue"><a href="#Vue" class="headerlink" title="Vue"></a>Vue</h2><h4 id="1-响应式原理-star"><a href="#1-响应式原理-star" class="headerlink" title="1. 响应式原理 :star:"></a>1. 响应式原理 :star:</h4><ul><li>Vue2：通过<code>Object.defineProperty()</code>(可以控制一个对象属性的一些特有操作)来劫持各个属性的setter，getter，在数据变动时发布消息给订阅者，触发相应的监听回调。</li><li>Vue3: 基于 <code>Proxy</code> 实现的，解决了 Vue2 响应式的缺陷。通过 new Proxy 代理了 obj 对象，然后通过 get、set 方法代理了对象的读取、修改和删除操作，从而实现了响应式的功能。</li></ul><p><a href="/2019/04/23/%E5%AF%B9Vue%E5%8E%9F%E7%90%86%E7%9A%84%E4%B8%80%E4%BA%9B%E7%90%86%E8%A7%A3/">Vue2和Vue3原理区别</a></p><h4 id="2-虚拟-dom-原理-和-diff-算法-star"><a href="#2-虚拟-dom-原理-和-diff-算法-star" class="headerlink" title="2. 虚拟 dom 原理 和 diff 算法 :star:"></a>2. 虚拟 dom 原理 和 diff 算法 :star:</h4><ul><li>虚拟DOM：它通过js对象模拟DOM中的节点，是对真实DOM的抽象，状态变更时，记录新树和旧树的差异，最后把差异更新到真正的dom中。（通过特定的render方法将其渲染成真实的DOM节点）</li><li>diff算法：一种通过同层的树节点进行比较的高效算法。通过JS层面的计算，返回一个<code>patch</code>对象，即补丁对象，在通过特定的操作解析patch对象，完成页面的重新渲染。其有两个特点：<ol><li>只会在同层级节点进行比较，不会跨级。</li><li>diff比较过程是从两侧向中间进行比较。</li></ol></li></ul><p>patch作用：更新 DOM 树,将两次渲染之间的 DOM 操作更新到树中。</p><p>虚拟dom原理：</p><ul><li>用 JavaScript 对象结构表示 DOM 树的结构；然后用这个树构建一个真正的 DOM 树，插到文档当中</li><li>当状态变更的时候，重新构造一棵新的对象树。然后用新的树和旧的树进行比较，记录两棵树差异</li><li>把2所记录的差异应用到步骤1所构建的真正的DOM树上，视图就更新了</li></ul><p>虚拟dom缺点：<br>⾸次渲染⼤量 DOM 时，由于多了⼀层虚拟 DOM 的计算，会⽐ innerHTML 插⼊慢</p><p><a href="/2019/04/23/%E5%AF%B9Vue%E5%8E%9F%E7%90%86%E7%9A%84%E4%B8%80%E4%BA%9B%E7%90%86%E8%A7%A3/">Vue2和Vue3原理区别</a></p><h4 id="3-vue-组件传值-star"><a href="#3-vue-组件传值-star" class="headerlink" title="3. vue 组件传值 :star:"></a>3. vue 组件传值 :star:</h4><p>props&#x2F;$emit、ref、$parent&#x2F;$children、$attrs&#x2F;$listeners、provide&#x2F;inject、eventBus、vuex</p><ul><li>父子：props&#x2F;$emit、ref、$attrs、$parent</li><li>兄弟：$parent、$root、eventBus、vuex</li><li>跨级：eventBus、vuex、provide&#x2F;inject</li></ul><p><a href="/2021/05/27/%E5%87%A0%E5%A4%A7%E6%A1%86%E6%9E%B6%E7%9F%A5%E8%AF%86%E7%82%B9%E6%A2%B3%E7%90%86/">几大框架知识点梳理</a></p><h4 id="4-vue生命周期钩子函数为什么不能使用箭头函数"><a href="#4-vue生命周期钩子函数为什么不能使用箭头函数" class="headerlink" title="4. vue生命周期钩子函数为什么不能使用箭头函数"></a>4. vue生命周期钩子函数为什么不能使用箭头函数</h4><p>所有生命周期钩子的 this 上下文将自动绑定至实例中，因此你可以访问 data、computed 和 methods。这意味着你不应该使用箭头函数来定义一个生命周期方法 (例如 created: () &#x3D;&gt; this.fetchTodos())。因为箭头函数绑定了父系上下文，所以 this 不会指向预期的组件实例，并且this.fetchTodos 将会是 undefined。</p><p>箭头函数自己没有定义 this 上下文，而是绑定到其父函数的上下文中。当你在 Vue 程序中使用箭头函数时，this 关键字并不会绑定到 Vue 实例，因此会引发错误。</p><h4 id="5-nextTick-实现原理-star"><a href="#5-nextTick-实现原理-star" class="headerlink" title="5. nextTick 实现原理 :star:"></a>5. nextTick 实现原理 :star:</h4><p>在下次DOM更新循环结束之后执行的延迟回调。应用于需要在视图更新之后，基于新的视图进行操作。</p><p>Vue 在更新 DOM 时是异步执行的。Vue的异步更新策略：如果数据变化，视图不会立即更新，而是把组件更新函数保存在一个队列中，在同一事件循环中发生的所有数据变更会异步的批量更新。</p><p>实现原理：nextTick方法主要是使用了事件循环机制，定义了一个异步方法，多次调用nextTick会将方法存入队列中，通过这个异步方法清空队列：</p><ul><li>Vue 在内部对异步队列尝试使用原生的 <code>Promise.then</code>、<code>MutationObserver</code> 和 <code>setImmediate</code>，找到存在的就调用他childrenRef；如果执行环境不支持，则会采用 <code>setTimeout(fn, 0)</code> 代替。(优先微任务)</li><li>先把传入的回调函数 cb 推入 回调队列 callbacks 数组，同时在接收第一个回调函数时，执行能力检测中对应的异步方法 timerFunc，在该方法中会使用当前浏览器支持的异步方法去异步执行 flushCallbacks，而 flushCallbacks 函数中会对 callbacks 进行遍历，并执行每一个回调函数。</li></ul><p><a href="https://juejin.cn/post/6977564213749219341" target="_blank" rel="noopener">nextTick的实现原理</a></p><h4 id="6-如何衡量组件封装的好与不好"><a href="#6-如何衡量组件封装的好与不好" class="headerlink" title="6.如何衡量组件封装的好与不好"></a>6.如何衡量组件封装的好与不好</h4><p>扩展性；如何设置扩展性比较好（预留插槽）</p><p>对于组件的设计，一般是一个组件能够完整的完成一件事情，或者一个完整的逻辑；另外组件的设计一般是讲究高内聚，低耦合等原则进行设计。一个组件的设计应该具备以下的内容：</p><ul><li>功能独立和唯一</li><li>可复⽤的模块，完成既定功能</li><li>有明确的接⼝规定</li><li>有上下⽂依赖、外部依赖资源的定义</li><li>可以独⽴发布</li></ul><h4 id="7-vue-style-scoped-属性作用是啥，如何是样式仅在当前模块生效"><a href="#7-vue-style-scoped-属性作用是啥，如何是样式仅在当前模块生效" class="headerlink" title="7. vue style scoped 属性作用是啥，如何是样式仅在当前模块生效"></a>7. vue style scoped 属性作用是啥，如何是样式仅在当前模块生效</h4><p>作用：实现组件的私有化，不对全局造成样式污染，表示当前 style 属性只属于当前模块<br>原理：打包之后，编译成特定样式，data-v-[hash]，即 CSS 带属性选择器</p><h4 id="8-Vue3-和-React的区别"><a href="#8-Vue3-和-React的区别" class="headerlink" title="8. Vue3 和 React的区别"></a>8. Vue3 和 React的区别</h4><p>Vue的优势：</p><ul><li>模板和渲染函数的弹性选择</li><li>简单的语法及项目创建</li><li>更快的渲染速度和更小的体积</li></ul><p>React的优势：</p><ul><li>适用于大型应用和更好的可测试性</li><li>同时适用于Web端和原生App</li></ul><p><a href="https://www.h5w3.com/238721.html" target="_blank" rel="noopener">Vue与React的区别和优势对比</a></p><h4 id="9-vue-key-的作用"><a href="#9-vue-key-的作用" class="headerlink" title="9. vue key 的作用"></a>9. vue key 的作用</h4><p>高效的更新虚拟 dom。</p><p>如果循环dom时没有key，在diff算法比较时（key相同且tag相同认为是相同元素）就可能判断出错，可能永远认为是相同节点，执行更新操作，导致DOM操作频繁，影响性能。</p><h4 id="10-vue-router-实现原理-star"><a href="#10-vue-router-实现原理-star" class="headerlink" title="10. vue-router 实现原理 :star:"></a>10. vue-router 实现原理 :star:</h4><ul><li>hash模式（浏览器的hashchange事件）</li><li>history模式（HTML5 History API）</li></ul><p><a href="/2021/08/06/%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84Vue%E8%B7%AF%E7%94%B1%E6%8C%87%E5%8D%97/">Vue路由</a></p><h4 id="11-v-model原理"><a href="#11-v-model原理" class="headerlink" title="11. v-model原理"></a>11. v-model原理</h4><p>v-model一般配合input框使用，实现双向数据绑定的效果，它是v-bind和v-on的语法糖，原理是通过v-bind将数据绑定给input框，再通过v-on:input，在input中的值改变时，通过$event可以获取到事件源对象 再通过target.value获取到input中更新后的值 将这个值再赋值给绑定的数据即可</p><h4 id="12-父子组件的周期执行顺序-star"><a href="#12-父子组件的周期执行顺序-star" class="headerlink" title="12. 父子组件的周期执行顺序 :star:"></a>12. 父子组件的周期执行顺序 :star:</h4><ol><li>初始化阶段时，先执行父组件的beforeCreate、created、beforeMount三个钩子函数，然后执行子组件的beforeCreate、created、beforeMount、mounted四个钩子函数，最后执行父组件的mounted钩子函数</li><li>更新阶段，先执行父组件的beforeUpdate，然后执行子组件的beforeUpdate，updated，最后执行父组件的updated</li><li>销毁阶段，先执行父组件的beforeDestroy，然后执行子组件的eforeDestroy，destroyed，最后执行父组件的destroyed</li></ol><h4 id="13-vite-和-webpack-区别"><a href="#13-vite-和-webpack-区别" class="headerlink" title="13. vite 和 webpack 区别"></a>13. vite 和 webpack 区别</h4><ol><li>工具本身定位不同：webpack是底层的东西，vite则是更上层的工具。webpack是配置化，灵活度极高的工具，vite是开箱即用，使用更简单的工具</li><li>原理不同：webpack是<code>bundle</code>，自己实现了一套模块导入导出机制。vite是利用浏览器的esm能力，是<code>unbundle</code>。</li></ol><p>优缺点:<br>vite开箱即用，基于浏览器esm，使得热更新更加优秀。(vite主要使用插件进行扩展功能)</p><p>webpack更加灵活，api以及插件生态更加丰富，高可定制，兼容更多浏览器，例如ie11。</p><h5 id="vite介绍"><a href="#vite介绍" class="headerlink" title="vite介绍"></a>vite介绍</h5><p>vite: 基于esbuild与Rollup，依靠浏览器自身ESM编译功能，实现的构建工具</p><p>vite的特点：</p><ul><li>冷启动快，不需要等待打包操作；</li><li>热更新快（采用 unbundle 机制，所以 dev server在监听到文件发生变化以后，只需要通过ws通知浏览器去重新加载变化的文件，剩下的工作就交给浏览器做）；</li><li>按需编译，不再等待整个应用编译完成（Vite 只需要在浏览器请求源码时进行转换并按需提供源码）</li><li>缺点：首屏和懒加载性能的下降</li></ul><p>vite常用配置：plugins，server, resolve&#x2F;alias, build(outDir, lib, rollupOptions)</p><h4 id="vite的打包原理"><a href="#vite的打包原理" class="headerlink" title="vite的打包原理"></a>vite的打包原理</h4><p>Vite 原理是利用现代浏览器支持原生的 ESM 规范，配合 server 做拦截，把代码编译成浏览器支持的。通过劫持浏览器的请求，并在后端进行相应的处理将项目中使用的文件通过简单的分解与整合，然后再返回给浏览器。</p><p>1.将应用中的模块区分为 依赖 和 源码 两类，改进了开发服务器启动时间。</p><ul><li>依赖 ：一般是第三方依赖，比如组件库，这一部分使用 esbuild 来进行 <code>依赖预构建</code>, esbuild 使用的是 Go 进行编写，比 JavaScript 编写的打包器预构建依赖快很多</li><li>源码 ：浏览器不能直接执行的非js代码(.jsx、.css、.vue等)，vite只在浏览器请求相关源码的时候进行转换，以提供ESM源码。</li></ul><p>2.拦截浏览器对模块的请求并返回处理后的结果。（浏览器执行ESM的import时，会向dev server发起该模块的ajax请求，服务器对源码做简单处理后返回给浏览器。）</p><p>过程：<br>依赖预构建 -&gt; 启动dev server -&gt; 浏览器解析模块并根据顺序请求 -&gt; 拦截浏览器请求并进行转换 -&gt; 源码返回给浏览器</p><p>vite首屏加载优化：在启动时对某些资源进行预打包，尽量避免后续的动态打包（vite-plugin-optimize-persist）</p><p><a href="http://www.icodebang.com/article/269243.html" target="_blank" rel="noopener">vite的打包原理</a></p><h4 id="14-vite为什么启动那么快（快速的冷启动和快速的热更新）"><a href="#14-vite为什么启动那么快（快速的冷启动和快速的热更新）" class="headerlink" title="14. vite为什么启动那么快（快速的冷启动和快速的热更新）"></a>14. vite为什么启动那么快（快速的冷启动和快速的热更新）</h4><p>Vite 借助了浏览器对 ESM 规范的支持，采取了与 Webpack 完全不同的 unbundle 机制。</p><p>unbundle 机制：不需要做 bundle 操作，即不需要构建、分解 module graph，源文件之间的依赖关系完全通过浏览器对 ESM 规范的支持来解析。这就使得 dev server 在启动过程中只需做一些初始化的工作，剩下的完全由浏览器支持。</p><ul><li>模块之间的依赖关系的解析由浏览器实现</li><li>文件的转换由 dev server 的 middlewares 实现并做缓存</li><li>不对源文件做合并捆绑操作</li></ul><p>unbundle 机制缺点：首屏慢，vite需要动态的解析依赖，并打包，引入。</p><p><a href="https://jishuin.proginn.com/p/763bfbd6f27b" target="_blank" rel="noopener">Vite为什么快</a></p><h4 id="15-vue-keep-alive-缓存组件的策略"><a href="#15-vue-keep-alive-缓存组件的策略" class="headerlink" title="15. vue keep-alive 缓存组件的策略"></a>15. vue keep-alive 缓存组件的策略</h4><p>keep-alive是Vue提供的一个内置的抽象组件，被keep-alive组件包裹的内部组件，其状态将被<code>缓存</code>(缓存不活跃的组件实例而不是销毁他们，在组件切换过程中状态保存在内存中，防止重复渲染)，该组件会新增两个生命周期函数activated和deactivated，每次进入都会被执行。</p><p>如何实现：主要是根据LRU策略缓存组件 VNode，最后在 render 时返回子组件的 VNode。</p><p>如何更新：缓存渲染过程会更新 keep-alive 插槽，重新再 render 一次，从缓存中读取之前的组件 VNode 实现状态缓存。(vnode中的componentInstance保存了组件实例，在patch过程中直接insert实例，避免重新渲染)。</p><p>在使用 keep-alive 时，可以添加 prop 属性 include、exclude、max 允许组件有条件的缓存。<br>$route.meta.keepAlive可以缓存页面。</p><p>原理：LRU（最近最少使用）策略，根据组件访问的时间，将很久未访问的组件从缓存中删除。组件首次渲染时，keep-alive 会将组件缓存起来。等到缓存渲染时，keep-alive 会更新插槽内容，之后 $forceUpdate 重新渲染。这样在 render 时就获取到最新的组件，如果命中缓存则从缓存中返回 VNode。</p><h4 id="16-组件渲染更新的过程"><a href="#16-组件渲染更新的过程" class="headerlink" title="16. 组件渲染更新的过程"></a>16. 组件渲染更新的过程</h4><p>初次渲染：</p><ul><li>解析模板为render函数（在开发环境下用vue-loader完成）</li><li>触发响应式，监听data属性（Object.freeze可以不做响应式监听）</li><li>执行render函数(会触发用到的getter)，生成vnode，并且进行patch(ele, vnode)</li></ul><p>更新：</p><ul><li>修改data触发setter</li><li>重新执行render函数，生成newVnode</li><li>执行patch(vnode, newVnode)，diff算法更新视图</li></ul><p>异步渲染：汇总data修改，一次性更新视图，减少DOM操作优化性能</p><h4 id="17-computed-和-watch-区别"><a href="#17-computed-和-watch-区别" class="headerlink" title="17. computed 和 watch 区别"></a>17. computed 和 watch 区别</h4><p>computed一般是根据data数据返回计算之后的结果。<br>computed与methods区别：computed是惰性的，有缓存性。如果依赖值不变就不重新计算。<br>watch没有返回值，一般用于监听数据变化执行一些异步操作或DOM操作。</p><h4 id="18-vuex的理解"><a href="#18-vuex的理解" class="headerlink" title="18. vuex的理解"></a>18. vuex的理解</h4><p>vue的状态管理库。可以统一管理所有组件的状态。vue中尽量使用单向数据流，所以在多个组件共享一个状态，就很难保证单向。所以我们需要把共享状态抽离出来，以全局单例模式管理，使得状态管理更加结构化和可维护。比较适合用于中大型项目。</p><p>原理：vuex利用了vue的mixin机制，混合 beforeCreate 钩子 将store注入至vue组件实例上，并注册了 vuex store的引用属性 $store。<br>vuex的state是借助vue的响应式data实现的。getter是借助vue的计算属性computed特性实现的。</p><h4 id="19-vue挂载过程发生了啥"><a href="#19-vue挂载过程发生了啥" class="headerlink" title="19. vue挂载过程发生了啥"></a>19. vue挂载过程发生了啥</h4><ul><li>初始化：创建组件实例，初始化组件状态，创建响应式数据</li><li>建立更新机制：执行组件渲染函数（patch将vnode转成DOM）；同时渲染时会创建内部响应式数据和依赖关系</li></ul><h4 id="20-vue可以做哪些性能优化"><a href="#20-vue可以做哪些性能优化" class="headerlink" title="20. vue可以做哪些性能优化"></a>20. vue可以做哪些性能优化</h4><p>路由懒加载、keep-alive缓存、对很大的组件可以用v-show（防止重复创建）、v-once(只变化一次就不改变)、长列表采用虚拟滚动、组件销毁时清空事件监听、图片懒加载、第三方组件按需引用、无状态的静态组件尽量不拆分，需要更新的大组件尽量拆分</p><h4 id="21-Vue2为什么只能一个根元素"><a href="#21-Vue2为什么只能一个根元素" class="headerlink" title="21. Vue2为什么只能一个根元素"></a>21. Vue2为什么只能一个根元素</h4><p>vdom是单根的树形结构，patch算法会从根元素开始遍历, 然后渲染组件。Vue3中增加了Fragment这个抽象节点，如果是多根元素，就会创建一个Fragment节点作为根节点包裹。</p><h4 id="22-router-link和router-view"><a href="#22-router-link和router-view" class="headerlink" title="22. router-link和router-view"></a>22. router-link和router-view</h4><p>vue-routers会监听popstate事件。点击后页面不会刷新，用当前path和routers中的path匹配找到对应路由组件然后在router-view（组件内容渲染）渲染。<br>router-link（路由导航）默认生成a标签，点击后取消默认跳转，执行pushState重新匹配路由。</p><h4 id="23-vue的data为什么是函数"><a href="#23-vue的data为什么是函数" class="headerlink" title="23. vue的data为什么是函数"></a>23. vue的data为什么是函数</h4><p>为了保证组件的独立性和可复用性。</p><p>组件可能在多次使用时多次实例化，如果写成对象形式，所有组件会复用一个data（引用类型）。函数可以保证每个组件实例化都有独立的作用域。</p><p>new Vue生成的是根元素实例，所以不会重复使用，可以用对象data。</p><h4 id="24-vue-loader用法"><a href="#24-vue-loader用法" class="headerlink" title="24. vue-loader用法"></a>24. vue-loader用法</h4><p>用来解析和转换vue文件，把script template style标签中的内容提取出来交给对应的loader处理。（调用预处理，允许热重载）</p><h4 id="25-如何解析template-如何解析指令？"><a href="#25-如何解析template-如何解析指令？" class="headerlink" title="25. 如何解析template, 如何解析指令？"></a>25. 如何解析template, 如何解析指令？</h4><p>解析模板字符串，先通过正则等方式将其转换为AST语法树，然后经过一步优化，将其中的静态节点打上标签，最后将AST转换为render函数。模板 ——&gt; 解析器（parser） ——&gt; 代码生成器 ——&gt; 渲染函数</p><h4 id="26-vue本地代理怎么解决跨域"><a href="#26-vue本地代理怎么解决跨域" class="headerlink" title="26. vue本地代理怎么解决跨域"></a>26. vue本地代理怎么解决跨域</h4><p>实际上就是把实际请求转到了本地开启的server里面去请求，就不存在什么浏览器同源安全问题了。</p><p><a href="https://zhuanlan.zhihu.com/p/528382666" target="_blank" rel="noopener">Vue模板解析原理</a></p><h4 id="26-导航守卫"><a href="#26-导航守卫" class="headerlink" title="26. 导航守卫"></a>26. 导航守卫</h4><p>全局：</p><ul><li>beforeEach((to, from, next)&#x3D;&gt;{})（全局前置守卫，跳转前触发）</li><li>beforeResolve （路由解析之前 这个几乎不用）</li><li>afterEach((to, from)&#x3D;&gt;{})（全局后置守卫）</li><li>beforeEnter(写在路由里面的，进入这个路由时才会调用)</li></ul><p>组件内：</p><ul><li>beforeRouteEnter（不能访问 this，此时组件实例还未创建）</li><li>beforeRouteUpdate</li><li>beforeRouteLeave</li></ul><h4 id="27-mixins"><a href="#27-mixins" class="headerlink" title="27. mixins"></a>27. mixins</h4><p>生命周期顺序：mixin的beforeCreate &gt; 父beforeCreate &gt; mixin的created &gt; 父created &gt; mixin的beforeMount &gt; 父beforeMount &gt; 子beforeCreate &gt; 子created &gt; 子beforeMount &gt; 子mounted &gt; mixin的mounted &gt;父mounted</p><ul><li>对于data定义属性，组件中定义属性覆盖mixins中同名字段</li><li>对于created、mounted等生命周期函数，mixins中生命周期函数优先执行（执行顺序按mixins中顺序），再执行组件中生命周期函数</li><li>对于methods中的同名方法，组件内的方法覆盖mixins中的方法</li></ul><h2 id="Css"><a href="#Css" class="headerlink" title="Css"></a>Css</h2><h4 id="1-z-index-无效问题"><a href="#1-z-index-无效问题" class="headerlink" title="1. z-index 无效问题"></a>1. z-index 无效问题</h4><ul><li>父级元素溢出隐藏或者不显示：父元素设置了 overflow:hidden &#x2F;display:none&#x2F; 等，那么子元素如果在父元素外部绝对定位，那么调节子元素 z-index 可能不会显示</li><li>父级元素层级低，z-index 被覆盖</li><li>没有设置定位(position)属性</li><li>父级元素 position 属性为 relative</li><li>含有浮动(float)属性</li><li>IE 不兼容</li></ul><h4 id="2-水平垂直居中实现-star"><a href="#2-水平垂直居中实现-star" class="headerlink" title="2. 水平垂直居中实现 :star:"></a>2. 水平垂直居中实现 :star:</h4><ul><li>相对定位和绝对定位：absolute + margin | absolute + transform | absolute + top + left</li><li>flex布局（移动端推荐）：flex + justify-content + align-items | flex + margin auto</li><li>table布局：table-cell + vertical-align:middle</li><li>grid布局：grid + justify-self + align-self；| place-items:center</li></ul><h4 id="3-link-和-import区别"><a href="#3-link-和-import区别" class="headerlink" title="3. link 和 import区别"></a>3. link 和 import区别</h4><p>link属于HTML标签，而@import完全是CSS提供的一种方式。<br>link引用的CSS会同时被加载，而@import引用的CSS会等到页面全部被下载完再被加载。</p><h4 id="4-scss的混合和函数"><a href="#4-scss的混合和函数" class="headerlink" title="4. scss的混合和函数"></a>4. scss的混合和函数</h4><p>混合：@mixin 和@include ，可以传参</p><p>函数：@function pxToRem($num) {}， 函数可以计算</p><h4 id="5-rem和em基于什么取值"><a href="#5-rem和em基于什么取值" class="headerlink" title="5. rem和em基于什么取值"></a>5. rem和em基于什么取值</h4><p>em 单位基于使用他们的元素的字体大小，可能受父元素字体大小影响。<br>rem 单位基于 html 元素的字体大小。（1rem等于html根元素设定的font-size的px值）</p><h4 id="6-三栏布局"><a href="#6-三栏布局" class="headerlink" title="6. 三栏布局"></a>6. 三栏布局</h4><ul><li>flex布局</li><li>grid布局</li><li>float + width</li></ul><h4 id="7-line-height-1-5"><a href="#7-line-height-1-5" class="headerlink" title="7. line-height 1.5"></a>7. line-height 1.5</h4><p>设置数字时，继承的是数字的值。line-height的值会是本身字体大小乘以数字。<br>设置em时，继承的时父元素的字体大小乘以em之后的值，所以可能会带来不确定的结果。</p><h3 id="8-display-none和visibility-hidden的区别"><a href="#8-display-none和visibility-hidden的区别" class="headerlink" title="8. display:none和visibility:hidden的区别"></a>8. display:none和visibility:hidden的区别</h3><p>none: 让这个元素也是直接消失，会影响到布局问题。<br>hidden: 可以让元素消失，属于css样式，它只是让元素看不见，但本身的<code>位置</code>还在，如果对div进行hidden，那么div除了看不见，其他所有的样式都在（比如块元素样式）。</p><h3 id="9-优先级"><a href="#9-优先级" class="headerlink" title="9. 优先级"></a>9. 优先级</h3><p>内联 &gt; id &gt; class &gt; 标签</p><h2 id="浏览器与网络安全"><a href="#浏览器与网络安全" class="headerlink" title="浏览器与网络安全"></a>浏览器与网络安全</h2><h4 id="从输入-url-到浏览器渲染经过了哪些流程-star"><a href="#从输入-url-到浏览器渲染经过了哪些流程-star" class="headerlink" title="从输入 url 到浏览器渲染经过了哪些流程 :star:"></a>从输入 url 到浏览器渲染经过了哪些流程 :star:</h4><p>URL 解析 -&gt; DNS 查询 -&gt; TCP 连接 -&gt; HTTP 请求 -&gt; 服务器响应请求 -&gt; 页面渲染</p><ul><li>首先浏览器在输入URL之后，会先解析URL，判断是否合法；</li><li>合法的话会查看浏览器缓存，判断是否有缓存，如果有缓存，则显示；</li><li>如果没有缓存，浏览器会向服务器发送HTTP协议，会进行DNS解析，获取IP地址；（通过ip寻址和arp，找到目标（服务器）地址）</li><li>浏览器和服务器进行TCP连接，进行三次握手，建立tcp连接；</li><li>握手成功之后，浏览器会向服务器发送http请求，请求数据包；</li><li>服务器处理请求，并对请求做出响应；</li><li>浏览器收到服务器响应，得到html代码；</li><li>渲染页面：获取到服务器相应之后，浏览器会根据相应的content-type字段对响应字符串进行解析。能够解析并成功解析就显示，能够解析但解析错误就报错，不能解析就下载。</li></ul><h4 id="从服务器返回数据会经过哪些阶段"><a href="#从服务器返回数据会经过哪些阶段" class="headerlink" title="从服务器返回数据会经过哪些阶段"></a>从服务器返回数据会经过哪些阶段</h4><p>浏览器首先去找本地hosts文件，检查在该文件中是否有相应的域名、IP对应关系，如果有，则向其IP地址发送请求，如果没有就发送给DNS（域名服务器）进行解析，解析成对应的服务器IP地址 ——&gt; 网络通信 ——&gt; 浏览器拿到数据 ——&gt; 四次挥手断开连接</p><h4 id="css是否会阻塞加载html"><a href="#css是否会阻塞加载html" class="headerlink" title="css是否会阻塞加载html"></a>css是否会阻塞加载html</h4><p>css的加载是不会阻塞DOM的解析,但是会阻塞DOM的渲染， 会阻塞link后面js语句的执行。（当cssdom还没构建完成时，页面是不会渲染到浏览器界面的）。</p><p>为了防止html页面的重复渲染而降低性能，所以浏览器只会在加载的时候去解析dom树，然后等在css加载完成之后才进行dom的渲染以及执行后面的js语句。</p><h4 id="http-和-网络攻击-star"><a href="#http-和-网络攻击-star" class="headerlink" title="http 和 网络攻击  :star:"></a>http 和 网络攻击 :star:</h4><p><a href="/2022/07/09/HTTP%E5%92%8CHTTPS%E5%8D%8F%E8%AE%AE/">HTTP和HTTPS协议</a></p><h4 id="get和post请求方式的区别"><a href="#get和post请求方式的区别" class="headerlink" title="get和post请求方式的区别"></a>get和post请求方式的区别</h4><ol><li>get重点在从服务器上获取资源；post重点在向服务器发送数据；</li><li>get传输数据是通过URL请求，参数加在url上；post的参数放在请求实体中，这个过程对用户是不可见的；</li><li>受URL长度限制，get传输的数据是有限的；post可以传输大量数据，所以上传文件时只能用post方式；</li><li>post安全性高（因为get参数放在url上可见）；</li><li>get方式只能支持ASCII字符，向服务器传的中文字符可能会乱码；post支持标准字符集，可以正确传递中文字符。</li></ol><p>post一般没有大小限制，但是实际上post所能传递的数据量大小取决于服务器的设置和内存大小。</p><h4 id="ajax发起请求的流程"><a href="#ajax发起请求的流程" class="headerlink" title="ajax发起请求的流程"></a>ajax发起请求的流程</h4><p>创建请求对象new XMLHttpRequest()  ——&gt; 创建HTTP请求 ——&gt; 设置响应HTTP请求状态变化的函数 ——&gt; 发送HTTP请求 ——&gt; 监听 onreadystatechange 事件，获取请求状态码和响应码状态</p><h2 id="前端性能优化-star-star-star"><a href="#前端性能优化-star-star-star" class="headerlink" title="前端性能优化 :star::star::star:"></a>前端性能优化 :star::star::star:</h2><h4 id="1-首页白屏解决"><a href="#1-首页白屏解决" class="headerlink" title="1. 首页白屏解决"></a>1. 首页白屏解决</h4><p>缓存；骨架屏(用css占位置，当资源加载完成即可填充，减少页面的回流与重绘)；减少包大小；解决阻塞；SSR；预渲染</p><h4 id="2-大数据量的问题处理"><a href="#2-大数据量的问题处理" class="headerlink" title="2. 大数据量的问题处理"></a>2. 大数据量的问题处理</h4><p>分页；分片；虚拟滚动；关键字远程搜索；异步加载</p><h4 id="3-跨域问题解决-star"><a href="#3-跨域问题解决-star" class="headerlink" title="3. 跨域问题解决 :star:"></a>3. 跨域问题解决 :star:</h4><p>跨域：浏览器同源策略的限制（协议，域名和端口），不允许非同源的 URL 之间进行资源的交互。</p><ul><li><p>JSONP：通过script标签（没有同源策略限制）向服务器端发送一个get请求（src&#x3D;url）（ajax是通过XmlHttpRequest动态获取数据，jsonp是通过添加script标签动态获取数据）</p><p>优缺点：兼容性好，缺点是只支持 GET 请求，不支持 POST 请求。</p></li><li><p>iframe；</p></li><li><p>postMessage（允许来自一个文档的脚本可以传递文本消息到另一个文档里的脚本）；</p></li><li><p>CORS：跨域资源共享, 设定http的 Access-Control-Allow-Origin 允许的值（分为简单请求和非简单请求）</p></li><li><p>Proxy；</p></li><li><p>WebSocket</p></li></ul><h4 id="4-项目优化"><a href="#4-项目优化" class="headerlink" title="4. 项目优化"></a>4. 项目优化</h4><ul><li>减少HTTP请求数量：减少图片使用，使用icon代替、合并静态资源，比如雪碧图、善用缓存；缓存接口请求；</li><li>压缩图片：背景图使用webp格式</li><li>源码优化：代码模块化、路由懒加载、使用keep-alive对组件缓存、图片懒加载（默认data-src渲染时src）、长列表优化（无限滚动、虚拟滚动）</li><li>项目打包优化：开启Gzip压缩（借助 Compression-webpack-plugin来生成 .gz文件，nginx也需要同时配置）、修改配置文件去掉打包的sourcemap、组件的按需引入</li><li>使用cdn的方式外部加载一些资源</li><li>减少页面回流和重绘：预加载（preload）、预渲染（prerender）、或者增加骨架屏和loading</li><li>使用浏览器控制台的performance查看渲染时间，使用lighthouse查看可优化项</li></ul><p>cdn原理：内容分发网络，通过中心平台的负载均衡、内容分发、调度等功能模块，使用户就近获取所需内容，降低网络拥塞，提高用户访问响应速度和命中率。</p><h2 id="实践"><a href="#实践" class="headerlink" title="实践"></a>实践</h2><h4 id="1-主题色"><a href="#1-主题色" class="headerlink" title="1. 主题色"></a>1. 主题色</h4><p>css颜色变量、scss变量 + @mixin</p><p><a href="https://blog.csdn.net/qq_40621378/article/details/124157105" target="_blank" rel="noopener">使用scss处理多主题切换功能</a></p><h4 id="2-权限管理（菜单权限，角色权限，路由权限，页面内部权限）-star"><a href="#2-权限管理（菜单权限，角色权限，路由权限，页面内部权限）-star" class="headerlink" title="2. 权限管理（菜单权限，角色权限，路由权限，页面内部权限） :star:"></a>2. 权限管理（菜单权限，角色权限，路由权限，页面内部权限） :star:</h4><p>路由守卫、动态路由加载、自定义指令按钮</p><h4 id="3-token-失效刷新如何实现的"><a href="#3-token-失效刷新如何实现的" class="headerlink" title="3. token 失效刷新如何实现的"></a>3. token 失效刷新如何实现的</h4><p>现token无感刷新对于前端来说是一项常用的技术,其本质是为了优化用户体验,当token过期时不需要用户跳回登录页重新登录,而是当token失效时,进行拦截,发送刷新token的ajax,获取最新的token进行覆盖,让用户感受不到token已经过期。</p><h4 id="4-webpack-star"><a href="#4-webpack-star" class="headerlink" title="4. webpack :star:"></a>4. webpack :star:</h4><p>个性化配置：拆包；less，sass 处理；打包加速优化 ；图片资源压缩(image-webpack-loader)</p><p>性能优化：</p><ul><li>并行压缩，开启多进程打包（happyPack、pipeline）；</li><li>dllplugin提升构建速度(可以将特定的类库提前打包然后引入。生成xx.dll.js文件,通过manifest.json引用)；</li><li>splitchunk 拆包；</li><li>混淆压缩；</li><li>cache，缓存中间产物，对性能优化作用最大，改善构建速度</li></ul><p><a href="/2020/07/15/%E6%B7%B1%E5%85%A5%E6%8E%A2%E7%A9%B6Webpack%E5%8E%9F%E7%90%86/">深入探究Webpack原理</a></p><h4 id="5-脚手架，一般会改哪些配置"><a href="#5-脚手架，一般会改哪些配置" class="headerlink" title="5. 脚手架，一般会改哪些配置"></a>5. 脚手架，一般会改哪些配置</h4><p>压缩的处理：插件的压缩处理，gzip 压缩，代码压缩</p><h4 id="6-浏览器兼容性问题-star"><a href="#6-浏览器兼容性问题-star" class="headerlink" title="6. 浏览器兼容性问题 :star:"></a>6. 浏览器兼容性问题 :star:</h4><ol><li>添加reset.scss（初始化全局样式，如*{margin:0px; padding:0px}），解决不同浏览器标签默认的内外间距不同</li><li>图片默认有间距，使用float:left</li><li>安装babel兼容ES6</li><li>js事件获取target兼容ie, 获取getSelection兼容ie（IE下，even对象有srcElement属性，但是没有target属性；Firefox下，even对象有target属性，但是没有srcElement属性。<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> ev = ev || <span class="built_in">window</span>.event</span><br><span class="line"><span class="keyword">var</span> w = <span class="built_in">document</span>.documentElement.clientWidth || <span class="built_in">document</span>.body.clientWidth</span><br><span class="line"><span class="keyword">var</span> target = ev.srcElement || ev.target</span><br></pre></td></tr></table></figure></li></ol><p>解决方法：使用srcObj &#x3D; event.srcElement ?event.srcElement : event.target;）<br>5. css不支持，css hack</p><p><a href="https://blog.csdn.net/Xiaocong__/article/details/122456782" target="_blank" rel="noopener">浏览器兼容性问题</a></p><h4 id="7-图片上传预览"><a href="#7-图片上传预览" class="headerlink" title="7. 图片上传预览"></a>7. 图片上传预览</h4><p>图片存在本地，通过 base64 转化</p><h4 id="8-单点登录实现"><a href="#8-单点登录实现" class="headerlink" title="8. 单点登录实现"></a>8. 单点登录实现</h4><p>单点登录（SSO），指在同一帐号平台下的多个应用系统中，用户只需登录一次，即可访问所有相互信任的系统。多个系统，统一登陆。</p><p>实现原理：sso需要一个独立的认证中心，所有子系统都通过认证中心的登录入口进行登录，登录时带上自己的地址，子系统只接受认证中心的授权，授权通过令牌（token）实现，sso认证中心验证用户的用户名密码正确，创建全局会话和token，token作为参数发送给各个子系统，子系统拿到token，即得到了授权，可以借此创建局部会话，局部会话登录方式与单系统的登录方式相同。</p><p>单点登录的实现方案，一般就包含：Cookies，Session同步，分布式Session，目前的大型网站都是采用分布式Session的方式。</p><h4 id="9-第三方登录对接"><a href="#9-第三方登录对接" class="headerlink" title="9. 第三方登录对接"></a>9. 第三方登录对接</h4><p><a href="https://blog.csdn.net/weixin_43594183/article/details/117132759" target="_blank" rel="noopener">前端对接第三方登录</a></p><h4 id="10-微信公众号，小程序的开发的区别，登陆的过程"><a href="#10-微信公众号，小程序的开发的区别，登陆的过程" class="headerlink" title="10. 微信公众号，小程序的开发的区别，登陆的过程"></a>10. 微信公众号，小程序的开发的区别，登陆的过程</h4><p>公众号授权登录：先在公众号管理页设置网页授权，然后通过authorize（调用<a href="https://open.weixin.qq.com/connect/oauth2/authorize%EF%BC%89%E8%8E%B7%E5%8F%96%E7%9A%84code" target="_blank" rel="noopener">https://open.weixin.qq.com/connect/oauth2/authorize）获取的code</a></p><p>小程序登录：小程序端检测登录信息(wx.checkSession)，然后调用wx.login，获取登录凭证（code），并调用接口，将code发送到第三方客户端，第三方服务器端调用接口，用code换取session_key和openid</p><p>绑定同一个微信开发平台账号下, 则同一用户，对同一个微信开放平台下的不同应用，unionid是相同的</p><h4 id="11-PC端扫码实现"><a href="#11-PC端扫码实现" class="headerlink" title="11. PC端扫码实现"></a>11. PC端扫码实现</h4><p><a href="https://www.163.com/dy/article/HB9DL32505537S0S.html" target="_blank" rel="noopener">扫码登录是如何实现的</a></p><h4 id="12-axios返回值"><a href="#12-axios返回值" class="headerlink" title="12. axios返回值"></a>12. axios返回值</h4><p>axios返回的是一个promise对象。</p><p>ajax是通过XmlHttpRequest对象来向服务器发异步请求，从服务器获得数据。</p><h4 id="13-常见命令"><a href="#13-常见命令" class="headerlink" title="13. 常见命令"></a>13. 常见命令</h4><figure class="highlight plain"><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">// vue-cli创建项目</span><br><span class="line">vue create projectName</span><br><span class="line"></span><br><span class="line">// vite创建vue-ts项目</span><br><span class="line">yarn create vite projectName --template vue-ts</span><br><span class="line"></span><br><span class="line">// vite创建项目</span><br><span class="line">yarn create vite projectName --template vue</span><br><span class="line"></span><br><span class="line">/* vite集成electron */</span><br><span class="line">// 安装依赖和打包</span><br><span class="line">npm install electron electron-builder -D</span><br><span class="line">// 安装调试</span><br><span class="line">npm install electron-devtools-installer -D</span><br><span class="line">npm install vite-plugin-electron -D</span><br></pre></td></tr></table></figure><h4 id="14-大文件上传分块和断点续传"><a href="#14-大文件上传分块和断点续传" class="headerlink" title="14. 大文件上传分块和断点续传"></a>14. 大文件上传分块和断点续传</h4><p>前端负责分块，服务端负责整合。首先是选择上传的文件资源，通过FileList对象获取到相应的文件，而 File.prototype.slice 方法可以实现资源的分块。</p><p>前端可以基于 Promise.all 将这多个接口整合，上传完成在发送一个合并的请求，通知服务端进行合并。</p><p>断点续传：下载文件时，不必重头开始下载，而是从指定的位置继续下载。可以利用请求头：Range:bytes&#x3D;5001-10000或者size传参。</p><h4 id="15-chrome调试"><a href="#15-chrome调试" class="headerlink" title="15. chrome调试"></a>15. chrome调试</h4><ul><li>Performance 查看性能: 火焰图中每个方块的宽度代表执行耗时</li><li>Memory 查看内存：<ul><li>Snapshot：某个时间点的 堆内存快照</li><li>TimeLine：实时的按照 时间线 显示的内存分配情况</li><li>Sampling：采样 的方式收集内存分配情况</li></ul></li></ul><h4 id="16-axios特性"><a href="#16-axios特性" class="headerlink" title="16. axios特性"></a>16. axios特性</h4><p>Axios实现是：基于nodejs的http或者https模块来发起请求的。</p><ul><li>从浏览器创建XMLHttpRequests，从node.js创建http请求</li><li>支持promise API</li><li>拦截请求和相应，转换请求和相应数据，取消请求，自动转换JSON数据</li><li>客户端支持防御XSRF</li></ul><p>取消请求：AbortController， cancelToken<br><a href="https://blog.csdn.net/qq_33437985/article/details/124537682" target="_blank" rel="noopener">axios要点</a></p><h4 id="17-组件库的按需加载"><a href="#17-组件库的按需加载" class="headerlink" title="17. 组件库的按需加载"></a>17. 组件库的按需加载</h4><ul><li><p>elementUI : 使用babel插件是目前大多数组件库实现按需引入的方式，安装babel-plugin-component(可以实现样式的按需加载)</p></li><li><p>Vant: 使用<code>tree-shaking</code>: 输入格式commonjs规范是最常见的使用方式，umd一般用于cdn方式直接在页面引入，而<code>esmodule</code>就是用来实现Tree Shaking。（esmodule是静态编译的，也就是在编译阶段就能确定某个模块导出了什么，引入了什么，代码执行阶段不会改变，所以打包工具在打包的时候就能分析出哪个方法被使用了）（rollup也是）</p><p>修改package.json，main是commonjs模块入口，module是esmodule模块，Vue CLI使用的是webpack，对应的我们需要在package.json文件里新增一个sideEffects字段（去掉副作用函数）。</p></li></ul><h4 id="18-echarts和antv区别"><a href="#18-echarts和antv区别" class="headerlink" title="18. echarts和antv区别"></a>18. echarts和antv区别</h4><p>echarts是以图表为主, 而antv则是以图形出发。echarts相对于antv来说是比较成熟的一套图表库, 使用方便、图表种类多， 也是比较容易上手的, 遇到问题网上的解决方式也有很多，而antv是以数据可视化底层引擎，以数据驱动， 相对于echart更具有拓展性和灵活性。</p>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;JavaScript&quot;&gt;&lt;a href=&quot;#JavaScript&quot; class=&quot;headerlink&quot; title=&quot;JavaScript&quot;&gt;&lt;/a&gt;JavaScript&lt;/h2&gt;&lt;h4 id=&quot;1-深拷贝和浅拷贝-递归调用死循环问题如何解决-star&quot;&gt;&lt;a href=&quot;#1-深拷贝和浅拷贝-递归调用死循环问题如何解决-star&quot; class=&quot;headerlink&quot; title=&quot;1. 深拷贝和浅拷贝(递归调用死循环问题如何解决) :star:&quot;&gt;&lt;/a&gt;1. 深拷贝和浅拷贝(递归调用死循环问题如何解决) :star:&lt;/h4&gt;&lt;blockquote&gt;&lt;p&gt;基本类型–名值都存储在栈内存中。&lt;br&gt;引用数据类型–存储的是地址（指针），数据存储在堆上（栈内存会提供一个引用的地址指向堆内存中的值）&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;基本类型赋值时，赋的是值，所以不存在深浅拷贝问题。&lt;/p&gt;&lt;p&gt;引用类型赋值时，复制的是原本变量的引用地址（指针），浅拷贝就是新值和旧值变量指向同一个内存地址，当地址中的值改变时，他们都会同时变化。&lt;/p&gt;&lt;p&gt;深拷贝就是实现新旧值互不影响，拷贝的过程中，独立地开辟了一个空间，这个对象指向这个地址，与原来的对象互不干扰。深拷贝也被称为值拷贝。&lt;/p&gt;
    
    </summary>
    
      <category term="面试" scheme="https://aartemida.github.io/categories/%E9%9D%A2%E8%AF%95/"/>
    
    
  </entry>
  
  <entry>
    <title>浅尝试一下实现简单的Vue3</title>
    <link href="https://aartemida.github.io/2022/08/02/%E6%B5%85%E5%B0%9D%E8%AF%95%E4%B8%80%E4%B8%8B%E6%89%8B%E5%86%99Vue/"/>
    <id>https://aartemida.github.io/2022/08/02/浅尝试一下手写Vue/</id>
    <published>2022-08-02T10:42:52.000Z</published>
    <updated>2022-09-08T07:42:46.445Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/vue/vue_160301.png" alt="vue3"></p><a id="more"></a><h2 id="渲染器"><a href="#渲染器" class="headerlink" title="渲染器"></a>渲染器</h2><p>作用：虚拟DOM转成真实DOM。</p><p>工作原理：递归地遍历虚拟DOM，调用原生js创建真实的DOM。</p><h3 id="虚拟DOM"><a href="#虚拟DOM" class="headerlink" title="虚拟DOM"></a>虚拟DOM</h3><p>虚拟DOM就是一个用来描述真实DOM的JS对象。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> vnode = &#123;</span><br><span class="line">  tag: <span class="string">'div'</span>,</span><br><span class="line">  props: &#123;</span><br><span class="line">    onClick: <span class="function"><span class="params">()</span> =&gt;</span> <span class="built_in">console</span>.log(<span class="string">'This is a div'</span>)</span><br><span class="line">  &#125;,</span><br><span class="line">  children: <span class="string">'click me'</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="组件"><a href="#组件" class="headerlink" title="组件"></a>组件</h3><p>虚拟DOM元素的封装，可以是返回虚拟DOM的函数，也可以是一个对象（这个对象必须有一个函数用来产出组件要渲染的虚拟DOM）</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> myComponent = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    tag: <span class="string">'div'</span>,</span><br><span class="line">    props: &#123;</span><br><span class="line">      onClick: <span class="function"><span class="params">()</span> =&gt;</span> <span class="built_in">console</span>.log(<span class="string">'This is a div'</span>)</span><br><span class="line">    &#125;,</span><br><span class="line">    children: <span class="string">'click me'</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><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 假设一个虚拟dom</span></span><br><span class="line"><span class="keyword">const</span> vnode = &#123;</span><br><span class="line">  tag: myComponent</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 渲染函数</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">renderer</span>(<span class="params">vnode, container</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 判断vnode描述的是dom标签还是组件</span></span><br><span class="line">  <span class="keyword">if</span>(<span class="keyword">typeof</span> vnode.tag === <span class="string">'string'</span>) &#123;</span><br><span class="line">    mountElement(vnode, container)</span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span>(<span class="keyword">typeof</span> vnode.tag === <span class="string">'function'</span>) &#123;</span><br><span class="line">    mountComponent(vnode, container)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 渲染标签元素</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">mountElement</span>(<span class="params">vnode, container</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 创建元素</span></span><br><span class="line">  <span class="keyword">const</span> el = <span class="built_in">document</span>.createElement(vnode.tag)</span><br><span class="line">  <span class="comment">// 绑定事件或设置属性</span></span><br><span class="line">  <span class="keyword">for</span>(<span class="keyword">const</span> key <span class="keyword">in</span> vnode.props) &#123;</span><br><span class="line">    <span class="comment">// 事件</span></span><br><span class="line">    <span class="keyword">if</span>(key.startsWith(<span class="string">'on'</span>)) &#123;</span><br><span class="line">      <span class="keyword">const</span> eName = key.substr(<span class="number">2</span>).toLowerCase()</span><br><span class="line">      el.addEventListener(eName, vnode.props[key])</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// children</span></span><br><span class="line">  <span class="keyword">if</span>(<span class="keyword">typeof</span> vnode.children === <span class="string">'string'</span>) &#123;</span><br><span class="line">    <span class="comment">// 文本节点</span></span><br><span class="line">    el.appendChild(<span class="built_in">document</span>.createTextNode(vnode.children))</span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span>(vnode.children <span class="keyword">instanceof</span> <span class="built_in">Array</span>) &#123;</span><br><span class="line">    <span class="comment">// 递归添加子元素</span></span><br><span class="line">    vnode.children.forEach(<span class="function"><span class="params">child</span> =&gt;</span> renderer(child, el))</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span>(container) container.appendChild(el)</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="function"><span class="keyword">function</span> <span class="title">mountComponent</span>(<span class="params">vnode, container</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 调用组件函数获取虚拟dom</span></span><br><span class="line">  <span class="keyword">const</span> subtree = vnode.tag()</span><br><span class="line">  <span class="comment">// 递归的调用渲染函数</span></span><br><span class="line">  renderer(subtree, container)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="响应式数据"><a href="#响应式数据" class="headerlink" title="响应式数据"></a>响应式数据</h2><p>实现原理：对数据的“读取”和“设置”操作的拦截，在<code>副作用函数</code>和响应式数据间建立联系。当“读取”时，把当前执行的副作用函数放入“桶”；当“设置”时，把副作用函数取出并执行。</p><h3 id="Proxy"><a href="#Proxy" class="headerlink" title="Proxy"></a>Proxy</h3><p>可以创建一个代理对象，能够实现对其他对象的代理。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> n = <span class="keyword">new</span> <span class="built_in">Proxy</span>(obj, &#123;</span><br><span class="line">  <span class="comment">// 拦截读取属性</span></span><br><span class="line">  <span class="keyword">get</span>() &#123;&#125;,</span><br><span class="line">  <span class="comment">// 拦截设置属性</span></span><br><span class="line">  <span class="keyword">set</span>() &#123;&#125;,</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><h3 id="实现简单的响应式数据"><a href="#实现简单的响应式数据" class="headerlink" title="实现简单的响应式数据"></a>实现简单的响应式数据</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 数据</span></span><br><span class="line"><span class="keyword">const</span> data = &#123;</span><br><span class="line">  text: <span class="string">'hello world'</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 全局变量存被注册的副作用函数</span></span><br><span class="line"><span class="keyword">let</span> activeEffect</span><br><span class="line"></span><br><span class="line"><span class="comment">// 注册副作用函数</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">effect</span>(<span class="params">fn</span>) </span>&#123;</span><br><span class="line">  <span class="comment">//设置当前激活的副作用函数</span></span><br><span class="line">  <span class="keyword">const</span> effectFn = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    activeEffect = fn</span><br><span class="line">    fn()</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 存所有与该副作用函数相关联的依赖集合</span></span><br><span class="line">  effectFn.deps = []</span><br><span class="line">  <span class="comment">// 执行副作用函数</span></span><br><span class="line">  effectFn()</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 存副作用函数的桶</span></span><br><span class="line"><span class="keyword">const</span> bucket = <span class="keyword">new</span> <span class="built_in">WeakMap</span>() <span class="comment">// new Set() =&gt; 副作用函数没有和目标对象字段绑定</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">WeakMap由 target --&gt; Map 构成</span></span><br><span class="line"><span class="comment">Map由 key --&gt; Set 构成</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 代理</span></span><br><span class="line"><span class="keyword">const</span> obj = <span class="keyword">new</span> <span class="built_in">Proxy</span>(data, &#123;</span><br><span class="line">  <span class="comment">// 拦截读取属性</span></span><br><span class="line">  <span class="keyword">get</span>(target, key) &#123;</span><br><span class="line">    <span class="comment">// 副作用函数添加到桶</span></span><br><span class="line">    track(target, key)</span><br><span class="line">    <span class="keyword">return</span> target[key]</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// 拦截设置属性</span></span><br><span class="line">  <span class="keyword">set</span>(target, key, newVal) &#123;</span><br><span class="line">    <span class="comment">// 设置新值</span></span><br><span class="line">    target[key] = newVal</span><br><span class="line">    <span class="comment">// 取出副作用函数并执行</span></span><br><span class="line">    trigger(target, key)</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line">  &#125;,</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// get调用</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">track</span>(<span class="params">target, key</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 没有activeEffect</span></span><br><span class="line">  <span class="keyword">if</span>(!activeEffect) <span class="keyword">return</span> target[key]</span><br><span class="line">  <span class="comment">// 从桶中获取depsMap</span></span><br><span class="line">  <span class="keyword">let</span> depsMap = bucket.get(target) <span class="comment">// Map类型</span></span><br><span class="line">  <span class="comment">// 没有depsMap，新建一个与target绑定</span></span><br><span class="line">  <span class="keyword">if</span>(!depsMap) &#123;</span><br><span class="line">    depsMap = <span class="keyword">new</span> <span class="built_in">Map</span>()</span><br><span class="line">    bucket.set(target, depsMap)</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 获取与key相关的所有副作用函数</span></span><br><span class="line">  <span class="keyword">let</span> deps = depsMap.get(key) <span class="comment">// Set类型</span></span><br><span class="line">  <span class="keyword">if</span>(!deps) &#123;</span><br><span class="line">    deps = <span class="keyword">new</span> <span class="built_in">Set</span>()</span><br><span class="line">    depsMap.set(key, deps)</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 存副作用函数</span></span><br><span class="line">  <span class="comment">// bucket.add(activeEffect)</span></span><br><span class="line">  <span class="comment">// 添加当前激活的副作用函数</span></span><br><span class="line">  deps.add(activeEffect)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// set调用</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">trigger</span>(<span class="params">target, key</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 取出副作用函数并执行</span></span><br><span class="line">  <span class="comment">// bucket.forEach(fn =&gt; fn())</span></span><br><span class="line">  <span class="keyword">const</span> depsMap = bucket.get(target)</span><br><span class="line">  <span class="keyword">if</span>(!depsMap) <span class="keyword">return</span></span><br><span class="line">  <span class="comment">// 获取与key相关的所有副作用函数</span></span><br><span class="line">  <span class="keyword">const</span> effects = depsMap.get(key)</span><br><span class="line">  effects &amp;&amp; effects.forEach(<span class="function"><span class="params">fn</span> =&gt;</span> fn())</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 需要执行的副作用函数</span></span><br><span class="line">effect(<span class="function"><span class="keyword">function</span> <span class="title">effectFn</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="built_in">document</span>.body.innerText = obj.text</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><h3 id="computed"><a href="#computed" class="headerlink" title="computed"></a>computed</h3><p>计算属性实际上是一个懒执行的副作用函数</p><h3 id="watch"><a href="#watch" class="headerlink" title="watch"></a>watch</h3><p>实现原理：利用副作用函数重新执行时的可调度性</p><h2 id="编译器"><a href="#编译器" class="headerlink" title="编译器"></a>编译器</h2><p>作用：将模板字符串编译成渲染函数。</p><p>render 函数是模板编译后的产物，它负责构建 VNode 树，构建好的 VNode 会传递给 patch，patch 根据 VNode 的关系生成真实dom节点树。</p><p>编译器过程：</p><ul><li>用来将模板字符串解析为模板AST的解析器（parser）</li><li>用来将模板AST转换为JavaScript AST的转换器（transformer）</li><li>根据JavaScript AST生成render函数代码的生成器（generator）</li></ul><h3 id="实现一个编译器"><a href="#实现一个编译器" class="headerlink" title="实现一个编译器"></a>实现一个编译器</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> template = <span class="string">`&lt;p&gt;&#123;&#123; message &#125;&#125;&lt;/p&gt;`</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">compiler</span>(<span class="params">template</span>) </span>&#123;<span class="comment">// render</span></span><br><span class="line">  <span class="comment">// 解析模版，生成 ast</span></span><br><span class="line">  <span class="keyword">const</span> ast = parse(template)</span><br><span class="line">  <span class="comment">// 将 ast 生成渲染函数</span></span><br><span class="line">  <span class="keyword">const</span> render = generate(ast)</span><br><span class="line">  <span class="keyword">return</span> render</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">parse</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">generate</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/vue/vue_160301.png&quot; alt=&quot;vue3&quot;&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="Vue" scheme="https://aartemida.github.io/categories/Vue/"/>
    
    
      <category term="vue" scheme="https://aartemida.github.io/tags/vue/"/>
    
  </entry>
  
  <entry>
    <title>深入探究Webpack原理</title>
    <link href="https://aartemida.github.io/2022/07/15/%E6%B7%B1%E5%85%A5%E6%8E%A2%E7%A9%B6Webpack%E5%8E%9F%E7%90%86/"/>
    <id>https://aartemida.github.io/2022/07/15/深入探究Webpack原理/</id>
    <published>2022-07-15T14:25:04.000Z</published>
    <updated>2022-10-16T09:47:19.663Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Webpack的介绍"><a href="#Webpack的介绍" class="headerlink" title="Webpack的介绍"></a>Webpack的介绍</h2><p>webpack 是一个模块打包工具(module bundler)。当 webpack 处理应用程序时，它会递归地构建一个依赖关系图(dependency graph)，其中包含应用程序需要的每个模块，然后将所有这些模块打包成一个或多个 bundle。</p><a id="more"></a><h3 id="webpack的作用"><a href="#webpack的作用" class="headerlink" title="webpack的作用"></a>webpack的作用</h3><ul><li>通过webpack可以将零散的js文件打包到一个文件中；</li><li>通过webpack Loader实现对代码的编译转换，兼容低版本浏览器</li><li>支持不同种类（js&#x2F;css&#x2F;img）的模块资源的打包</li><li>具备代码拆分能力：所有模块按需分块打包，一般会把应用初次加载必须的模块打包到一起，其他模块单独打包，等到应用工作过程中再异步加载这个模块（渐进式加载就不用担心打包单文件过大而加载慢的问题）</li></ul><h2 id="核心概念"><a href="#核心概念" class="headerlink" title="核心概念"></a>核心概念</h2><p>webpack的核心有以下几点：</p><ol><li>Entry：入口</li><li>Output：出口</li><li>Loader：加载器</li><li>Plugin：插件</li><li>Module：模块，在 Webpack 里一切皆模块，一个模块对应着一个文件。</li><li>Chunk：代码块，一个 Chunk 由多个模块组合而成，用于代码合并与分割。</li></ol><p>具体可查阅：<a href="/2020/03/12/webpack%E5%AE%89%E8%A3%85%E4%BD%BF%E7%94%A8/">webpack安装使用</a></p><h3 id="Loader和Plugin的区别"><a href="#Loader和Plugin的区别" class="headerlink" title="Loader和Plugin的区别"></a>Loader和Plugin的区别</h3><p>Loader 本质就是一个函数，主要用于对特殊类型资源的加载，转换输出成webpack识别的格式（接受文件作为参数,返回转化后的结构）。webpack自身只支持js和json这两种格式的文件，对于其他文件需要通过loader将其转换为commonJS规范的文件后，webpack才能解析到。</p><p>Plugin 就是插件，包含apply方法。apply方法会被 webpack的 compiler（编译器）对象调用，并且 compiler 对象可在整个 compilation（编译）生命周期内访问。</p><p>Plugin可以理解为扩展webpack，实现各种自动化构建任务。在webpack打包编译过程里，针对loader结束后，webpack打包的整个过程，它并不直接操作文件，而是基于webpack的事件机制（webpack在每一个工作环境都预留了合适的钩子），会监听webpack打包过程中的某些节点，挂载并执行任务。</p><h5 id="开发一个Plugin"><a href="#开发一个Plugin" class="headerlink" title="开发一个Plugin"></a>开发一个Plugin</h5><p>在插件项目中定义一个插件名类，在该类中通过compiler的hooks属性访问到xx钩子，再通过tap方法注册一个钩子函数（挂载要执行的函数在钩子上），返回处理后的新内容。</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">///src/plugins/helloPlugin.js</span></span><br><span class="line"><span class="comment">// 命名函数。</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">HelloPlugin</span>(<span class="params">options</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 使用 options 设置插件实例……</span></span><br><span class="line">  <span class="built_in">console</span>.log(options);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//插件函数的 prototype 上定义一个 apply 方法</span></span><br><span class="line">HelloPlugin.prototype.apply = <span class="function"><span class="params">compiler</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">//hooks</span></span><br><span class="line">  compiler.hooks.done.tap(<span class="string">'HelloPlugin'</span>, status =&gt; &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(status.toJson());</span><br><span class="line">  &#125;)</span><br><span class="line">  <span class="comment">// 设置回调来访问 compilation 中的步骤：</span></span><br><span class="line">  compilation.plugin(<span class="string">'optimize'</span>, () =&gt; &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">'optimize'</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="built_in">module</span>.exports = HelloPlugin</span><br></pre></td></tr></table></figure><h5 id="实现loader"><a href="#实现loader" class="headerlink" title="实现loader"></a>实现loader</h5><p>loader 的处理策略:</p><ul><li>CSS：转换成 js 的模块，执行模块会在 DOM 中创建 style 标签并且插入CSS内容</li><li>Img：转换成图片路径</li><li>JSON：转化成 js 模块, default export &#x3D; json</li></ul><p>loader的执行顺序是从右到左，从下到上。而loader的加载顺序是从左到右，从上到下。</p><p>babel-loader的主要原理:<br>调动@babel&#x2F;core这个包下面的transform方法，将源码通过presets预设来进行转换，然后生成新的代码、map和ast语法树传给下一个loader。这里的presets，比如@babel&#x2F;preset-env这个预设其实就是各类插件的集合，基本上一个插件转换一个语法，比如箭头函数转换，有箭头函数转换的插件，这些插件集合就组成了预设。</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="comment">// myloader替换尖括号</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="keyword">function</span>(<span class="params">source</span>)</span>&#123;</span><br><span class="line">  <span class="keyword">var</span> content = <span class="string">""</span>;</span><br><span class="line">  content = source.replace(<span class="string">"/[&lt;&gt;]/g"</span>,<span class="string">"尖括号"</span>);</span><br><span class="line">  <span class="keyword">return</span> content; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="webpack钩子"><a href="#webpack钩子" class="headerlink" title="webpack钩子"></a>webpack钩子</h3><ul><li>beforeRun: 在编译器执行前</li><li>run: 在编译器开始读取记录前执行</li><li>compilation: 创建compilation后</li><li>beforeCompile: 在编译前</li><li>compile: 创建compilation前</li><li>make: 编译完成前</li><li>done: 编译完成后</li><li>emit: 文件提交到dist目录前</li><li>afterEmit: 文件提交到dist目录后</li></ul><h3 id="source-map配置"><a href="#source-map配置" class="headerlink" title="source map配置"></a>source map配置</h3><p>在webpack配置文件中，指定devtool: “source-map”等字段可以输出对应的.map文件，方便在浏览器中调试，sourceMap对应的模式有很多，建议：</p><ul><li>开发环境：cheap-module-eval-source-map，报错可以定位到源码的行信息，且构建速度比较快</li><li>生产环境: nosources-source-map，可以定位到报错的位置，且不会暴露源代码</li></ul><h3 id="webpack的特性"><a href="#webpack的特性" class="headerlink" title="webpack的特性"></a>webpack的特性</h3><ul><li><p><code>HMR</code>(热更新)</p></li><li><p><code>Tree-shaking</code>(摇树)：生产环境自动启动，打包时去掉没有用到的代码成员</p><p>可以通过配置webpack的 optimization 对象控制：</p><ul><li>usedExports 打包结果中只导出外部用到的成员；</li><li>minimize 压缩打包结果，去掉无效代码</li></ul></li><li><p><code>sideEffects</code>：开启可以打包时无视无效模块中的副作用代码（需要在package.json中同时配置）</p></li><li><p><code>code splitting</code>：通过把项目资源模块按照我们的规则打包到不同的bundle中，降低应用成本，提高响应速度</p><p>webpack实现<code>分包</code>方式：</p><ul><li>根据业务不同配置多个打包入口，输出多个打包结果（适用于传统多页应用）</li><li>结合ES Module的动态导入特性，按需加载模块（import(module path).then()）</li></ul></li></ul><h3 id="webpack的扩展方式"><a href="#webpack的扩展方式" class="headerlink" title="webpack的扩展方式"></a>webpack的扩展方式</h3><p>loader、plugin、minimizer</p><h2 id="构建流程-x2F-打包流程"><a href="#构建流程-x2F-打包流程" class="headerlink" title="构建流程&#x2F;打包流程"></a>构建流程&#x2F;打包流程</h2><p>Webpack 的运行流程是一个串行的过程，从启动到结束会依次执行以下流程：</p><ul><li>初始化：<ul><li>初始化参数：启动打包流程，读取并合并配置参数</li><li>创建编译器对象：载入webpack核心模块，传入配置项，创建Compiler对象</li><li>开始编译：使用Compiler对象开始编译项目</li></ul></li><li>编译：<ul><li>确定入口：从入口文件（entry）开始解析，并且找到其导入的依赖模块，递归遍历分析，形成依赖关系树；</li><li>编译模块：将每个模块交给Loader；对不同文件类型的依赖模块文件使用对应的<code>Loader</code>进行编译，最终转为JS模块文件（对于无法被转成js的资源文件，Loader一般会将它们单独作为资源文件拷贝到输出目标中，将资源文件的路径作为导出成员）</li></ul></li><li>输出：将编译后的模块组合成 Chunk，将 Chunk 转换成文件，输出到文件目录中</li></ul><p>PS:<br>编译的过程中，webpack会通过发布订阅模式，向外抛出一些hooks，而webpack的插件即可通过监听这些关键的事件节点，执行插件任务进而达到干预输出结果的目的。所以Plugin应该在webpack生命周期开始之前注册，才能监控到事件节点。</p><h3 id="打包原理"><a href="#打包原理" class="headerlink" title="打包原理"></a>打包原理</h3><p>webpack通过模拟module，exports，require变量，将我们的模块代码打包成一个IIFE(立即执行函数)，,函数参数是我们写的各个模块被包装之后组成的数组,浏览器执行这个立即执行函数就可以运行我们的模块代码。</p><ol><li>根据入口文件，先逐级递归识别依赖，构建依赖图谱</li><li>将代码转化成AST抽象语法树</li><li>在AST阶段中处理代码</li><li>把AST抽象语法树变成浏览器可以识别的代码， 然后输出</li></ol><h3 id="打包优化"><a href="#打包优化" class="headerlink" title="打包优化"></a>打包优化</h3><p>常见的优化就是拆包、分块、压缩等，主要是两个优化点：</p><ol><li>减少打包时间</li><li>减少打包大小</li></ol><h4 id="减少打包时间"><a href="#减少打包时间" class="headerlink" title="减少打包时间"></a>减少打包时间</h4><ol><li>优化解析时间：开启多进程打包</li></ol><ul><li>thread-loader（webpack4 官方推荐）：把这个 loader 放置在其他 loader 之前， 之后的 loader 就会在一个单独的 worker 池里运行，一个worker 就是一个nodeJS 进程。</li><li>HappyPack(将不再维护)：将loader的同步执行转为并行，减少loader的编译等待时间</li></ul><ol start="2"><li>合理利用缓存</li></ol><ul><li>缓存已编译过的文件（如设置babel-loader的cacheDirectory）</li><li>cache-loader：对性能开销较大的 loader 使用。可以将使用了该loader的结果缓存到磁盘里，显著提升二次构建速度</li></ul><ol start="3"><li>优化压缩时间：webpack4内置默认使用<code>terser-webpack-plugin</code> 插件压缩优化代码。</li><li>优化搜索时间：缩小文件搜索范围 减小不必要的编译工作</li></ol><ul><li>优化 loader 文件搜索范围：使用 Loader 时可以通过 test 、 include 、 exclude 三个配置项来命中 Loader 要应用规则的文件</li><li>优化resolve下的配置</li></ul><h4 id="减少打包大小"><a href="#减少打包大小" class="headerlink" title="减少打包大小"></a>减少打包大小</h4><ul><li>启用gzip压缩</li><li>按需加载：首页加载文件越小越好，将每个页面单独打包为一个文件</li><li>在webpack4中production下打包默认开启UglifyJS压缩代码</li><li>开启Tree-shaking等去掉无效代码</li></ul><h3 id="热更新（HMR）"><a href="#热更新（HMR）" class="headerlink" title="热更新（HMR）"></a>热更新（HMR）</h3><p>热更新（Hot Module Replacement）是指能够不用刷新浏览器而将新变更的模块替换掉旧的模块（不用刷新整个页面）。热更新实现主要分为几部分功能:</p><ul><li>服务器构建、推送更新消息</li><li>浏览器模块更新</li><li>模块更新后页面渲染</li></ul><p>原理：</p><ul><li>通过webpack-dev-server创建两个服务器：提供静态资源的服务（express）和Socket服务</li><li>使用 webpack-dev-server 托管静态资源，同时以 Runtime 方式注入 HMR 客户端代码；</li><li>浏览器加载页面后，与 WDS 建立 WebSocket 连接</li><li>Webpack 监听到文件变化后，增量构建发生变更的模块，并通过 WebSocket 发送 hash 事件</li><li>浏览器接收到 hash 事件后，请求 manifest 资源文件，确认增量变更范围</li><li>浏览器加载发生变更的增量模块</li><li>Webpack 运行时触发变更模块的 module.hot.accept 回调，执行代码变更逻辑</li></ul><p>简单理解：dev server 启动以后，会 watch 源文件的变化。当源文件发生变化后，Webpack 会重新编译打包, 再通过 ws 连接通知浏览器去获取新的打包文件，然后对页面做局部更新。</p><h2 id="webpack和rollup的选择"><a href="#webpack和rollup的选择" class="headerlink" title="webpack和rollup的选择"></a>webpack和rollup的选择</h2><p>Rollup 是一个 JavaScript 模块打包器（ES Module打包器），和webpack类似，但小巧的多，它可以将小块代码编译成整块代码，从而使得这些模块更好的运行在浏览器或nodeJS环境。Rollup中只能通过插件扩展。</p><p>Rollup优点：</p><ul><li>输出结果更加<code>扁平</code>，执行<code>效率</code>更高</li><li>良好的tree-shaking, 自动移除未引用代码</li><li>打包结果可读</li></ul><p>Rollup缺点：</p><ul><li>如要加载非ES Module第三方模块会比较复杂</li><li>模块最终会打包到全局，不能HMR</li><li>浏览器环境中，代码拆分模块需要用require.js这样的AMD库</li></ul><p>webpack 拆分代码， 按需加载；Rollup 所有资源放在同一个地方，一次性加载，利用 tree-shake 特性来剔除项目中未使用的代码，减少冗余。</p><p>Rollup偏向应用于js库、框架；webpack偏向应用于应用开发。如果应用场景中只是js代码，希望做ES转换，模块解析，可以使用Rollup。如果应用场景中涉及到css、html，涉及到复杂的代码拆分合并，建议使用webpack。</p><p>组件库选择rollup: webpack 无法构建出 esm 格式的 js 文件。 即使借助一些插件实现了，产出代码也比不上 rollup 简洁、干净。</p><h2 id="gulp和webpack"><a href="#gulp和webpack" class="headerlink" title="gulp和webpack"></a>gulp和webpack</h2><p><a href="https://blog.csdn.net/qq_41846861/article/details/88625460" target="_blank" rel="noopener">Glup和Webpack区别</a></p><h2 id="webpack5和webpack4区别"><a href="#webpack5和webpack4区别" class="headerlink" title="webpack5和webpack4区别"></a>webpack5和webpack4区别</h2><ul><li><p>webpack5通过优化 Tree Shaking 和代码生成来减小Bundle体积(更好的处理嵌套 tree-shaking)</p></li><li><p>压缩代码：<br>webpack4需要安装terser-webpack-plugin 插件</p><p>webpack5: 内置了 terser-webpack-plugin 插件，在 mode&#x3D;“production” 的时候会自动开启 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></pre></td><td class="code"><pre><span class="line"><span class="comment">// webpack.config.js中</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  optimization: &#123;</span><br><span class="line">    usedExports: <span class="literal">true</span>, <span class="comment">//只导出被使用的模块</span></span><br><span class="line">    minimize : <span class="literal">true</span> <span class="comment">// 启动压缩</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>缓存设置：<br>webpack5 内部内置了 cache 缓存机制, cache 会在开发模式下被设置成 type： memory 而且会在生产模式把cache 给禁用掉</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">// webpack.config.js</span></span><br><span class="line"><span class="built_in">module</span>.exports= &#123;</span><br><span class="line">  <span class="comment">// 使用持久化缓存</span></span><br><span class="line">  cache: &#123;</span><br><span class="line">    type: <span class="string">'filesystem'</span>，</span><br><span class="line">    cacheDirectory: path.join(__dirname, <span class="string">'node_modules/.cac/webpack'</span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>启动服务：<br>webpack4通过 webpack-dev-server 启动服务</p><p>webpack5内置使用 webpack serve 启动</p></li><li><p>模块依赖关系构建</p></li><li><p>打包体积更小</p></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;h2 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;/h2&gt;&lt;p&gt;webpack 是一个模块打包工具(module bundler)。当 webpack 处理应用程序时，它会递归地构建一个依赖关系图(dependency graph)，其中包含应用程序需要的每个模块，然后将所有这些模块打包成一个或多个 bundle。&lt;/p&gt;
    
    </summary>
    
      <category term="工程化" scheme="https://aartemida.github.io/categories/%E5%B7%A5%E7%A8%8B%E5%8C%96/"/>
    
    
      <category term="webpack" scheme="https://aartemida.github.io/tags/webpack/"/>
    
  </entry>
  
  <entry>
    <title>HTTP和HTTPS协议</title>
    <link href="https://aartemida.github.io/2022/07/09/HTTP%E5%92%8CHTTPS%E5%8D%8F%E8%AE%AE/"/>
    <id>https://aartemida.github.io/2022/07/09/HTTP和HTTPS协议/</id>
    <published>2022-07-09T08:05:54.000Z</published>
    <updated>2022-09-18T06:56:48.305Z</updated>
    
    <content type="html"><![CDATA[<h2 id="HTTP介绍"><a href="#HTTP介绍" class="headerlink" title="HTTP介绍"></a>HTTP介绍</h2><p>http是一个超文本传输协议。是一个客户端和服务器端请求和应答的标准。是一个基于请求与响应，无状态的，应用层协议，通常基于TCP&#x2F;IP协议传输数据。</p><a id="more"></a><p>特点：</p><ul><li>无状态：协议自身不对请求和响应之间的通信状态进行保存，任何两次请求之间都没有依赖关系。</li><li>无连接：服务器处理完客户端的请求，并收到客户端的应答后，立即断开连接。</li><li>基于请求与响应：由客户端发起请求，服务端响应</li><li>优点：简单快速灵活</li></ul><h3 id="HTTP的发展历程"><a href="#HTTP的发展历程" class="headerlink" title="HTTP的发展历程"></a>HTTP的发展历程</h3><table><thead><tr><th align="center">版本</th><th align="center">时间</th><th align="center">主要内容</th></tr></thead><tbody><tr><td align="center">HTTP&#x2F;0.9</td><td align="center">1991年</td><td align="center">只有GET请求，不涉及数据包传输(只有文本传输)</td></tr><tr><td align="center">HTTP 1.0</td><td align="center">1996年</td><td align="center">传输内容格式不限、增加HEAD、POST 等新方法、响应状态码、增加请求头</td></tr><tr><td align="center">HTTP 1.1</td><td align="center">1997年</td><td align="center">客户端缓存（强缓存和协商缓存）、长连接(持久连续)、断点续传优化、新增请求方法和状态码：OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法</td></tr><tr><td align="center">HTTP 2.0</td><td align="center">2015年</td><td align="center">多路复用、服务端推送、头部压缩、二进制分帧</td></tr><tr><td align="center">HTTP 3.0</td><td align="center">2021年</td><td align="center">类似TCP的流量控制、集成TLS加密、快速握手</td></tr></tbody></table><ul><li><p>长连接：HTTP 1.1的长连接(Connection: keep-alive)是复用一个TCP连接，在一个TCP中可以发送多次请求，当客户端与服务端的某一方或者同时关闭连接的时候，或者网络原因，这个连接才会断掉。缺点是HTTP 1.1 基于串行文件传输数据，因此这些请求必须是有序的。</p></li><li><p>多路复用：客户端和服务端可以并行发起或者回复，避免串行带来的阻塞。HTTP2.0对同一域名下所有请求都是基于流，也就是说同一域名不管访问多少文件，也只建立一次连接。提高了最大并发数。</p></li><li><p>二进制分帧：HTTP&#x2F;2 把 HTTP 协议通信的基本单位缩小为一个一个的帧，这些帧对应着逻辑流中的消息。并行地在同一个 TCP 连接上双向交换消息。二进制分帧层在 应用层(HTTP&#x2F;2)和传输层(TCP or UDP)之间。</p><p>HTTP2.0 引入二进制数据帧和流的概念，其中帧对数据进行顺序标识，这样浏览器收到数据之后，就可以按照序列对数据进行合并，而不会出现合并后数据错乱的情况。同样是因为有了序列，服务器就可以并行的传输数据。</p></li><li><p>缓存处理：HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准，HTTP1.1则引入了更多的缓存控制策略例如Entity tag，If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略。</p></li></ul><h4 id="HTTP1-1的缺点"><a href="#HTTP1-1的缺点" class="headerlink" title="HTTP1.1的缺点"></a>HTTP1.1的缺点</h4><ul><li>无状态特性，带来巨大的HTTP头部（解决方案：头部压缩）</li><li>明文传输，不安全</li><li>不支持服务器推送消息</li><li>请求延迟比较高：浏览器客户端在同一时间，针对同一域名下的请求有一定数量限制，而超过限制数目的请求会被阻塞（解决方案：多路复用）</li></ul><h4 id="HTTP2-0的缺点"><a href="#HTTP2-0的缺点" class="headerlink" title="HTTP2.0的缺点"></a>HTTP2.0的缺点</h4><ul><li>TCP建立连接的延迟（握手延迟）</li><li>TCP的队头阻塞没有彻底解决</li></ul><h4 id="HTTP1-1和HTTP2的主要区别"><a href="#HTTP1-1和HTTP2的主要区别" class="headerlink" title="HTTP1.1和HTTP2的主要区别"></a>HTTP1.1和HTTP2的主要区别</h4><ul><li>HTTP2采用二进制分帧(基于SPDY)</li><li>HTTP2是完全<code>多路复用</code>的，而非有序并阻塞的——只需一个连接即可实现并行</li><li>使用报头压缩，HTTP2降低了开销</li><li>HTTP2让服务器可以将响应主动“推送”到客户端缓存中</li></ul><h4 id="HTTP3"><a href="#HTTP3" class="headerlink" title="HTTP3"></a>HTTP3</h4><p>HTTP3.0的底层支撑协议<code>QUIC</code>基于<code>UDP</code>实现，又含TCP的特点，实现了又快又可靠的协议。</p><h3 id="HTTP报文"><a href="#HTTP报文" class="headerlink" title="HTTP报文"></a>HTTP报文</h3><p>HTTP交互的信息称为HTTP报文，HTTP报文是由多行数据构成的字符串文本。</p><p>HTTP请求报文主要包括请求行（由请求方法、URL和HTTP协议版本组成）、请求头部、请求的实体（传参）三部分，HTTP响应报文同样也分为三部分，有状态行（协议版本、状态码与原因短语）、响应头部、实体。</p><h3 id="HTTP缺点"><a href="#HTTP缺点" class="headerlink" title="HTTP缺点"></a>HTTP缺点</h3><ul><li>通信使用明文传输不安全</li><li>不验证通信方身份可能遭遇伪装</li><li>无法证明报文完整性，可能遭篡改</li></ul><h3 id="HTTPS介绍"><a href="#HTTPS介绍" class="headerlink" title="HTTPS介绍"></a>HTTPS介绍</h3><p>可以理解为 HTTPS &#x3D; HTTP + 加密 + 认证 + 数据完整性保护。基于HTTP协议，通过SSL或TSL提供加密数据处理、认证身份以及数据完整性保护。</p><p>特点：</p><ul><li>内容加密：采用混合加密（结合非对称加密和对称加密技术），中间者无法直接查看明文内容</li><li>验证身份：通过证书认证客户端访问的是自己的服务器</li><li>保护数据完整性：防止传输的内容被中间人篡改</li></ul><h4 id="SSL建立连接过程"><a href="#SSL建立连接过程" class="headerlink" title="SSL建立连接过程"></a>SSL建立连接过程</h4><ol><li>客户端向服务器发送请求（发送随机值1和客户端加密算法）</li><li>服务器接收到后给客户端发送响应握手（发送随机值2和协商好的加密算法）</li><li>服务器给客户端发送第二个响应报文（数字证书：一对公钥和私钥）</li><li>客户端解析证书。（TLS验证公钥是否有效，如果没有问题就生成随机值：预主秘钥）</li><li>认证通过后，组装所有随机值成会话秘钥，通过证书的公钥加密会话秘钥</li><li>传输加密信息</li><li>服务端解密，组装会话秘钥，和客户端一致</li><li>客户端通过会话秘钥加密信息给服务器，验证服务器是否能正常接受消息</li><li>服务器通过会话秘钥加密消息并回传给客户端，如果客户端正常接受就表面连接建立</li></ol><h4 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h4><ul><li>依然对黑客攻击、服务器劫持等方面没有什么安全作用</li><li>https页面加载时间比http长，影响用户体验</li><li>连接缓存不如http高效，流量成本高</li><li>服务器端资源占用更多，需要更高的成本</li></ul><h2 id="TCP-x2F-IP协议族"><a href="#TCP-x2F-IP协议族" class="headerlink" title="TCP&#x2F;IP协议族"></a>TCP&#x2F;IP协议族</h2><p>TCP&#x2F;IP协议族分层划分。</p><ul><li>应用层：向用户提供应用服务时的通信活动，如FTP（文件传输协议）、DNS（域名系统，提供域名到IP地址的解析服务）、<code>HTTP</code></li><li>传输层：<code>TCP</code>（传输控制协议，提供可靠的字节流服务）以及UDP（用户数据报协议）</li><li>网络层：处理网络上流动的数据包，如<code>IP协议</code></li><li>数据链路层：处理网络连接的硬件部分</li></ul><h3 id="HTTP的通信传输"><a href="#HTTP的通信传输" class="headerlink" title="HTTP的通信传输"></a>HTTP的通信传输</h3><ol><li>建立TCP连接（三次握手建立连接）</li><li>客户端向服务器发送请求，客户端发送请求头</li><li>服务器应答，服务器发送应答头，服务器发送数据</li><li>服务器关闭连接（如果Connection: keep-alive，发送完数据连接也不会断开）</li></ol><p>TCP三次握手过程：</p><ol><li>客户端发送带SYN标志的数据包给服务器</li><li>服务器收到后，回传一个SYN&#x2F;ACK标志的数据包确认信息</li><li>客户端最后回传一个ACK标志数据包，代表握手结束</li></ol><p>三次握手优点：确认可靠传输；防止已失效或者滞留过期的信息发送到服务端，导致资源浪费。</p><p>TCP四次挥手：</p><ol><li>客户端发送一个释放连接TCP数据报文段</li><li>服务器进入关闭等待状态，并向客户端发送一条普通的TCP确认报文段</li><li>服务器没有数据后会向客户端发送一条TCP释放连接报文段</li><li>客户端回向服务器发送一个TCP确认报文段</li></ol><h3 id="TCP与UDP的区别"><a href="#TCP与UDP的区别" class="headerlink" title="TCP与UDP的区别"></a>TCP与UDP的区别</h3><ul><li>TCP是面向连接的，UDP是无连接的</li><li>TCP提供可靠的服务，UDP是不保证可靠性交付(可能丢包)</li><li>TCP是面向字节流，UDP是面向报文</li><li>TCP只能是1对1的，而UDP可以1对1，也可以1对多</li></ul><p>有些应用场景对可靠性要求不高会用到UPD，比如直播，要求速率。</p><h2 id="常见状态码"><a href="#常见状态码" class="headerlink" title="常见状态码"></a>常见状态码</h2><ul><li>1xx：指示信息</li><li>2xx：成功</li><li>3xx：重定向<ul><li>301：永久性重定向（如果没有额外指定，这个响应默认会被浏览器缓存）</li><li>302：临时重定向（这个响应默认不会被浏览器缓存）</li><li>304：对客户端有缓存情况下服务端的一种响应（发送附带条件的请求时，条件不满足时返回，与重定向无关）</li></ul></li><li>4xx：客户端错误<ul><li>400：请求报文语法有误，服务器无法识别</li><li>401：请求需要认证</li><li>403：请求的对应资源禁止被访问</li><li>404：服务器无法找到对应资源</li></ul></li><li>5xx：服务器端错误</li></ul>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;HTTP介绍&quot;&gt;&lt;a href=&quot;#HTTP介绍&quot; class=&quot;headerlink&quot; title=&quot;HTTP介绍&quot;&gt;&lt;/a&gt;HTTP介绍&lt;/h2&gt;&lt;p&gt;http是一个超文本传输协议。是一个客户端和服务器端请求和应答的标准。是一个基于请求与响应，无状态的，应用层协议，通常基于TCP&amp;#x2F;IP协议传输数据。&lt;/p&gt;
    
    </summary>
    
      <category term="HTTP" scheme="https://aartemida.github.io/categories/HTTP/"/>
    
    
      <category term="http" scheme="https://aartemida.github.io/tags/http/"/>
    
  </entry>
  
  <entry>
    <title>写给自己的前端面试指南</title>
    <link href="https://aartemida.github.io/2022/04/19/%E5%86%99%E7%BB%99%E8%87%AA%E5%B7%B1%E7%9A%84%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E6%8C%87%E5%8D%97/"/>
    <id>https://aartemida.github.io/2022/04/19/写给自己的前端面试指南/</id>
    <published>2022-04-19T07:03:20.000Z</published>
    <updated>2022-10-17T13:58:29.965Z</updated>
    
    <content type="html"><![CDATA[<p>分类收集整理一些前端面试要点。</p><a id="more"></a><h2 id="熟练掌握JavaScript"><a href="#熟练掌握JavaScript" class="headerlink" title="熟练掌握JavaScript"></a>熟练掌握JavaScript</h2><h3 id="this指向-star"><a href="#this指向-star" class="headerlink" title="this指向 :star:"></a>this指向 :star:</h3><p>代表函数运行时，自动生成的一个内部对象，只能在函数内部使用。</p><ul><li><p>全局环境下的this：指向全局对象window</p></li><li><p>函数内的this：</p><ul><li>默认情况：非严格模式下this指向全局对象, 严格模式下this会指向undefined</li><li>箭头函数：箭头函数本身没有 this， this的指向由外层作用域决定的</li><li>构造函数：构造函数中的this是指向实例，不会被任何方式改变 this</li><li>通过call()、apply()或者bind()直接指定this的绑定对象，bind()是创建一个新的函数，需要手动调用才会执行；多次bind，this指向第一次的bind</li></ul></li><li><p>对象中的this：</p><ul><li>函数的定义位置不影响其this指向，this指向只和调用函数的对象有关(this 永远指向最后<code>调用</code>它的那个对象)</li><li>多层嵌套的对象，内部方法的this指向离被调用函数最近的对象</li></ul></li></ul><p><a href="https://juejin.cn/post/6844904083707396109" target="_blank" rel="noopener">this面试题</a></p><h4 id="箭头函数-star"><a href="#箭头函数-star" class="headerlink" title="箭头函数 :star:"></a>箭头函数 :star:</h4><p>箭头函数内部的this是词法作用域（本身没有this），由上下文确定。在以下需要用this的场景尽量不使用：</p><ul><li>定义对象的方法</li><li>定义原型方法</li><li>构造函数使用箭头函数</li><li>作为事件的回调函数</li></ul><h3 id="原型-star"><a href="#原型-star" class="headerlink" title="原型 :star:"></a>原型 :star:</h3><p>原型：用于实现对象的继承，可以理解为对象的父对象。每个对象都有__proto__属性，指向了创建该对象的构造函数的原型。</p><p><code>原型链</code>是由原型对象组成，__proto__将对象连接起来组成了原型链。是一个用来实现继承和共享属性的对象链。</p><p>实例的 constructor 属性指向构造函数，构造函数又通过 prototype 属性指向原型。</p><blockquote><p><code>__proto__</code>和<code>constructor</code>属性是对象所独有的。prototype属性是函数所独有的。但是由于JS中函数也是一种对象，所以函数也拥有__proto__和constructor属性。</p></blockquote><blockquote><p><code>prototype</code>是从函数指向一个对象（以当前函数作为构造函数构造出来的对象的原型对象，包含了constructor），可以让所有实例都共享公用的属性和方法。</p></blockquote><blockquote><p><code>constructor</code>是指向该对象的构造函数。可以理解为所有函数(对象)都由<code>Function</code>构造而来，所以<code>constructor</code>的终点就是<code>Function</code>。</p></blockquote><p>属性查找机制： <code>__proto__</code>属性（即<code>[[prototype]]</code>）是一个对象指向它的原型对象的指针。当访问一个对象的属性，如果找不到对象的该属性，就会沿着<code>__proto__</code>指向的父对象找，如果还没找到就会继续沿着<code>__proto__</code>指向找，直到找到原型链的终点<code>null</code>，这种就是<code>原型链</code>。</p><p>对象本身 -&gt; 构造函数（constructor） -&gt; 对象的原型（__proto__） -&gt; 构造函数的原型（prototype）</p><p>属性修改机制：只会修改实例对象本身的属性，不会直接修改到原型对象。</p><p><a href="https://blog.51cto.com/u_14582976/2826828" target="_blank" rel="noopener">搞懂JS中的prototype、__proto__与constructor</a></p><h4 id="new操作符"><a href="#new操作符" class="headerlink" title="new操作符"></a>new操作符</h4><p>new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。</p><p>操作：</p><ol><li><p>.开辟一个新的内存空间，创建一个空的JS对象；</p></li><li><p>为创建的对象添加<code>__proto__</code>属性，该属性指向构造函数的原型对象prototype；</p><figure class="highlight js"><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">newobj.__proto__ = father.protetype</span><br><span class="line"><span class="comment">// newobj.__proto__.constuctor = father</span></span><br></pre></td></tr></table></figure></li><li><p>将构造函数的作用域赋给新对象（因此this就指向了这个对象）；</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">father.call(newobj)</span><br></pre></td></tr></table></figure></li></ol><p>执行构造函数中的代码（为这个新对象添加属性）。<br>4. 返回的对象赋值给newobj；如果函数没有返回对象，则返回这个this（这也是为什么构造函数通常不写return语句）</p><h3 id="继承"><a href="#继承" class="headerlink" title="继承"></a>继承</h3><ul><li>原型链继承（原型的共享性会导致实例的<code>引用类型值相同</code>）、</li><li>构造函数继承（不能继承原型属性&#x2F;方法；无法实现函数复用，每个方法都要在实例上重新创建一遍，影响性能）、</li><li>原型链+借用构造函数的组合继承</li><li>class 继承</li></ul><p>ES5的继承时通过原型或构造函数机制来实现。ES6通过class定义类实现，里面有构造方法，类之间通过extends关键字实现继承。</p><p>ES5和ES6继承机制的区别：<br>ES5先创建子类，再将父类的方法添加到这个对象上面，即<code>“实例在前，继承在后”</code>；ES6先将父类的属性和方法，加到一个空的对象上面，然后再将该对象作为子类的实例，在子类中通过调用super方法访问父级后，通过修改this实现继承，即<code>“继承在前，实例在后”</code>。</p><p><a href="https://segmentfault.com/a/1190000016708006" target="_blank" rel="noopener">JavaScript常见的六种继承方式</a></p><h4 id="class继承"><a href="#class继承" class="headerlink" title="class继承"></a>class继承</h4><p>修饰符：</p><ul><li>pubilc</li><li>peivate(只能在当前定义的类里面进行访问 在其它子类中进行访问会提示错误)</li><li>protected(子类可以访问它的属性 不属于它的子类 访问会报错)</li><li>readonly</li><li>static(可以直接访问的属性 不需要实例化 可以直接在类上进行调用，静态属性通过浅拷贝实现继承)</li></ul><p>extends 来实现继承(es6继承 &#x3D; 寄生组合式继承 + 修改子类__proto__)：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// B 的实例继承 A 的实例</span></span><br><span class="line"><span class="built_in">Object</span>.setPrototypeOf(B.prototype, A.prototype); <span class="comment">// B.prototype.__proto__ === A.prototype // true</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// B 继承 A 的静态属性</span></span><br><span class="line"><span class="built_in">Object</span>.setPrototypeOf(B, A); <span class="comment">//B.__proto__ === A // true</span></span><br></pre></td></tr></table></figure><p>extends关键字不仅可以用来继承类，还可以用来继承原生的构造函数。</p><h5 id="为什么子类的构造函数，一定要调用super-，不然就报错？"><a href="#为什么子类的构造函数，一定要调用super-，不然就报错？" class="headerlink" title="为什么子类的构造函数，一定要调用super()，不然就报错？"></a>为什么子类的构造函数，一定要调用super()，不然就报错？</h5><p>ES6 要求，子类的构造函数必须执行一次super函数。（新建子类实例时，父类的构造函数必定会先运行一次）</p><p>这和class的继承机制有关，super会生成一个继承父类的this对象，通过这个继承父类。class中的super表示父类的构造函数，用来新建一个父类的实例对象。</p><p>super可以作为函数使用，也可以作为对象使用。super内部的this指的是子类的实例。</p><h3 id="Promise和-async-x2F-await-star"><a href="#Promise和-async-x2F-await-star" class="headerlink" title="Promise和 async&#x2F;await :star:"></a>Promise和 async&#x2F;await :star:</h3><p>Promise是ES6中新加的一个函数，用来解决js中进行异步编程的方式。</p><p>Promise解决了异步嵌套的怪圈，异步请求方法用链式表达更加清晰，但是如果有大量的异步请求的时候，流程复杂的情况下，会发现充满了屏幕的then，看起来非常吃力，而ES7的Async&#x2F;Await的出现就是为了解决这种复杂的情况。</p><p>错误捕捉方式：promise 使用.catch；async&#x2F;await 可以.then.catch，也可以 try&#x2F;catch</p><p>调用方式：promise 连续调用，链式调用；async&#x2F;await 简洁易懂，异步操作同步化</p><p><a href="https://segmentfault.com/a/1190000016788484" target="_blank" rel="noopener">异步Promise及Async&#x2F;Await可能最完整入门攻略</a></p><h4 id="Promise的特性"><a href="#Promise的特性" class="headerlink" title="Promise的特性"></a>Promise的特性</h4><ul><li>Promise的状态不受外界影响。Promise对象代表一个异步操作，有三种状态：<code>pending</code>（进行中）、<code>fulfilled</code>（已成功）和<code>rejected</code>（已失败）。只有异步操作的结果，可以决定当前是哪一种状态，任何其他操作都无法改变这个状态。</li><li>一旦状态改变，就不会再变，任何时候都可以得到这个结果。Promise对象的状态改变，只有两种可能：从pending变为fulfilled和从pending变为rejected。只要这两种情况发生，状态就凝固了，不会再变了，会一直保持这个结果，这时就称为 resolved（已定型）。如果改变已经发生了，再对Promise对象添加回调函数，也会立即得到这个结果。</li></ul><h4 id="Promise-all"><a href="#Promise-all" class="headerlink" title="Promise.all"></a>Promise.all</h4><ul><li>all() : 所有都成功的时候返回的是一个结果数组，而失败的时候则返回最先被reject失败状态的值。</li><li>race(): 哪个结果获得的快，就返回那个结果，不管结果本身是成功状态还是失败状态。</li><li>allSettled() : 当给定的promise数组中的所有promise被拒绝后会返回一个拒绝的promise数组，一一对应。</li></ul><h4 id="Promise中的then第二个参数和catch有什么区别？"><a href="#Promise中的then第二个参数和catch有什么区别？" class="headerlink" title="Promise中的then第二个参数和catch有什么区别？"></a>Promise中的then第二个参数和catch有什么区别？</h4><p>如果在then的第一个函数里抛出了异常，后面的catch能捕获到，而then的第二个函数捕获不到。</p><p>then的第二个参数和catch捕获错误信息的时候会就近原则，如果是promise内部报错，reject抛出错误后，then的第二个参数和catch方法都存在的情况下，只有then的第二个参数能捕获到，如果then的第二个参数不存在，则catch方法会捕获到。</p><h4 id="promise实现并发个数限制"><a href="#promise实现并发个数限制" class="headerlink" title="promise实现并发个数限制"></a>promise实现并发个数限制</h4><p><a href="https://blog.csdn.net/guizi0809/article/details/117227693" target="_blank" rel="noopener">限制并发请求数量</a></p><h3 id="Event-loop-star"><a href="#Event-loop-star" class="headerlink" title="Event loop :star:"></a>Event loop :star:</h3><p>事件循环机制指浏览器或Node的一种解决javaScript单线程运行时不会阻塞的一种机制，也是使用异步的原理。</p><p>javascript是一门单线程语言，执行事件是先同步再异步，异步中先微任务，再宏任务。event loop它最主要是分三部分：主线程、宏任务（macro-task）、微任务（micro-task）。</p><ul><li>macro-task(宏任务)：setTimeout，setInterval， script (宏任务是由浏览器规定的)</li><li>micro-task(微任务)：Promise.then&#x2F;catch，process.nextTick (微任务是js语法规定的)</li></ul><p>执行流程：主线程从”任务队列”中读取执行事件，这个过程是循环不断的，这个机制被称为事件循环。主线程会不断从任务队列中按顺序取任务执行，每执行完一个任务都会检查微任务队列是否为空（执行完一个任务的具体标志是函数执行栈为空），如果不为空则会一次性执行完所有微任务。然后再进入下一个循环去任务队列中取下一个任务执行。</p><ol><li>所有同步任务都在主线程上执行，形成一个执行栈</li><li>主线程之外，还存在一个”任务队列”。只要异步任务有了运行结果，就在”任务队列”之中放置一个事件</li><li>一旦”执行栈”中的所有同步任务执行完毕，系统就会读取”任务队列”，看看里面有哪些事件。那些对应的异步任务，于是结束等待状态，进入执行栈，开始执行。</li></ol><p><a href="https://segmentfault.com/a/1190000038928521" target="_blank" rel="noopener">js事件循环机制event-loop</a></p><h4 id="为什么区分宏任务和微任务"><a href="#为什么区分宏任务和微任务" class="headerlink" title="为什么区分宏任务和微任务"></a>为什么区分宏任务和微任务</h4><p>区分微任务和宏任务的根本原因是为了插队。由于微任务执行快，一次性可以执行很多个，在当前宏任务执行后立刻清空微任务可以达到伪同步的效果，这对视图渲染效果起到至关重要的作用。</p><ul><li>微任务是线程之间的切换，速度快。不用进行上下文切换，可以快速的一次性做完所有的微任务。</li><li>宏任务是进程之间的切换，速度慢，且每次执行需要切换上下文。因此一个Eventloop中只执行一个宏任务。</li></ul><h4 id="node和浏览器循环的区别"><a href="#node和浏览器循环的区别" class="headerlink" title="node和浏览器循环的区别"></a>node和浏览器循环的区别</h4><p>区别主要是node的 宏任务 分好几种，而这好几种又有不同的 任务队列，而不同的 任务队列 又有顺序区别，而 微任务是穿插在每一种宏任务之间的。</p><p>浏览器：当某个宏任务执行完后,会查看是否有微任务队列。如果有，先执行微任务队列中的所有任务，如果没有，会读取宏任务队列中排在最前的任务，执行宏任务的过程中，遇到微任务，依次加入微任务队列。栈空后，再次读取微任务队列里的任务，依次类推。</p><p>node中：libuv 引擎中的事件循环分为 6 个阶段，它们会按照顺序反复运行。每当进入某一个阶段的时候，都会从对应的回调队列中取出函数去执行。当队列为空或者执行的回调函数数量到达系统设定的阈值，就会进入下一阶段。</p><h3 id="作用域和作用域链"><a href="#作用域和作用域链" class="headerlink" title="作用域和作用域链"></a>作用域和作用域链</h3><p>JavaScript采用的是静态作用域（词法作用域），函数的作用域在函数定义的时候就决定了。</p><p><code>作用域</code>就是代码的执行环境，全局执行环境就是全局作用域，函数的执行环境就是私有作用域，它们都是栈内存。（规定变量和函数的可使用范围称为作用域，JS的作用域靠函数形成）</p><p><code>作用域链</code>（作用域形成的链条）是在函数定义的时候创建的(函数有一个内部属性 [[scope]], 创建时保存所有父变量对象到其中)。当查找变量的时候，会先从当前上下文的变量对象中查找，如果没有找到，就会从上级执行上下文的变量对象中查找，一直找到全局上下文的变量对象。这样由多个执行上下文的<code>变量对象</code>构成的链表就叫做作用域链。</p><p><a href="https://blog.csdn.net/destinytaoer/article/details/82114848" target="_blank" rel="noopener">JS 作用域和作用域链</a></p><h3 id="js执行环境"><a href="#js执行环境" class="headerlink" title="js执行环境"></a>js执行环境</h3><p>js代码的执行分为<code>编译器的编译解析</code>和<code>js引擎与作用域执行</code>两个阶段。</p><p>执行环境定义了变量或函数是否具有访问其他数据的权限,进而决定各自行为。</p><p>JS解释器不会编译代码，而是边解析边执行的，对于语法错误，如果没有被执行到就不会报错。语法检查之后，进入<code>预解析</code>阶段，在预解析阶段会在内存中创建<code>执行上下文</code>，也叫执行环境。</p><p>当函数执行&#x2F;调用时，会进入一个特定的执行环境，分为全局执行环境、函数执行环境、eval()函数的动态执行环境。</p><p>每个执行环境由三部分构成：</p><ul><li>变量对象(所有的变量(变量 函数 形参 arguments)组成的一个对象)</li><li>作用域链([[Scope]]作用域即变量对象，作用域链是一个由变量对象组成的单向链表，作用是用来进行变量查找。)</li><li>this(指向一个环境对象)</li></ul><p>执行环境生命周期：在预解析阶段被<code>创建</code>、代码执行阶段被<code>重新赋值</code>，代码执行完<code>出栈</code>、等待被<code>回收</code>。</p><h4 id="执行上下文栈"><a href="#执行上下文栈" class="headerlink" title="执行上下文栈"></a>执行上下文栈</h4><p>JS实现为单线程，因此同一时刻仅有一个环境处于运行状态。因此，可以把JS的执行实现为栈，每次进入一个新的执行环境，都会把该环境置于栈顶执行，执行完毕后可弹出。因此形成了一个执行上下文栈。而变量的查找正是基于该栈的。</p><h3 id="变量提升"><a href="#变量提升" class="headerlink" title="变量提升"></a>变量提升</h3><p>函数在运行的时候，会首先创建执行上下文，然后将执行上下文入栈，然后当此执行上下文处于栈顶时，开始运行执行上下文。</p><p>在创建执行上下文的过程中：创建变量对象，创建作用域链，确定 this 指向。其中创建变量对象的过程中，首先会为 arguments 创建一个属性，值为 arguments，然后会扫 function 函数声明，创建一个同名属性，值为函数的引用，接着会扫 var 变量声明，创建一个同名属性，值为 undefined，这就是变量提升。</p><h3 id="闭包"><a href="#闭包" class="headerlink" title="闭包"></a>闭包</h3><p>闭包就是能够读取其他函数内部变量的函数，参数和变量不会被垃圾回收机制收回。</p><p>闭包的原理：利用作用域链的特性，所以闭包访问的上级作用域中的变量对象，其值为其运算结束后的最后一个值。（当前环境中存在指向父级作用域的引用）</p><p>如何产生闭包：</p><ul><li>函数中返回函数</li><li>函数当做参数传递</li></ul><p>闭包的特点：</p><ul><li>里面的变量和参数不会被垃圾回收机制回收（按照作用域链的特点，闭包使用的变量不会销毁，因为函数会一直被调用，所以只能一直存在）</li><li>闭包能访问外部变量，闭包的局部变量无法被外部访问（闭包的私有变量在自执行后直接销毁）</li></ul><p>闭包的缺点：</p><ul><li>可能造成多余的内存消耗</li><li>可能造成内存泄漏</li></ul><p>闭包的作用：</p><ul><li>封装私有变量，防止变量被全局污染</li><li>存储变量，实现累加</li></ul><p><a href="https://blog.csdn.net/happy_kx/article/details/124330489" target="_blank" rel="noopener">闭包的作用</a></p><h3 id="判断Object类型"><a href="#判断Object类型" class="headerlink" title="判断Object类型"></a>判断Object类型</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">isPlainObject</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> val !== <span class="literal">null</span> &amp;&amp; !<span class="built_in">Array</span>.isArray(val) &amp;&amp; <span class="keyword">typeof</span> val === <span class="string">'object'</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//2</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">isPlainObject</span>(<span class="params">val</span>)</span>&#123;</span><br><span class="line">  <span class="keyword">return</span> val.constructor == <span class="built_in">Object</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">isPlainObject</span>(<span class="params">val</span>)</span>&#123;</span><br><span class="line">  <span class="keyword">return</span> Obejct.prototype.toString.call(val) == <span class="string">'[object Object]'</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="instanceof-和-typeof"><a href="#instanceof-和-typeof" class="headerlink" title="instanceof 和 typeof"></a>instanceof 和 typeof</h3><p><code>typeof</code> 对于原始类型来说，除了 null 都可以显示正确的类型。</p><p>typeof原理：不同的对象在底层都表示为二进制，在Javascript中二进制前（低）三位存储其类型信息</p><p><code>instanceof</code> 判断一个<code>对象</code>是否是另一个对象的实例。通过<code>原型链</code>的方式来判断是否为构造函数的实例（用于判断某个实例是否属于某构造函数）。</p><blockquote><p>instanceof 可以准确判断对象(引用)类型，但是不能准确检测原始类型。</p></blockquote><p>PS：可以通过修改原型的指向修改 instanceof。</p><p>instanceof原理：查找构造函数的原型对象是否在实例对象的原型链上，如果在返回true，如果不在返回false。<code>a.__proto__ === Fn.prototype</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">typeof</span> <span class="literal">undefined</span> <span class="comment">// 'undefined'</span></span><br><span class="line"><span class="keyword">typeof</span> <span class="literal">null</span> <span class="comment">//object</span></span><br><span class="line"><span class="keyword">typeof</span> [] <span class="comment">//object</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typeof</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125; <span class="comment">// 'function'</span></span><br><span class="line"><span class="keyword">typeof</span> <span class="class"><span class="keyword">class</span> <span class="title">a</span></span>&#123;&#125; <span class="comment">// function</span></span><br><span class="line"><span class="keyword">typeof</span> /a/ <span class="comment">// 'object'</span></span><br><span class="line"><span class="keyword">typeof</span> <span class="keyword">new</span> <span class="built_in">Map</span>() <span class="comment">// 'object'</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//instanceof</span></span><br><span class="line"><span class="keyword">const</span> Fn = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.name = <span class="string">'构造函数'</span></span><br><span class="line">&#125;</span><br><span class="line">Fn.prototype = <span class="built_in">Object</span>.create(<span class="built_in">Array</span>.prototype)</span><br><span class="line"><span class="keyword">let</span> a = <span class="keyword">new</span> Fn()</span><br><span class="line"><span class="built_in">console</span>.log(a <span class="keyword">instanceof</span> <span class="built_in">Array</span>) <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log <span class="keyword">instanceof</span> <span class="built_in">Function</span> <span class="comment">//true</span></span><br><span class="line"><span class="built_in">console</span>.log <span class="keyword">instanceof</span> <span class="built_in">Object</span> <span class="comment">//true</span></span><br><span class="line"><span class="number">1</span> <span class="keyword">instanceof</span> <span class="built_in">Number</span> <span class="comment">// false</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">myInstanceof</span>(<span class="params">left, right</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> proto = <span class="built_in">Object</span>.getPrototypeOf(left); <span class="comment">// 获取对象的原型</span></span><br><span class="line">    prototype = right.prototype; <span class="comment">// 获取构造函数的 prototype 对象</span></span><br><span class="line">  <span class="comment">// 判断构造函数的 prototype 对象是否在对象的原型链上</span></span><br><span class="line">  <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (!proto) <span class="keyword">return</span> <span class="literal">false</span>; <span class="comment">//找到也没有找到 尽头Object.prototype.__proto__ = null</span></span><br><span class="line">    <span class="keyword">if</span> (proto === prototype) <span class="keyword">return</span> <span class="literal">true</span>; <span class="comment">//找到了</span></span><br><span class="line"></span><br><span class="line">    proto = <span class="built_in">Object</span>.getPrototypeOf(proto); <span class="comment">// 接着往上找</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>在JavaScript中拥有自动的垃圾回收机制，通过一些回收算法，找出不再使用引用的变量或属性，由JS引擎按照固定时间间隔周期性的释放其所占的内存空间。</p><p>JavaScript 中主要的内存管理概念是可达性。大概意思是以某种方式可以访问到或者可以使用的值，它们就是需要保存在内存中，无法访问，也无法使用的值，则需要被垃圾回收机制回收。</p><ul><li>引用计数（当该引用类型的值的引用次数为0，就说明没有变量被该引用类型的值赋值）</li><li>标记清除（销毁那些未带标记的值并回收他们所占用的内存空间）</li></ul><h3 id="call-apply-bind"><a href="#call-apply-bind" class="headerlink" title="call apply bind"></a>call apply bind</h3><p>call的性能优于apply。</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">Function</span>.prototype.myCall = <span class="function"><span class="keyword">function</span>(<span class="params">context, ...args</span>)</span>&#123;</span><br><span class="line">    <span class="comment">// 不传第一个参数默认为windows</span></span><br><span class="line">    context = context || <span class="built_in">window</span></span><br><span class="line">    <span class="comment">//普通函数的this指向调用者, 故this就是要改造的那个函数</span></span><br><span class="line">    <span class="comment">//将函数挂载到目标对象上</span></span><br><span class="line">    context.func = <span class="keyword">this</span></span><br><span class="line">    <span class="comment">//执行函数，并保存返回值</span></span><br><span class="line">    <span class="keyword">let</span> res = context.func(...args)</span><br><span class="line">    <span class="comment">//删除之前挂载在目标对象上的函数，还原目标对象</span></span><br><span class="line">    <span class="keyword">delete</span> context.func</span><br><span class="line">    <span class="comment">//return返回值</span></span><br><span class="line">    <span class="keyword">return</span> res</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">Function</span>.prototype.myApply = <span class="function"><span class="keyword">function</span>(<span class="params">context, args</span>)</span>&#123;</span><br><span class="line">    context = context || <span class="built_in">window</span></span><br><span class="line">    context.func = <span class="keyword">this</span></span><br><span class="line">    <span class="keyword">let</span> res = context.func(...args)</span><br><span class="line">    <span class="keyword">delete</span> context.func</span><br><span class="line">    <span class="keyword">return</span> res</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">Function</span>.prototype.myBind = <span class="function"><span class="keyword">function</span>(<span class="params">context, ...args</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">let</span> _this = <span class="keyword">this</span></span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> <span class="title">Fn</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">      <span class="comment">//因为返回值是一个函数，需要判断 new Fn() 的情况</span></span><br><span class="line">      <span class="comment">//new 会让 this 指向这个新的对象，所以直接判断 Fn 是否在 this 的原型链上</span></span><br><span class="line">      <span class="keyword">if</span>(<span class="keyword">this</span> <span class="keyword">instanceof</span> Fn)&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> _this(...args, ...arguments)</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="comment">//调用 call 改变 this 指向</span></span><br><span class="line">      <span class="keyword">return</span> _this.call(context, ...args, ...arguments)</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>闭包，全局变量的不恰当使用，没清空的定时器，没清理的dom引用等</p><h3 id="常用设计模式"><a href="#常用设计模式" class="headerlink" title="常用设计模式"></a>常用设计模式</h3><p>设计模式是代码设计经验的总结，为了可重用代码，保证代码的可靠性等。</p><ul><li>工厂模式：主要用来创建同一类对象，在创建对象时不会对客户端暴露创建逻辑，并且是通过使用一个共同的接口来指向新创建的对象。</li><li>单例模式：只允许实例化一次的对象类，该类负责创建自己的对象，同时确保只有单个对象被创建。</li><li>装饰器模式（结合到es7的装饰器）：允许向一个现有的对象添加新的功能，同时又不改变其结构，这种模式创建了一个装饰类，用来包装原有的类，并在保持类方法签名完整性的前提下，提供了额外的功能。</li><li>代理模式 （结合es6的proxy）：创建具有现有对象的对象，以便向外界提供功能接口。</li><li>观察者模式：当对象间存在一对多关系时使用。当一个对象被修改时，则会自动通知依赖它的对象。</li><li>原型模式：使用于创建新的对象的类共享原型对象的属性以及方法</li></ul><p><a href="https://segmentfault.com/a/1190000007899742" target="_blank" rel="noopener">JavaScript设计模式</a></p><h3 id="数据结构与算法"><a href="#数据结构与算法" class="headerlink" title="数据结构与算法"></a>数据结构与算法</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> arr = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>]   </span><br><span class="line"><span class="keyword">const</span> arr1 = <span class="keyword">new</span> <span class="built_in">Array</span>()</span><br></pre></td></tr></table></figure><p>栈：后进先出(LIFO)，只用 pop 和 push 完成增删的“数组”</p><p>队列：先进先出(FIFO)，只用 push 和 shift 完成增删的“数组”</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 结点格式</span></span><br><span class="line"><span class="comment">&#123;</span></span><br><span class="line"><span class="comment">  // 数据域</span></span><br><span class="line"><span class="comment">  val: 1,</span></span><br><span class="line"><span class="comment">  // 指针域，指向下一个结点</span></span><br><span class="line"><span class="comment">  next: &#123;</span></span><br><span class="line"><span class="comment">    val:2,</span></span><br><span class="line"><span class="comment">    next: ...</span></span><br><span class="line"><span class="comment">  &#125;</span></span><br><span class="line"><span class="comment">&#125;</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="comment">// 创建链表结点</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">ListNode</span>(<span class="params">val</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.val = val</span><br><span class="line">  <span class="keyword">this</span>.next = <span class="literal">null</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">const</span> node = <span class="keyword">new</span> ListNode(<span class="number">1</span>)  </span><br><span class="line">node.next = <span class="keyword">new</span> ListNode(<span class="number">2</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 二叉树结点的构造函数</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">TreeNode</span>(<span class="params">val</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.val = val</span><br><span class="line">  <span class="keyword">this</span>.left = <span class="keyword">this</span>.right = <span class="literal">null</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">const</span> node  = <span class="keyword">new</span> TreeNode(<span class="number">1</span>)</span><br></pre></td></tr></table></figure><p><a href="https://github.com/trekhleb/javascript-algorithms/blob/master/README.zh-CN.md" target="_blank" rel="noopener">JavaScript 算法与数据结构</a></p><h2 id="进击的CSS"><a href="#进击的CSS" class="headerlink" title="进击的CSS"></a>进击的CSS</h2><h3 id="css盒模型"><a href="#css盒模型" class="headerlink" title="css盒模型"></a>css盒模型</h3><p>盒模型包括包括margin、border、padding、content</p><ul><li>标准盒模型：content 部分不包含其他部分</li><li>IE盒模型：content 部分包含了 border 和 padding</li></ul><p><a href="https://segmentfault.com/a/1190000015235886" target="_blank" rel="noopener">面试官：谈谈你对 CSS 盒模型的认识?</a></p><h4 id="文档流和BFC-star"><a href="#文档流和BFC-star" class="headerlink" title="文档流和BFC :star:"></a>文档流和BFC :star:</h4><p>文档流：将窗体自上而下分成一行行，并在每一行中按从左到右的顺序来排放元素，这个我们称之为文档流。</p><p>非文档流(脱离文档流)：也就是将元素从普通的布局排版中拿走，其他盒子在定位的时候，会当做脱离文档流的元素不存在而进行定位。（float、absolute、fixed）</p><p><code>BFC(Block formatting context)</code>: 称为块级格式化上下文，是CSS中的一种渲染机制。BFC是一个独立的渲染区域，它决定了块级元素如何对它的子元素内容进行布局，以及与子元素同级别的兄弟元素的关系和相互作用。</p><p>可以理解为：创建了 BFC的元素就是一个独立的盒子，里面的子元素不会在布局上影响外面的元素（里面怎么布局都不会影响外部），BFC仍属于文档中的普通流。</p><p>BFC 的原理：</p><ul><li>独立的容器，内外元素互不影响</li><li>内部的Box会在垂直方向，一个接一个地放置</li><li>Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠</li><li>BFC 的区域不会与浮动元素的 float 重叠</li><li>计算 BFC 高度，浮动元素也参与计算</li></ul><p>如何创建BFC：</p><ul><li>根元素</li><li>float属性不为none</li><li>position为absolute或fixed</li><li>overflow不为visible</li><li>display为inline-block, table-cell, table-caption, flex, inline-flex</li></ul><p>BFC作用：</p><ul><li>防止外边距重叠</li><li>清除浮动的影响</li><li>防止文字环绕</li></ul><h3 id="flex和grid"><a href="#flex和grid" class="headerlink" title="flex和grid"></a>flex和grid</h3><p>flex 布局是一维布局，Grid 布局是二维布局。Flex布局是轴线布局，只能指定”项目”针对轴线的位置，可以看作是一维布局，Grid 布局则是将容器划分成“行”和“列”，产生单元格，然后指定”项目所在”的单元格，可以看作是二维布局，Grid布局远比 Flex布局强大。</p><p><a href="https://www.ruanyifeng.com/blog/2015/07/flex-grammar.html" target="_blank" rel="noopener">flex布局详解</a></p><p><a href="https://blog.csdn.net/leilei__66/article/details/122360901https://blog.csdn.net/leilei__66/article/details/122360901" target="_blank" rel="noopener">grid布局</a></p><figure class="highlight plain"><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">flex: 1</span><br><span class="line">// flex: 1; === flex: 1 1 auto;</span><br></pre></td></tr></table></figure><p>flex-grow是用来增大盒子的，比如，当父盒子的宽度大于子盒子的宽度，父盒子的剩余空间可以利用flex-grow来设置子盒子增大的占比</p><p>flex-shrink用来设置子盒子超过父盒子的宽度后，超出部分进行缩小的取值比例</p><p>flex-basis是用来设置盒子的基准宽度，并且basis和width同时存在basis会把width干掉</p><h3 id="css动画"><a href="#css动画" class="headerlink" title="css动画"></a>css动画</h3><p>animation: name duration timing-function delay iteration-count direction;</p><p>@keyframes（@keyframes动画是循环的，而transform 只执行一遍）</p><p><a href="https://www.cnblogs.com/coco1s/p/15796478.html" target="_blank" rel="noopener">深入浅出 CSS 动画</a></p><h2 id="必备的HTML"><a href="#必备的HTML" class="headerlink" title="必备的HTML"></a>必备的HTML</h2><h3 id="history-api"><a href="#history-api" class="headerlink" title="history api"></a>history api</h3><p>window 对象通过 history 对象提供了对浏览器的会话历史的访问，提供了对history栈中内容的操作。可以在不刷新页面的前提下动态改变浏览器地址栏中的URL地址，动态修改页面上所显示资源。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//在history中向后跳转：</span></span><br><span class="line"><span class="built_in">window</span>.history.back()</span><br><span class="line"><span class="built_in">window</span>.history.go(<span class="number">-1</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">//向前跳转：</span></span><br><span class="line"><span class="built_in">window</span>.history.forward()</span><br><span class="line"><span class="built_in">window</span>.history.go(<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">//历史记录中页面总数</span></span><br><span class="line">history.length</span><br></pre></td></tr></table></figure><p>HTML5 引入了<code>history.pushState()</code>和<code>history.replaceState()</code>方法，它们分别可以添加和修改历史记录条目。</p><ul><li>history.pushState(state, title, url): 添加一条历史记录，不刷新页面</li><li>history.replaceState(state, title, url): 替换当前的历史记录，不刷新页面</li><li>popstate 事件：历史记录发生改变时触发，调用history.pushState()或者history.replaceState()不会触发popstate事件</li><li>hashchange 事件：当页面的hash值改变的时候触发，常用于构建单页面应用</li></ul><h2 id="主流的前端框架"><a href="#主流的前端框架" class="headerlink" title="主流的前端框架"></a>主流的前端框架</h2><p><a href="/2021/05/27/%E5%87%A0%E5%A4%A7%E6%A1%86%E6%9E%B6%E7%9F%A5%E8%AF%86%E7%82%B9%E6%A2%B3%E7%90%86/">几大框架知识点梳理</a></p><h2 id="模块化和工程化"><a href="#模块化和工程化" class="headerlink" title="模块化和工程化"></a>模块化和工程化</h2><p>前端工程化是一种工程化的思想，而并不是一种技术手段。前端工程化的目标就是让前端项目，更利于团队的协作，解耦团队的分工，提高团队的开发效率。对于后期，更利于前端项目的维护。</p><p>工程化可以理解为，系统的、规范的进行开发和部署的流程。</p><p><a href="https://mp.weixin.qq.com/s/Zvor5SBSy4RYtj_zmzhuAg" target="_blank" rel="noopener">结合代码实践，全面学习前端工程化</a></p><h3 id="模块化"><a href="#模块化" class="headerlink" title="模块化"></a>模块化</h3><p>在浏览器中使用esm，在Node中使用CommonJS的模块化支持。</p><ul><li><p>CommonJs (典型代表：<code>node.js</code> 早期，<code>require / module.exports</code>)：用于服务端</p><ul><li>都运行在模块作用域，不会污染全局作用域</li><li>同步加载的，即只有加载完成，才能执行后面的操作</li><li>首次执行后就会缓存，再次加载只返回缓存结果，如果想要再次执行，可清除缓存</li><li>require 返回的值是被输出的值的拷贝，模块内部的变化也不会影响这个值</li><li>缺点：在浏览器环境<code>同步加载</code>大量模块会导致性能问题</li></ul></li><li><p>AMD (典型代表：require.js，<code>require / defined</code>):异步模块定义。<br>所有依赖模块的语句，都定义在一个回调函数中，等到模块加载完成之后，这个回调函数才会运行<br>require。</p></li><li><p>ES6 的 Module：<code>import/export</code>（或者在script中指定type&#x3D;”module”）</p><ul><li>CommonJS 和 AMD 模块，都只能在运行时确定这些东西</li><li>编译时确定依赖：ES6 设计思想是尽量的静态化，使得编译时就能确定模块的依赖关系，以及输入和输出的变量</li><li>按需加载：import()允许您仅在需要时动态加载模块，而不必预先加载所有模块</li></ul></li></ul><h3 id="脚手架"><a href="#脚手架" class="headerlink" title="脚手架"></a>脚手架</h3><p>脚手架的出现就是为减少重复性工作而引入的命令行工具，实现脚手架的核心思想就是自动化思维。</p><h3 id="webpack"><a href="#webpack" class="headerlink" title="webpack"></a>webpack</h3><p>webpack 是一个模块打包工具(module bundler)。当 webpack 处理应用程序时，它会递归地构建一个依赖关系图(dependency graph)，其中包含应用程序需要的每个模块，然后将所有这些模块打包成一个或多个 bundle。</p><p><a href="/2022/07/15/%E6%B7%B1%E5%85%A5%E6%8E%A2%E7%A9%B6Webpack%E5%8E%9F%E7%90%86/">深入探究Webpack原理</a></p><h4 id="基础配置和常用插件-star"><a href="#基础配置和常用插件-star" class="headerlink" title="基础配置和常用插件 :star:"></a>基础配置和常用插件 :star:</h4><p><a href="/2020/03/12/webpack%E5%AE%89%E8%A3%85%E4%BD%BF%E7%94%A8/">webpack安装使用</a></p><h4 id="打包优化-star"><a href="#打包优化-star" class="headerlink" title="打包优化 :star:"></a>打包优化 :star:</h4><p><a href="https://juejin.cn/post/6844904071736852487" target="_blank" rel="noopener">玩转 webpack，使你的打包速度提升 90%</a></p><p><a href="https://segmentfault.com/a/1190000018493260" target="_blank" rel="noopener">Webpack构建速度优化</a></p><h4 id="摇树的原理"><a href="#摇树的原理" class="headerlink" title="摇树的原理"></a>摇树的原理</h4><p>采用删除不需要的额外代码的方式优化代码体积的技术。ES6的模块方案：import()引入模块的方式采用静态导入，可以采用一次导入所以的依赖包再根据条件判断的方式，获取不需要的包，然后执行删除操作。</p><h4 id="webpack4和3区别"><a href="#webpack4和3区别" class="headerlink" title="webpack4和3区别"></a>webpack4和3区别</h4><p><a href="https://www.cnblogs.com/vickylinj/p/14421814.html" target="_blank" rel="noopener">webpack3和webpack4区别</a></p><h3 id="rollup"><a href="#rollup" class="headerlink" title="rollup"></a>rollup</h3><p>对于组件库项目，支持按需加载需要满足：组件库以ES模块化方式导出。 而rollup本来就支持ES模块的导出。</p><ul><li>rollup-plugin-postcss：增加配置可以抽离单独的css文件<br><a href="https://zhuanlan.zhihu.com/p/486644411" target="_blank" rel="noopener">rollup常用扩展插件</a></li></ul><h3 id="单元测试"><a href="#单元测试" class="headerlink" title="单元测试"></a>单元测试</h3><p>JavaScript 缺少类型检查，编译期间无法定位到错误，单元测试可以帮助你测试多种异常情况。通过编写测试用例，可以做到一次编写，多次运行。</p><p>使用框架：Jest（内置了集成度比较高的断言库expect.js，自带断言、测试覆盖率工具，实现了开箱即用）。<br>它会自动识别一些常用的测试文件，比如*.spec.js和 *.test.js后缀的测试脚本，所有的测试脚本都放在tests或__tests__目录下。</p><h2 id="浏览器"><a href="#浏览器" class="headerlink" title="浏览器"></a>浏览器</h2><h3 id="浏览器存储"><a href="#浏览器存储" class="headerlink" title="浏览器存储"></a>浏览器存储</h3><p>Cookie、localStorage、sessionStorage、IndexedDB</p><p><a href="/2020/06/17/%E5%B8%B8%E8%A7%81%E7%9A%84%E6%B5%8F%E8%A7%88%E5%99%A8%E6%9C%AC%E5%9C%B0%E5%AD%98%E5%82%A8/">常见的浏览器本地存储</a></p><h3 id="浏览器原理-star"><a href="#浏览器原理-star" class="headerlink" title="浏览器原理 :star:"></a>浏览器原理 :star:</h3><p><a href="https://segmentfault.com/a/1190000022633988" target="_blank" rel="noopener">前端都该懂的浏览器工作原理</a></p><h4 id="输入url到浏览器渲染-star"><a href="#输入url到浏览器渲染-star" class="headerlink" title="输入url到浏览器渲染 :star:"></a>输入url到浏览器渲染 :star:</h4><p>URL 解析 -&gt; DNS 查询 -&gt; TCP 连接 -&gt; HTTP 请求 -&gt; 服务器响应请求 -&gt; 页面渲染</p><p><a href="https://blog.csdn.net/Amnesiac666/article/details/124783446" target="_blank" rel="noopener">从输入URL到页面渲染的整个过程</a></p><h4 id="浏览器的本地缓存"><a href="#浏览器的本地缓存" class="headerlink" title="浏览器的本地缓存"></a>浏览器的本地缓存</h4><p>优先级:MemoryCache &gt; ServiceWorker&gt; DiskCache。</p><ul><li>Memory Cache：内存中的缓存，主要包含的是当前中页面中已经抓取到的资源。内存缓存虽然读取高效，可是缓存持续性很短，会随着进程的释放而释放。(关闭Tab页面就释放)。浏览器使用的内存是有限的，一般优先存小的文件或请求，优先存文件。</li><li>Disk Cache： 硬盘中的缓存，读取速度慢，胜在容量大和存储时效性上（根据 HTTP Herder 中的字段判断哪些资源需要缓存）绝大多数缓存都是disk cache。</li><li>Service Worker：必须HTTPS中使用（Service Worker 中涉及到请求拦截），运行在浏览器背后的独立线程。Service Worker 的缓存与浏览器其他内建的缓存机制不同，它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存，并且缓存是持续性的。</li><li>Push Cache：HTTP&#x2F;2中的，缓存时间短暂，只在会话（Session）中存在，一旦会话结束就被释放。（当以上三种缓存都没有命中时，它才会被使用。）</li></ul><h3 id="浏览器的缓存机制"><a href="#浏览器的缓存机制" class="headerlink" title="浏览器的缓存机制"></a>浏览器的缓存机制</h3><p><code>强缓存</code>：浏览器直接从本地缓存中获取数据，不与服务器进行交互。强缓存是利用http头中的Expires和Cache-Control两个字段来控制的，用来表示资源的缓存时间。（请求返回Code 200）</p><p><code>协商缓存</code>：浏览器发送请求到服务器，服务器判断是否可使用本地缓存（主要通过请求头中的Etag和If-None-Match、Last-Modified和If-Modified-Since标识通信）更改了就重新请求，没有改变就直接用缓存。</p><p><code>ETag</code>：协商缓存中判断是否变更的标志（优先级更高）。ETag 类似于文件指纹，If-None-Match 会将当前 ETag 发送给服务器，询问该资源 ETag 是否变动，有变动的话就将新的资源发送回来。</p><p>ETag一般不以明文形式相应给客户端。在资源的各个生命周期中，它都具有不同的值，用于标识出资源的状态。当资源发生变更时，那么ETag也随之发生变化。</p><p><a href="https://blog.csdn.net/jianchibuxie0/article/details/108296223" target="_blank" rel="noopener">强缓存和协商缓存</a></p><p><a href="https://www.cnblogs.com/tiwlin/archive/2011/06/10/2077193.html" target="_blank" rel="noopener">Etag详解</a></p><h2 id="网络"><a href="#网络" class="headerlink" title="网络"></a>网络</h2><h3 id="http协议-star"><a href="#http协议-star" class="headerlink" title="http协议 :star:"></a>http协议 :star:</h3><p><code>http</code>：是一个客户端和服务器端请求和应答的标准（TCP），是一个超文本传输协议。</p><p><code>https</code>：是以安全为目标的 HTTP 通道，即 HTTP 下 加入 SSL 层进行加密。其作用是：建立一个信息安全通道，来确保数据的传输，确保网站的真实性。</p><p>HTTP 1.1 和 HTTP 2.0 的区别：</p><ul><li>HTTP&#x2F;2采用二进制格式而非文本格式</li><li>HTTP&#x2F;2是完全多路复用的，而非有序并阻塞的——只需一个连接即可实现并行</li><li>使用报头压缩，HTTP&#x2F;2降低了开销</li><li>HTTP&#x2F;2让服务器可以将响应主动“推送”到客户端缓存中</li></ul><p>HTTPS如何防止中间人攻击：<br>服务器是通过 SSL 证书来传递公钥，客户端会对 SSL 证书进行验证，其中证书认证体系就是确保SSL安全的关键。</p><p><a href="/2022/07/09/HTTP%E5%92%8CHTTPS%E5%8D%8F%E8%AE%AE/">HTTP和HTTPS协议</a></p><p><a href="https://cloud.tencent.com/developer/article/1464938" target="_blank" rel="noopener">详解HTTP&#x2F;1.0、HTTP&#x2F;1.1、HTTP&#x2F;2、HTTPS</a></p><p><a href="https://blog.csdn.net/wu_noah/article/details/108883328" target="_blank" rel="noopener">HTTPS怎么避免中间人攻击</a></p><h3 id="CSRF和XSS、SQL-注入攻击-star"><a href="#CSRF和XSS、SQL-注入攻击-star" class="headerlink" title="CSRF和XSS、SQL 注入攻击 :star:"></a>CSRF和XSS、SQL 注入攻击 :star:</h3><p><code>CSRF</code>：跨站请求伪造，攻击者诱导受害者进入第三方网站，在第三方网站中，向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证，绕过后台的用户验证，达到冒充用户对被攻击的网站执行某项操作的目的。</p><p>防御办法：</p><ul><li>在服务器端验证请求来源的站点，禁止第三方站点的请求（验证 HTTP Referer 字段， Referer记录了该 HTTP 请求的来源地址）</li><li>随机token（在请求地址中添加 token 并验证）</li><li>为Set-Cookie响应头新增Samesite属性（在 HTTP 头中自定义属性并验证）</li></ul><p><code>XSS</code>：跨站脚本攻击，攻击者通过“注入”，在网页中插入恶意代码，从而被浏览器执行所造成的一种攻击方式。</p><ul><li>反射型：url请求中加上script代码</li><li>存储型：输入框中输入脚本提交到服务器，服务器返回信息执行</li><li>DOM型：通过脚本修改页面的DOM节点</li></ul><p>防御办法：</p><ul><li>对输入进行检查和转码；</li><li>CSP(内容安全策略): 设置 HTTP Header 的 Content-Security-Policy; 或者设置 meta 标签的方式</li></ul><p><code>SQL 注入</code>: 主要是利用前端表单提交，比如输入框、富文本框，输入sql语句提交到后端，当后端服务通过提交的字段拼接成sql语句进行数据库查询的时候，恶意sql被执行，从而达到攻击的目的。</p><p><code>中间人攻击</code>: 攻击方同时与服务端和客户端建立起了连接，并让对方认为连接是安全的。攻击者不仅能获得双方的通信信息，还能修改通信信息。防御办法：https</p><p>防止token泄露：HTTPS认证、对称加密、将请求 URL、时间戳、token 三者进行合并加盐签名，服务端校验有效性</p><p><a href="https://www.cnblogs.com/zhouyyBlog/p/14505961.html" target="_blank" rel="noopener">xss和csrf</a></p><h3 id="websocket"><a href="#websocket" class="headerlink" title="websocket"></a>websocket</h3><p><code>Websocket</code>是 HTML5 新增的基于TCP的网络通信协议（全双工通讯的协议）。websocket不属于http无状态协议，协议名为”ws”。(没有同源限制)</p><blockquote><p>WebSocket使得客户端和服务器之间的数据交换变得更加简单，允许服务端主动向客户端推送数据。在WebSocket API中，浏览器和服务器只需要完成一次握手，两者之间就直接可以创建持久性的连接，并进行双向数据传输。它实现了浏览器与服务器全双工通信，能更好的节省服务器资源和带宽并达到实时通讯的目的。</p></blockquote><p>优点：</p><ul><li>较少的控制开销</li><li>更强的实时性</li><li>保持连接状态</li><li>性能更好</li></ul><p>过程：</p><ol><li>客户端发起http请求，经过3次握手后，建立起TCP连接；http请求里存放WebSocket支持的版本号等信息；</li><li>服务器收到客户端的握手请求后，同样采用HTTP协议回馈数据；</li><li>客户端收到连接成功的消息后，开始借助于<code>TCP传输信道</code>进行全双工通信（可以通过 send() 方法来向服务器发送数据，通过 onmessage 事件来接收服务器返回的数据）</li></ol><p>最初的握手阶段是http协议，握手完成后就切换到websocket协议，借助于<code>TCP传输信道</code>进行全双工通信。建立通讯时，是由客户端主动发起连接请求，服务端被动监听，通信的数据是基于“帧(frame)”的，可以传输文本数据，也可以直接传输二进制数据，效率高。</p><p><a href="https://blog.csdn.net/sinat_36422236/article/details/85051547" target="_blank" rel="noopener">websocket深入浅出</a></p><p><a href="https://blog.csdn.net/qq_54773998/article/details/123863493" target="_blank" rel="noopener">WebSocket</a></p><h2 id="可视化"><a href="#可视化" class="headerlink" title="可视化"></a>可视化</h2><h3 id="canvas常用API"><a href="#canvas常用API" class="headerlink" title="canvas常用API"></a>canvas常用API</h3><p>Canvas API 提供了一个通过JavaScript和HTML的<code>&lt;canvas&gt;</code>元素来绘制图形的方式。它可以用于动画、游戏画面、数据可视化、图片编辑以及实时视频处理等方面。</p><p>rect、clearRect、beginPath、moveTo、lineTo、closePath、stroke、arc、rotate、translate、transform</p><p><a href="https://juejin.cn/post/6844904104121073678" target="_blank" rel="noopener">Canvas常用API</a></p><p><a href="https://m.yisu.com/zixun/408468.html" target="_blank" rel="noopener">html5中canvas的绘图API</a></p><h3 id="webgl-和-threejs"><a href="#webgl-和-threejs" class="headerlink" title="webgl 和 threejs"></a>webgl 和 threejs</h3><h3 id="js动画库"><a href="#js动画库" class="headerlink" title="js动画库"></a>js动画库</h3><p>Anime.js（可以处理CSS属性，单个CSS转换，SVG 或任 何 DOM 属性以及 JS 对象）、Mo.js（用于 Web 的动态图形工具带）</p><h2 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h2><h3 id="TS"><a href="#TS" class="headerlink" title="TS"></a>TS</h3><p>TypeScript 是基于 JavaScript 创造的强类型编程语言，可以进行任意程度的扩展。</p><p>优点：</p><ul><li>类型定义：良好的类型定义和对 ES6 的支持</li><li>可维护性：增长了代码的可读性和可维护性</li><li>提早报错：类型错误会在编译过程中被编译器发现，便于发现BUG</li><li>代码可预测性：声明的变量一旦指定类型，它的类型就再也不能修改。这样变量就具有可预测性。</li><li>便于重构</li></ul><p>缺点：</p><ul><li>因为需要兼容 JavaScript 的缘故，TypeScript 的类型是可选的。所以还是会有些类型问题（如any）</li><li>需要编译：浏览器和 Nodejs 并不支持 TypeScript，所以多了一步编译操作。</li><li>增加开发成本：如类型定义和接口声明</li></ul><p><a href="https://juejin.cn/post/6988763249982308382" target="_blank" rel="noopener">「面试题」TypeScript</a></p><h4 id="与js的区别"><a href="#与js的区别" class="headerlink" title="与js的区别"></a>与js的区别</h4><ul><li>ts是静态语言，js是动态类语言</li><li>ts对比js基础类型上，增加了 void&#x2F;never&#x2F;any&#x2F;元组&#x2F;枚举&#x2F;以及一些高级类型</li><li>js没有重载概念，ts可以重载</li></ul><h4 id="js项目如何升级为ts"><a href="#js项目如何升级为ts" class="headerlink" title="js项目如何升级为ts"></a>js项目如何升级为ts</h4><p>安装typescript -&gt; 安装eslint相关依赖 -&gt; 新建tsconfig.json -&gt; 修改文件后缀</p><h4 id="配置项"><a href="#配置项" class="headerlink" title="配置项"></a>配置项</h4><ul><li>target: 控制编译后输出的是什么js版本</li><li>lib: 指定要引入的库文件，是内置对象所应用的声明（如配置：[‘ES2015’, ‘DOM’]）</li><li>module: 指定要使用的模块标准</li></ul><h3 id="Nodejs"><a href="#Nodejs" class="headerlink" title="Nodejs"></a>Nodejs</h3><p>一个基于Chrome V8引擎的<code>JavaScript运行环境</code>。它是一个轻量级框架，用于创建服务器端 Web 应用程序并扩展 JavaScript API 以提供常用的服务器端功能，创建前端工具库等。</p><p><a href="https://blog.51cto.com/u_13225813/3118537" target="_blank" rel="noopener">NodeJS面试题</a></p><p><a href="https://blog.csdn.net/lgno2/article/details/117138044" target="_blank" rel="noopener">NodeJS面试题</a></p><h4 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h4><ul><li>基于 Express 框架 (opens new window)，可以快速构建 Web 应用</li><li>基于 Electron 框架 (opens new window)，可以构建跨平台的桌面应用</li><li>基于 restify 框架 (opens new window)，可以快速构建 API 接口项目</li><li>读写和操作数据库、创建实用的命令行工具辅助前端开发</li></ul><h4 id="Buffer"><a href="#Buffer" class="headerlink" title="Buffer"></a>Buffer</h4><p>Buffer是nodejs全局上的一个內置模块，作用就是让JavaScript可以直接操作二进制数据。</p><h4 id="全局对象"><a href="#全局对象" class="headerlink" title="全局对象"></a>全局对象</h4><p>global、 process, console、 module和 exports</p><h4 id="事件循环"><a href="#事件循环" class="headerlink" title="事件循环"></a>事件循环</h4><p>事件循环其实就是一个事件队列，先加入先执行，执行完一次队列，再次循环遍历看有没有新事件加入队列。</p><p>目的：处理非阻塞 I&#x2F;O 操作的机制 （指以异步来执行函数，先执行同步任务，耗时任务放在事件队列中，以此轮询执行）</p><p>IO事件→ setImmediate→ setTimeout&#x2F;setInterval→ process. nextTick</p><p>libuv 引擎中的事件循环分为 6 个阶段，它们会按照顺序反复运行。每当进入某一个阶段的时候，都会从对应的回调队列中取出函数去执行。当队列为空或者执行的回调函数数量到达系统设定的阈值，就会进入下一阶段。<br>事件循环中细分为这六个阶段：</p><ul><li>Timers: 执行定时器回调事件（依次执行完所有定时器回调才到下个阶段，微任务队列是在每个阶段完成后立即执行）</li><li>Pending: 一些系统级回调将会在此阶段执行</li><li>Idle,prepare: 此阶段”仅供内部使用”</li><li>Poll: IO回调函数，这个阶段较为重要也复杂些，</li><li>Check: 执行 setImmediate() 的回调</li><li>Close: 执行 socket 的 close 事件回调</li></ul><h4 id="如何支撑高并发"><a href="#如何支撑高并发" class="headerlink" title="如何支撑高并发"></a>如何支撑高并发</h4><p>虽然js是单线程（缺点：不能充分利用多核，容易阻塞），node支持高并发主要基于事件循环机制。<br>nodejs 是异步非阻塞的，所以能扛住高并发。</p><h4 id="stream"><a href="#stream" class="headerlink" title="stream"></a>stream</h4><p>Node.js 中有四种基本的流类型：</p><ul><li>Writable: 可写入数据的流（例如 fs.createWriteStream()）。</li><li>Readable: 可读取数据的流（例如 fs.createReadStream()）。</li><li>Duplex: 可读又可写的流（例如 net.Socket）。</li><li>Transform: 在读写过程中可以修改或转换数据的 Duplex 流（例如 zlib.createDeflate()）。</li></ul><h4 id="npm安装机制"><a href="#npm安装机制" class="headerlink" title="npm安装机制"></a>npm安装机制</h4><ul><li>查询 node_modules 目录之中是否已经存在指定模块</li><li>若存在，不再重新安装</li><li>若不存在，npm 向 registry 查询模块压缩包的网址</li><li>下载压缩包，存放在根目录下的.npm目录里</li><li>解压压缩包到当前项目的 node_modules 目录</li></ul><h3 id="移动端"><a href="#移动端" class="headerlink" title="移动端"></a>移动端</h3><h4 id="uniapp"><a href="#uniapp" class="headerlink" title="uniapp"></a>uniapp</h4><h4 id="Flutter"><a href="#Flutter" class="headerlink" title="Flutter"></a>Flutter</h4><p>Flutter 和 React Native 不同主要在于 Flutter UI是直接通过 skia 渲染的 ，而 React Native 是将 js 中的控件转化为原生控件，通过原生去渲染的。</p><p><a href="https://juejin.cn/post/6864816494749057038" target="_blank" rel="noopener">Flutter面试指南</a></p><h4 id="小程序"><a href="#小程序" class="headerlink" title="小程序"></a>小程序</h4><p>小程序的逻辑层和渲染层是在两个不同的线程的。</p><p>直接把JavaScript执行的逻辑层环境放到沙盒，一个纯JavaScript的执行环境，没有浏览器的概念，这样就没有DOM相关的API了，那小程序也得有页面，所以渲染层就单独开一个线程了。</p><ul><li>不能直接操作DOM, 保护页面上用户隐私</li><li>限制一些API的调用, 规避了XSS攻击</li></ul><h5 id="安装axios"><a href="#安装axios" class="headerlink" title="安装axios"></a>安装axios</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm i axios axios-miniprogram-adapter</span><br></pre></td></tr></table></figure><p>安装 -&gt; 工具 -&gt; 构建npm -&gt; 引入</p><h3 id="性能优化"><a href="#性能优化" class="headerlink" title="性能优化"></a>性能优化</h3><ul><li><p>资源打包分析：webpack-bundle-analyzer</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">npm install --save-dev webpack-bundle-analyzer</span><br><span class="line"><span class="comment">// webpack.config.js 文件</span></span><br><span class="line"><span class="keyword">const</span> BundleAnalyzerPlugin = <span class="built_in">require</span>(<span class="string">'webpack-bundle-analyzer'</span>).BundleAnalyzerPlugin</span><br><span class="line"><span class="built_in">module</span>.exports=&#123;</span><br><span class="line">  plugins: [</span><br><span class="line">    <span class="keyword">new</span> BundleAnalyzerPlugin(&#123;</span><br><span class="line">          analyzerMode: <span class="string">'server'</span>,</span><br><span class="line">          analyzerHost: <span class="string">'127.0.0.1'</span>,</span><br><span class="line">          analyzerPort: <span class="number">8889</span>,</span><br><span class="line">          reportFilename: <span class="string">'report.html'</span>,</span><br><span class="line">          defaultSizes: <span class="string">'parsed'</span>,</span><br><span class="line">          openAnalyzer: <span class="literal">true</span>,</span><br><span class="line">          generateStatsFile: <span class="literal">false</span>,</span><br><span class="line">          statsFilename: <span class="string">'stats.json'</span>,</span><br><span class="line">          statsOptions: <span class="literal">null</span>,</span><br><span class="line">          logLevel: <span class="string">'info'</span></span><br><span class="line">        &#125;),</span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// package.json</span></span><br><span class="line"><span class="string">"analyz"</span>: <span class="string">"NODE_ENV=production npm_config_report=true npm run build"</span></span><br></pre></td></tr></table></figure></li><li><p>Gzip压缩: Compression-webpack-plugin</p></li><li><p>JavaScript、Css、Html压缩：UglifyJS、webpack-parallel-uglify-plugin、terser-webpack-plugin、mini-css-extract-plugin</p></li></ul><p><a href="https://segmentfault.com/a/1190000022205291" target="_blank" rel="noopener">前端性能优化 24 条建议</a><br><a href="https://juejin.cn/post/6911472693405548557" target="_blank" rel="noopener">聊一聊前端性能优化</a></p><h3 id="electron"><a href="#electron" class="headerlink" title="electron"></a>electron</h3><h4 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h4><p>Electron 是使用 JavaScript等前端技术构建跨平台的桌面应用程序的框架，可构建出兼容 Mac、Windows 和 Linux 三个平台的应用程序。允许使用Node.js(作为后端)和Chromium(作为前端)完成桌面GUI应用程序的开发。</p><p>Electron &#x3D; Chromium + Node.js + Native API</p><h4 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h4><p>它通过集成浏览器内核，使用前端的技术来实现不同平台下的渲染，并结合了 Chromium 、Node.js 和用于调用系统本地功能的 API 三大板块。</p><ul><li>Chromium 为 Electron 提供强大的 UI 渲染能力，由于 Chromium 本身跨平台，因此无需考虑代码的兼容性。</li><li>Chromium 并不具备原生 GUI 的操作能力，因此 Electron 内部集成 Node.js，编写 UI 的同时也能够调用操作系统的底层 API，例如 path、fs、crypto 等模块。</li><li>Native API 为 Electron 提供原生系统的 GUI 支持，借此 Electron 可以调用原生应用程序接口。</li></ul><h4 id="架构"><a href="#架构" class="headerlink" title="架构"></a>架构</h4><p>基于Chromium 架构（Chromium 是 Chrome 的开源版，也是一个浏览器）。</p><p>当用Electron启动一个应用，会创建一个主进程。这个主进程负责与你系统原生的GUI进行交互并为你的应用创建GUI。</p><p>electron由主进程main process启动，通过BrowserWindow实例创建渲染进程renderer process(渲染Web页面)，每个渲染进程都是相互独立渲染。考虑到安全问题，在渲染进程里面不允许直接调用GUI API，如果想要调用，必须通过和主进程通讯，请求主进程完成相应的调用。</p><p>每个 Electron 应用有且只要一个主进程(Main Process)、以及一个或多个渲染进程(Renderer Process), 对应多个 Web 页面。除此之外还有 GPU 进程、扩展进程等等。主进程负责创建页面窗口、协调进程间通信、事件分发。为了安全考虑，原生 GUI 相关的 API 是无法在渲染进程直接访问的，它们必须通过 IPC 调用主进程。这种主从进程模型缺点也非常明显，即主进程单点故障。主进程崩溃或者阻塞，会影响整个应用的响应。</p><blockquote><p>Node.js 事件循环基于 libuv，但 Chromium 基于 message_pump。<br>将 Node.js 集成到 Chromium 中的原理：<br>Electron 起了一个新的安全线程去轮询 backend_fd，当 Node.js 有一个新的事件后，通过 PostTask 转发到 Chromium 的事件循环中，这样就实现了 Electron 的事件融合。</p></blockquote><h4 id="生命周期"><a href="#生命周期" class="headerlink" title="生命周期"></a>生命周期</h4><ul><li>ready：应用程序初始化完成</li><li>dom-ready：一个窗口中的文本加载完成</li><li>did-finish-load: 导航完成时触发</li><li>window-all-closed：所有窗口都被关闭时触发；监听了此事件，需要主动执行app.quit() 事件，before-quit、will-quit、quit 这三个生命周期才会生效</li><li>before-quit：窗口关闭之前</li><li>will-quit：所有窗口都已经关闭,应用程序将退出触发</li><li>quit：应用程序关闭</li><li>closed：当窗口关闭时触发，此时应删除窗口引用</li></ul><h4 id="修改自定义图标"><a href="#修改自定义图标" class="headerlink" title="修改自定义图标"></a>修改自定义图标</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><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> BrowserWindow(&#123; </span><br><span class="line">  width: <span class="number">900</span>, </span><br><span class="line">  height: <span class="number">600</span>,</span><br><span class="line">  icon: path.join(__dirname,<span class="string">'./public/icons/login.png'</span>)</span><br><span class="line">&#125;)，</span><br></pre></td></tr></table></figure><p>桌面图标：在package.json中配置图标路径，或者在vue.config.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></pre></td><td class="code"><pre><span class="line">pluginOptions: &#123;</span><br><span class="line">  electronBuilder: &#123;</span><br><span class="line">    builderOptions: &#123;</span><br><span class="line">      nsis: &#123;</span><br><span class="line">        allowToChangeInstallationDirectory: <span class="literal">true</span>,</span><br><span class="line">        oneClick: <span class="literal">false</span>,</span><br><span class="line">        installerIcon: <span class="string">"./public/icon.ico"</span>, <span class="comment">//安装logo</span></span><br><span class="line">        installerHeaderIcon: <span class="string">"./public/icon.ico"</span> <span class="comment">//安装logo</span></span><br><span class="line">      &#125;,</span><br><span class="line">      electronDownload: &#123;</span><br><span class="line">        mirror: <span class="string">"npm.taobao.org/mirrors/"</span> <span class="comment">//镜像设置</span></span><br><span class="line">      &#125;,</span><br><span class="line">      win: &#123;</span><br><span class="line">        icon: <span class="string">'./public/icon.ico'</span> <span class="comment">//打包windows版本的logo</span></span><br><span class="line">      &#125;,</span><br><span class="line">      productName: <span class="string">"vfirstss"</span>, <span class="comment">//应用的名称</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;p&gt;分类收集整理一些前端面试要点。&lt;/p&gt;
    
    </summary>
    
      <category term="面试" scheme="https://aartemida.github.io/categories/%E9%9D%A2%E8%AF%95/"/>
    
    
  </entry>
  
  <entry>
    <title>Vue3的几个新特性</title>
    <link href="https://aartemida.github.io/2022/02/14/Vue3%E7%9A%84%E5%87%A0%E4%B8%AA%E7%9F%A5%E8%AF%86%E7%82%B9/"/>
    <id>https://aartemida.github.io/2022/02/14/Vue3的几个知识点/</id>
    <published>2022-02-14T08:18:05.000Z</published>
    <updated>2022-09-22T08:58:35.443Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Composition-API"><a href="#Composition-API" class="headerlink" title="Composition API"></a>Composition API</h2><p>Composition API 也叫组合式 API，它主要就是为了解决 Vue2 中 Options API 的问题。</p><blockquote><p>vue2中，我们在methods、computed、watch、data中定义属性和方法，共同处理页面逻辑，我们称这种方式为Options API。<br>这种代码模式下存在几个问题：</p><ul><li>随着功能的增长，复杂组件的代码变得越来越难以维护。 尤其去新接手别人的代码时。 根本原因是 Vue 的现有 API 通过「选项」组织代码，但是在大部分情况下，通过逻辑考虑来组织代码更有意义。</li><li>缺少一种比较「干净」的在多个组件之间提取和复用逻辑的机制。</li><li>类型推断不够友好。</li></ul></blockquote><a id="more"></a><blockquote><p>Composition API顾名思义就是不再传入data、mounted等参数，通过引入的ref、onMounted等方法实现数据的双向绑定、生命周期函数的执行。Composition API目的是通过一组低侵入式的、函数式的 API，使得我们能够更灵活地「组合」组件的逻辑，因此其中的代码是根据逻辑功能来组织的，同一功能所定义的所有api会放在一起。</p></blockquote><h3 id="setup"><a href="#setup" class="headerlink" title="setup"></a>setup</h3><p>vue3中使用<code>setup</code>代替了<code>beforeCreate</code>和<code>created</code>这两个生命周期，<code>setup()</code>是在创建vue组件实例并完成props的初始化之后执行（在 created() 生命周期函数之前执行）。同时setup返回的值，可以在模板和其他option中使用。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">setup(props, context)&#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    text: <span class="string">'test'</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>与Vue2区别：</p><p>不能使用this，而是通过 context 对象来代替当前执行上下文绑定的对象，context 对象有四个属性：attrs、slots、emit、expose。</p><h3 id="ref、reactive"><a href="#ref、reactive" class="headerlink" title="ref、reactive"></a>ref、reactive</h3><p>reactive 和 ref 都是用来定义响应式数据的。一般来说，<code>ref</code>用于基础赋值类型的数据，而<code>reactive</code>用于引用类型的数据。ref 的底层就是 reactive。</p><h4 id="ref"><a href="#ref" class="headerlink" title="ref"></a>ref</h4><p><code>ref</code>函数传入一个值作为参数，一般传入基本数据类型（原始数据），<code>ref</code>的返回值是一个对象，这个对象上只包含一个<code>.value</code>属性。（可以理解为ref是在reactive上的封装）</p><blockquote><ul><li>基本类型数据：响应式依然是通过Object.defineProperty()。</li><li>引用类型数据：响应式是通过调用内部方法reactive函数（通过Proxy）</li></ul></blockquote><h4 id="reactive"><a href="#reactive" class="headerlink" title="reactive"></a>reactive</h4><p><code>reactive</code>是用来定义更加复杂的数据类型，但是解构就不具备响应式了（相当于跳过了代理重新赋新值了）。</p><h4 id="toRef-和-toRefs"><a href="#toRef-和-toRefs" class="headerlink" title="toRef 和 toRefs"></a>toRef 和 toRefs</h4><p>toRef是将对象中的某个值转化为响应式数据 toRef(obj, key)。</p><p><code>toRefs</code>函数可以将<code>reactive</code>创建出来的响应式对象转换为普通的对象，这个对象上的每个属性都是<code>Ref</code>类型的响应式数据。</p><p>toRef和toRefs是对原始数据的引用，修改响应式数据时，原始数据也会发生改变，但是视图并不会更新。toRef修改的是对象的某个属性，toRefs修改的是整个对象</p><h4 id="isRef"><a href="#isRef" class="headerlink" title="isRef"></a>isRef</h4><p>判断是否是ref对象。</p><h4 id="unref"><a href="#unref" class="headerlink" title="unref"></a>unref</h4><p>返回ref的<code>.value</code>值，或返回原值。</p><h3 id="watch、watchEffect"><a href="#watch、watchEffect" class="headerlink" title="watch、watchEffect"></a>watch、watchEffect</h3><p>watchEffect立即运行一个函数，然后被动的追踪它的依赖，当依赖重新改变时执行该函数。watch监测一个或多个响应式数据，并在变化时调用一个回调函数。</p><p>watchEffect是特殊的watch，传入的函数既是数据源又是回调。如果不关心数据变化前后值，只想监听拿到数据执行事件就可以用这个。watch更底层，可以接受多个数据源，可以获取变化前后的值。</p><p>watchEffect会立即执行一次，watch默认不会立即执行，除非传入immdiate。</p><p>watchEffect与watch区别：</p><ul><li>不需手动传入依赖</li><li>初始化时会立即执行</li><li>无法获取到原值，只能得到变化后的值</li></ul><h4 id="watch"><a href="#watch" class="headerlink" title="watch"></a>watch</h4><p>watch 作用是监听传值（和Vue2一致，是惰性的，会返回新值和旧值）。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ref 数据</span></span><br><span class="line"><span class="keyword">const</span> name = ref(<span class="string">'name1'</span>)</span><br><span class="line">watch(</span><br><span class="line">  name,</span><br><span class="line">  (newValue, oldValue) =&gt; &#123;&#125;,</span><br><span class="line">  &#123; <span class="attr">immediate</span>: <span class="literal">true</span>, <span class="attr">deep</span>: <span class="literal">true</span> &#125;</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// reactive 数据</span></span><br><span class="line"><span class="keyword">const</span> user = reactive(&#123; <span class="attr">name</span>: <span class="string">'username'</span> &#125;)</span><br><span class="line">watch(</span><br><span class="line">  () =&gt; user.name,</span><br><span class="line">  (newValue, oldValue) =&gt; &#123;&#125;</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 多个数据</span></span><br><span class="line">watch(</span><br><span class="line">  [name, () =&gt; user.name],</span><br><span class="line">  (newValue, oldValue) =&gt; &#123;&#125;</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 立即执行，在变化时追踪</span></span><br><span class="line">watchEffect(<span class="function"><span class="params">()</span> =&gt;</span> <span class="built_in">console</span>.log(<span class="string">'Value: '</span> + user.name))</span><br></pre></td></tr></table></figure><p>与Vue2区别：</p><ol><li>传参不一样，watch需要传监听的响应式变量，回调函数，options配置</li><li>停止监听: 在组件中创建的watch监听，会在组件被销毁时自动停止。如果在组件销毁之前想要停止掉某个监听， 可以调用watch()函数的返回值。<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> watchFun = watch(</span><br><span class="line">  name,</span><br><span class="line">  (newValue, oldValue) =&gt; &#123;&#125;,</span><br><span class="line">  &#123; <span class="attr">immediate</span>: <span class="literal">true</span>, <span class="attr">deep</span>: <span class="literal">true</span> &#125;</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">setTimeout(<span class="function"><span class="params">()</span>=&gt;</span>&#123;</span><br><span class="line">  <span class="comment">// 停止监听</span></span><br><span class="line">  watchFun()</span><br><span class="line">&#125;, <span class="number">3000</span>)</span><br></pre></td></tr></table></figure></li></ol><h4 id="watchEffect"><a href="#watchEffect" class="headerlink" title="watchEffect"></a>watchEffect</h4><p>watchEffect用法和watch不一样。watchEffect 是传入一个立即执行函数，所以默认第一次也会执行一次；不需要传入监听内容，会自动收集函数内的数据源作为依赖，在依赖变化的时候又会重新执行该函数，如果没有依赖就不会执行；而且不会返回变化前后的新值和老值。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">watchEffect(<span class="function"><span class="params">()</span> =&gt;</span> &#123;&#125;)</span><br></pre></td></tr></table></figure><h3 id="生命周期钩子"><a href="#生命周期钩子" class="headerlink" title="生命周期钩子"></a>生命周期钩子</h3><ul><li>setup() :开始创建组件之前，在beforeCreate和created之前执行。创建的是data和method</li><li>onBeforeMount() : 组件挂载到节点上之前执行的函数。</li><li>onMounted() : 组件挂载完成后执行的函数。</li><li>onBeforeUpdate(): 组件更新之前执行的函数。</li><li>onUpdated(): 组件更新完成之后执行的函数。</li><li>onBeforeUnmount(): 组件卸载之前执行的函数。</li><li>onUnmounted(): 组件卸载完成后执行的函数</li><li>onActivated(): 被包含在keep-alive中的组件，会多出两个生命周期钩子函数。被激活时执行。</li><li>onDeactivated(): 比如从 A 组件，切换到 B 组件，A 组件消失时执行。</li><li>onErrorCaptured(): 当捕获一个来自子孙组件的异常时激活钩子函数（以后用到再讲，不好展现）。</li></ul><h2 id="Fragment-碎片化节点"><a href="#Fragment-碎片化节点" class="headerlink" title="Fragment(碎片化节点)"></a>Fragment(碎片化节点)</h2><p>vue3组件的模板结构中出现多个标签时，可以不用根标签。编译时vue会在这些元素节点上添加一个<code>&lt;Fragment&gt;&lt;/Fragment&gt;</code>标签。并且该标签不会出现在dom树中。</p><h2 id="Teleport（传送门）"><a href="#Teleport（传送门）" class="headerlink" title="Teleport（传送门）"></a>Teleport（传送门）</h2><blockquote><p><teleport>是一个内置组件，使我们可以将一个组件的一部分模板“传送”到该组件的 DOM 层次结构之外的 DOM 节点中。</teleport></p></blockquote><p>使用场景：有时组件模板的一部分逻辑上属于该组件，但元素上最好将模板的这一部分移动到组件之外的其他位置。比如点击按钮出现的弹出框。</p><figure class="highlight html"><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">// 渲染到body标签下</span><br><span class="line"><span class="tag">&lt;<span class="name">teleport</span> <span class="attr">to</span>=<span class="string">"body"</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"modal"</span>&gt;</span></span><br><span class="line">    模态框</span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">teleport</span>&gt;</span></span><br></pre></td></tr></table></figure><h2 id="Suspense-异步组件"><a href="#Suspense-异步组件" class="headerlink" title="Suspense(异步组件)"></a>Suspense(异步组件)</h2><p>vue3中提供一个<code>&lt;Suspense&gt;&lt;/Suspense&gt;</code>组件用于控制异步组件。</p><blockquote><p>作用：等待异步组件时渲染一些额外内容，让应用有更好的用户体验</p></blockquote><p>使用：</p><figure class="highlight html"><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="tag">&lt;<span class="name">Suspense</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">template</span> #<span class="attr">default</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 异步组件 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">async-component</span> /&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">template</span> #<span class="attr">fallback</span>&gt;</span></span><br><span class="line">    加载中 ...</span><br><span class="line">  <span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">Suspense</span>&gt;</span></span><br></pre></td></tr></table></figure><h2 id="其他框架层面的优化"><a href="#其他框架层面的优化" class="headerlink" title="其他框架层面的优化"></a>其他框架层面的优化</h2><ul><li>更快<ul><li>虚拟DOM重写：</li><li>编译器优化：静态提升、patchFlags.block等</li><li>基于Proxy的响应式</li></ul></li><li>更小：Tree-shaking优化</li><li>更好维护：Ts + 模块化</li><li>更容易扩展<ul><li>独立的响应化模块</li><li>自定义渲染器</li></ul></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;Composition-API&quot;&gt;&lt;a href=&quot;#Composition-API&quot; class=&quot;headerlink&quot; title=&quot;Composition API&quot;&gt;&lt;/a&gt;Composition API&lt;/h2&gt;&lt;p&gt;Composition API 也叫组合式 API，它主要就是为了解决 Vue2 中 Options API 的问题。&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;vue2中，我们在methods、computed、watch、data中定义属性和方法，共同处理页面逻辑，我们称这种方式为Options API。&lt;br&gt;这种代码模式下存在几个问题：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;随着功能的增长，复杂组件的代码变得越来越难以维护。 尤其去新接手别人的代码时。 根本原因是 Vue 的现有 API 通过「选项」组织代码，但是在大部分情况下，通过逻辑考虑来组织代码更有意义。&lt;/li&gt;&lt;li&gt;缺少一种比较「干净」的在多个组件之间提取和复用逻辑的机制。&lt;/li&gt;&lt;li&gt;类型推断不够友好。&lt;/li&gt;&lt;/ul&gt;&lt;/blockquote&gt;
    
    </summary>
    
      <category term="Vue" scheme="https://aartemida.github.io/categories/Vue/"/>
    
    
      <category term="vue" scheme="https://aartemida.github.io/tags/vue/"/>
    
  </entry>
  
  <entry>
    <title>一个简单的Vue路由指南</title>
    <link href="https://aartemida.github.io/2021/08/06/%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84Vue%E8%B7%AF%E7%94%B1%E6%8C%87%E5%8D%97/"/>
    <id>https://aartemida.github.io/2021/08/06/一个简单的Vue路由指南/</id>
    <published>2021-08-06T08:51:45.000Z</published>
    <updated>2022-09-12T14:36:41.097Z</updated>
    
    <content type="html"><![CDATA[<h2 id="vue-router"><a href="#vue-router" class="headerlink" title="vue-router"></a>vue-router</h2><h3 id="路由模式"><a href="#路由模式" class="headerlink" title="路由模式"></a>路由模式</h3><h4 id="hash模式"><a href="#hash模式" class="headerlink" title="hash模式"></a>hash模式</h4><p>使用 URL hash 值来作路由。支持所有浏览器，包括不支持 HTML5 History Api 的浏览器。</p><ul><li><p>原理</p><p>基于浏览器的<code>window.onhashchange</code>事件，当地址变化时，通过window.location.hash 获取地址上的hash值；并通过构造Router类，配置routes对象设置hash值与对应的组件内容。</p></li></ul><a id="more"></a><ul><li><p>优点</p><p>hash值不会被包含在Http请求中, 因此hash值改变不会重新加载页面；</p><p>hash改变会触发onhashchange事件, 能控制浏览器的前进后退；</p><p>兼容性好；</p></li><li><p>缺点</p><p>地址栏中携带#不美观；</p><p>hash有体积限制，只可添加短字符串；</p><p>当设置的新值必须与原来不一样才会触发hashchange事件；</p></li></ul><h4 id="history模式"><a href="#history模式" class="headerlink" title="history模式"></a>history模式</h4><p>用url规范的路由，但跳转时也不会刷新页面。依赖 HTML5 History API 和服务器配置。</p><ul><li><p>原理</p><p>基于HTML5新增的<code>history.pushState()</code>和<code>history.replaceState()</code>两个api（改变url不刷新页面），以及浏览器的<code>window.onpopstate</code>事件（用来监听前进或后退事件），地址变化时，通过window.location.pathname找到对应的组件。并通过构造Router类，配置routes对象设置pathname值与对应的组件内容。</p><p>PS: 调用history.pushState()或history.replaceState()不会触发popstate事件。只有在做出浏览器动作时，才会触发该事件。</p></li><li><p>优点</p><p>pushState() 设置的新 URL 可以是与当前 URL 同源的任意 URL；</p><p>pushState() 通过 stateObject 参数可以添加任意类型的数据到记录中；</p><p>浏览器的进后退能触发浏览器的onpopstate事件，获取window.location.pathname来控制页面的变化；</p></li><li><p>缺点</p><p>URL的改变属于http请求，借助history.pushState实现页面的无刷新跳转，因此会重新请求服务器，所以前端的 URL 必须和实际向后端发起请求的 URL 一致。需要在服务端增加一个覆盖所有情况的候选资源：如果 URL 匹配不到任何静态资源，则应该返回同一个 index.html 页面；</p><p>兼容性差，特定浏览器支持；</p></li><li><p>后端配置例子</p><p><a href="https://router.vuejs.org/zh/guide/essentials/history-mode.html#%E5%90%8E%E7%AB%AF%E9%85%8D%E7%BD%AE%E4%BE%8B%E5%AD%90" target="_blank" rel="noopener">HTML5 History 模式</a></p></li></ul><h4 id="abstract"><a href="#abstract" class="headerlink" title="abstract"></a>abstract</h4><p>支持所有 JavaScript 运行环境，如 Node.js 服务器端。如果发现没有浏览器的 API，路由会自动强制进入这个模式。</p><h3 id="响应路由"><a href="#响应路由" class="headerlink" title="响应路由"></a>响应路由</h3><p>当使用路由参数时，例如从 &#x2F;user&#x2F;id1 导航到 &#x2F;user&#x2F;id2，原来的组件实例会被复用。这也意味着组件的生命周期钩子不会再被调用。<br>此时可以监听路由的变化：</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">const</span> User = &#123;</span><br><span class="line">  template: <span class="string">'...'</span>,</span><br><span class="line">  watch: &#123;</span><br><span class="line">    $route(to, <span class="keyword">from</span>) &#123;</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">&#125;</span><br></pre></td></tr></table></figure><h3 id="导航守卫（beforeRouteUpdate）"><a href="#导航守卫（beforeRouteUpdate）" class="headerlink" title="导航守卫（beforeRouteUpdate）"></a>导航守卫（beforeRouteUpdate）</h3><p>1.全局前置守卫</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> router = <span class="keyword">new</span> VueRouter(&#123; ... &#125;)</span><br><span class="line"></span><br><span class="line">router.beforeEach(<span class="function">(<span class="params">to, <span class="keyword">from</span>, next</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// ...</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">router.afterEach(<span class="function">(<span class="params">to, <span class="keyword">from</span></span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><p>2.全局解析守卫<br>可以使用router.beforeResolve注册一个全局守卫，在导航被确认之前，同时在所有组件内守卫和异步路由组件被解析之后，解析守卫就被调用。</p><p>3.路由独享的守卫</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> router = <span class="keyword">new</span> VueRouter(&#123;</span><br><span class="line">  routes: [</span><br><span class="line">    &#123;</span><br><span class="line">      path: <span class="string">'/foo'</span>,</span><br><span class="line">      component: Foo,</span><br><span class="line">      beforeEnter: <span class="function">(<span class="params">to, <span class="keyword">from</span>, next</span>) =&gt;</span> &#123;</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">&#125;)</span><br></pre></td></tr></table></figure><p>4.组件内的守卫</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"><span class="keyword">const</span> Foo = &#123;</span><br><span class="line">  template: <span class="string">`...`</span>,</span><br><span class="line">  beforeRouteEnter(to, <span class="keyword">from</span>, next) &#123;</span><br><span class="line">    <span class="comment">// 在渲染该组件的对应路由被 confirm 前调用</span></span><br><span class="line">    <span class="comment">// 不！能！获取组件实例 `this`</span></span><br><span class="line">    <span class="comment">// 因为当守卫执行前，组件实例还没被创建</span></span><br><span class="line">  &#125;,</span><br><span class="line">  beforeRouteUpdate(to, <span class="keyword">from</span>, next) &#123;</span><br><span class="line">    <span class="comment">// 在当前路由改变，但是该组件被复用时调用</span></span><br><span class="line">    <span class="comment">// 举例来说，对于一个带有动态参数的路径 /foo/:id，在 /foo/1 和 /foo/2 之间跳转的时候，</span></span><br><span class="line">    <span class="comment">// 由于会渲染同样的 Foo 组件，因此组件实例会被复用。而这个钩子就会在这个情况下被调用。</span></span><br><span class="line">    <span class="comment">// 可以访问组件实例 `this`</span></span><br><span class="line">  &#125;,</span><br><span class="line">  beforeRouteLeave(to, <span class="keyword">from</span>, next) &#123;</span><br><span class="line">    <span class="comment">// 导航离开该组件的对应路由时调用</span></span><br><span class="line">    <span class="comment">// 可以访问组件实例 `this`</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>5.完整的导航解析流程<br>失活组件beforeRouteLeave -&gt; beforeEach -&gt; 重用组件beforeRouteUpdate -&gt; 路由beforeEnter -&gt; 激活组件beforeRouteEnter -&gt; beforeResolve -&gt; afterEach -&gt; DOM更新</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://router.vuejs.org/zh/guide/" target="_blank" rel="noopener">Vue Router</a></p><p><a href="https://juejin.cn/post/6867875626611654663" target="_blank" rel="noopener">原来这就是hash模式和history模式的区别</a></p>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;vue-router&quot;&gt;&lt;a href=&quot;#vue-router&quot; class=&quot;headerlink&quot; title=&quot;vue-router&quot;&gt;&lt;/a&gt;vue-router&lt;/h2&gt;&lt;h3 id=&quot;路由模式&quot;&gt;&lt;a href=&quot;#路由模式&quot; class=&quot;headerlink&quot; title=&quot;路由模式&quot;&gt;&lt;/a&gt;路由模式&lt;/h3&gt;&lt;h4 id=&quot;hash模式&quot;&gt;&lt;a href=&quot;#hash模式&quot; class=&quot;headerlink&quot; title=&quot;hash模式&quot;&gt;&lt;/a&gt;hash模式&lt;/h4&gt;&lt;p&gt;使用 URL hash 值来作路由。支持所有浏览器，包括不支持 HTML5 History Api 的浏览器。&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;原理&lt;/p&gt;&lt;p&gt;基于浏览器的&lt;code&gt;window.onhashchange&lt;/code&gt;事件，当地址变化时，通过window.location.hash 获取地址上的hash值；并通过构造Router类，配置routes对象设置hash值与对应的组件内容。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
    
    </summary>
    
      <category term="Vue" scheme="https://aartemida.github.io/categories/Vue/"/>
    
    
      <category term="vue" scheme="https://aartemida.github.io/tags/vue/"/>
    
  </entry>
  
  <entry>
    <title>养猫指南大全</title>
    <link href="https://aartemida.github.io/2021/08/04/%E5%85%BB%E7%8C%AB%E6%8C%87%E5%8D%97%E5%A4%A7%E5%85%A8/"/>
    <id>https://aartemida.github.io/2021/08/04/养猫指南大全/</id>
    <published>2021-08-04T02:22:12.000Z</published>
    <updated>2022-07-19T07:54:03.903Z</updated>
    
    <content type="html"><![CDATA[<h2 id="养猫之前需要了解的："><a href="#养猫之前需要了解的：" class="headerlink" title="养猫之前需要了解的："></a>养猫之前需要了解的：</h2><h4 id="疫苗"><a href="#疫苗" class="headerlink" title="疫苗"></a>疫苗</h4><p>猫咪是否打过猫三联疫苗和狂犬疫苗，打过的话会一般有小本本。（一般1年打一次，1年带猫咪做一次体检）</p><h4 id="驱虫"><a href="#驱虫" class="headerlink" title="驱虫"></a>驱虫</h4><p>是否驱虫，准备好驱虫药，后期自己定期给猫驱虫。</p><a id="more"></a><p>驱虫分内驱、外驱、内外同驱3种方式。</p><p>猫咪刚到新家，刚注射完疫苗的7天内，生病时等情况下，都不适合驱虫。建议是断奶后，大概两个月左右开始驱虫。</p><p>六个月以后的猫咪：如果猫咪不外出、不捕猎、不吃生肉的情况下，可以每隔3个月驱虫一次（体内3个月1次，体外2个月一次（体外驱虫一定要带伊丽莎白圈防止猫猫舔舐，且驱虫后2天内不能洗澡））。</p><h2 id="带猫咪回家之前，你需要准备的东西"><a href="#带猫咪回家之前，你需要准备的东西" class="headerlink" title="带猫咪回家之前，你需要准备的东西"></a>带猫咪回家之前，你需要准备的东西</h2><h4 id="自动饮水机"><a href="#自动饮水机" class="headerlink" title="自动饮水机"></a>自动饮水机</h4><p>猫咪也需要多喝水，尤其是公猫，喝水太少的话容易有泌尿系统的疾病</p><h4 id="航空箱"><a href="#航空箱" class="headerlink" title="航空箱"></a>航空箱</h4><h4 id="猫抓板（2块）或者-猫爬架"><a href="#猫抓板（2块）或者-猫爬架" class="headerlink" title="猫抓板（2块）或者 猫爬架"></a>猫抓板（2块）或者 猫爬架</h4><h4 id="选择性"><a href="#选择性" class="headerlink" title="选择性"></a>选择性</h4><ul><li>猫罐头（主食罐头）</li><li>羊奶粉（幼猫）</li></ul><h2 id="养猫之前，你必需要知道的安全知识："><a href="#养猫之前，你必需要知道的安全知识：" class="headerlink" title="养猫之前，你必需要知道的安全知识："></a>养猫之前，你必需要知道的安全知识：</h2><ol><li>猫窝不要买</li><li>封窗</li></ol><h2 id="新猫到家，需要注意的事项："><a href="#新猫到家，需要注意的事项：" class="headerlink" title="新猫到家，需要注意的事项："></a>新猫到家，需要注意的事项：</h2><ol><li>接回家之前，要给猫猫体检，确保没有寄生虫等其他疾病，确认猫猫疫苗接种情况</li><li>进门第一件事：把猫咪从航空箱里抱到猫砂盆里，让它闻一下，也可以轻轻用它的小爪子扒拉两下。让小猫知道这是上厕所的地方</li><li>大部分猫咪胆子都很小，可能会钻到沙发&#x2F; 床底下躲起来。因为呆在狭窄且黑暗的地方，猫咪会比较有感全感。</li></ol><h2 id="日常护理建议"><a href="#日常护理建议" class="headerlink" title="日常护理建议"></a>日常护理建议</h2><h4 id="注意饮食"><a href="#注意饮食" class="headerlink" title="注意饮食"></a>注意饮食</h4><p>猫的饮食最好的是：湿粮&gt;成品干粮&gt;菜汤拌饭</p><p>宠物幼年时期就要注意饮食，因为这直接决定了它们成年后的免疫力、发育能力、抵抗力。成年后的宠物要注意不要喂高糖、高盐、高脂肪食物。</p><p>宠物的便便关系着它的身体健康，铲屎官们要学会观察，及时判断异常，宠物便便形态分级可见下图：</p><p><img src="/images/cat/cat_poop.jpg" alt="cat"></p><h4 id="猫猫食物"><a href="#猫猫食物" class="headerlink" title="猫猫食物"></a>猫猫食物</h4><ol><li>猫粮（换猫粮的时候需要将新粮和旧粮按照比例1:5&#x2F;2:5&#x2F;1:2&#x2F;3:5&#x2F;4:5&#x2F;全新。这样的比例更换）</li></ol><p>选猫粮最简单的办法就是看成分表，前几位都是肉就是好粮食</p><ol start="2"><li>化毛膏&#x2F;猫草</li><li>营养膏</li><li>喝水：一定要用烧开晾凉的水，或者矿泉水都可以（水要每天更换，自动喂水器可以3-5天更换）</li></ol><h4 id="猫猫用品"><a href="#猫猫用品" class="headerlink" title="猫猫用品"></a>猫猫用品</h4><ol><li>猫碗2个（吃饭一个，喝水一个）</li><li>猫砂盆1（推荐封闭式或顶入式，不然猫砂会被猫猫播出来，同时猫屎很臭）</li><li>猫砂（推荐豆腐猫砂，可以冲厕所的，很省事）</li><li>指甲剪（猫咪专用的那种）</li><li>宠物梳子（梳毛用）</li><li>猫咪沐浴液</li><li>猫用牙刷牙膏</li><li>毛巾（推荐鹿皮毛巾，超级吸水超便宜）</li><li>针管20ml(供强制猫猫喝水喂药用)</li><li>伊丽莎白圈（给猫驱虫时用需要带头上防止他舔舐）</li><li>猫笼（你打扫卫生或者他犯错误都可以先关进去）</li><li>猫药品（体内驱虫药片，体外驱虫滴剂，益生菌，洗耳液，猫咪赖氨酸，眼药水）</li><li>猫消毒（杜邦卫可消毒粉）</li></ol><h4 id="猫猫药品"><a href="#猫猫药品" class="headerlink" title="猫猫药品"></a>猫猫药品</h4><ol><li>益生菌，2天一次加到猫食物里调节猫咪肠胃，防止软便拉稀</li><li>洗耳液，法国维克洗耳液，滴入猫耳朵里，扣住按摩松开猫咪会自动将脏东西甩出，再擦干 就好。预防猫咪耳部疾病</li><li>赖氨酸，猫咪受凉得了感冒就是大病，很难治，所以在天气要转冷或者夏天常开空调记得给猫加到饭里作为预防</li><li>眼药水，这个不是必须，但是猫咪有时候会不小心眼睛感染，流浑浊泪水，眼屎多，这个时候就需要眼药水帮它康复，托百士就可以。特别严重还是要上医院</li><li>杜邦卫可消毒粉，这个是能有效抵御猫瘟病毒的消毒粉，一般用它一周拖地消毒，以及用来喷他的饭碗，舔舐也无害</li></ol><h4 id="日常清理"><a href="#日常清理" class="headerlink" title="日常清理"></a>日常清理</h4><ol><li>早晚铲屎，猫砂每天更换</li><li>一周剪指甲一次，一周刷牙一次，一周梳毛一次，1～2周清洗一次耳朵</li><li>每隔半年洗一次澡（一般猫咪都很怕自己的毛沾湿，所以也很抗拒洗澡，洗澡也容易让猫咪受惊，产生应激反应，洗澡后要吹干）</li></ol><h2 id="其他养猫的基础知识"><a href="#其他养猫的基础知识" class="headerlink" title="其他养猫的基础知识"></a>其他养猫的基础知识</h2><p>1.猫打呼噜<br>2.清洁猫砂盆：最好是一天一次<br>3.猫咪可绝育年龄：6个月-1岁左右（母猫7个月大绝育，公猫6个月大绝育）<br>4.不要出门遛猫<br>5.如果要出远门，最好找朋友隔两天来家里照顾下猫咪</p>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;养猫之前需要了解的：&quot;&gt;&lt;a href=&quot;#养猫之前需要了解的：&quot; class=&quot;headerlink&quot; title=&quot;养猫之前需要了解的：&quot;&gt;&lt;/a&gt;养猫之前需要了解的：&lt;/h2&gt;&lt;h4 id=&quot;疫苗&quot;&gt;&lt;a href=&quot;#疫苗&quot; class=&quot;headerlink&quot; title=&quot;疫苗&quot;&gt;&lt;/a&gt;疫苗&lt;/h4&gt;&lt;p&gt;猫咪是否打过猫三联疫苗和狂犬疫苗，打过的话会一般有小本本。（一般1年打一次，1年带猫咪做一次体检）&lt;/p&gt;&lt;h4 id=&quot;驱虫&quot;&gt;&lt;a href=&quot;#驱虫&quot; class=&quot;headerlink&quot; title=&quot;驱虫&quot;&gt;&lt;/a&gt;驱虫&lt;/h4&gt;&lt;p&gt;是否驱虫，准备好驱虫药，后期自己定期给猫驱虫。&lt;/p&gt;
    
    </summary>
    
      <category term="生活" scheme="https://aartemida.github.io/categories/%E7%94%9F%E6%B4%BB/"/>
    
    
  </entry>
  
  <entry>
    <title>几大框架知识点梳理</title>
    <link href="https://aartemida.github.io/2021/05/27/%E5%87%A0%E5%A4%A7%E6%A1%86%E6%9E%B6%E7%9F%A5%E8%AF%86%E7%82%B9%E6%A2%B3%E7%90%86/"/>
    <id>https://aartemida.github.io/2021/05/27/几大框架知识点梳理/</id>
    <published>2021-05-27T02:42:45.000Z</published>
    <updated>2022-09-11T11:44:01.274Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Vue"><a href="#Vue" class="headerlink" title="Vue"></a>Vue</h2><h3 id="组件通信"><a href="#组件通信" class="headerlink" title="组件通信"></a>组件通信</h3><h4 id="props-x2F-emit"><a href="#props-x2F-emit" class="headerlink" title="props&#x2F;$emit"></a>props&#x2F;$emit</h4><p>父组件通过props向下传递数据给子组件，子组件通过$emit发送事件给父组件发消息（传值）。</p><p>缺点：比较适合父子组件通信，在兄弟组件和跨级组件中使用会比较复杂</p><a id="more"></a><h4 id="eventBus"><a href="#eventBus" class="headerlink" title="eventBus"></a>eventBus</h4><p>通过一个空的Vue实例作为中央事件总线（事件中心），用它来触发事件和监听事件,巧妙地实现了任何组件间的通信，包括父子、兄弟、跨级。</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> EventBus = <span class="keyword">new</span> Vue()</span><br><span class="line">EventBus.$emit(eventName, data)</span><br><span class="line">EventBus.$on(eventName, data =&gt; &#123;&#125;)</span><br></pre></td></tr></table></figure><p>缺点：比较适合兄弟组件和跨级组件通信，但事件太多会比较混乱不好管理</p><h4 id="vuex"><a href="#vuex" class="headerlink" title="vuex"></a>vuex</h4><p>缺点：使用vuex组件传值需要把值存到全局仓库中，对于小型的项目，通信十分简单，这时使用 Vuex 反而会显得冗余和繁琐。</p><h4 id="attrs-x2F-listeners"><a href="#attrs-x2F-listeners" class="headerlink" title="$attrs&#x2F;$listeners"></a>$attrs&#x2F;$listeners</h4><ul><li><code>$attrs</code>：包含了父作用域中不被 prop 所识别的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时，这里会包含所有父作用域的绑定 (class 和 style 除外)，并且可以通过 v-bind&#x3D;”$attrs” 传入内部组件。通常配合 interitAttrs 选项一起使用。</li><li><code>$listeners</code>：包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器（存放的是父组件中绑定的非原生事件）。它可以通过 v-on&#x3D;”$listeners” 传入内部组件。</li></ul><p>缺点：适用于特定的情况，比如父组件和孙子组件</p><h4 id="provide-x2F-inject"><a href="#provide-x2F-inject" class="headerlink" title="provide&#x2F;inject"></a>provide&#x2F;inject</h4><p>祖先组件中通过provider来提供变量，然后在子孙组件中通过inject来注入变量。</p><p>provide &#x2F; inject API 主要解决了跨级组件间的通信问题，不过它的使用场景，主要是子组件获取上级组件的状态，跨级组件间建立了一种主动提供与依赖注入的关系。</p><p>缺点：provide 和 inject 绑定并不是可响应的。（如果你传入了一个可监听的对象，那么其对象的属性还是可响应的。）它被设计是为组件库和高阶组件服务的，平常业务中的代码不建议使用。</p><h4 id="ref"><a href="#ref" class="headerlink" title="ref"></a>ref</h4><p>ref如果在普通的 DOM 元素上使用，引用指向的就是 DOM 元素；如果用在子组件上，引用就指向组件实例。</p><p>缺点：不太适合嵌套层级太多的时候使用。$refs 不是响应式的，只能拿到获取它的那一刻子组件实例的状态。</p><h4 id="parent-x2F-children"><a href="#parent-x2F-children" class="headerlink" title="$parent &#x2F; $children"></a>$parent &#x2F; $children</h4><p>$parent 属性可以用来从一个子组件访问父组件的实例，$children 属性 可以获取当前实例的直接子组件。</p><p>缺点：如果使用 $parent 导致父组件数据变更后会破坏单项数据流，在绝大多数情况下，不推荐使用。</p><h3 id="生命周期"><a href="#生命周期" class="headerlink" title="生命周期"></a>生命周期</h3><p><a href="/2019/04/24/Vue%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F/">Vue生命周期</a></p><h3 id="路由（建议vue-router"><a href="#路由（建议vue-router" class="headerlink" title="路由（建议vue-router)"></a>路由（建议vue-router)</h3><p><a href="/2021/08/06/%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84Vue%E8%B7%AF%E7%94%B1%E6%8C%87%E5%8D%97/">Vue路由</a></p><h4 id="配置404"><a href="#配置404" class="headerlink" title="配置404"></a>配置404</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">&#123;</span><br><span class="line">  path: <span class="string">'*'</span>,    <span class="comment">//至于最底部</span></span><br><span class="line">  redirect: <span class="string">'/404'</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="状态管理（Vuex"><a href="#状态管理（Vuex" class="headerlink" title="状态管理（Vuex)"></a>状态管理（Vuex)</h3><h4 id="state"><a href="#state" class="headerlink" title="state"></a>state</h4><p>定义了应用状态的数据结构，可以在这里设置默认的初始状态。</p><h4 id="getters"><a href="#getters" class="headerlink" title="getters"></a>getters</h4><p>允许组件从 Store 中获取数据，mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。</p><h4 id="mutations"><a href="#mutations" class="headerlink" title="mutations"></a>mutations</h4><p>唯一更改 store 中状态的方法，且必须是同步函数。</p><h4 id="actions"><a href="#actions" class="headerlink" title="actions"></a>actions</h4><p>用于提交 mutation，而不是直接变更状态，可以包含任意异步操作。</p><h4 id="modules"><a href="#modules" class="headerlink" title="modules"></a>modules</h4><p>允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。</p><h3 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h3><h3 id="Vue3"><a href="#Vue3" class="headerlink" title="Vue3"></a>Vue3</h3><h4 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h4><p>1.vue3对比vue2具有明显的性能提升</p><p>2.具有的composition API实现逻辑模块化和重用</p><p>3.支持TS</p><h4 id="区别"><a href="#区别" class="headerlink" title="区别"></a>区别</h4><p>1.原理上</p><p>Vue2实现双向数据绑定原理，是通过es5的 <code>Object.defineProperty</code>，根据具体的key去读取和修改。其中的getter方法来实现数据劫持的，setter实现数据的修改。因此无法监听属性的添加和删除，不能监听数组的变化等。</p><p>Vue3使用原生<code>Proxy</code> 替代 <code>Object.defineProperty</code>，Proxy可以理解成，在对象之前架设一层“拦截”，拦截了对象的get和set操作。Proxy可以直接监听对象而非属性，并返回一个新对象，具有更好的响应式支持。</p><p>详细可看<a href="/2019/04/23/%E5%AF%B9Vue%E5%8E%9F%E7%90%86%E7%9A%84%E4%B8%80%E4%BA%9B%E7%90%86%E8%A7%A3/">Vue2和Vue3原理区别</a></p><h2 id="React"><a href="#React" class="headerlink" title="React"></a>React</h2><h3 id="setState"><a href="#setState" class="headerlink" title="setState"></a>setState</h3><p>1.setState 操作合并</p><ul><li>当第一个传入的参数是 object 数据类型它就会合并，类型调用了 Object.assgin</li><li>当参数类型是 function 数据类型时，不会合并</li></ul><p>2.setState 是否异步</p><ul><li>React 获得控制权的时候，它就是异步执行</li><li>当 React 丧失控制权的时候，它就是同步执行（setTimeout、Promise 等异步函数；自定义注册 DOM 事件等）</li></ul><h3 id="组件生命周期的方法-react-v17"><a href="#组件生命周期的方法-react-v17" class="headerlink" title="组件生命周期的方法(react v17)"></a>组件生命周期的方法(react v17)</h3><p>1.挂载</p><ul><li>constructor()</li><li>static getDerivedStateFromProps()</li><li>render()</li><li>componentDidMount()</li></ul><p>2.更新</p><ul><li>static getDerivedStateFromProps()</li><li>shouldComponentUpdate()</li><li>render()</li><li>getSnapshotBeforeUpdate()</li><li>componentDidUpdate()</li></ul><p>3.卸载</p><ul><li>componentWillUnmount()</li></ul><p>4.错误处理</p><ul><li>static getDerivedStateFromError()</li><li>componentDidCatch()</li></ul><h3 id="Redux、React-redux、Redux-中间件"><a href="#Redux、React-redux、Redux-中间件" class="headerlink" title="Redux、React-redux、Redux 中间件"></a>Redux、React-redux、Redux 中间件</h3><h3 id="React-Hook"><a href="#React-Hook" class="headerlink" title="React Hook"></a>React Hook</h3><blockquote><p>Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。</p></blockquote><p><a href="https://blog.csdn.net/xuewenjie0217/article/details/119112122" target="_blank" rel="noopener">react hook 详解及使用</a></p><h2 id="微信小程序"><a href="#微信小程序" class="headerlink" title="微信小程序"></a>微信小程序</h2><h3 id="生命周期-1"><a href="#生命周期-1" class="headerlink" title="生命周期"></a>生命周期</h3><ul><li><p>应用生命周期</p><p>小程序从启动 -&gt; 运行 -&gt; 销毁的过程</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">App(&#123;</span><br><span class="line">  <span class="comment">//小程序初始化完成时，执行此函数，全局只触发一次。可以做一些初始化的工作</span></span><br><span class="line">  onLaunch: <span class="function"><span class="keyword">function</span>(<span class="params">options</span>) </span>&#123;&#125;,</span><br><span class="line">  </span><br><span class="line">  <span class="comment">//小程序启动，或从后台进入前台显示时触发</span></span><br><span class="line">  onShow: <span class="function"><span class="keyword">function</span>(<span class="params">options</span>) </span>&#123;&#125;,</span><br><span class="line">  </span><br><span class="line">  <span class="comment">//小程序从前台进入后台时触发</span></span><br><span class="line">  onHide: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;,</span><br><span class="line"></span><br><span class="line">  <span class="comment">//小程序发生脚本错误或 API 调用报错时触发</span></span><br><span class="line">  onError: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure></li><li><p>页面生命周期</p><p>小程序中，每个页面的加载 -&gt; 渲染 -&gt; 销毁的过程</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">Page(&#123;</span><br><span class="line">  <span class="comment">//监听页面加载，一个页面只调用1次</span></span><br><span class="line">  onLoad: <span class="function"><span class="keyword">function</span>(<span class="params">options</span>) </span>&#123;&#125;,</span><br><span class="line">  </span><br><span class="line">  <span class="comment">//监听页面显示</span></span><br><span class="line">  onShow: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;,</span><br><span class="line">  </span><br><span class="line">  <span class="comment">//监听页面初次渲染完成，一个页面只调用1次</span></span><br><span class="line">  onReady: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;,</span><br><span class="line">  </span><br><span class="line">  <span class="comment">//监听页面隐藏</span></span><br><span class="line">  onHide: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;,</span><br><span class="line">  </span><br><span class="line">  <span class="comment">//监听页面卸载，一个页面只调用1次</span></span><br><span class="line">  onUnload: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure></li><li><p>组件的生命周期</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">Component(&#123;</span><br><span class="line">  <span class="comment">//监听组件创建</span></span><br><span class="line">  created: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;,</span><br><span class="line">  </span><br><span class="line">  <span class="comment">//在组件完全初始化完毕、进入页面节点树后被触发</span></span><br><span class="line">  attached: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;,</span><br><span class="line"></span><br><span class="line">  <span class="comment">//在组件离开页面节点树后被触发</span></span><br><span class="line">  detached: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;,</span><br><span class="line"></span><br><span class="line">  <span class="comment">//在组件在视图层布局完成后执行</span></span><br><span class="line">  ready: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;,</span><br><span class="line">  </span><br><span class="line">  <span class="comment">//在组件实例被移动到节点树另一个位置时执行</span></span><br><span class="line">  moved: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;,</span><br><span class="line">  </span><br><span class="line">  <span class="comment">//组件抛出错误时执行</span></span><br><span class="line">  error: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;&#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure></li></ul><h2 id="Flutter"><a href="#Flutter" class="headerlink" title="Flutter"></a>Flutter</h2><h3 id="生命周期-2"><a href="#生命周期-2" class="headerlink" title="生命周期"></a>生命周期</h3><h3 id="状态管理"><a href="#状态管理" class="headerlink" title="状态管理"></a>状态管理</h3><h3 id="路由管理"><a href="#路由管理" class="headerlink" title="路由管理"></a>路由管理</h3><h2 id="脑图"><a href="#脑图" class="headerlink" title="脑图"></a>脑图</h2><p>最后附上我去年画的思维脑图吧：</p><p><img src="/images/other/mindMap.png" alt="force"></p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://vuex.vuejs.org/zh/guide/" target="_blank" rel="noopener">Vuex</a></p><p><a href="https://react.docschina.org/docs/react-component.html" target="_blank" rel="noopener">react</a></p>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;Vue&quot;&gt;&lt;a href=&quot;#Vue&quot; class=&quot;headerlink&quot; title=&quot;Vue&quot;&gt;&lt;/a&gt;Vue&lt;/h2&gt;&lt;h3 id=&quot;组件通信&quot;&gt;&lt;a href=&quot;#组件通信&quot; class=&quot;headerlink&quot; title=&quot;组件通信&quot;&gt;&lt;/a&gt;组件通信&lt;/h3&gt;&lt;h4 id=&quot;props-x2F-emit&quot;&gt;&lt;a href=&quot;#props-x2F-emit&quot; class=&quot;headerlink&quot; title=&quot;props&amp;#x2F;$emit&quot;&gt;&lt;/a&gt;props&amp;#x2F;$emit&lt;/h4&gt;&lt;p&gt;父组件通过props向下传递数据给子组件，子组件通过$emit发送事件给父组件发消息（传值）。&lt;/p&gt;&lt;p&gt;缺点：比较适合父子组件通信，在兄弟组件和跨级组件中使用会比较复杂&lt;/p&gt;
    
    </summary>
    
      <category term="前端" scheme="https://aartemida.github.io/categories/%E5%89%8D%E7%AB%AF/"/>
    
    
  </entry>
  
  <entry>
    <title>Typescript的接口、类、泛型</title>
    <link href="https://aartemida.github.io/2020/09/09/Typescript%E6%8E%A5%E5%8F%A3%E5%92%8C%E7%B1%BB/"/>
    <id>https://aartemida.github.io/2020/09/09/Typescript接口和类/</id>
    <published>2020-09-09T14:13:06.000Z</published>
    <updated>2022-09-18T07:00:33.441Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Typescript"><a href="#Typescript" class="headerlink" title="Typescript"></a>Typescript</h2><h3 id="类型断言"><a href="#类型断言" class="headerlink" title="类型断言"></a>类型断言</h3><p>TypeScript 类型断言允许覆盖编译器的推断，说明你更了解这个类型，编译器不应该再发出错误。</p><a id="more"></a><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> str = <span class="string">'aaa'</span></span><br><span class="line"><span class="comment">//断言str为string类型</span></span><br><span class="line"><span class="comment">// 方式一</span></span><br><span class="line"><span class="keyword">let</span> len = (&lt;<span class="built_in">string</span>&gt;str).len </span><br><span class="line"></span><br><span class="line"><span class="comment">// 方式二</span></span><br><span class="line"><span class="keyword">let</span> len = (str <span class="keyword">as</span> <span class="built_in">string</span>).length</span><br></pre></td></tr></table></figure><h3 id="类型别名"><a href="#类型别名" class="headerlink" title="类型别名"></a>类型别名</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 方式一</span></span><br><span class="line">type types = string | number | boolean</span><br><span class="line">type names = <span class="string">'小明'</span> | <span class="string">'小红'</span></span><br><span class="line"><span class="keyword">let</span> person1 : names <span class="comment">// person1 只能为 '小明' 或者 '小红'</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 方式二</span></span><br><span class="line">type testPerson &#123;</span><br><span class="line">  name: string,</span><br><span class="line">  age: number</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> person2 : testPerson = &#123;</span><br><span class="line">  name: <span class="string">'小明'</span>,</span><br><span class="line">  age: <span class="number">20</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>类型别名也支持扩展：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">type test1 = &#123;</span><br><span class="line">  t1: string</span><br><span class="line">&#125;</span><br><span class="line">type testNew = test1 &amp; &#123;</span><br><span class="line">  t2: string</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></ul><p>不同点：</p><ul><li>type 可以声明基本类型，联合类型，数组，元组等；interface只能声明对象</li><li>type 语句中还可以使用 typeof 获取实例的类型进行赋值</li><li>interface 会自动合并，type不会（当type和interface声明同名数据时，type会报错，interface会进行组合）</li></ul><h3 id="接口"><a href="#接口" class="headerlink" title="接口"></a>接口</h3><h4 id="可选属性和只读属性"><a href="#可选属性和只读属性" class="headerlink" title="可选属性和只读属性"></a>可选属性和只读属性</h4><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">interface Info &#123;</span><br><span class="line">  readonly name: string, <span class="comment">//只读</span></span><br><span class="line">  age?: number <span class="comment">//可选</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="索引签名"><a href="#索引签名" class="headerlink" title="索引签名"></a>索引签名</h4><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">// 可以用来定义不确定的object属性</span></span><br><span class="line">interface Info &#123;</span><br><span class="line">  [props: string]: number | string</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 对数组约束</span></span><br><span class="line">interface MyArr &#123;</span><br><span class="line">  [index: number]: string</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="函数接口"><a href="#函数接口" class="headerlink" title="函数接口"></a>函数接口</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><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">interface PersonName &#123;</span><br><span class="line">  (firstName: string, <span class="attr">lastName</span>: string): string;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 为函数定义参数和返回值类型</span></span><br><span class="line"><span class="keyword">let</span> getName: PersonName = <span class="function"><span class="keyword">function</span>(<span class="params">x: string, y: string</span>) : <span class="title">string</span> </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> x + y</span><br><span class="line">&#125; </span><br><span class="line"><span class="built_in">console</span>.log(getName(<span class="string">'li'</span>, <span class="string">'ming'</span>))</span><br></pre></td></tr></table></figure><h4 id="接口继承"><a href="#接口继承" class="headerlink" title="接口继承"></a>接口继承</h4><p>Ts允许接口继承多个接口，可以使用继承来扩展接口。</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></pre></td><td class="code"><pre><span class="line">interface Person &#123;</span><br><span class="line">  name: string</span><br><span class="line">&#125;</span><br><span class="line">interface PersonInfo extends Person &#123;</span><br><span class="line">  age: number</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> p: PersonInfo = &#123;</span><br><span class="line">  name: <span class="string">'aa'</span>,</span><br><span class="line">  age: <span class="number">18</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">interface Title &#123;</span><br><span class="line">  title: string</span><br><span class="line">&#125;</span><br><span class="line">interface PersonInfoNew extends Person, Title &#123;</span><br><span class="line">  age: number</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> p2: PersonInfoNew = &#123;</span><br><span class="line">  name: <span class="string">'p2'</span>,</span><br><span class="line">  age: <span class="number">18</span>,</span><br><span class="line">  title: <span class="string">'title'</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><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">// 匿名函数</span></span><br><span class="line"><span class="keyword">const</span> fun1 = <span class="function"><span class="keyword">function</span>(<span class="params">a: number</span>) : <span class="title">number</span> </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> a</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="function"><span class="keyword">function</span> <span class="title">fun2</span>(<span class="params">a: number</span>) : <span class="title">number</span> </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> a</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 箭头函数</span></span><br><span class="line"><span class="keyword">const</span> fun3 = (a: number) : <span class="function"><span class="params">number</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> a</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">type funType = <span class="function">(<span class="params">a: number, b: number</span>) =&gt;</span> number</span><br><span class="line"><span class="keyword">const</span> fun4: funType = <span class="function">(<span class="params">a: number, b: number</span>) =&gt;</span> a + b</span><br><span class="line"><span class="comment">// 或者</span></span><br><span class="line"><span class="keyword">const</span> fun5: <span class="function">(<span class="params">a: number, b: number</span>) =&gt;</span> number = <span class="function"><span class="keyword">function</span>(<span class="params">a, b</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> a + b</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="构造函数"><a href="#构造函数" class="headerlink" title="构造函数"></a>构造函数</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 构造函数</span></span><br><span class="line"><span class="keyword">let</span> myFun = <span class="keyword">new</span> <span class="built_in">Function</span>(<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'return a * b'</span>)</span><br></pre></td></tr></table></figure><h4 id="函数重载"><a href="#函数重载" class="headerlink" title="函数重载"></a>函数重载</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><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="function"><span class="keyword">function</span> <span class="title">addFun</span>(<span class="params">a: string, b: string</span>) : <span class="title">string</span></span></span><br><span class="line"><span class="function"><span class="title">function</span> <span class="title">addFun</span>(<span class="params">a: number, b: number</span>) : <span class="title">number</span></span></span><br><span class="line"><span class="function"><span class="title">function</span> <span class="title">addFun</span>(<span class="params">a: string, b: number</span>) : <span class="title">string</span></span></span><br><span class="line"><span class="function"><span class="title">function</span> <span class="title">addFun</span>(<span class="params">a: number, b: string</span>) : <span class="title">string</span></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"><span class="title">function</span> <span class="title">addFun</span>(<span class="params">a: any, b: any</span>) : <span class="title">any</span> </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> a + b</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="类"><a href="#类" class="headerlink" title="类"></a>类</h3><p>类描述了所创建的对象共同的属性和方法。</p><h4 id="类的继承"><a href="#类的继承" class="headerlink" title="类的继承"></a>类的继承</h4><p>子类除了不能继承父类的私有属性和构造函数，其他都可以继承。TS支持多重继承。</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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Parent</span> </span>&#123;</span><br><span class="line">  name: string</span><br><span class="line">  <span class="keyword">static</span> title: string <span class="comment">//静态成员可以通过类名调用</span></span><br><span class="line">  <span class="keyword">constructor</span>(name: string, title: string) &#123;</span><br><span class="line">    <span class="keyword">this</span>.name = name</span><br><span class="line">    <span class="keyword">this</span>.title = title</span><br><span class="line">  &#125;</span><br><span class="line">  say(): <span class="keyword">void</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'我是父类'</span>, <span class="keyword">this</span>.name)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">console</span>.log(Parent.title)</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Child</span> <span class="keyword">extends</span> <span class="title">Parent</span> </span>&#123;</span><br><span class="line">  age: number</span><br><span class="line">  <span class="keyword">constructor</span>(name: string, title: string, age: number) &#123;</span><br><span class="line">    <span class="keyword">super</span>(name, title) <span class="comment">//父类的属性</span></span><br><span class="line">    <span class="keyword">this</span>.age = age</span><br><span class="line">  &#125;</span><br><span class="line">  say(): <span class="keyword">void</span> &#123;</span><br><span class="line">    <span class="keyword">super</span>.say() <span class="comment">//调用父类的方法</span></span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'我是子类'</span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="抽象类"><a href="#抽象类" class="headerlink" title="抽象类"></a>抽象类</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><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">abstract <span class="class"><span class="keyword">class</span> <span class="title">Person</span> </span>&#123;</span><br><span class="line">  abstract name: string</span><br><span class="line">  abstract say(): string <span class="comment">//抽象方法</span></span><br><span class="line"></span><br><span class="line">  sayName() &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="keyword">this</span>.say())</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>抽象类和接口的区别：</p><ul><li>抽象类要被子类继承，接口要被类实现(implements继承需要重写才能使用)</li><li>抽象类可以有具体的方法，还可以有方法的实现，接口只能有抽象方法。</li><li>接口不包含构造器；而抽象类中可以包含构造器。</li><li>一个类最多有一个直接父类，包括抽象类；但是一个类可以直接实现多个接口。</li></ul><h4 id="implements"><a href="#implements" class="headerlink" title="implements"></a>implements</h4><p>类可以通过implements实现接口。implements继承类，必须<code>重写</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">interface Info &#123;</span><br><span class="line">  name: string,</span><br><span class="line">  say(): <span class="keyword">void</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">InfoClass</span> <span class="title">implements</span> <span class="title">Info</span> </span>&#123;</span><br><span class="line">  name: string = <span class="string">'aa'</span></span><br><span class="line">  say(): <span class="keyword">void</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="keyword">this</span>.name)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="ts的类和js的类有什么区别"><a href="#ts的类和js的类有什么区别" class="headerlink" title="ts的类和js的类有什么区别"></a>ts的类和js的类有什么区别</h4><p>ts中类的属性可以有修饰符:</p><ul><li>public：公有</li><li>protected：保护类型，在类、子类里面可以访问，在类外部不能访问</li><li>private：私有，在类可以访问，子类、类外边不能访问</li><li>readonly：只读</li></ul><h3 id="泛型"><a href="#泛型" class="headerlink" title="泛型"></a>泛型</h3><p>泛型指的是在定义函数&#x2F;接口&#x2F;类型时，不预先指定具体的类型，而是在使用的时候在指定类型限制的一种特性。</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="function"><span class="keyword">function</span> <span class="title">test</span> &lt;<span class="title">T</span>&gt; (<span class="params">a: T</span>) : <span class="title">T</span> </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(a)</span><br><span class="line">  <span class="keyword">return</span> a</span><br><span class="line">&#125;</span><br><span class="line">test&lt;number&gt;(<span class="number">1</span>)</span><br><span class="line">test&lt;string | boolean&gt;(<span class="string">'hh'</span>)</span><br><span class="line">test&lt;string | boolean&gt;(<span class="literal">true</span>)</span><br></pre></td></tr></table></figure><p>和any的区别：可以定义参数和返回值类型一致的情况。</p><h4 id="泛型接口"><a href="#泛型接口" class="headerlink" title="泛型接口"></a>泛型接口</h4><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">// 可以解决函数中不同类型获取某一属性。</span></span><br><span class="line">interface ILength &#123;</span><br><span class="line">  length: number</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getLen</span> &lt;<span class="title">T</span> <span class="title">extends</span> <span class="title">ILength</span>&gt; (<span class="params">a: T</span>) : <span class="title">number</span> </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> a.length</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">interface IPerson&lt;T1, T2=string&gt; &#123;<span class="comment">// 泛型也可以指定默认类型</span></span><br><span class="line">  name: T1,</span><br><span class="line">  age: T2</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> p0 :  IPerson&lt;string, number&gt; = &#123;</span><br><span class="line">  name: <span class="string">'a'</span>,</span><br><span class="line">  age: <span class="number">10</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="使用类型参数约束"><a href="#使用类型参数约束" class="headerlink" title="使用类型参数约束"></a>使用类型参数约束</h4><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="function"><span class="keyword">function</span> <span class="title">getKey</span>&lt;<span class="title">T</span>, <span class="title">K</span> <span class="title">extends</span> <span class="title">keyof</span> <span class="title">T</span>&gt;(<span class="params">obj: T, key: K</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> obj[key]</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> x = &#123;</span><br><span class="line">  a: <span class="number">1</span>,</span><br><span class="line">&#125;</span><br><span class="line">getKey(x, <span class="string">'a'</span>)</span><br></pre></td></tr></table></figure><h3 id="is-关键字"><a href="#is-关键字" class="headerlink" title="is 关键字"></a>is 关键字</h3><p>被称为类型谓词，用来判断一个变量属于某个接口或类型。</p>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;Typescript&quot;&gt;&lt;a href=&quot;#Typescript&quot; class=&quot;headerlink&quot; title=&quot;Typescript&quot;&gt;&lt;/a&gt;Typescript&lt;/h2&gt;&lt;h3 id=&quot;类型断言&quot;&gt;&lt;a href=&quot;#类型断言&quot; class=&quot;headerlink&quot; title=&quot;类型断言&quot;&gt;&lt;/a&gt;类型断言&lt;/h3&gt;&lt;p&gt;TypeScript 类型断言允许覆盖编译器的推断，说明你更了解这个类型，编译器不应该再发出错误。&lt;/p&gt;
    
    </summary>
    
      <category term="笔记" scheme="https://aartemida.github.io/categories/%E7%AC%94%E8%AE%B0/"/>
    
      <category term="TypeScript" scheme="https://aartemida.github.io/categories/%E7%AC%94%E8%AE%B0/TypeScript/"/>
    
    
      <category term="Typescript" scheme="https://aartemida.github.io/tags/Typescript/"/>
    
  </entry>
  
  <entry>
    <title>常见的浏览器存储方式</title>
    <link href="https://aartemida.github.io/2020/06/17/%E5%B8%B8%E8%A7%81%E7%9A%84%E6%B5%8F%E8%A7%88%E5%99%A8%E6%9C%AC%E5%9C%B0%E5%AD%98%E5%82%A8/"/>
    <id>https://aartemida.github.io/2020/06/17/常见的浏览器本地存储/</id>
    <published>2020-06-17T06:05:43.000Z</published>
    <updated>2022-09-18T13:16:56.549Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在很久很久以前，由于HTTP 协议是一个无状态协议，服务器是无法记录浏览器的状态的。如果每次都要在通过表单提交信息给服务器，或者通过向服务器请求某些固定的数据，这无疑是十分繁琐的事情，因此如何进行状态管理就是急需解决的一个问题。因此cookie就这样诞生啦，在此后的许多年，浏览器发展迅速，出现了更多的存储方式，他们各有优缺点，接下来让我们详细了解下吧！</p><a id="more"></a><h2 id="Cookie"><a href="#Cookie" class="headerlink" title="Cookie"></a>Cookie</h2><p>Cookie最初是用于存储会话信息的，它会自动随着HTTP请求头信息一起发送，因此服务器就可以获取到cookie中的数据。</p><p>Cookie的存储是以域名形式进行区分的，不同的域下存储的cookie是独立的。</p><h3 id="Cookie的优点"><a href="#Cookie的优点" class="headerlink" title="Cookie的优点"></a>Cookie的优点</h3><ol><li>Cookie相比于其他的数据存储方式，兼容性更好。</li><li>Cookie可以设置过期时间，默认是会话结束时到期自动销毁。cookie的有效时间默认为-1，如果不进行设置的话，就会默认在浏览器会话关闭时结束。</li></ol><h3 id="Cookie的缺点"><a href="#Cookie的缺点" class="headerlink" title="Cookie的缺点"></a>Cookie的缺点</h3><ol><li>Cookie能存储的数据容量有限，最大只能有 4KB，因此Cookie只能用来存少量信息。</li><li>Cookie会被放到统一域名下的所有请求的请求头中，而有些请求并不需要cookie中的数据，因此会增加一部分不必要的网络开销。</li><li>Cookie一般只能存储字符串，存储的格式有限。</li><li>由于Cookie中的键值对是经过URL编码的，所以读取Cookie需要解码，使用不太方便。</li><li>Cookie是使用HTTP连接传递数据，所以 cookie 存储的信息容易被窃取，安全性不高。</li></ol><h3 id="Cookie的用法"><a href="#Cookie的用法" class="headerlink" title="Cookie的用法"></a>Cookie的用法</h3><h4 id="读取"><a href="#读取" class="headerlink" title="读取"></a>读取</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getCookie</span>(<span class="params">name</span>)</span>&#123;</span><br><span class="line">  <span class="keyword">var</span> start = <span class="built_in">document</span>.cookie.indexOf(name+<span class="string">"="</span>) ;</span><br><span class="line">  <span class="keyword">if</span> ( start != <span class="number">-1</span> )&#123;</span><br><span class="line">      start = start + name.length + <span class="number">1</span> ;</span><br><span class="line">      <span class="keyword">var</span> end = <span class="built_in">document</span>.cookie.indexOf(<span class="string">";"</span>, start) ;</span><br><span class="line">      <span class="keyword">if</span> ( end == <span class="number">-1</span> ) end = <span class="built_in">document</span>.cookie.length ;</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">unescape</span>(<span class="built_in">document</span>.cookie.substring(start,end)) ;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="写入"><a href="#写入" class="headerlink" title="写入"></a>写入</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">setCookie</span>(<span class="params">name, value</span>)</span>&#123;</span><br><span class="line">  <span class="keyword">var</span> expdate = <span class="keyword">new</span> <span class="built_in">Date</span>();</span><br><span class="line">  <span class="comment">//设置过期时间</span></span><br><span class="line">  expdate.setTime(expdate.getTime() + <span class="number">60</span> * <span class="number">60</span>  * <span class="number">1000</span>);<span class="comment">//1 hour</span></span><br><span class="line">  <span class="built_in">document</span>.cookie = name + <span class="string">"="</span> + <span class="built_in">escape</span>(value) + <span class="string">";expires="</span> + expdate.toUTCString();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="删除"><a href="#删除" class="headerlink" title="删除"></a>删除</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">delCookie</span>(<span class="params">name</span>)</span>&#123;</span><br><span class="line">  <span class="keyword">let</span> newVue = <span class="keyword">new</span> Vue();</span><br><span class="line">  newVue.setCookie( name, <span class="literal">null</span>, <span class="number">-1</span>) ;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="localStorage"><a href="#localStorage" class="headerlink" title="localStorage"></a>localStorage</h2><p>localStorage是HTML5中新增的浏览器存储机制，它可以作为持久保存客户端数据的方案，除非主动删除localStorage中的数据，否则数据是永远不会过期的。(localStorage受同源策略的限制)</p><h3 id="localStorage的优点"><a href="#localStorage的优点" class="headerlink" title="localStorage的优点"></a>localStorage的优点</h3><ol><li>localStorage的存储量大。</li><li>localStorage也是以键值对的方式存储字符串，相对于cookie，它可存的数据格式更多。</li><li>localStorage不会与服务端发生通信，仅位于浏览器端。</li></ol><h3 id="localStorage的缺点"><a href="#localStorage的缺点" class="headerlink" title="localStorage的缺点"></a>localStorage的缺点</h3><ol><li>如果要存储JSON格式的数据，存储和获取的时候需要进行JSON和字符串之间的转换，因此它还是更适合存储数据结构较为简单的数据。</li><li>localStorage中的数据没有过期时间设置，不能自动删除，如果存储内容多的话会消耗内存空间。</li></ol><h3 id="localStorage的用法"><a href="#localStorage的用法" class="headerlink" title="localStorage的用法"></a>localStorage的用法</h3><h4 id="读取-1"><a href="#读取-1" class="headerlink" title="读取"></a>读取</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">localStorage.getItem(key)</span><br><span class="line"><span class="comment">//或者</span></span><br><span class="line">localStorage[key]</span><br></pre></td></tr></table></figure><h4 id="写入-1"><a href="#写入-1" class="headerlink" title="写入"></a>写入</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">localStorage.setItem(key,value);</span><br></pre></td></tr></table></figure><h4 id="删除-1"><a href="#删除-1" class="headerlink" title="删除"></a>删除</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">localStorage.removeItem(key)</span><br><span class="line"><span class="comment">//或者清除所有的数据</span></span><br><span class="line">localStorage.clear()</span><br></pre></td></tr></table></figure><h2 id="sessionStorage"><a href="#sessionStorage" class="headerlink" title="sessionStorage"></a>sessionStorage</h2><p>sessionStorage和localStroage差不多，也是本地存储。但sessionStorage的生命周期和localStroage不同，它是会话级别的本地存储，当会话结束时存储内容就会被销毁。</p><p>Local Storage、Session Storage 和 Cookie 都遵循同源策略。但 Session Storage 特别的一点在于，即便是相同域名下的两个页面，只要它们不在同一个浏览器窗口中打开，那么它们的 Session Storage 内容便无法共享。</p><h3 id="sessionStorage的用法"><a href="#sessionStorage的用法" class="headerlink" title="sessionStorage的用法"></a>sessionStorage的用法</h3><h4 id="读取-2"><a href="#读取-2" class="headerlink" title="读取"></a>读取</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sessionStorage.getItem(key)</span><br></pre></td></tr></table></figure><h4 id="写入-2"><a href="#写入-2" class="headerlink" title="写入"></a>写入</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sessionStorage.setItem(key,value);</span><br></pre></td></tr></table></figure><h4 id="删除-2"><a href="#删除-2" class="headerlink" title="删除"></a>删除</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">sessionStorage.removeItem(key)</span><br><span class="line"><span class="comment">//或者清除所有的数据</span></span><br><span class="line">sessionStorage.clear()</span><br></pre></td></tr></table></figure><h2 id="IndexedDB"><a href="#IndexedDB" class="headerlink" title="IndexedDB"></a>IndexedDB</h2><p>以上三种都只适用于少量数据，如果要存储更多更复杂的数据就不适用了。因此我们需要新的存储方式，比如IndexedDB。</p><p>IndexedDB 是一个运行在浏览器上的非关系型数据库。</p><p>IndexedDB与 Web storage 一致，均是在创建数据库的域名下才能访问。且不能指定访问域名。IndexedDB存储时间永久，除非用户清除数据，可以用作长效的存储。</p>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;在很久很久以前，由于HTTP 协议是一个无状态协议，服务器是无法记录浏览器的状态的。如果每次都要在通过表单提交信息给服务器，或者通过向服务器请求某些固定的数据，这无疑是十分繁琐的事情，因此如何进行状态管理就是急需解决的一个问题。因此cookie就这样诞生啦，在此后的许多年，浏览器发展迅速，出现了更多的存储方式，他们各有优缺点，接下来让我们详细了解下吧！&lt;/p&gt;
    
    </summary>
    
      <category term="前端" scheme="https://aartemida.github.io/categories/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="浏览器" scheme="https://aartemida.github.io/tags/%E6%B5%8F%E8%A7%88%E5%99%A8/"/>
    
  </entry>
  
  <entry>
    <title>适合新手看的webpack安装和使用教程</title>
    <link href="https://aartemida.github.io/2020/03/12/webpack%E5%AE%89%E8%A3%85%E4%BD%BF%E7%94%A8/"/>
    <id>https://aartemida.github.io/2020/03/12/webpack安装使用/</id>
    <published>2020-03-12T08:45:50.000Z</published>
    <updated>2022-09-11T11:18:54.341Z</updated>
    
    <content type="html"><![CDATA[<p>现在前端开发越来越偏向模块化，我们的项目也逐渐转向脚手架搭建。这其中，webpack的力量是巨大的，之前因为不了解webpack的某些配置所以在项目中也耽搁了一些时间，因此我觉得很有必要深入了解下webpack。之后需要自己尝试手动搭建项目工程，才能真正理解脚手架的原理。</p><a id="more"></a><h2 id="webpack"><a href="#webpack" class="headerlink" title="webpack"></a>webpack</h2><p>官方的话来说，Webpack 是一个模块打包器。它将根据模块的依赖关系进行静态分析，然后将这些模块按照指定的规则生成对应的静态资源。</p><p>接下来让我们安装并初步配置下<code>webpack4</code>吧~</p><h2 id="全局安装"><a href="#全局安装" class="headerlink" title="全局安装"></a>全局安装</h2><p>全局安装，安装成功之后可以在全局使用<code>webpack</code>：</p><figure class="highlight plain"><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">npm install -g webpack</span><br><span class="line">npm install -g webpack-cli</span><br></pre></td></tr></table></figure><p>全局安装失败可能是网络的原因，可以使用淘宝镜像安装：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cnpm install webpack -g</span><br></pre></td></tr></table></figure><h2 id="栗子初体验"><a href="#栗子初体验" class="headerlink" title="栗子初体验"></a>栗子初体验</h2><h4 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a>初始化</h4><p>新建一个文件夹<code>weproject</code>并初始化项目：</p><figure class="highlight plain"><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">cd weproject</span><br><span class="line">npm init</span><br></pre></td></tr></table></figure><p><code>npm init</code>会自动创建一个<code>package.json</code>文件，这个文件存放着当前项目的相关信息。</p><h4 id="安装webpack"><a href="#安装webpack" class="headerlink" title="安装webpack"></a>安装webpack</h4><p>在项目中安装webpack：</p><figure class="highlight plain"><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 webpack webpack-cli</span><br></pre></td></tr></table></figure><h4 id="构建目录"><a href="#构建目录" class="headerlink" title="构建目录"></a>构建目录</h4><p>新建文件夹<code>src</code></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mkdir src</span><br></pre></td></tr></table></figure><p>在src文件夹中新建一个<code>index.js</code>。该文件是webpack的入口文件。webpack打包后会自动生成一个dist文件夹，默认生成构建文件<code>main.js</code>。</p><figure class="highlight plain"><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">//index.js</span><br><span class="line">document.write(&quot;I&apos;m index.js&quot;)</span><br></pre></td></tr></table></figure><p>在src文件夹中新建一个新建静态页面index.html，方便之后查看效果：</p><figure class="highlight html"><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="meta">&lt;!DOCTYPE <span class="meta-keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">title</span>&gt;</span>Index<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">"../dist/main.js"</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">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><p>webpack4是可以不用手动配置（webpack会提供一套默认配置），但如果需要自定义配置，我们可以新建一个webpack的配置文件<code>webpack.config.js</code>，通过配置文件可以告诉webpack需要做些什么。</p><p>如下，在该文件中配置了入口文件和输出文件的路径：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  entry: <span class="string">'./src/index.js'</span>,  <span class="comment">//入口文件</span></span><br><span class="line">  output: &#123; <span class="comment">//打包出口文件</span></span><br><span class="line">    path: path.resolve(__dirname, <span class="string">'dist'</span>),</span><br><span class="line">    filename: <span class="string">'./main.js'</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>PS： 可以通过<code>npx</code>直接执行node_modules中的依赖。</p><h4 id="执行webpack"><a href="#执行webpack" class="headerlink" title="执行webpack"></a>执行webpack</h4><p>在文件夹中执行命令<code>webpack</code>可以看到打包后的js文件在自动生成的dist文件夹中：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">webpack //或者 webpack --mode development</span><br></pre></td></tr></table></figure><p>在浏览器打开index.html可以查看效果，说明webpack把src&#x2F;index.js文件打包成dist&#x2F;main.js了。</p><h4 id="修改执行命令"><a href="#修改执行命令" class="headerlink" title="修改执行命令"></a>修改执行命令</h4><p>只需在package.json的<code>scripts</code>中加入:</p><figure class="highlight"><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">"scripts": &#123;</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">"dev":"webpack",</span><br><span class="line">"build":"webpack --mode production"</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>就可以用<code>npm run dev</code>代替<code>webpack</code>，用<code>npm run build</code>代替<code>webpack --mode production</code>了。</p><h4 id="新建模块"><a href="#新建模块" class="headerlink" title="新建模块"></a>新建模块</h4><p>新建一个module.js：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//module.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="string">"I'm module.js"</span></span><br></pre></td></tr></table></figure><p>在index.js中引用module.js：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ...</span></span><br><span class="line"><span class="keyword">let</span> myModule = <span class="built_in">require</span>(<span class="string">'./module.js'</span>)</span><br><span class="line">alert(myModule)</span><br></pre></td></tr></table></figure><p>重新执行<code>npm run dev</code>打开页面可以看到页面变化。webpack会分析入口文件，并解析包含依赖关系的文件，把他们都打包到main.js中。</p><h4 id="让配置文件支持智能提示"><a href="#让配置文件支持智能提示" class="headerlink" title="让配置文件支持智能提示"></a>让配置文件支持智能提示</h4><p>通过 Configuration 让 VSCode 提供webpack配置的智能提示，修改webpack.config.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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 运行webpack时注释下面这句</span></span><br><span class="line"><span class="keyword">import</span> &#123; Configuration &#125; <span class="keyword">from</span> <span class="string">'webpack'</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* <span class="doctag">@type <span class="type">&#123;Configuration&#125;</span></span></span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">const</span> config = &#123;</span><br><span class="line"><span class="comment">//...配置项</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = config</span><br></pre></td></tr></table></figure><p>到这里我们就初步了解了webpack的安装和使用啦，不过如果要实现更多自定义功能的话，我们还需要了解webpack的几个核心概念。</p><h2 id="webpack核心概念"><a href="#webpack核心概念" class="headerlink" title="webpack核心概念"></a>webpack核心概念</h2><p>webpack的核心有以下几点：</p><ol><li>Entry：入口</li><li>Output：出口</li><li>Loader：加载器</li><li>Plugins：插件</li><li>Mode：模式</li></ol><h3 id="Entry"><a href="#Entry" class="headerlink" title="Entry"></a>Entry</h3><p>入口文件，默认是<code>src/index.js</code>，entry可以是字符串（单入口），可以是数组或对象（多入口），如下：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  entry: <span class="string">'./src/index.js'</span>,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>或</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  entry: &#123;</span><br><span class="line">    main: [</span><br><span class="line">      <span class="string">'./src/index.js'</span>,</span><br><span class="line">      <span class="string">'./src/index2.js'</span></span><br><span class="line">    ]</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>webpack会解析多个入口文件的依赖后进行打包。</p><h3 id="Output"><a href="#Output" class="headerlink" title="Output"></a>Output</h3><p>输出文件，是一个对象，webpack默认创建的输出内容就是 <code>./dist/main.js</code>。也可以自定义路径：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// webpack.config.js运行在nodeJs环境中，可以使用nodeJS的内置模块</span></span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  output: &#123;</span><br><span class="line">    <span class="comment">// filename: 'main.js',</span></span><br><span class="line"><span class="comment">// 或（生成hash结尾）</span></span><br><span class="line">filename: <span class="string">'[name].[contenthash].js'</span></span><br><span class="line">path: path.resolve(__dirname, <span class="string">'dist'</span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="path"><a href="#path" class="headerlink" title="path"></a>path</h4><p>表示生成文件的根目录，需要传入一个绝对路径。path参数和后面的filename参数共同组成入口文件的完整路径。</p><h4 id="publicPath"><a href="#publicPath" class="headerlink" title="publicPath"></a>publicPath</h4><p>表示一个URL路径（指向生成文件的根目录），用于生成css&#x2F;js&#x2F;图片&#x2F;字体文件等资源的路径。 publicPath参数跟path参数的区别是：path参数其实是针对本地文件系统的，而publicPath则针对的是浏览器，因此，publicPath既可以是一个相对路径，也可以是一个绝对路径。</p><h4 id="filename"><a href="#filename" class="headerlink" title="filename"></a>filename</h4><p>表示如何命名生成出来的入口文件</p><h4 id="chunkFilename"><a href="#chunkFilename" class="headerlink" title="chunkFilename"></a>chunkFilename</h4><p>chunkFilename参数与filename参数类似，都是用来定义生成文件的命名方式的，只不过，chunkFilename参数指定的是除入口文件外的chunk（这些chunk通常是由于webpack对代码的优化所形成的，比如因应实际运行的情况来异步加载）的命名。</p><h3 id="Loader"><a href="#Loader" class="headerlink" title="Loader"></a>Loader</h3><p>Webpack 本身只能处理 JavaScript 模块，如果要处理其他类型的文件（如css，图片，字体等），就需要使用 loader 进行转换。可以理解为Loader 将各个类型的文件，转换为JS代码能够为webpack所打包。</p><p>Loader 可以在<code>require()</code>引用模块的时候添加，也可以在 webpack 全局配置中进行绑定，还可以通过命令行的方式使用。</p><h4 id="打包css（css-loader）"><a href="#打包css（css-loader）" class="headerlink" title="打包css（css-loader）"></a>打包css（css-loader）</h4><p>新建一个文件style.css：</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">body</span> &#123; <span class="attribute">background</span>: <span class="number">#fc9</span>; &#125;</span><br></pre></td></tr></table></figure><p>加载css需要安装以下loader：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install css-loader style-loader  --save-dev</span><br></pre></td></tr></table></figure><p>过程：css-loader将css模块转成js交给webpack打包，style-loader将css打包结果通过style标签添加到页面上。</p><p>修改<code>webpack.config.js</code>：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ...</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line"><span class="built_in">module</span>:&#123;</span><br><span class="line">rules: [</span><br><span class="line">&#123;</span><br><span class="line">test: <span class="regexp">/\.css$/</span>,</span><br><span class="line">use: [<span class="string">"style-loader"</span>,<span class="string">"css-loader"</span>]</span><br><span class="line"><span class="comment">// 多个loader的执行顺序是从后往前执行，css-loader应该放后面</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>在index.js中引用css文件（webpack建议在js文件中引入css等资源文件，更方便建立依赖关系）：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ...</span></span><br><span class="line"><span class="comment">// require('./style.css') </span></span><br><span class="line"><span class="keyword">import</span> <span class="string">'./style.css'</span></span><br></pre></td></tr></table></figure><p>重新执行打包，打开页面就可以看到背景颜色了。</p><h4 id="处理图片"><a href="#处理图片" class="headerlink" title="处理图片"></a>处理图片</h4><p>我们添加图片在项目中后（我的图片统一放在项目的img文件夹下），可以在style.css中继续补充：</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.box</span> &#123;</span><br><span class="line"><span class="attribute">width</span>: <span class="number">200px</span>;</span><br><span class="line"><span class="attribute">height</span>: <span class="number">200px</span>;</span><br><span class="line"><span class="attribute">background</span>: <span class="built_in">url</span>(<span class="string">'../img/bg.jpg'</span>) no-repeat;</span><br><span class="line"><span class="attribute">background-size</span>: <span class="number">100%</span> <span class="number">100%</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在index.html中添加一个div：</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">div</span> <span class="attr">class</span>=<span class="string">"box"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><p>加载图片需要安装以下loader：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install url-loader  --save-dev</span><br></pre></td></tr></table></figure><p>修改<code>webpack.config.js</code>，在<code>module.rules</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="comment">//...</span></span><br><span class="line">&#123;</span><br><span class="line">test: <span class="regexp">/\.(jpg|png|gif|jpeg)$/</span>,</span><br><span class="line">loader: <span class="string">'url-loader'</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//...</span></span><br></pre></td></tr></table></figure><p>重新执行打包就可以看到效果了。</p><ul><li>file-loader：能够根据配置项复制使用到的资源到构建之后的文件夹，并且能够更改对应的链接。</li><li>url-loader ：包含 file-loader 的全部功能，并且能够根据配置将符合配置的文件转换成 Base64 方式引入，将小体积的图片 Base64 引入项目可以减少 http 请求。</li></ul><h4 id="压缩图片"><a href="#压缩图片" class="headerlink" title="压缩图片"></a>压缩图片</h4><p>安装image-webpack-loader：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install image-webpack-loader --save-dev</span><br></pre></td></tr></table></figure><p>修改配置项：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ...</span></span><br><span class="line">&#123;</span><br><span class="line">test: <span class="regexp">/\.(png|jpg|gif|svg)$/</span>,</span><br><span class="line">use: [</span><br><span class="line">&#123;</span><br><span class="line">loader: <span class="string">'url-loader'</span>,</span><br><span class="line">options: &#123;</span><br><span class="line">limit: <span class="number">10000</span>,</span><br><span class="line">name: <span class="string">'images/[name].[ext]'</span></span><br><span class="line">&#125;</span><br><span class="line">&#125;,</span><br><span class="line">&#123;</span><br><span class="line">loader:<span class="string">'image-webpack-loader'</span>,</span><br><span class="line">options: &#123;</span><br><span class="line">bypassOnDebug: <span class="literal">true</span>,</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="处理字体等（file-loader）"><a href="#处理字体等（file-loader）" class="headerlink" title="处理字体等（file-loader）"></a>处理字体等（file-loader）</h4><p>处理字体和图片差不多，首先我把下载的字体文件放在新建的<code>icon</code>文件夹，如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install file-loader  --save-dev</span><br></pre></td></tr></table></figure><p>修改<code>webpack.config.js</code>，在<code>module.rules</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="comment">//...</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="comment">// exclude排除资源</span></span><br><span class="line">exclude: <span class="regexp">/\.(css|js|html|less|json|jpg|png|gif)$/</span>,</span><br><span class="line">loader: <span class="string">'file-loader'</span>,</span><br><span class="line">options: &#123;</span><br><span class="line">name:<span class="string">'[hash:10].[ext]'</span></span><br><span class="line">&#125;,</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//...</span></span><br></pre></td></tr></table></figure><p>在入口文件引入<code>iconfont.css</code>, 并index.html中使用iconfont：</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">i</span> <span class="attr">class</span>=<span class="string">"iconfont icon-jia"</span>&gt;</span><span class="tag">&lt;/<span class="name">i</span>&gt;</span></span><br></pre></td></tr></table></figure><p>重新执行打包就可以看到效果了。</p><h4 id="编译JS文件（babel-loader）"><a href="#编译JS文件（babel-loader）" class="headerlink" title="编译JS文件（babel-loader）"></a>编译JS文件（babel-loader）</h4><p>安装babel相关依赖：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install babel-loader @babel/core @babel/preset-env --save-dev</span><br></pre></td></tr></table></figure><p>配置loader匹配JS文件：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ...</span></span><br><span class="line"><span class="built_in">module</span>:&#123;</span><br><span class="line">rules: [</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">&#123;</span><br><span class="line">test: <span class="regexp">/\.js$/</span>,</span><br><span class="line">exclude: <span class="regexp">/node_modules/</span>,</span><br><span class="line">user: &#123;</span><br><span class="line">loader: <span class="string">'babel-loader'</span>,</span><br><span class="line"><span class="comment">// loader的配置</span></span><br><span class="line">options: &#123;</span><br><span class="line"><span class="comment">// js转译成es5</span></span><br><span class="line">presets: [<span class="string">'@babel/preset-env'</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><h3 id="Plugins"><a href="#Plugins" class="headerlink" title="Plugins"></a>Plugins</h3><p>插件目的在于解决 loader无法实现的其他事，增强webpack在项目自动化构建方面的能力。插件接口功能极其强大，可以用来处理各种各样的任务。</p><p>想要使用一个插件，你只需要<code>require()</code>它，然后把它添加到 plugins 数组中。</p><h4 id="自动构建html（html-webpack-plugin）"><a href="#自动构建html（html-webpack-plugin）" class="headerlink" title="自动构建html（html-webpack-plugin）"></a>自动构建html（html-webpack-plugin）</h4><p>webpack支持自动创建html文件，首先得安装插件<code>html-webpack-plugin</code>：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install html-webpack-plugin --save-dev</span><br></pre></td></tr></table></figure><p>然后在配置文件<code>webpack.config.js</code>中引用并调用该插件：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ...</span></span><br><span class="line"><span class="keyword">let</span> HtmlWebpackPlugin = <span class="built_in">require</span>(<span class="string">'html-webpack-plugin'</span>)</span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">plugins:[</span><br><span class="line"><span class="keyword">new</span> HtmlWebpackPlugin()</span><br><span class="line">]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>执行命令<code>webpack</code>会自动创建相应的html和js文件，并自动引用相应的静态资源文件。</p><p>如果需要自定义html路径或者增加其他配置，可以修改HtmlWebpackPlugin的参数：</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">new</span> HtmlWebpackPlugin(&#123;</span><br><span class="line">title: <span class="string">'首页'</span>,</span><br><span class="line">meta: &#123;</span><br><span class="line">viewport: <span class="string">'width=device-width'</span></span><br><span class="line">&#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><p>自动生成html的优势：</p><ul><li>html输出在dist中，方便打包发布</li><li>html中的script是自动引入的，确保资源文件路径正常</li></ul><p>如果需要对html进行大量自定义，最好在源代码中添加生成html模板。如在src修改index.html作为模板：</p><figure class="highlight html"><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="meta">&lt;!DOCTYPE <span class="meta-keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"X-UA-Compatible"</span> <span class="attr">content</span>=<span class="string">"IE=edge"</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1.0"</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">title</span>&gt;</span><span class="tag">&lt;<span class="name">%=htmlWebpackPlugin.options.title%</span>&gt;</span><span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">h1</span>&gt;</span>标签1<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">"app"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><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="keyword">new</span> HtmlWebpackPlugin(&#123;</span><br><span class="line">template: <span class="string">'./src/index.html'</span>,</span><br><span class="line">title: <span class="string">'首页'</span>,</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><p>如果在plugin中添加多个HtmlWebpackPlugin实例，可以打包出多个html。</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">plugins: [</span><br><span class="line"><span class="keyword">new</span> HtmlWebpackPlugin(&#123;</span><br><span class="line">template: <span class="string">'./src/index.html'</span>,</span><br><span class="line">title: <span class="string">'首页'</span>,</span><br><span class="line">filename: <span class="string">'index.html'</span>,</span><br><span class="line">chunks: [<span class="string">'index'</span>] <span class="comment">//指定使用的js, 在entry中可以用对象指定多个入口文件</span></span><br><span class="line">&#125;),</span><br><span class="line"><span class="keyword">new</span> HtmlWebpackPlugin(&#123;</span><br><span class="line">template: <span class="string">'./src/about.html'</span>,</span><br><span class="line">title: <span class="string">'关于'</span>,</span><br><span class="line">filename: <span class="string">'about.html'</span>,</span><br><span class="line">chunks: [<span class="string">'about'</span>] <span class="comment">//指定使用的js, 在entry中可以用对象指定多个入口文件</span></span><br><span class="line">&#125;)</span><br><span class="line">]</span><br></pre></td></tr></table></figure><h4 id="压缩JS（terser-webpack-plugin）"><a href="#压缩JS（terser-webpack-plugin）" class="headerlink" title="压缩JS（terser-webpack-plugin）"></a>压缩JS（terser-webpack-plugin）</h4><p>压缩打包的js文件，可以减少文件体积。安装插件：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm i terser-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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> TerserPlugin = <span class="built_in">require</span>(<span class="string">'terser-webpack-plugin'</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">optimization: &#123;</span><br><span class="line">minimize: <span class="literal">true</span>, <span class="comment">//是否压缩</span></span><br><span class="line">minimizer: [<span class="keyword">new</span> TerserPlugin()], <span class="comment">//压缩工具</span></span><br><span class="line">&#125;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="清理dist文件夹（clean-webpack-plugin）"><a href="#清理dist文件夹（clean-webpack-plugin）" class="headerlink" title="清理dist文件夹（clean-webpack-plugin）"></a>清理dist文件夹（clean-webpack-plugin）</h4><p>每次打包之后都会生成dist文件夹，而生成的文件夹不会清理掉之前遗留的文件，会造成不必要的资源浪费。因此我们打包之前最好删除之前的dist文件夹，或者配置自动清除dist文件夹的插件<code>clean-webpack-plugin</code>。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm i -D clean-webpack-plugin</span><br></pre></td></tr></table></figure><p>然后在配置文件<code>webpack.config.js</code>中引用并调用该插件：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ...</span></span><br><span class="line"><span class="keyword">const</span> &#123; CleanWebpackPlugin &#125; = <span class="built_in">require</span>(<span class="string">"clean-webpack-plugin"</span>);</span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">plugins:[</span><br><span class="line"><span class="keyword">new</span> CleanWebpackPlugin(&#123;</span><br><span class="line">cleanAfterEveryBuildPatterns: [<span class="string">"dist"</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>这样我们下次打包的时候就会自动清理上次打包的文件啦~</p><h4 id="复制文件（copy-webpack-plugin）"><a href="#复制文件（copy-webpack-plugin）" class="headerlink" title="复制文件（copy-webpack-plugin）"></a>复制文件（copy-webpack-plugin）</h4><p>建议处理无需打包的静态文件（如图标等），一般把静态文件放在public或者static文件夹中，然后把该文件夹下所有文件直接复制到dist中，安装：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm i -D copy-webpack-plugin</span><br></pre></td></tr></table></figure><p>引用插件：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ...</span></span><br><span class="line"><span class="keyword">const</span> CopyPlugin = <span class="built_in">require</span>(<span class="string">"copy-webpack-plugin"</span>);</span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">plugins:[</span><br><span class="line"><span class="keyword">new</span> CopyPlugin([</span><br><span class="line"><span class="string">'public'</span> <span class="comment">//需要copy的文件路径</span></span><br><span class="line">]),</span><br><span class="line">]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="分析文件大小（webpack-bundle-analyzer）"><a href="#分析文件大小（webpack-bundle-analyzer）" class="headerlink" title="分析文件大小（webpack-bundle-analyzer）"></a>分析文件大小（webpack-bundle-analyzer）</h4><p>安装一个可视化的打包分析工具：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm i webpack-bundle-analyzer --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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> BundlePlugin = <span class="built_in">require</span>(<span class="string">'webpack-bundle-analyzer'</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">plugins:[</span><br><span class="line"><span class="keyword">new</span> BundlePlugin.BundleAnalyzerPlugin(&#123;),</span><br><span class="line">]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>启动就可以看到文件大小分布图。</p><h3 id="Mode"><a href="#Mode" class="headerlink" title="Mode"></a>Mode</h3><p>webpack的工作模式：</p><ul><li>production（默认）：启动内置优化插件，自动优化打包结果，打包速度偏慢</li><li>development: 自动优化打包速度，添加一些调试过程中的辅助插件</li><li>none: 不会做优化操作，一般用于分析模块打包结果</li></ul><h2 id="其他优化"><a href="#其他优化" class="headerlink" title="其他优化"></a>其他优化</h2><h4 id="自动编译刷新（webpack-dev-server）"><a href="#自动编译刷新（webpack-dev-server）" class="headerlink" title="自动编译刷新（webpack-dev-server）"></a>自动编译刷新（webpack-dev-server）</h4><p>每次修改代码后，都需要执行打包命令重新打包才能看到新的页面效果，webpack提供一个代理服务器可以监听文件编号并自动重新编译：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm i webpack-dev-server --D</span><br></pre></td></tr></table></figure><p>在package.json中指定启动服务并自动打开浏览器的命令：</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="comment">//...</span></span><br><span class="line"><span class="string">"scripts"</span>: &#123;</span><br><span class="line"><span class="string">"start"</span>: <span class="string">"webpack serve --open"</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>修改配置指定server从哪里修改代码：</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="built_in">module</span>.exports = &#123;</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">devServer: &#123;</span><br><span class="line"><span class="keyword">static</span>: <span class="string">'./dist'</span>,</span><br><span class="line">port: <span class="number">9000</span>,</span><br><span class="line">contentBase: <span class="string">'public'</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>如果需要启动热更新（HMR），即不刷新页面只更新部分模块，可以在配置文件的devServer中增加：</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="built_in">module</span>.exports = &#123;</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">devServer: &#123;</span><br><span class="line"><span class="comment">// 开启HMR</span></span><br><span class="line">hot: <span class="literal">true</span></span><br><span class="line">&#125;,</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">plugins: [</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line"><span class="keyword">new</span> webpack.HotModuleReplacementPlugin()</span><br><span class="line">]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;现在前端开发越来越偏向模块化，我们的项目也逐渐转向脚手架搭建。这其中，webpack的力量是巨大的，之前因为不了解webpack的某些配置所以在项目中也耽搁了一些时间，因此我觉得很有必要深入了解下webpack。之后需要自己尝试手动搭建项目工程，才能真正理解脚手架的原理。&lt;/p&gt;
    
    </summary>
    
      <category term="工程化" scheme="https://aartemida.github.io/categories/%E5%B7%A5%E7%A8%8B%E5%8C%96/"/>
    
    
      <category term="webpack" scheme="https://aartemida.github.io/tags/webpack/"/>
    
  </entry>
  
  <entry>
    <title>TypeScript类型和安装编译</title>
    <link href="https://aartemida.github.io/2020/01/18/TypeScript%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <id>https://aartemida.github.io/2020/01/18/TypeScript学习笔记/</id>
    <published>2020-01-18T09:25:22.000Z</published>
    <updated>2022-09-11T15:00:57.024Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Typescript"><a href="#Typescript" class="headerlink" title="Typescript"></a>Typescript</h2><p>TypeScript 是 JavaScript 的类型的超集，它可以编译成 JavaScript。TypeScript是一门静态语言，新增了静态类型、类、模块、接口和类型注解，规避了一些类型上的低级错误，更适合应用于开发大型应用。</p><h2 id="全局安装-TypeScript"><a href="#全局安装-TypeScript" class="headerlink" title="全局安装 TypeScript"></a>全局安装 TypeScript</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install -g typescript</span><br></pre></td></tr></table></figure><a id="more"></a><h2 id="TypeScript-类型"><a href="#TypeScript-类型" class="headerlink" title="TypeScript 类型"></a>TypeScript 类型</h2><h3 id="基础类型"><a href="#基础类型" class="headerlink" title="基础类型"></a>基础类型</h3><p>原始类型：</p><p>string, number, boolean(非严格模式下，这三种都可以存放null或undefined), null, undefined,</p><p>void(空值，一般用来标记没有返回值的函数返回类型，只能存放null或undefined)</p><p>symbol</p><h4 id="boolean"><a href="#boolean" class="headerlink" title="boolean"></a>boolean</h4><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">let</span> isDone: boolean = <span class="literal">false</span>;</span><br></pre></td></tr></table></figure><h4 id="number"><a href="#number" class="headerlink" title="number"></a>number</h4><figure class="highlight js"><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="keyword">let</span> num: number = <span class="number">6</span>;</span><br><span class="line"><span class="keyword">let</span> hexLiteral: number = <span class="number">0xf00d</span>;</span><br></pre></td></tr></table></figure><h4 id="string"><a href="#string" class="headerlink" title="string"></a>string</h4><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">let</span> myName: string = <span class="string">'Tom'</span>;</span><br></pre></td></tr></table></figure><h4 id="数组"><a href="#数组" class="headerlink" title="数组"></a>数组</h4><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="comment">//定义数组的两种方式</span></span><br><span class="line"><span class="comment">//方式一</span></span><br><span class="line"><span class="keyword">let</span> list: number[] = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</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> newlist: <span class="built_in">Array</span>&lt;number&gt; = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];</span><br></pre></td></tr></table></figure><h4 id="元组"><a href="#元组" class="headerlink" title="元组"></a>元组</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><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Declare a tuple type</span></span><br><span class="line"><span class="keyword">let</span> x: [string, number];</span><br><span class="line"><span class="comment">// Initialize it</span></span><br><span class="line">x = [<span class="string">'hello'</span>, <span class="number">10</span>]; <span class="comment">// OK</span></span><br><span class="line"><span class="comment">// Initialize it incorrectly</span></span><br><span class="line">x = [<span class="number">10</span>, <span class="string">'hello'</span>]; <span class="comment">// Error</span></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> tup : [<span class="built_in">string</span>, <span class="built_in">number</span>] = [<span class="string">'aaa'</span>, <span class="number">1</span>]</span><br></pre></td></tr></table></figure><h4 id="枚举-enum"><a href="#枚举-enum" class="headerlink" title="枚举(enum)"></a>枚举(enum)</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><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">enum Color &#123; Red, Green, Blue &#125;</span><br><span class="line"><span class="keyword">let</span> c: Color = Color.Green;</span><br><span class="line"></span><br><span class="line"><span class="comment">//枚举类型的本质是数值类型， 所以赋值数值不会报错。</span></span><br><span class="line"><span class="keyword">let</span> c1: Color</span><br><span class="line">c1 = <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ts中枚举类型的取值，默认从0开始，从上到下依次递增</span></span><br><span class="line">enum TestNum &#123; </span><br><span class="line">  First = <span class="number">3</span>,</span><br><span class="line">  Second</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">console</span>.log(TestNum.First, TestNum.Second) <span class="comment">// 3, 4</span></span><br></pre></td></tr></table></figure><p>和object的区别：</p><ul><li>枚举可以通过枚举的名称，获取枚举的值，也可以通过枚举的值获取枚举的名称。object只能通过key获取value；</li><li>不指定初始值，枚举值会默认从0开始递增</li></ul><h4 id="任意值-Any"><a href="#任意值-Any" class="headerlink" title="任意值(Any)"></a>任意值(Any)</h4><p>任意值（Any）用来表示允许赋值为任意类型。</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> anyThing: any = <span class="string">'hello'</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(anyThing.myName);</span><br><span class="line"><span class="built_in">console</span>.log(anyThing.myName.firstName);</span><br><span class="line"></span><br><span class="line">anyThing.setName(<span class="string">'Jerry'</span>);</span><br></pre></td></tr></table></figure><p>变量如果在声明的时候，未指定其类型，那么它会被识别为任意值类型。</p><h4 id="空值（Void"><a href="#空值（Void" class="headerlink" title="空值（Void)"></a>空值（Void)</h4><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="function"><span class="keyword">function</span> <span class="title">alertName</span>(<span class="params"></span>): <span class="title">void</span> </span>&#123;</span><br><span class="line">  alert(<span class="string">'My name is Tom'</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在 TypeScript 中，可以用 void 表示没有任何返回值的函数。声明一个 void 类型的变量没有什么用，因为你只能将它赋值为 undefined 和 null。</p><h4 id="Null-和-Undefined"><a href="#Null-和-Undefined" class="headerlink" title="Null 和 Undefined"></a>Null 和 Undefined</h4><p>与 void 的区别是，undefined 和 null 是所有类型的子类型。</p><figure class="highlight js"><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="keyword">let</span> u: <span class="literal">undefined</span>;</span><br><span class="line"><span class="keyword">let</span> num: number = u;</span><br></pre></td></tr></table></figure><h4 id="Never"><a href="#Never" class="headerlink" title="Never"></a>Never</h4><p>never类型表示的是那些永不存在的值的类型。</p><p>never是任何类型的子类型，可以赋值给任何类型。但是没有类型可以赋值给never。</p><p>可以用于总是抛出异常或者永远不会有返回值的函数表达式。</p><h4 id="Object"><a href="#Object" class="headerlink" title="Object"></a>Object</h4><p>object表示非原始类型，也就是除number，string，boolean，symbol，null或undefined之外的类型。</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">declare <span class="function"><span class="keyword">function</span> <span class="title">create</span>(<span class="params">o: object | null</span>): <span class="title">void</span>;</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"><span class="title">create</span>(<span class="params">&#123; prop: <span class="number">0</span> &#125;</span>); // <span class="title">OK</span></span></span><br><span class="line"><span class="function"><span class="title">create</span>(<span class="params">null</span>); // <span class="title">OK</span></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"><span class="title">create</span>(<span class="params"><span class="number">42</span></span>); // <span class="title">Error</span></span></span><br><span class="line"><span class="function"><span class="title">create</span>(<span class="params"><span class="string">"string"</span></span>); // <span class="title">Error</span></span></span><br><span class="line"><span class="function"><span class="title">create</span>(<span class="params">false</span>); // <span class="title">Error</span></span></span><br><span class="line"><span class="function"><span class="title">create</span>(<span class="params">undefined</span>); // <span class="title">Error</span></span></span><br></pre></td></tr></table></figure><p>Object类型可以存放对象，数组，函数。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 限制对象: 1. 类似字面量</span></span><br><span class="line"><span class="keyword">const</span> obj : &#123; name: <span class="built_in">string</span> &#125; = &#123; name: <span class="string">'person1'</span> &#125;</span><br><span class="line"><span class="comment">// 限制对象: 2. 接口</span></span><br><span class="line"><span class="keyword">interface</span> PersonModel &#123;</span><br><span class="line">  name: <span class="built_in">string</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">const</span> obj2 : PersonModel = &#123; name: <span class="string">'person2'</span> &#125;</span><br></pre></td></tr></table></figure><h4 id="BigInt"><a href="#BigInt" class="headerlink" title="BigInt"></a>BigInt</h4><p>表示最大的数</p><h4 id="Symbol"><a href="#Symbol" class="headerlink" title="Symbol"></a>Symbol</h4><p>表示全局唯一的引用（标识）</p><h4 id="unknown"><a href="#unknown" class="headerlink" title="unknown"></a>unknown</h4><p>unknown 表示未知的类型。unknown和any都可以赋值给任意类型的， any 会绕过类型检查，而如果把unknown类型的值赋值给别的类型会报错。</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="comment">// unknown与其他类型组合或交叉最后都是其他类型</span></span><br><span class="line">type t = number &amp; unknown <span class="comment">//t是number类型</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// unknown和任何类型组成的联合类型都是unknown（除了和any）</span></span><br><span class="line">type t1 = unknown | any <span class="comment">// t1 是 any类型</span></span><br><span class="line">type t2 = unknown | number <span class="comment">// t2 是unknown类型</span></span><br></pre></td></tr></table></figure><h3 id="类型推论"><a href="#类型推论" class="headerlink" title="类型推论"></a>类型推论</h3><p>如果没有明确的指定类型，那么 TypeScript 会依照类型推论（Type Inference）的规则推断出一个类型。</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> myFavoriteNumber = <span class="string">'seven'</span>;</span><br><span class="line"><span class="comment">//等价于</span></span><br><span class="line"><span class="keyword">let</span> myFavoriteNumber: string = <span class="string">'seven'</span>;</span><br></pre></td></tr></table></figure><p>如果定义的时候没有赋值，不管之后有没有赋值，都会被推断成 any 类型而完全不被类型检查：</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> myFavoriteNumber;</span><br><span class="line">myFavoriteNumber = <span class="string">'seven'</span>;</span><br><span class="line">myFavoriteNumber = <span class="number">7</span>;</span><br></pre></td></tr></table></figure><h3 id="类型声明"><a href="#类型声明" class="headerlink" title="类型声明"></a>类型声明</h3><p>如果一个成员在定义时没有声明类型，可以在使用之前做类型声明（兼容第三方非ts编写的js模块）</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">declare</span> <span class="function"><span class="keyword">function</span> <span class="title">cameCase</span>(<span class="params">word: <span class="built_in">string</span></span>) : <span class="title">string</span></span></span><br></pre></td></tr></table></figure><h2 id="接口"><a href="#接口" class="headerlink" title="接口"></a>接口</h2><p>接口的作用就是为一些类型命名和代码定义契约，进行类型检查。</p><figure class="highlight js"><table><tr><td class="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">interface LabelledValue &#123;</span><br><span class="line">  label: string;</span><br><span class="line">  color?: number; <span class="comment">//可选</span></span><br><span class="line">  readonly width: number; <span class="comment">//只读</span></span><br><span class="line">&#125;</span><br><span class="line">interface LabelledValue &#123;</span><br><span class="line">  addProp?: string <span class="comment">//接口可以扩充，但如果两个接口中同时声明了同名的非函数成员且它们的类型不同，则编译器会报错</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//简单用法</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">printLabel</span>(<span class="params">config: LabelledValue</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(config.label);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">createLabel</span>(<span class="params">config: LabelledValue</span>): </span>&#123;label: string; color: string; width: number&#125; &#123;</span><br><span class="line">  <span class="keyword">let</span> newLabel = &#123;<span class="attr">label</span>: <span class="string">''</span>, <span class="attr">color</span>: <span class="string">"white"</span>, <span class="attr">width</span>: <span class="number">10</span>&#125;;</span><br><span class="line">  <span class="keyword">if</span> (config.color) &#123;</span><br><span class="line">    newLabel.color = config.color;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> newLabel;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> mySquare = createLabel(&#123;<span class="attr">label</span>: <span class="string">'aaa'</span>, <span class="attr">color</span>: <span class="string">"black"</span>, <span class="attr">width</span>: <span class="number">10</span>&#125;);</span><br></pre></td></tr></table></figure><p>TypeScript具有ReadonlyArray<t>类型，它与Array<t>相似，只是把所有可变方法去掉了，因此可以确保数组创建后再也不能被修改:</t></t></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">let</span> a: number[] = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>];</span><br><span class="line"><span class="keyword">let</span> ro: ReadonlyArray&lt;number&gt; = a;</span><br><span class="line">ro[<span class="number">0</span>] = <span class="number">12</span>; <span class="comment">// error!</span></span><br><span class="line">ro.push(<span class="number">5</span>); <span class="comment">// error!</span></span><br><span class="line">ro.length = <span class="number">100</span>; <span class="comment">// error!</span></span><br><span class="line">a = ro; <span class="comment">// error!</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//可以用类型断言重写</span></span><br><span class="line">a = ro <span class="keyword">as</span> number[];</span><br></pre></td></tr></table></figure><h2 id="开始"><a href="#开始" class="headerlink" title="开始"></a>开始</h2><h3 id="创建项目"><a href="#创建项目" class="headerlink" title="创建项目"></a>创建项目</h3><p>新建一个文件夹<code>learn_ts</code>，在文件夹中新建<code>src</code>：</p><figure class="highlight plain"><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">cd learn_ts</span><br><span class="line">mkdir src</span><br></pre></td></tr></table></figure><p>初始化文件夹：</p><figure class="highlight plain"><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">npm init</span><br><span class="line">tsc --init</span><br></pre></td></tr></table></figure><p>目录下自动生成了一个<code>package.json</code>文件和<code>tsconfig.json</code>文件。</p><p>tsconfig.json是TypeScript 的配置文件，其中包含了初始化的一些配置，之后我们也可以自定义一些配置。</p><h3 id="初体验"><a href="#初体验" class="headerlink" title="初体验"></a>初体验</h3><p>在src文件夹下新建一个index.ts文件：</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="function"><span class="keyword">function</span> <span class="title">sayHello</span>(<span class="params">person: string</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">'Hello, '</span> + person;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> user = <span class="string">'Tom'</span>;</span><br><span class="line"><span class="built_in">console</span>.log(sayHello(user));</span><br></pre></td></tr></table></figure><p>编译ts文件：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tsc index.ts</span><br></pre></td></tr></table></figure><p>编译之后会在文件夹中生成一个同名的js文件。</p><blockquote><p>TypeScript 只会进行静态检查，如果发现有错误，编译的时候就会报错。</p></blockquote><p>TypeScript 编译的时候即使报错了，还是会生成编译结果。（如果要在报错的时候终止 js 文件的生成，可以在 tsconfig.json 中配置 noEmitOnError 即可）</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://www.tslang.cn/docs/handbook/basic-types.html" target="_blank" rel="noopener">https://www.tslang.cn/docs/handbook/basic-types.html</a></p>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;Typescript&quot;&gt;&lt;a href=&quot;#Typescript&quot; class=&quot;headerlink&quot; title=&quot;Typescript&quot;&gt;&lt;/a&gt;Typescript&lt;/h2&gt;&lt;p&gt;TypeScript 是 JavaScript 的类型的超集，它可以编译成 JavaScript。TypeScript是一门静态语言，新增了静态类型、类、模块、接口和类型注解，规避了一些类型上的低级错误，更适合应用于开发大型应用。&lt;/p&gt;&lt;h2 id=&quot;全局安装-TypeScript&quot;&gt;&lt;a href=&quot;#全局安装-TypeScript&quot; class=&quot;headerlink&quot; title=&quot;全局安装 TypeScript&quot;&gt;&lt;/a&gt;全局安装 TypeScript&lt;/h2&gt;&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;npm install -g typescript&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
    
    </summary>
    
      <category term="笔记" scheme="https://aartemida.github.io/categories/%E7%AC%94%E8%AE%B0/"/>
    
      <category term="TypeScript" scheme="https://aartemida.github.io/categories/%E7%AC%94%E8%AE%B0/TypeScript/"/>
    
    
      <category term="Typescript" scheme="https://aartemida.github.io/tags/Typescript/"/>
    
  </entry>
  
  <entry>
    <title>Flutter TextField属性详解</title>
    <link href="https://aartemida.github.io/2019/10/22/Flutter-TextField%E5%B1%9E%E6%80%A7%E8%AF%A6%E8%A7%A3/"/>
    <id>https://aartemida.github.io/2019/10/22/Flutter-TextField属性详解/</id>
    <published>2019-10-22T03:10:17.000Z</published>
    <updated>2022-08-05T15:18:46.618Z</updated>
    
    <content type="html"><![CDATA[<h2 id="TextField和TextFormField的属性"><a href="#TextField和TextFormField的属性" class="headerlink" title="TextField和TextFormField的属性"></a>TextField和TextFormField的属性</h2><p>输入框是比较复杂的组件，文本输入是最常见的一种交互方式，应用的情况也比较多。在上一个项目中，关于输入框的组件处理了很多，所以觉得很有必要单独拎出来了解下。</p><a id="more"></a><p>以下是输入框的属性和方法：</p><table><thead><tr><th align="center">属性名</th><th align="center">类型</th><th align="center">说明</th></tr></thead><tbody><tr><td align="center">decoration</td><td align="center">InputDecoration</td><td align="center">输入框的装饰器，设置内容或者装饰</td></tr><tr><td align="center">controller</td><td align="center">TextEditingController</td><td align="center">控制器，跟输入框的交互一般都通过该属性完成，如果不创建的话默认会自动创建</td></tr><tr><td align="center">focusNode</td><td align="center">FocusNode</td><td align="center">焦点控制</td></tr><tr><td align="center">keyboardType</td><td align="center">TextInputType</td><td align="center">设置输入类型(键盘类型)</td></tr><tr><td align="center">inputFormatters</td><td align="center">List<textinputformatter></textinputformatter></td><td align="center">限制输入文本的格式</td></tr><tr><td align="center">obscureText</td><td align="center">bool</td><td align="center">是否隐藏输入的文字</td></tr><tr><td align="center">textInputAction</td><td align="center">TextInputAction</td><td align="center">键盘事件类型</td></tr><tr><td align="center">textCapitalization</td><td align="center">TextCapitalization</td><td align="center">设置文本大写</td></tr><tr><td align="center">style</td><td align="center">TextStyle</td><td align="center">输入文本的样式</td></tr><tr><td align="center">textAlign</td><td align="center">TextAlign</td><td align="center">文本的对齐方式</td></tr><tr><td align="center">textDirection</td><td align="center">TextDirection</td><td align="center">文字的排列方向</td></tr><tr><td align="center">autofocus</td><td align="center">bool</td><td align="center">是否自动获取焦点</td></tr><tr><td align="center">autocorrect</td><td align="center">bool</td><td align="center">是否自动校验</td></tr><tr><td align="center">maxLines</td><td align="center">int</td><td align="center">最大行数</td></tr><tr><td align="center">maxLength</td><td align="center">int</td><td align="center">输入的最大字符个数</td></tr><tr><td align="center">maxLengthEnforced</td><td align="center">bool</td><td align="center">配合maxLength一起使用，在达到最大长度时是否阻止输入</td></tr><tr><td align="center">onEditingComplete</td><td align="center">VoidCallback</td><td align="center">点击键盘完成按钮时触发的回调，该回调没有参数，(){}</td></tr><tr><td align="center">onSubmitted</td><td align="center">VoidCallback</td><td align="center">点击键盘完成按钮时触发的回调，该回调有参数，参数即为当前输入框中的值。(v){}</td></tr><tr><td align="center">enabled</td><td align="center">bool</td><td align="center">输入框是否可用</td></tr><tr><td align="center">cursorWidth</td><td align="center">double</td><td align="center">光标的宽度</td></tr><tr><td align="center">cursorRadius</td><td align="center">Radius</td><td align="center">光标的圆角</td></tr><tr><td align="center">cursorColor</td><td align="center">Color</td><td align="center">光标的颜色</td></tr></tbody></table><h3 id="TextField-普通输入框"><a href="#TextField-普通输入框" class="headerlink" title="TextField(普通输入框)"></a>TextField(普通输入框)</h3><p>普通输入框指的是单独的输入框，没有在表单中的输入框。它和表单中的输入框TextFormField方法略有不同，TextFormField是在TextField的基础上扩展的。</p><table><thead><tr><th align="center">属性名</th><th align="center">类型</th><th align="center">说明</th></tr></thead><tbody><tr><td align="center">onChanged</td><td align="center">VoidCallback</td><td align="center">用户输入时的回调(v){}</td></tr><tr><td align="center">onTap</td><td align="center">VoidCallback</td><td align="center">点击输入框时的回调</td></tr></tbody></table><h3 id="TextFormField-表单里的输入框"><a href="#TextFormField-表单里的输入框" class="headerlink" title="TextFormField(表单里的输入框)"></a>TextFormField(表单里的输入框)</h3><p>表单中的输入框最主要的特点就是提供了表单验证。</p><table><thead><tr><th align="center">属性名</th><th align="center">类型</th><th align="center">说明</th></tr></thead><tbody><tr><td align="center">validator</td><td align="center">FormFieldValidator&lt;T&gt;</td><td align="center">表单验证器</td></tr><tr><td align="center">autovalidate</td><td align="center">bool</td><td align="center">自动验证值</td></tr><tr><td align="center">onSaved</td><td align="center">FormFieldSetter&lt;T&gt;</td><td align="center">表单保存的回调方法</td></tr><tr><td align="center">initalValue</td><td align="center">T</td><td align="center">表单字段初始值</td></tr></tbody></table><h3 id="InputDecoration"><a href="#InputDecoration" class="headerlink" title="InputDecoration"></a>InputDecoration</h3><p>TextField装饰器，可以理解成输入框的默认属性和一些样式定义，以下是它的构造方法：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">InputDecoration(&#123;</span><br><span class="line">    this.icon,    //位于装饰器外部和输入框前面的图片</span><br><span class="line">    this.labelText,  //用于描述输入框，当输入框获取焦点时默认会浮动到上方，</span><br><span class="line">    this.labelStyle,  // 控制labelText的样式,接收一个TextStyle类型的值</span><br><span class="line">    this.helperText, //辅助文本，位于输入框下方，如果errorText不为空的话，则helperText不会显示</span><br><span class="line">    this.helperStyle, //helperText的样式</span><br><span class="line">    this.hintText,  //提示文本，位于输入框内部</span><br><span class="line">    this.hintStyle, //hintText的样式</span><br><span class="line">    this.hintMaxLines, //提示信息最大行数</span><br><span class="line">    this.errorText,  //错误信息提示</span><br><span class="line">    this.errorStyle, //errorText的样式</span><br><span class="line">    this.errorMaxLines,   //errorText最大行数</span><br><span class="line">    this.hasFloatingPlaceholder = true,  //labelText是否浮动，默认为true，修改为false则labelText在输入框获取焦点时不会浮动且不显示</span><br><span class="line">    this.isDense,   //改变输入框是否为密集型，默认为false，修改为true时，图标及间距会变小</span><br><span class="line">    this.contentPadding, //内间距</span><br><span class="line">    this.prefixIcon,  //位于输入框内部起始位置的图标。</span><br><span class="line">    this.prefix,   //预先填充的Widget,跟prefixText同时只能出现一个</span><br><span class="line">    this.prefixText,  //预填充的文本，例如手机号前面预先加上区号等</span><br><span class="line">    this.prefixStyle,  //prefixText的样式</span><br><span class="line">    this.suffixIcon, //位于输入框后面的icon</span><br><span class="line">    this.suffix,  //位于输入框尾部的控件，同样的不能和suffixText同时使用</span><br><span class="line">    this.suffixText,//位于尾部的填充文字</span><br><span class="line">    this.suffixStyle,  //suffixText的样式</span><br><span class="line">    this.counter,//位于输入框右下方的小控件，不能和counterText同时使用</span><br><span class="line">    this.counterText,//位于右下方显示的文本，常用于显示输入的字符数量</span><br><span class="line">    this.counterStyle, //counterText的样式</span><br><span class="line">    this.filled,  //如果为true，则输入使用fillColor指定的颜色填充</span><br><span class="line">    this.fillColor,  //相当于输入框的背景颜色</span><br><span class="line">    this.errorBorder,   //errorText不为空，输入框没有焦点时要显示的边框</span><br><span class="line">    this.focusedBorder,  //输入框有焦点时的边框,如果errorText不为空的话，该属性无效</span><br><span class="line">    this.focusedErrorBorder,  //errorText不为空时，输入框有焦点时的边框</span><br><span class="line">    this.disabledBorder,  //输入框禁用时显示的边框，如果errorText不为空的话，该属性无效</span><br><span class="line">    this.enabledBorder,  //输入框可用时显示的边框，如果errorText不为空的话，该属性无效</span><br><span class="line">    this.border, //正常情况下的border</span><br><span class="line">    this.enabled = true,  //输入框是否可用</span><br><span class="line">    this.semanticCounterText,</span><br><span class="line">    this.alignLabelWithHint,</span><br><span class="line">  &#125;)</span><br></pre></td></tr></table></figure><h3 id="inputFormatters"><a href="#inputFormatters" class="headerlink" title="inputFormatters"></a>inputFormatters</h3><p>用于限制输入的内容。</p><ul><li>WhitelistingTextInputFormatter 白名单校验，也就是只允许输入符合规则的字符</li><li>BlacklistingTextInputFormatter 黑名单校验，除了规定的字符其他的都可以输入</li><li>LengthLimitingTextInputFormatter 长度限制，跟maxLength作用类似</li></ul><p>例如：只允许输入数字，并且不超过6个字符</p><figure class="highlight plain"><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">inputFormatters: [</span><br><span class="line">      WhitelistingTextInputFormatter.digitsOnly,</span><br><span class="line">      LengthLimitingTextInputFormatter(6)</span><br><span class="line">]</span><br></pre></td></tr></table></figure><h2 id="实例"><a href="#实例" class="headerlink" title="实例"></a>实例</h2><h3 id="文本监听"><a href="#文本监听" class="headerlink" title="文本监听"></a>文本监听</h3><p>新建一个简单的输入框，并实现文本监听，栗子1：</p><figure class="highlight dart"><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></pre></td><td class="code"><pre><span class="line"><span class="built_in">String</span> _inputText = <span class="string">''</span>;</span><br><span class="line"><span class="comment">//控制器</span></span><br><span class="line">TextEditingController _controller = <span class="keyword">new</span> TextEditingController.fromValue(</span><br><span class="line">  TextEditingValue(</span><br><span class="line">    text: _inputText,</span><br><span class="line">    selection: <span class="keyword">new</span> TextSelection.fromPosition(</span><br><span class="line">      TextPosition(</span><br><span class="line">          affinity: TextAffinity.downstream, offset:_inputText.length),</span><br><span class="line">    ),</span><br><span class="line">  ),</span><br><span class="line">);</span><br><span class="line"><span class="comment">//输入框</span></span><br><span class="line">TextField(</span><br><span class="line">  controller:_controller,</span><br><span class="line">  decoration: InputDecoration(</span><br><span class="line">    icon: Icon(Icons.person),</span><br><span class="line">    labelText:<span class="string">'用户名'</span></span><br><span class="line">  ),</span><br><span class="line">  onChanged: (v)&#123;</span><br><span class="line">    setState(() &#123;</span><br><span class="line">      _inputText = v;</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>当使用TextFormField时没有onChanged，只能手动实现监听，栗子2：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">String</span> _inputText = <span class="string">''</span>;</span><br><span class="line"><span class="comment">//控制器</span></span><br><span class="line">TextEditingController _controller = <span class="keyword">new</span> TextEditingController.fromValue(</span><br><span class="line">  TextEditingValue(</span><br><span class="line">    text: _inputText,</span><br><span class="line">    selection: <span class="keyword">new</span> TextSelection.fromPosition(</span><br><span class="line">      TextPosition(</span><br><span class="line">          affinity: TextAffinity.downstream, offset:_inputText.length),</span><br><span class="line">    ),</span><br><span class="line">  ),</span><br><span class="line">);</span><br><span class="line"><span class="comment">//监听值的变化</span></span><br><span class="line"><span class="keyword">void</span> onChange() &#123;</span><br><span class="line">  <span class="built_in">String</span> v = _textController.text;</span><br><span class="line">  setState(() &#123;</span><br><span class="line">    _inputText = v;</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br><span class="line">_controller.addListener(onChange);</span><br><span class="line"><span class="comment">//输入框</span></span><br><span class="line">TextFormField(</span><br><span class="line">  controller:_controller,</span><br><span class="line">  decoration: InputDecoration(</span><br><span class="line">    icon: Icon(Icons.person),</span><br><span class="line">    labelText:<span class="string">'用户名'</span></span><br><span class="line">  ),</span><br><span class="line">)</span><br></pre></td></tr></table></figure><h3 id="实现文本清除按钮"><a href="#实现文本清除按钮" class="headerlink" title="实现文本清除按钮"></a>实现文本清除按钮</h3><p>在输入框有内容时显示清除按钮，在输入框内容为空时隐藏清除按钮，栗子3：</p><figure class="highlight plain"><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">TextEditingController _controller = new TextEditingController();</span><br><span class="line">//带清除的输入框</span><br><span class="line">TextField(</span><br><span class="line">  controller:_controller,</span><br><span class="line">  decoration: InputDecoration(</span><br><span class="line">    icon: Icon(Icons.person),</span><br><span class="line">    suffixIcon:_controller.text.length &gt; 0</span><br><span class="line">              ? IconButton(</span><br><span class="line">                  icon: Icon(</span><br><span class="line">                    Icons.cancel,</span><br><span class="line">                    size:20.0,</span><br><span class="line">                    color: Colors.grey,</span><br><span class="line">                  ),</span><br><span class="line">                  onPressed: () &#123;</span><br><span class="line">                    WidgetsBinding.instance.addPostFrameCallback((_) =&gt; _controller.clear());</span><br><span class="line">                  &#125;,</span><br><span class="line">                ) : Text(&quot;&quot;),</span><br><span class="line">  ),</span><br><span class="line">)</span><br></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://blog.csdn.net/yuzhiqiang_1993/article/details/88204031" target="_blank" rel="noopener">Flutter文本输入框TextField属性</a></p>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;TextField和TextFormField的属性&quot;&gt;&lt;a href=&quot;#TextField和TextFormField的属性&quot; class=&quot;headerlink&quot; title=&quot;TextField和TextFormField的属性&quot;&gt;&lt;/a&gt;TextField和TextFormField的属性&lt;/h2&gt;&lt;p&gt;输入框是比较复杂的组件，文本输入是最常见的一种交互方式，应用的情况也比较多。在上一个项目中，关于输入框的组件处理了很多，所以觉得很有必要单独拎出来了解下。&lt;/p&gt;
    
    </summary>
    
      <category term="Flutter" scheme="https://aartemida.github.io/categories/Flutter/"/>
    
    
      <category term="Flutter" scheme="https://aartemida.github.io/tags/Flutter/"/>
    
  </entry>
  
  <entry>
    <title>Flutter布局</title>
    <link href="https://aartemida.github.io/2019/10/22/Flutter%E5%B8%83%E5%B1%80/"/>
    <id>https://aartemida.github.io/2019/10/22/Flutter布局/</id>
    <published>2019-10-22T03:03:32.000Z</published>
    <updated>2022-08-05T15:18:43.232Z</updated>
    
    <content type="html"><![CDATA[<h2 id="布局组件"><a href="#布局组件" class="headerlink" title="布局组件"></a>布局组件</h2><p>布局类组件就是指直接或间接继承(包含)MultiChildRenderObjectWidget的Widget，它们一般都会有一个children属性用于接收子Widget。</p><h3 id="线性布局（Row、Column）"><a href="#线性布局（Row、Column）" class="headerlink" title="线性布局（Row、Column）"></a>线性布局（Row、Column）</h3><p>线性布局，即指沿水平或垂直方向排布子组件，将子组件排成一行或一列。</p><a id="more"></a><h4 id="Row"><a href="#Row" class="headerlink" title="Row"></a>Row</h4><p>Row可以在水平方向排列其子widget。Row默认只有一行，如果超出屏幕不会折行，会报溢出错误。Row以及Column都是Flex的子类，它们的具体实现也都是由Flex完成，只是参数不同。</p><table><thead><tr><th align="center">属性</th><th align="center">类型</th><th align="center">默认值</th><th align="center">说明</th></tr></thead><tbody><tr><td align="center">mainAxisAlignment</td><td align="center">MainAxisAlignment</td><td align="center">MainAxisAlignment.start</td><td align="center">水平方向的对齐方式</td></tr><tr><td align="center">crossAxisAlignment</td><td align="center">CrossAxisAlignment</td><td align="center"></td><td align="center">纵轴方向的对齐方式</td></tr><tr><td align="center">mainAxisSize</td><td align="center">MainAxisSize</td><td align="center">MainAxisSize.max</td><td align="center">Row在主轴(水平)方向占用的空间</td></tr><tr><td align="center">textDirection</td><td align="center">TextDirection</td><td align="center"></td><td align="center">水平方向子组件的布局顺序</td></tr><tr><td align="center">verticalDirection</td><td align="center">VerticalDirection</td><td align="center">VerticalDirection.down</td><td align="center">纵轴（垂直）方向的布局顺序</td></tr><tr><td align="center">children</td><td align="center">List&lt;Widget&gt;</td><td align="center"></td><td align="center">子组件数组</td></tr><tr><td align="center">MainAxisAlignment：</td><td align="center"></td><td align="center"></td><td align="center"></td></tr></tbody></table><ul><li>center：将children放置在主轴的中心；</li><li>end：将children放置在主轴的末尾；</li><li>spaceAround：将主轴方向上的空白区域均分，使得children之间的空白区域相等，但是首尾child的空白区域为1&#x2F;2；</li><li>spaceBetween：将主轴方向上的空白区域均分，使得children之间的空白区域相等，首尾child都靠近首尾，没有间隙；</li><li>spaceEvenly：将主轴方向上的空白区域均分，使得children之间的空白区域相等，包括首尾child；</li><li>start：将children放置在主轴的起点；</li></ul><p>CrossAxisAlignment：</p><ul><li>baseline：在交叉轴方向，使得children的baseline对齐；</li><li>center：children在交叉轴上居中展示；</li><li>end：children在交叉轴上末尾展示；</li><li>start：children在交叉轴上起点处展示；</li><li>stretch：让children填满交叉轴方向；</li></ul><h4 id="Column"><a href="#Column" class="headerlink" title="Column"></a>Column</h4><p>Column可以在垂直方向排列其子组件，因此和Row的主轴正好相反。</p><h3 id="弹性布局（Flex）"><a href="#弹性布局（Flex）" class="headerlink" title="弹性布局（Flex）"></a>弹性布局（Flex）</h3><p>弹性布局允许子组件按照一定比例来分配父容器空间。</p><h4 id="Flex"><a href="#Flex" class="headerlink" title="Flex"></a>Flex</h4><p>其属性和Row，Cloumn的属性差不多。<br>特有属性：</p><table><thead><tr><th align="center">属性</th><th align="center">类型</th><th>说明</th></tr></thead><tbody><tr><td align="center">direction</td><td align="center">Axis</td><td>必须项，弹性布局的方向, Row默认为水平方向(Axis.horizontal)，默认为垂直方向(Axis.vertical)</td></tr></tbody></table><h4 id="Expanded"><a href="#Expanded" class="headerlink" title="Expanded"></a>Expanded</h4><p>可以按比例“扩伸” Row、Column和Flex子组件所占用的空间。</p><table><thead><tr><th align="center">属性</th><th align="center">类型</th><th align="center">说明</th></tr></thead><tbody><tr><td align="center">flex</td><td align="center">number</td><td align="center">flex参数为弹性系数，如果为0或null，则child是没有弹性的，即不会被扩伸占用的空间。如果大于0，所有的Expanded按照其flex的比例来分割主轴的全部空闲空间</td></tr><tr><td align="center">child</td><td align="center">Widget</td><td align="center">需要分配的子组件</td></tr></tbody></table><h3 id="流式布局（Warp、Flow）"><a href="#流式布局（Warp、Flow）" class="headerlink" title="流式布局（Warp、Flow）"></a>流式布局（Warp、Flow）</h3><p>超出屏幕显示范围会自动折行的布局。</p><h4 id="Warp"><a href="#Warp" class="headerlink" title="Warp"></a>Warp</h4><p>特有属性：</p><table><thead><tr><th align="center">属性</th><th align="center">类型</th><th align="center">说明</th></tr></thead><tbody><tr><td align="center">spacing</td><td align="center">MainAxisAlignment</td><td align="center">主轴方向子widget的间距</td></tr><tr><td align="center">runSpacing</td><td align="center">CrossAxisAlignment</td><td align="center">纵轴方向的间距</td></tr><tr><td align="center">runAlignment</td><td align="center">MainAxisSize</td><td align="center">纵轴方向的对齐方式</td></tr></tbody></table><figure class="highlight dart"><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">Wrap(</span><br><span class="line">  spacing: <span class="number">8.0</span>, <span class="comment">// 主轴(水平)方向间距</span></span><br><span class="line">  runSpacing: <span class="number">4.0</span>, <span class="comment">// 纵轴（垂直）方向间距</span></span><br><span class="line">  alignment: WrapAlignment.center, <span class="comment">//沿主轴方向居中</span></span><br><span class="line">  children: &lt;Widget&gt;[</span><br><span class="line">    <span class="keyword">new</span> Chip(</span><br><span class="line">      avatar: <span class="keyword">new</span> CircleAvatar(backgroundColor: Colors.blue, child: Text(<span class="string">'A'</span>)),</span><br><span class="line">      label: <span class="keyword">new</span> Text(<span class="string">'Hamilton'</span>),</span><br><span class="line">    ),</span><br><span class="line">    <span class="keyword">new</span> Chip(</span><br><span class="line">      avatar: <span class="keyword">new</span> CircleAvatar(backgroundColor: Colors.blue, child: Text(<span class="string">'M'</span>)),</span><br><span class="line">      label: <span class="keyword">new</span> Text(<span class="string">'Lafayette'</span>),</span><br><span class="line">    ),</span><br><span class="line">    <span class="keyword">new</span> Chip(</span><br><span class="line">      avatar: <span class="keyword">new</span> CircleAvatar(backgroundColor: Colors.blue, child: Text(<span class="string">'H'</span>)),</span><br><span class="line">      label: <span class="keyword">new</span> Text(<span class="string">'Mulligan'</span>),</span><br><span class="line">    )</span><br><span class="line">  ],</span><br><span class="line">)</span><br></pre></td></tr></table></figure><h4 id="Flow"><a href="#Flow" class="headerlink" title="Flow"></a>Flow</h4><p>主要用于一些需要自定义布局策略或性能要求较高(如动画中)的场景。缺点是使用复杂并且不能自适应子组件大小，必须通过指定父容器大小或实现TestFlowDelegate的getSize返回固定大小。</p><h3 id="层叠布局（Stack、Positioned）"><a href="#层叠布局（Stack、Positioned）" class="headerlink" title="层叠布局（Stack、Positioned）"></a>层叠布局（Stack、Positioned）</h3><p>层叠布局和Web中的绝对定位相似，子组件可以根据距父容器四个角的位置来确定自身的位置。绝对定位允许子组件堆叠起来。</p><h4 id="Stack"><a href="#Stack" class="headerlink" title="Stack"></a>Stack</h4><table><thead><tr><th align="center">属性</th><th align="center">类型</th><th align="center">默认值</th><th align="center">说明</th></tr></thead><tbody><tr><td align="center">alignment</td><td align="center">AlignmentDirectional</td><td align="center">AlignmentDirectional.topStart</td><td align="center">如何去对齐没有定位（没有使用Positioned）或部分定位的子组件</td></tr><tr><td align="center">fit</td><td align="center">StackFit</td><td align="center">StackFit.loose</td><td align="center">确定没有定位的子组件如何去适应Stack的大小</td></tr><tr><td align="center">textDirection</td><td align="center">TextDirection</td><td align="center">TextDirection.ltr</td><td align="center">用于确定alignment对齐的参考系</td></tr><tr><td align="center">overflow</td><td align="center">Overflow</td><td align="center">Overflow.clip</td><td align="center">如何显示超出Stack显示空间的子组件</td></tr><tr><td align="center">children</td><td align="center">List&lt;Widget&gt;</td><td align="center"></td><td align="center">子组件数组</td></tr></tbody></table><figure class="highlight dart"><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">Stack(</span><br><span class="line">    alignment:Alignment.center ,</span><br><span class="line">    children: &lt;Widget&gt;[</span><br><span class="line">      Container(child: Text(<span class="string">"Hello world"</span>,style: TextStyle(color: Colors.white)),</span><br><span class="line">        color: Colors.red,</span><br><span class="line">      ),</span><br><span class="line">      Positioned(</span><br><span class="line">        left: <span class="number">18.0</span>,</span><br><span class="line">        child: Text(<span class="string">"I am Jack"</span>),</span><br><span class="line">      )</span><br><span class="line">    ],</span><br><span class="line">  ),</span><br></pre></td></tr></table></figure><h4 id="Positioned"><a href="#Positioned" class="headerlink" title="Positioned"></a>Positioned</h4><p>属性： left, right, top, bottom, width, height, child。示例：</p><figure class="highlight dart"><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">Positioned(</span><br><span class="line">       top: <span class="number">18.0</span>,</span><br><span class="line">       child: Text(<span class="string">"Your friend"</span>),</span><br><span class="line">     )</span><br></pre></td></tr></table></figure><h3 id="对齐（Align）"><a href="#对齐（Align）" class="headerlink" title="对齐（Align）"></a>对齐（Align）</h3><p>Align 组件可以调整子组件的位置，并且可以根据子组件的宽高来确定自身的的宽高。</p><table><thead><tr><th align="center">属性</th><th align="center">类型</th><th align="center">说明</th></tr></thead><tbody><tr><td align="center">alignment</td><td align="center">Alignment</td><td align="center">表示子组件在父组件中的起始位置</td></tr><tr><td align="center">widthFactor</td><td align="center">number</td><td align="center">组件本身的宽</td></tr><tr><td align="center">heightFactor</td><td align="center">number</td><td align="center">组件本身的高</td></tr><tr><td align="center">child</td><td align="center"></td><td align="center">子组件</td></tr><tr><td align="center">示例：</td><td align="center"></td><td align="center"></td></tr></tbody></table><figure class="highlight dart"><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">Align(</span><br><span class="line">  widthFactor: <span class="number">2</span>,</span><br><span class="line">  heightFactor: <span class="number">2</span>,</span><br><span class="line">  alignment: Alignment.topRight,</span><br><span class="line">  child: FlutterLogo(</span><br><span class="line">    size: <span class="number">60</span>,</span><br><span class="line">  ),</span><br><span class="line">),</span><br></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://book.flutterchina.club/chapter4/wrap_and_flow.html" target="_blank" rel="noopener">《Flutter实战》</a></p><p><a href="https://juejin.im/post/5b623d8c5188257f0b583c77" target="_blank" rel="noopener">Flutter 布局（七）- Row、Column详解</a></p>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;布局组件&quot;&gt;&lt;a href=&quot;#布局组件&quot; class=&quot;headerlink&quot; title=&quot;布局组件&quot;&gt;&lt;/a&gt;布局组件&lt;/h2&gt;&lt;p&gt;布局类组件就是指直接或间接继承(包含)MultiChildRenderObjectWidget的Widget，它们一般都会有一个children属性用于接收子Widget。&lt;/p&gt;&lt;h3 id=&quot;线性布局（Row、Column）&quot;&gt;&lt;a href=&quot;#线性布局（Row、Column）&quot; class=&quot;headerlink&quot; title=&quot;线性布局（Row、Column）&quot;&gt;&lt;/a&gt;线性布局（Row、Column）&lt;/h3&gt;&lt;p&gt;线性布局，即指沿水平或垂直方向排布子组件，将子组件排成一行或一列。&lt;/p&gt;
    
    </summary>
    
      <category term="Flutter" scheme="https://aartemida.github.io/categories/Flutter/"/>
    
    
      <category term="Flutter" scheme="https://aartemida.github.io/tags/Flutter/"/>
    
  </entry>
  
  <entry>
    <title>Flutter中State的生命周期</title>
    <link href="https://aartemida.github.io/2019/08/30/flutter%E4%B8%ADState%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F/"/>
    <id>https://aartemida.github.io/2019/08/30/flutter中State的生命周期/</id>
    <published>2019-08-30T06:18:10.000Z</published>
    <updated>2022-08-05T15:18:39.374Z</updated>
    
    <content type="html"><![CDATA[<h2 id="State-的生命周期"><a href="#State-的生命周期" class="headerlink" title="State 的生命周期"></a>State 的生命周期</h2><p>StatefulWidget中的State的生命周期如下：</p><a id="more"></a><p><img src="https://user-gold-cdn.xitu.io/2019/4/6/169f0af0a1b78bef?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="State Tree"></p><h4 id="createState"><a href="#createState" class="headerlink" title="createState"></a>createState</h4><p>创建一个State。</p><h4 id="initState"><a href="#initState" class="headerlink" title="initState"></a>initState</h4><p>当插入渲染树的时候调用，这个函数在生命周期中只调用一次，可以进行初始化操作。</p><h4 id="didChangeDependencies"><a href="#didChangeDependencies" class="headerlink" title="didChangeDependencies"></a>didChangeDependencies</h4><p>在 initState() 调用结束后，这个函数会被调用。</p><p>在didChangeDependencies中，可以跨组件拿到数据。</p><h4 id="build"><a href="#build" class="headerlink" title="build"></a>build</h4><p>在这里构建视图。</p><h4 id="didUpdateWidget"><a href="#didUpdateWidget" class="headerlink" title="didUpdateWidget"></a>didUpdateWidget</h4><p>当组件的状态改变的时候就会调用didUpdateWidget。这个函数调用后，会调用 build()。</p><p>实际上这里flutter框架会创建一个新的Widget,绑定本State，并在这个函数中传递老的Widget。这个函数一般用于比较新、老Widget，看看哪些属性改变了，并对State做一些调整。</p><p>另外如果某些 Widget 上涉及到 controller 的变更，要么一定要在这个回调方法中移除旧的 controller 并创建新的 controller 监听。</p><h4 id="setState"><a href="#setState" class="headerlink" title="setState"></a>setState</h4><p>可以手动调用这个函数更新视图，调用这个之后会触发didUpdateWidget()。</p><h4 id="deactivate"><a href="#deactivate" class="headerlink" title="deactivate"></a>deactivate</h4><p>当 State 被暂时从视图树中移除时，会调用这个函数。</p><p>页面切换时，也会调用它，因为此时 State 在视图树中的位置发生了变化，需要先暂时移除后添加。</p><p>PS: 重写的时候必须要调用 super.deactivate()。</p><h4 id="dispose"><a href="#dispose" class="headerlink" title="dispose"></a>dispose</h4><p>当State被销毁时调用，可以在这里移除监听释放资源。在调用这个函数之前，总会先调用 deactivate()。</p><p>PS: 重写的时候必须要调用 super.dispose()。</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://juejin.im/post/5ca81c80e51d4509f8232e9b" target="_blank" rel="noopener">Flutter：界面刷新和生命周期</a></p>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;State-的生命周期&quot;&gt;&lt;a href=&quot;#State-的生命周期&quot; class=&quot;headerlink&quot; title=&quot;State 的生命周期&quot;&gt;&lt;/a&gt;State 的生命周期&lt;/h2&gt;&lt;p&gt;StatefulWidget中的State的生命周期如下：&lt;/p&gt;
    
    </summary>
    
      <category term="Flutter" scheme="https://aartemida.github.io/categories/Flutter/"/>
    
    
      <category term="Flutter" scheme="https://aartemida.github.io/tags/Flutter/"/>
    
  </entry>
  
  <entry>
    <title>Flutter组件</title>
    <link href="https://aartemida.github.io/2019/08/29/Flutter%E7%BB%84%E4%BB%B6/"/>
    <id>https://aartemida.github.io/2019/08/29/Flutter组件/</id>
    <published>2019-08-29T08:45:33.000Z</published>
    <updated>2022-08-05T15:18:34.851Z</updated>
    
    <content type="html"><![CDATA[<h2 id="基础组件"><a href="#基础组件" class="headerlink" title="基础组件"></a>基础组件</h2><h3 id="Widget"><a href="#Widget" class="headerlink" title="Widget"></a>Widget</h3><p>在Flutter中几乎所有的组件都是一个Widget。Widget不仅可以表示UI元素，也可以表示一些功能性的组件。在Flutter中，Widget的功能是“描述一个UI元素的配置数据”。Widget分为有状态StatefulWidget和无状态StatelessWidget两种，StatelessWidget类和StatefulWidget类直接继承自Widget类。</p><a id="more"></a><h4 id="StatelessWidget"><a href="#StatelessWidget" class="headerlink" title="StatelessWidget"></a>StatelessWidget</h4><p>用于不需要维护状态的场景，通过build返回一个布局好的组件。</p><figure class="highlight dart"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//StatelessWidgetDemo.dart</span></span><br><span class="line"><span class="keyword">import</span> <span class="string">'package:flutter/material.dart'</span>;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">StatelessWidgetDemo</span> <span class="keyword">extends</span> <span class="title">StatelessWidget</span> </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">return</span> Center(</span><br><span class="line">      child: Container(</span><br><span class="line">        color: Colors.grey,</span><br><span class="line">        child: Text(<span class="string">"test"</span>),</span><br><span class="line">      ),</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"><span class="comment">//Home.dart</span></span><br><span class="line"><span class="keyword">import</span> <span class="string">'package:flutter/material.dart'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">'package:testflutter/StatelessWidgetDemo.dart'</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">IndexWidget</span> <span class="keyword">extends</span> <span class="title">StatelessWidget</span></span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context)&#123;</span><br><span class="line">   <span class="keyword">return</span> StatelessWidgetDemo();</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="StatefulWidget"><a href="#StatefulWidget" class="headerlink" title="StatefulWidget"></a>StatefulWidget</h4><p>用于数据改变的时候，当数据更新时会重新绘制Widget。一个StatefulWidget类会对应一个State类，State表示与其对应的StatefulWidget要维护的状态。</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//StatefulWidgetDemo.dart</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">StatefulWidgetDemo</span> <span class="keyword">extends</span> <span class="title">StatefulWidget</span></span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  _StatefulWidgetDemoState createState() =&gt; <span class="keyword">new</span> _StatefulWidgetDemoState();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">_StatefulWidgetDemoState</span> <span class="keyword">extends</span> <span class="title">State</span>&lt;<span class="title">StatefulWidgetDemo</span>&gt;</span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context)&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> Scaffold(</span><br><span class="line">      body:Text(<span class="string">"test2"</span>)</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="文本组件（Text、TextSpan）"><a href="#文本组件（Text、TextSpan）" class="headerlink" title="文本组件（Text、TextSpan）"></a>文本组件（Text、TextSpan）</h3><h4 id="Text"><a href="#Text" class="headerlink" title="Text"></a>Text</h4><p>负责显示文本和定义显示样式。</p><table><thead><tr><th align="center">属性名</th><th align="center">类型</th><th align="center">默认值</th><th align="center">说明</th></tr></thead><tbody><tr><td align="center">data</td><td align="center">String</td><td align="center"></td><td align="center">文本</td></tr><tr><td align="center">maxLines</td><td align="center">int</td><td align="center">0</td><td align="center">最大行数</td></tr><tr><td align="center">textAlign</td><td align="center">TextAlign</td><td align="center">TextAlign.center</td><td align="center">水平对齐</td></tr><tr><td align="center">textDirection</td><td align="center">TextDirection</td><td align="center">TextDirection.ltr</td><td align="center">文本书写方向</td></tr><tr><td align="center">textScaleFactor</td><td align="center">double</td><td align="center">1.0</td><td align="center">字体缩放系数</td></tr><tr><td align="center">style</td><td align="center">TextStyle</td><td align="center">null</td><td align="center">样式</td></tr><tr><td align="center">textSpan</td><td align="center">TextSpan</td><td align="center">null</td><td align="center">文本块</td></tr></tbody></table><p>示例：</p><figure class="highlight dart"><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">Text(<span class="string">"Hello world"</span>,</span><br><span class="line">  maxLines: <span class="number">1</span>,</span><br><span class="line">  overflow: TextOverflow.ellipsis,</span><br><span class="line">  style: TextStyle(</span><br><span class="line">    color: Colors.blue,</span><br><span class="line">    fontSize: <span class="number">18.0</span>,</span><br><span class="line">    height: <span class="number">1.2</span>,</span><br><span class="line">    fontFamily: <span class="string">"Courier"</span>,</span><br><span class="line">    background: <span class="keyword">new</span> Paint()..color=Colors.yellow,</span><br><span class="line">    decoration:TextDecoration.underline,</span><br><span class="line">    decorationStyle: TextDecorationStyle.dashed</span><br><span class="line">  ),</span><br><span class="line">);</span><br></pre></td></tr></table></figure><h4 id="TextSpan"><a href="#TextSpan" class="headerlink" title="TextSpan"></a>TextSpan</h4><p>Text的所有文本内容只能按同一种样式，如果我们需要对一个Text内容的不同部分按照不同的样式显示，这时就可以使用TextSpan，它代表文本的一个“片段”。示例：</p><figure class="highlight dart"><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">Text.rich(TextSpan(</span><br><span class="line">    children: [</span><br><span class="line">     TextSpan(</span><br><span class="line">       text: <span class="string">"Home: "</span></span><br><span class="line">     ),</span><br><span class="line">     TextSpan(</span><br><span class="line">       text: <span class="string">"https://flutterchina.club"</span>,</span><br><span class="line">       style: TextStyle(</span><br><span class="line">         color: Colors.blue</span><br><span class="line">       ),</span><br><span class="line">       recognizer: _tapRecognizer</span><br><span class="line">     ),</span><br><span class="line">    ]</span><br><span class="line">))</span><br></pre></td></tr></table></figure><h3 id="按钮"><a href="#按钮" class="headerlink" title="按钮"></a>按钮</h3><h4 id="RaisedButton"><a href="#RaisedButton" class="headerlink" title="RaisedButton"></a>RaisedButton</h4><p>“漂浮”按钮，它默认带有阴影和灰色背景。示例：</p><figure class="highlight dart"><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">RaisedButton(</span><br><span class="line">  child: Text(<span class="string">"normal"</span>),</span><br><span class="line">  onPressed: () &#123;&#125;,</span><br><span class="line">);</span><br></pre></td></tr></table></figure><h4 id="FlatButton"><a href="#FlatButton" class="headerlink" title="FlatButton"></a>FlatButton</h4><p>扁平按钮，默认背景透明并不带阴影。按下后，会有背景色。</p><h4 id="OutlineButton"><a href="#OutlineButton" class="headerlink" title="OutlineButton"></a>OutlineButton</h4><p>默认有一个边框，不带阴影且背景透明。按下后，边框颜色会变亮、同时出现背景和阴影。</p><h3 id="图标"><a href="#图标" class="headerlink" title="图标"></a>图标</h3><p>展示iconfont的组件。Material Design所有图标可以在其<a href="https://material.io/tools/icons/" target="_blank" rel="noopener">官网</a>查看。</p><h4 id="Icons"><a href="#Icons" class="headerlink" title="Icons"></a>Icons</h4><p>框架自带的Icon示例：</p><figure class="highlight dart"><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">new</span> Icon(</span><br><span class="line">        Icons.android,<span class="comment">//图标Icon</span></span><br><span class="line">        color: Colors.green,<span class="comment">//图标颜色，设置为绿色，原本的颜色是黑色的</span></span><br><span class="line">        size: <span class="number">150.0</span>,<span class="comment">//Icon的大小</span></span><br><span class="line">      )</span><br></pre></td></tr></table></figure><table><thead><tr><th align="center">属性名</th><th align="center">类型</th><th align="center">默认值</th><th align="center">说明</th></tr></thead><tbody><tr><td align="center">icon</td><td align="center">Icons</td><td align="center">null</td><td align="center">展示的图标</td></tr><tr><td align="center">color</td><td align="center">Color</td><td align="center">null</td><td align="center">颜色</td></tr><tr><td align="center">size</td><td align="center">Double</td><td align="center">24.0</td><td align="center">大小</td></tr><tr><td align="center">textDirection</td><td align="center">TextDirection</td><td align="center">TextDirection.ltr</td><td align="center">文本书写方向</td></tr><tr><td align="center">style</td><td align="center">TextStyle</td><td align="center">null</td><td align="center">样式</td></tr></tbody></table><h4 id="IconButton"><a href="#IconButton" class="headerlink" title="IconButton"></a>IconButton</h4><p>可交互的Icon。支持响应按下事件，如果它的onPressed回调函数为null，那么这个按钮处于禁用的状态，并且不可以按下。</p><table><thead><tr><th align="center">属性名</th><th align="center">类型</th><th align="center">默认值</th><th align="center">说明</th></tr></thead><tbody><tr><td align="center">icon</td><td align="center">Widget</td><td align="center">null</td><td align="center">必须项，展示的图标</td></tr><tr><td align="center">color</td><td align="center">Color</td><td align="center">null</td><td align="center">颜色</td></tr><tr><td align="center">disabledColor</td><td align="center">Color</td><td align="center">ThemeData.disableColor</td><td align="center">禁用的颜色</td></tr><tr><td align="center">splashColor</td><td align="center">Color</td><td align="center"></td><td align="center">splashColor</td></tr><tr><td align="center">highlightColor</td><td align="center">Color</td><td align="center"></td><td align="center">点击时间稍长的时候背景渐变到这个颜色</td></tr><tr><td align="center">iconSize</td><td align="center">Double</td><td align="center">24.0</td><td align="center">大小</td></tr><tr><td align="center">alignment</td><td align="center">AlignmentGeometry</td><td align="center">TextDirection.ltr</td><td align="center">Icon的对齐方式</td></tr><tr><td align="center">onPressed</td><td align="center">VoidCallBack</td><td align="center">null</td><td align="center">必须项，按下回调事件</td></tr><tr><td align="center">tooltip</td><td align="center">String</td><td align="center"></td><td align="center">按下的提示语</td></tr></tbody></table><h4 id="ImageIcon"><a href="#ImageIcon" class="headerlink" title="ImageIcon"></a>ImageIcon</h4><p>通过AssetImages或者其他图片显示Icon</p><h3 id="图片"><a href="#图片" class="headerlink" title="图片"></a>图片</h3><table><thead><tr><th align="center">属性</th><th align="center">类型</th><th align="center">说明</th></tr></thead><tbody><tr><td align="center">image</td><td align="center">ImageProvider</td><td align="center">必须项</td></tr><tr><td align="center">width&#x2F;height</td><td align="center">double</td><td align="center">Image容器显示区域的宽度和高度</td></tr><tr><td align="center">fit</td><td align="center">BoxFit</td><td align="center">图片填充模式</td></tr><tr><td align="center">color</td><td align="center">Color</td><td align="center">图片的混合色值</td></tr><tr><td align="center">colorBlendMode</td><td align="center">BlendMode</td><td align="center">混合模式</td></tr><tr><td align="center">alignment</td><td align="center">Alignment</td><td align="center">对齐方式</td></tr><tr><td align="center">repeat</td><td align="center">ImageRepeat</td><td align="center">当图片本身大小小于显示空间时，图片重复方式</td></tr><tr><td align="center">centerSlice</td><td align="center">Rect</td><td align="center">拉伸的矩形区域&#x2F;9图的中心区域切片</td></tr><tr><td align="center">matchTextDirection</td><td align="center">bool</td><td align="center">和Directionality配合使用，是否匹配文字分析</td></tr><tr><td align="center">gaplessPlayback</td><td align="center">bool</td><td align="center">图片更新过程中原图是否保留</td></tr><tr><td align="center">semanticLabel</td><td align="center">String</td><td align="center">语义标签</td></tr><tr><td align="center">FilterQuality</td><td align="center">FilterQuality</td><td align="center">过滤器品质</td></tr></tbody></table><p>图片的适应模式:</p><ul><li>BoxFit.none：原始大小</li><li>BoxFit.contain：保持Box的纵横比至至少有一边填充满父控件</li><li>BoxFit.cover：保持Box的纵横比进行缩放至Box完全填充满父控件，超出部分进行裁剪</li><li>BoxFit.fill：Box被完全填充</li><li>BoxFit.fitHeigh：缩放Box高直至填充满父控件</li><li>BoxFit.fitWidth：缩放Box宽直至填充满父控件</li><li>BoxFit.scaleDown：Box大于父控件，则采用与contain一致的缩放模式，否则采用none缩放模式</li></ul><h4 id="加载资源图片"><a href="#加载资源图片" class="headerlink" title="加载资源图片"></a>加载资源图片</h4><figure class="highlight dart"><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">//pubspec.yaml</span></span><br><span class="line">assets:</span><br><span class="line">    - images/avatar.png</span><br><span class="line"></span><br><span class="line"><span class="comment">//使用</span></span><br><span class="line">Image.asset(<span class="string">"images/avatar.png"</span>,</span><br><span class="line">  width: <span class="number">100.0</span>,</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">//混合模式</span></span><br><span class="line">Image(</span><br><span class="line">  image: AssetImage(<span class="string">"images/avatar.png"</span>),</span><br><span class="line">  width: <span class="number">100.0</span>,</span><br><span class="line">  color: Colors.blue,</span><br><span class="line">  colorBlendMode: BlendMode.difference,</span><br><span class="line">);</span><br></pre></td></tr></table></figure><h4 id="网络图片"><a href="#网络图片" class="headerlink" title="网络图片"></a>网络图片</h4><figure class="highlight dart"><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">Image.network(</span><br><span class="line">  <span class="string">"https://avatars2.githubusercontent.com/u/20411648?s=460&amp;v=4"</span>,</span><br><span class="line">  width: <span class="number">100.0</span>,</span><br><span class="line">)</span><br></pre></td></tr></table></figure><h3 id="表单-Form"><a href="#表单-Form" class="headerlink" title="表单(Form)"></a>表单(Form)</h3><p>Form继承自StatefulWidget对象，它对应的状态类为FormState。</p><table><thead><tr><th align="center">属性</th><th align="center">类型</th><th align="center">说明</th></tr></thead><tbody><tr><td align="center">Key</td><td align="center">Key</td><td align="center">globalKey,用于获取FormState</td></tr><tr><td align="center">autovalidate</td><td align="center">bool</td><td align="center">是否自动校验输入内容</td></tr><tr><td align="center">child</td><td align="center">Widget</td><td align="center">组件child只能有一个组件</td></tr><tr><td align="center">onChange</td><td align="center">VoidCallback</td><td align="center">当FormField值改变时的回调函数</td></tr><tr><td align="center">onWillPop</td><td align="center">WillPopCallback</td><td align="center">决定Form在的路由是否能直接返回</td></tr></tbody></table><p>FormState可以通过Form.of()或GlobalKey获得。我们可以通过它来对Form的子孙FormField进行统一操作。</p><ul><li>FormState.validate()：调用FormField的validate回调，如果有一个校验失败，则返回false，所有校验失败项都会返回用户返回的错误提示。</li><li>FormState.save()：调用FormField的save回调，用于保存表单内容</li><li>FormState.reset()：清空FormField的内容。</li></ul><h3 id="输入框（TextField、TextFormField）"><a href="#输入框（TextField、TextFormField）" class="headerlink" title="输入框（TextField、TextFormField）"></a>输入框（TextField、TextFormField）</h3><p>用于文本输入，输入框是比较复杂的组件了。<a href>属性详情</a></p><h3 id="单选框和复选框（）"><a href="#单选框和复选框（）" class="headerlink" title="单选框和复选框（）"></a>单选框和复选框（）</h3><p>单选开关Switch和复选框Checkbox都是继承自StatelessWidget，所以它们本身不会保存当前选择状态，因此它们的选中状态都是由父组件来管理的。当Switch或Checkbox被点击时，会触发它们的onChanged回调，可以在此回调中处理选中状态改变逻辑。</p><h2 id="滚动组件"><a href="#滚动组件" class="headerlink" title="滚动组件"></a>滚动组件</h2><h3 id="SingleChildScrollView"><a href="#SingleChildScrollView" class="headerlink" title="SingleChildScrollView"></a>SingleChildScrollView</h3><h3 id="ListView"><a href="#ListView" class="headerlink" title="ListView"></a>ListView</h3><h3 id="GridView"><a href="#GridView" class="headerlink" title="GridView"></a>GridView</h3><h2 id="常用组件"><a href="#常用组件" class="headerlink" title="常用组件"></a>常用组件</h2><h3 id="Container"><a href="#Container" class="headerlink" title="Container"></a>Container</h3><p>可以设置大小和装饰的容器。</p><table><thead><tr><th align="center">属性</th><th align="center">类型</th><th align="center">说明</th></tr></thead><tbody><tr><td align="center">alignment</td><td align="center">Alignment</td><td align="center">对齐方式</td></tr><tr><td align="center">child</td><td align="center">Widget</td><td align="center">子元素</td></tr><tr><td align="center">width</td><td align="center">double</td><td align="center">宽度</td></tr><tr><td align="center">height</td><td align="center">double</td><td align="center">高度</td></tr><tr><td align="center">padding</td><td align="center">EdgeInsets</td><td align="center">内边距</td></tr><tr><td align="center">margin</td><td align="center">EdgeInsets</td><td align="center">外边距</td></tr><tr><td align="center">color</td><td align="center">Color</td><td align="center">背景颜色(不能与decoration一块设置)</td></tr><tr><td align="center">decoration</td><td align="center">BoxDecoration</td><td align="center">背景装饰</td></tr><tr><td align="center">foregroundDecoration</td><td align="center">Decoration</td><td align="center">前景装饰</td></tr><tr><td align="center">transform</td><td align="center">Matrix4</td><td align="center">变换(转换矩阵)</td></tr><tr><td align="center">constraints</td><td align="center">BoxConstraints</td><td align="center">容器大小的限制条件</td></tr></tbody></table><h3 id="SizeBox"><a href="#SizeBox" class="headerlink" title="SizeBox"></a>SizeBox</h3><h3 id="Scaffold"><a href="#Scaffold" class="headerlink" title="Scaffold"></a>Scaffold</h3><p>Scaffold实现了基本的material风格的布局结构，包含了导航栏，抽屉菜单，底部导航等。常用属性如下：</p><table><thead><tr><th align="center">属性</th><th align="center">类型</th><th align="center">说明</th></tr></thead><tbody><tr><td align="center">appBar</td><td align="center">AppBar</td><td align="center">顶部导航栏</td></tr><tr><td align="center">backgroundColor</td><td align="center">Color</td><td align="center">背景色</td></tr><tr><td align="center">body</td><td align="center">Widget</td><td align="center">内容元素</td></tr><tr><td align="center">bottomNavigationBar</td><td align="center">BottomNavigationBar</td><td align="center">底部导航栏</td></tr><tr><td align="center">drawer</td><td align="center">Drawer</td><td align="center">抽屉菜单</td></tr><tr><td align="center">floatingActionButton</td><td align="center">FloatingActionButton</td><td align="center">悬浮按钮</td></tr></tbody></table><h3 id="AppBar"><a href="#AppBar" class="headerlink" title="AppBar"></a>AppBar</h3><p>一个Material风格的导航栏，通过它可以设置导航栏标题、导航栏菜单、导航栏底部的Tab标题等。常用属性如下：</p><table><thead><tr><th align="center">属性</th><th align="center">类型</th><th align="center">说明</th></tr></thead><tbody><tr><td align="center">actions</td><td align="center">List<widget></widget></td><td align="center">导航栏右侧菜单</td></tr><tr><td align="center">backgroundColor</td><td align="center">Color</td><td align="center">背景色</td></tr><tr><td align="center">title</td><td align="center"></td><td align="center">标题</td></tr><tr><td align="center">leading</td><td align="center">Widget</td><td align="center">导航栏左侧Widget</td></tr><tr><td align="center">bottom</td><td align="center">Widget</td><td align="center">导航栏底部菜单</td></tr><tr><td align="center">centerTitle</td><td align="center">bool</td><td align="center">标题是否居中</td></tr><tr><td align="center">elevation</td><td align="center">double</td><td align="center">导航栏阴影大小</td></tr></tbody></table><h3 id="TabBar和TabBarView"><a href="#TabBar和TabBarView" class="headerlink" title="TabBar和TabBarView"></a>TabBar和TabBarView</h3><h3 id="Card"><a href="#Card" class="headerlink" title="Card"></a>Card</h3><h3 id="Table"><a href="#Table" class="headerlink" title="Table"></a>Table</h3><h3 id="Chip"><a href="#Chip" class="headerlink" title="Chip"></a>Chip</h3><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://flutterchina.club/widgets/" target="_blank" rel="noopener">Widgets 目录</a></p><p><a href="https://book.flutterchina.club/chapter4/wrap_and_flow.html" target="_blank" rel="noopener">《Flutter实战》</a></p><p><a href="https://www.cnblogs.com/upwgh/p/11241818.html" target="_blank" rel="noopener">Flutter学习笔记</a></p>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;基础组件&quot;&gt;&lt;a href=&quot;#基础组件&quot; class=&quot;headerlink&quot; title=&quot;基础组件&quot;&gt;&lt;/a&gt;基础组件&lt;/h2&gt;&lt;h3 id=&quot;Widget&quot;&gt;&lt;a href=&quot;#Widget&quot; class=&quot;headerlink&quot; title=&quot;Widget&quot;&gt;&lt;/a&gt;Widget&lt;/h3&gt;&lt;p&gt;在Flutter中几乎所有的组件都是一个Widget。Widget不仅可以表示UI元素，也可以表示一些功能性的组件。在Flutter中，Widget的功能是“描述一个UI元素的配置数据”。Widget分为有状态StatefulWidget和无状态StatelessWidget两种，StatelessWidget类和StatefulWidget类直接继承自Widget类。&lt;/p&gt;
    
    </summary>
    
      <category term="Flutter" scheme="https://aartemida.github.io/categories/Flutter/"/>
    
    
      <category term="Flutter" scheme="https://aartemida.github.io/tags/Flutter/"/>
    
  </entry>
  
  <entry>
    <title>Vue生命周期</title>
    <link href="https://aartemida.github.io/2019/04/24/Vue%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F/"/>
    <id>https://aartemida.github.io/2019/04/24/Vue生命周期/</id>
    <published>2019-04-24T09:30:09.000Z</published>
    <updated>2022-09-22T12:55:52.886Z</updated>
    
    <content type="html"><![CDATA[<p>每个 Vue 实例在被创建时都要经过一系列的初始化过程，在这个过程中也会运行一些叫做生命周期钩子的函数，这给了用户在不同阶段添加自己的代码的机会。</p><p>一个组件从开始到最后消亡所经历的各种状态，就是一个组件的生命周期。</p><a id="more"></a><h3 id="vue的生命周期"><a href="#vue的生命周期" class="headerlink" title="vue的生命周期"></a>vue的生命周期</h3><ul><li>beforeCreate</li><li>created</li><li>beforeMount</li><li>mounted</li><li>beforeUpdate</li><li>updated</li><li>beforeDestroy</li><li>destroyed</li></ul><h3 id="过程"><a href="#过程" class="headerlink" title="过程"></a>过程</h3><h4 id="beforeCreate"><a href="#beforeCreate" class="headerlink" title="beforeCreate"></a>beforeCreate</h4><p>el 和 data 并未初始化。在实例初始化之后，数据观测(data observer) 和 event&#x2F;watcher 事件配置之前被调用。</p><p>主要应用: 通常用于插件开发中执行一些初始化任务。</p><h4 id="在beforeCreate和created钩子函数之间的生命周期"><a href="#在beforeCreate和created钩子函数之间的生命周期" class="headerlink" title="在beforeCreate和created钩子函数之间的生命周期"></a>在beforeCreate和created钩子函数之间的生命周期</h4><p>options init</p><h4 id="created"><a href="#created" class="headerlink" title="created"></a>created</h4><p>完成了 data 数据的初始化，el未初始化。实例已经创建完成之后被调用。在这一步，实例已完成以下的配置：数据观测(data observer)，属性和方法的运算， watch&#x2F;event 事件回调。然而，挂载阶段还没开始，$el 属性目前不可见。</p><p>主要应用：调用数据，调用方法，调用异步函数。</p><p>created钩子可以获取Vue的data，调用Vue方法，获取原本HTML上的直接加载出来的DOM，但是无法获取到通过挂载模板生成的DOM（例如：v-for循环遍历Vue.list生成li）</p><h4 id="created钩子函数和beforeMount间的生命周期"><a href="#created钩子函数和beforeMount间的生命周期" class="headerlink" title="created钩子函数和beforeMount间的生命周期"></a>created钩子函数和beforeMount间的生命周期</h4><p>首先会判断对象是否有el选项。如果有的话就继续向下编译，如果没有el选项，则停止编译，也就意味着停止了生命周期，直到在该vue实例上调用vm.$mount(el)。</p><p>template参数选项的有无对生命周期的影响。</p><ul><li>如果vue实例对象中有template参数选项，则将其作为模板编译成render函数。</li><li>如果没有template选项，则将外部HTML作为模板编译。</li><li>可以看到template中的模板优先级要高于outer HTML的优先级。</li></ul><p>在vue对象中还有一个render函数，它是以createElement作为参数，然后做渲染操作，而且我们可以直接嵌入JSX.（嵌入JSX应该要引入babel-plugin-transform-vue-jsx这个插件）</p><p>优先级：render函数选项 &gt; template选项 &gt; outer HTML.</p><h4 id="beforeMount和mounted-钩子函数间的生命周期"><a href="#beforeMount和mounted-钩子函数间的生命周期" class="headerlink" title="beforeMount和mounted 钩子函数间的生命周期"></a>beforeMount和mounted 钩子函数间的生命周期</h4><p>给vue实例对象添加$el成员，并且替换掉挂载的DOM元素。beforeMount之前el上还是undefined。</p><ul><li>beforeMount:在挂载开始之前被调用：相关的 render 函数（模板）首次被调用。 例如通过v-for生成的html还没有被挂载到页面上</li></ul><h4 id="mounted"><a href="#mounted" class="headerlink" title="mounted"></a>mounted</h4><p>el 被新创建的 vm.$el 替换，并挂载到实例上去之后调用该钩子。 有初始值的DOM渲染，可以对DOM进行操作。</p><p>当你vue没有挂在el时，我们可以用$mount</p><figure class="highlight"><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> app = <span class="keyword">new</span> Vue(&#123;</span><br><span class="line">  data:&#123;<span class="attr">message</span>:<span class="string">'hello'</span>&#125;,</span><br><span class="line">&#125;).$mount(#app);</span><br></pre></td></tr></table></figure><p>主要应用：访问数据和DOM元素、访问子组件</p><h4 id="beforeUpdate钩子函数和updated钩子函数间的生命周期"><a href="#beforeUpdate钩子函数和updated钩子函数间的生命周期" class="headerlink" title="beforeUpdate钩子函数和updated钩子函数间的生命周期"></a>beforeUpdate钩子函数和updated钩子函数间的生命周期</h4><p>data中的数据发生了改变，会触发对应组件的重新渲染</p><ul><li>beforeUpdate:数据更新时调用，发生在虚拟 DOM 重新渲染和打补丁之前。 你可以在这个钩子中进一步地更改状态，这不会触发附加的重渲染过程。 当我们更改Vue的任何数据，都会触发该函数</li><li>updated:组件 DOM 已经更新,可以执行依赖于 DOM 的操作。然而在大多数情况下，你应该避免在此期间更改状态，因为这可能会导致更新无限循环。 该钩子在服务器端渲染期间不被调用。</li></ul><h4 id="beforeDestroy和destroyed钩子函数间的生命周期"><a href="#beforeDestroy和destroyed钩子函数间的生命周期" class="headerlink" title="beforeDestroy和destroyed钩子函数间的生命周期"></a>beforeDestroy和destroyed钩子函数间的生命周期</h4><ul><li>beforeDestroy:实例销毁之前调用。在这一步，实例仍然完全可用。主要用于销毁定时器或取消订阅</li><li>destroyed:Vue 实例销毁后调用。调用后，Vue 实例指示的所有东西都会解绑定，所有的事件监听器会被移除，所有的子实例也会被销毁。 该钩子在服务器端渲染期间不被调用。</li></ul><h3 id="Vue3生命周期"><a href="#Vue3生命周期" class="headerlink" title="Vue3生命周期"></a>Vue3生命周期</h3><ul><li>setup取代beforeCreate和created</li><li>beforeMount -&gt; onBeforeMount，在挂载前被调用</li><li>mounted -&gt; onMounted，挂载完成后调用</li><li>beforeUpdate -&gt; onBeforeUpdate，数据更新时调用，发生在虚拟 DOM 打补丁之前。此时内存中的数据已经被修改，但还没有更新到页面上</li><li>updated -&gt; onUpdated，数据更新后调用，此时内存数据已经修改，页面数据也已经更新</li><li>beforeDestroy -&gt; onBeforeUnmount，组件卸载前调用</li><li>destroyed -&gt; onUnmounted，卸载组件实例后调用。</li><li>errorCaptured -&gt; onErrorCaptured，每当事件处理程序或生命周期钩子抛出错误时调用</li><li>activated -&gt; onActivated，与keep-alive一起使用，当keep-alive包裹的组件激活时调用</li><li>deactivated -&gt; onDeactivated，与keep-alive一起使用，当keep-alive包裹的组件停用时调用</li><li>onRenderTracked，状态跟踪，vue3新引入的钩子函数，只有在开发环境有用，用于跟踪所有响应式变量和方法，一旦页面有update，就会跟踪他们并返回一个event对象</li><li>onRenderTriggered，状态触发，同样是vue3新引入的钩子函数，只有在开发环境有效，定点追踪发生改变的数据，同样返回一个event对象</li></ul><h4 id="为什么setup中没有created"><a href="#为什么setup中没有created" class="headerlink" title="为什么setup中没有created?"></a>为什么setup中没有created?</h4><p>组件实例在setup内部已经创建，不需要用到created钩子。</p><h4 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h4><p><a href="https://segmentfault.com/a/1190000011381906?utm_source=tag-newest" target="_blank" rel="noopener">详解vue生命周期</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;每个 Vue 实例在被创建时都要经过一系列的初始化过程，在这个过程中也会运行一些叫做生命周期钩子的函数，这给了用户在不同阶段添加自己的代码的机会。&lt;/p&gt;&lt;p&gt;一个组件从开始到最后消亡所经历的各种状态，就是一个组件的生命周期。&lt;/p&gt;
    
    </summary>
    
      <category term="Vue" scheme="https://aartemida.github.io/categories/Vue/"/>
    
    
      <category term="vue" scheme="https://aartemida.github.io/tags/vue/"/>
    
  </entry>
  
  <entry>
    <title>Vue2和Vue3原理区别</title>
    <link href="https://aartemida.github.io/2019/04/23/%E5%AF%B9Vue%E5%8E%9F%E7%90%86%E7%9A%84%E4%B8%80%E4%BA%9B%E7%90%86%E8%A7%A3/"/>
    <id>https://aartemida.github.io/2019/04/23/对Vue原理的一些理解/</id>
    <published>2019-04-23T09:50:48.000Z</published>
    <updated>2022-09-22T13:04:27.259Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Vue2"><a href="#Vue2" class="headerlink" title="Vue2"></a>Vue2</h2><h3 id="响应式原理"><a href="#响应式原理" class="headerlink" title="响应式原理"></a>响应式原理</h3><p>用数据劫持结合发布者-订阅者模式实现：通过<code>Object.defineProperty()</code>来劫持各个属性的setter，getter，在数据变动时发布消息给订阅者，触发相应的监听回调。</p><p>创建订阅者dep和观察者watcher进行依赖收集与派发更新：在getter中添加对应的Dep，在setter中通知相关Watcher进行更新。</p><p>对数组：通过重写数组更新数组一系列更新元素的方法来实现元素修改的劫持</p><a id="more"></a><ol><li>Observer：监听器，监听数据变化，对数据对象的所有属性进行监听，如有变动可拿到最新值并通知订阅者</li><li>Compile：解析指令，对每个元素节点的指令进行扫描和解析，根据指令模板替换数据，以及绑定相应的更新函数</li><li>Watcher:订阅者，作为连接Observer和Compile的桥梁，能够订阅并收到每个属性变动的通知，执行指令绑定的相应回调函数，从而更新视图（Vue的核心功能强调的是状态到界面的映射）</li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Object</span>.defineProperty(obj, key, &#123;</span><br><span class="line">    enumerable: <span class="literal">true</span>,</span><br><span class="line">    configurable: <span class="literal">true</span>,</span><br><span class="line">    <span class="keyword">get</span>: function reactiveGetter () &#123; ... &#125;,</span><br><span class="line">    <span class="keyword">set</span>: function reactiveSetter (newVal) &#123; ... &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><h3 id="MVVM"><a href="#MVVM" class="headerlink" title="MVVM"></a>MVVM</h3><p><code>MVVM</code>，是<code>Model-View-ViewModel</code>的简写。MVVM 的ViewModel将视图 UI 和业务逻辑分开，它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。</p><p>MVVM采用双向数据绑定，view中数据变化将自动反映到viewModel上，反之，model中数据变化也将会自动展示在页面上。把Model和View关联起来的就是ViewModel。ViewModel负责把Model的数据同步到View显示出来，还负责把View的修改同步回Model。</p><p>MVVM核心思想，是关注model的变化，让MVVM框架利用自己的机制自动更新DOM，也就是所谓的数据-视图分离。</p><p>优点：</p><ul><li>Controller简洁清晰</li><li>开发解耦、方便测试</li></ul><h3 id="虚拟DOM"><a href="#虚拟DOM" class="headerlink" title="虚拟DOM"></a>虚拟DOM</h3><p>虚拟DOM就是一个用来表示真实DOM的对象, 为了解决浏览器性能问题。它通过js的Object对象模拟DOM中的节点，然后再通过特定的render方法将其渲染成真实的DOM节点 dom。diff 则是通过JS层面的计算，返回一个patch对象，即补丁对象，在通过特定的操作解析patch对象，完成页面的重新渲染。</p><p>1.用 JavaScript 对象结构表示 DOM 树的结构；然后用这个树构建一个真正的 DOM 树，插到文档当中 –&gt; VNode</p><p>2.当状态变更的时候，重新构造一棵新的对象树。然后用新的树和旧的树进行比较，记录两棵树差异 –&gt; diff</p><p>3.把所记录的差异应用到所构建的真正的DOM树上，视图就更新了 –&gt; patch</p><p>优点：</p><ul><li>不会进行回流和重绘；</li><li>对于频繁操作，只进行一次对比差异并修改真实 DOM，减少了真实 DOM 中多次回流重绘引起的性能损耗；</li><li>有效降低大面积的重绘与排版，只更新差异部分，进行渲染局部。</li></ul><h3 id="Diff算法"><a href="#Diff算法" class="headerlink" title="Diff算法"></a>Diff算法</h3><p>Diff算法是一种用来对比新旧虚拟DOM的算法，通过对比找出改变的虚拟DOM，然后单独更新这个虚拟DOM对应的真实节点，提高性能。</p><p>新旧虚拟DOM对比的时候，Diff算法比较只会在同层级进行, 不会跨层级比较。 所以Diff算法是:广度优先算法。 时间复杂度:O(n)。</p><p>优化时间复杂度：</p><ul><li>只比较同层级，不会跨层级比较</li><li>tag不同，直接删除重建，不深度比较</li><li>tag和key都相同，则认为是相同节点，不深度比较</li></ul><p>diff遵循以下原则：</p><ul><li>在旧的虚拟DOM中找到与新的虚拟DOM相同的key<ul><li>tag没有发生变化,就直接使用原来的真实DOM</li><li>内容发生改变,就替换掉之前旧的虚拟DOM,生成新的真实DOM</li></ul></li><li>在旧的虚拟DOM中未找到与新的虚拟DOM相同的key<ul><li>直接生成新的真实DOM</li></ul></li></ul><h4 id="vue的diff算法优化（双端-diff-算法）"><a href="#vue的diff算法优化（双端-diff-算法）" class="headerlink" title="vue的diff算法优化（双端 diff 算法）"></a>vue的diff算法优化（双端 diff 算法）</h4><p>diff 算法的目的是根据 key <code>复用 dom 节点</code>，通过移动节点而不是创建新节点来减少 dom 操作。</p><p>当数据改变时，会触发setter，并且通过Dep.notify去通知所有订阅者Watcher，订阅者们就会调用patch方法，给真实DOM打补丁，更新相应的视图。</p><p>整个过程是逐步找到更新前后vdom的差异，然后将差异反应到DOM树上（也就是patch），特别要提一下Vue的patch是即时的，并不是打包所有修改最后一起操作DOM（React则是将更新放入队列后集中处理）</p><p>优先处理特殊场景、“原地复用”（Vue会尽可能复用DOM，尽可能不发生DOM的移动）</p><h5 id="双端-diff-算法"><a href="#双端-diff-算法" class="headerlink" title="双端 diff 算法"></a>双端 diff 算法</h5><p>diff算法从两边向中间比较，需要四个指针，分别指向新旧两个 vnode 数组的头尾。头和尾的指针向中间移动，直到 oldStartIdx &lt;&#x3D; oldEndIdx 并且 newStartIdx &lt;&#x3D; newEndIdx，说明就处理完了全部的节点。</p><p>双端 diff 是头尾指针向中间移动的同时，对比头头、尾尾、头尾、尾头是否可以复用，如果可以的话就移动对应的 dom 节点。</p><p>如果头尾没找到可复用节点就遍历 vnode 数组来查找，然后移动对应下标的节点到头部。</p><p>最后还剩下旧的 vnode 就批量删除，剩下新的 vnode 就批量新增。</p><h4 id="Vue中diff算法的实现"><a href="#Vue中diff算法的实现" class="headerlink" title="Vue中diff算法的实现"></a>Vue中diff算法的实现</h4><ol><li>创建节点：新的VNode中有而旧的oldVNode中没有，就在旧的oldVNode中创建。</li><li>删除节点：新的VNode中没有而旧的oldVNode中有，就从旧的oldVNode中删除。</li><li>更新节点：新的VNode和旧的oldVNode中都有，就以新的VNode为准，更新旧的oldVNode。</li></ol><p>相关方法：</p><ul><li>通过函数<code>sameVnode</code>判断两个vnode是否相同</li><li>patch: 当数据发生改变时，<code>defineProperty =&gt; get</code>会调用<code>Dep</code>的<code>notify</code>方法调用<code>Watcher</code>进行更新，当每次走get调用_update的时候，都会走<code>patch</code>函数，更新真实DOM</li><li>patchVnode: 该函数是递归调用updateChildren的入口，除了比对子节点以外，还会将老节点上的东西更新到新节点中</li><li>updateChildren：更新节点调用的方法</li></ul><h4 id="vue3优化diff算法（快速-diff-算法）"><a href="#vue3优化diff算法（快速-diff-算法）" class="headerlink" title="vue3优化diff算法（快速 diff 算法）"></a>vue3优化diff算法（快速 diff 算法）</h4><h3 id="与Angular区别"><a href="#与Angular区别" class="headerlink" title="与Angular区别"></a>与Angular区别</h3><p>与Aangular双向数据绑定不同，Vue组件不能检测到实例化后data属性的添加、删除，因为Vue组件在实例化时才会对属性执行getter&#x2F;setter处理，所以data对象上的属性必须在实例化之前存在，Vue才能够正确的进行转换。因而，Vue提供的并非真正意义上的双向绑定，更准确的描述应该是单向绑定，响应式更新，而Angular即可以通过$scope影响view上的数据绑定，也可以通过视图层操作$scope上的对象属性，属于真正意义上的视图与模型的双向绑定。</p><h3 id="vue2原理存在的问题"><a href="#vue2原理存在的问题" class="headerlink" title="vue2原理存在的问题"></a>vue2原理存在的问题</h3><ul><li>初始化时（Object.defineProperty）需要递归遍历对象所有 key，如果对象层次较深，性能不好</li><li>通知更新过程需要维护大量 dep 实例和 watcher 实例，额外占用内存较多</li><li>Object.defineProperty 无法监听到数组元素及数组长度的变化，只能通过劫持重写会改变原数组的方法</li><li>动态新增，删除对象属性无法拦截，只能用特定 Vue.set()&#x2F;delete 解决</li><li>不支持 Map、Set、WeakMap 等数据结构</li></ul><h2 id="Vue3"><a href="#Vue3" class="headerlink" title="Vue3"></a>Vue3</h2><p>用 <code>Proxy</code> 代替 <code>Object.defineProperty</code> 重构了响应式系统。<br>通过Proxy(代理): 拦截对对象属性的操作, 包括属性值的增删改查。</p><p>通过 Reflect(反射): 对被代理对象的相应属性进行特定的操作。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> <span class="built_in">Proxy</span>(target, &#123;  <span class="comment">// target 为组件的 data 返回的对象</span></span><br><span class="line">  <span class="keyword">get</span>(target, key) &#123;&#125;,</span><br><span class="line">  <span class="keyword">set</span>(target, key, value) &#123;&#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><p>使用Proxy代理的缺点：</p><ul><li>原始值的响应式系统的实现（proxy 的使用本身就是对于 对象的拦截，导致必须将他包装为一个对象， 通过<code>.value</code>的方式访问</li><li>ES6 解构，不能随意使用。会破坏他的响应式特性</li><li>不兼容IE</li></ul><h3 id="vue3相对vue2的优点"><a href="#vue3相对vue2的优点" class="headerlink" title="vue3相对vue2的优点"></a>vue3相对vue2的优点</h3><p>代码层面：</p><ul><li>更完善的响应式系统（初始化时间和内存占用都得到改善）</li><li>更好的Ts支持</li><li>Composition API</li><li>支持多根节点组件</li></ul><p>编译：</p><ul><li><p>diff算法的优化:</p><p>vue2中的虚拟dom是全量的对比（每个节点不论写死的还是动态的都会一层一层比较，这就浪费了大部分时间在对比静态节点上）。</p><p>vue3新增了静态标记（<code>patchflag</code>）与上次虚拟节点对比时，只对比带有patch flag的节点（动态数据所在的节点）；可通过flag信息得知当前节点要对比的具体内容。</p></li><li><p>hoistStatic 静态提升:</p><p>vue2无论元素是否参与更新，每次都会重新创建然后再渲染。</p><p>vue3对于不参与更新的元素（静态元素），会做静态提升（放在render函数外面），只会被创建一次，在渲染时直接复用即可。</p></li><li><p>cacheHandlers 事件侦听器缓存:</p><p>vue2.x中，绑定事件每次触发都要重新生成全新的function去更新，cacheHandlers 是Vue3中提供的事件缓存对象，当 cacheHandlers 开启，会自动生成一个内联函数，同时生成一个静态节点。当事件再次触发时，只需从缓存中调用即可，无需再次更新。</p></li></ul><p>打包：更好的支持tree-sharking，打包的体积更小</p><h3 id="依赖收集"><a href="#依赖收集" class="headerlink" title="依赖收集"></a>依赖收集</h3><p>Vue2 中是通过 Observer，Dep，Watcher 这三个类来实现依赖收集。</p><p>Vue3 中是通过 track 收集依赖，通过 trigger 触发更新，本质上就是用 WeakMap，Map，Set 来实现。</p><h3 id="defineProperty-和-Proxy"><a href="#defineProperty-和-Proxy" class="headerlink" title="defineProperty 和 Proxy"></a>defineProperty 和 Proxy</h3><ul><li>Object.defineProperty 是 Es5 的方法，Proxy 是 Es6 的方法</li><li>defineProperty 不能监听到数组下标变化和对象新增属性，Proxy 可以</li><li>defineProperty 是劫持对象属性，Proxy 是代理整个对象</li><li>defineProperty 局限性大，只能针对单属性监听，所以在一开始就要全部递归监听。Proxy 对象嵌套属性运行时递归，用到才代理，也不需要维护特别多的依赖关系，性能提升很大，且首次渲染更快</li><li>defineProperty 会污染原对象，修改时是修改原对象，Proxy 是对原对象进行代理并会返回一个新的代理对象，修改的是代理对象</li><li>defineProperty 不兼容 IE8，Proxy 不兼容 IE11</li></ul><h2 id="Vue3和Vue2的区别"><a href="#Vue3和Vue2的区别" class="headerlink" title="Vue3和Vue2的区别"></a>Vue3和Vue2的区别</h2><ul><li>响应式原理</li><li>生命周期钩子名称</li><li>自定义指令钩子名称</li><li>新的内置组件</li><li>diff 算法</li><li>Composition API</li></ul><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://segmentfault.com/a/1190000006599500" target="_blank" rel="noopener">剖析Vue原理&amp;实现双向绑定MVVM</a></p><p><a href="https://blog.csdn.net/lishanleilixin/article/details/79360244" target="_blank" rel="noopener">深入理解vue</a></p><p><a href="https://blog.csdn.net/m6i37jk/article/details/78140159" target="_blank" rel="noopener">深入Vue2.x的虚拟DOM diff原理</a></p><p><a href="https://juejin.cn/post/6994959998283907102" target="_blank" rel="noopener">20分钟吃透Diff算法核心原理</a></p><p><a href="https://segmentfault.com/a/1190000040695447" target="_blank" rel="noopener">vue3，对比 vue2 有什么优点？</a></p>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;Vue2&quot;&gt;&lt;a href=&quot;#Vue2&quot; class=&quot;headerlink&quot; title=&quot;Vue2&quot;&gt;&lt;/a&gt;Vue2&lt;/h2&gt;&lt;h3 id=&quot;响应式原理&quot;&gt;&lt;a href=&quot;#响应式原理&quot; class=&quot;headerlink&quot; title=&quot;响应式原理&quot;&gt;&lt;/a&gt;响应式原理&lt;/h3&gt;&lt;p&gt;用数据劫持结合发布者-订阅者模式实现：通过&lt;code&gt;Object.defineProperty()&lt;/code&gt;来劫持各个属性的setter，getter，在数据变动时发布消息给订阅者，触发相应的监听回调。&lt;/p&gt;&lt;p&gt;创建订阅者dep和观察者watcher进行依赖收集与派发更新：在getter中添加对应的Dep，在setter中通知相关Watcher进行更新。&lt;/p&gt;&lt;p&gt;对数组：通过重写数组更新数组一系列更新元素的方法来实现元素修改的劫持&lt;/p&gt;
    
    </summary>
    
      <category term="Vue" scheme="https://aartemida.github.io/categories/Vue/"/>
    
    
      <category term="vue" scheme="https://aartemida.github.io/tags/vue/"/>
    
  </entry>
  
</feed>
