<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>有树&#39;s Blog</title>
  <icon>https://www.gravatar.com/avatar/9e5b4fbb3059bee6f5e112a42edae5f2</icon>
  
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://309893147.github.io/"/>
  <updated>2019-08-17T04:58:55.947Z</updated>
  <id>https://309893147.github.io/</id>
  
  <author>
    <name>春有百花,秋有树</name>
    <email>ls309893147@gmail.com</email>
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>element</title>
    <link href="https://309893147.github.io/2019/08/17/element/"/>
    <id>https://309893147.github.io/2019/08/17/element/</id>
    <published>2019-08-17T04:50:48.000Z</published>
    <updated>2019-08-17T04:58:55.947Z</updated>
    
    <content type="html"><![CDATA[<p>element Upload 上传</p><pre><code>     &lt;el-upload          class=&quot;upload-demo&quot;          ref=&quot;upload&quot;          action=&quot;/manager/publicproject/addPictureUrl&quot;          :on-preview=&quot;handlePreview&quot;          :on-remove=&quot;handleRemove&quot;          :auto-upload=&quot;false&quot;          :on-success=&quot;updateSuccess&quot;          :limit=&quot;1&quot;        &gt;          &lt;el-button slot=&quot;trigger&quot; size=&quot;small&quot; type=&quot;primary&quot;&gt;选取文件&lt;/el-button&gt;          &lt;el-button            style=&quot;margin-left: 10px;&quot;            size=&quot;small&quot;            type=&quot;success&quot;            @click=&quot;submitUpload&quot;          &gt;上传到服务器&lt;/el-button&gt;          &lt;div slot=&quot;tip&quot; class=&quot;el-upload__tip&quot;&gt;只能上传jpg/png文件，且不超过500kb&lt;/div&gt;        &lt;/el-upload&gt;updateSuccess(response, file, fileList) {  console.log(response);  console.log(response.body);  this.pictureItem.url = response.body;  console.log(file);  console.log(fileList);},submitUpload() {  this.$refs.upload.submit();},handleRemove(file, fileList) {  console.log(file, fileList);  fileList = [];},handlePreview(file) {  console.log(file);},</code></pre>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;element Upload 上传&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;     &amp;lt;el-upload
          class=&amp;quot;upload-demo&amp;quot;
          ref=&amp;quot;upload&amp;quot;
          ac
      
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>v-charts百度地图</title>
    <link href="https://309893147.github.io/2019/08/02/v-charts%E7%99%BE%E5%BA%A6%E5%9C%B0%E5%9B%BE/"/>
    <id>https://309893147.github.io/2019/08/02/v-charts百度地图/</id>
    <published>2019-08-02T08:34:40.000Z</published>
    <updated>2019-08-02T09:03:08.697Z</updated>
    
    <content type="html"><![CDATA[<p>场景:后台首页数据统计面板中使用地图统计在线机器与故障机器</p><p>安装</p><pre><code>npm i v-charts echarts -S</code></pre><p>开始使用</p><pre><code>// main.jsimport Vue from &apos;vue&apos;import VCharts from &apos;v-charts&apos;import App from &apos;./App.vue&apos;Vue.use(VCharts)</code></pre><p>页面中使用</p><pre><code>import VeBmap from &quot;v-charts/lib/bmap.common&quot;;</code></pre><p> script中引进ve-bmap标签,template中使用</p><pre><code>  &lt;ve-bmap  :settings=&quot;chartSettings&quot;  :after-set-option-once=&quot;afterSet&quot;  :series=&quot;chartSeries&quot;  :tooltip=&quot;chartTooltip&quot;&gt;&lt;/ve-bmap&gt;</code></pre>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;场景:后台首页数据统计面板中使用地图统计在线机器与故障机器&lt;/p&gt;
&lt;p&gt;安装&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm i v-charts echarts -S
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;开始使用&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// main.js
import Vu
      
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>微信支付-售后退款</title>
    <link href="https://309893147.github.io/2019/07/31/%E5%BE%AE%E4%BF%A1%E6%94%AF%E4%BB%98-%E5%94%AE%E5%90%8E%E9%80%80%E6%AC%BE/"/>
    <id>https://309893147.github.io/2019/07/31/微信支付-售后退款/</id>
    <published>2019-07-31T09:22:39.000Z</published>
    <updated>2019-07-31T10:55:14.159Z</updated>
    
    <content type="html"><![CDATA[<p>PostSale 表</p><p>用户Id,订单Id,机器Id,订单,用户备注,机器备注,图片,售后状态</p><pre><code>@OneToOne@JoinColumn(name = &quot;orderId&quot;,updatable = false,insertable = false)private StoreOrder order;</code></pre><p>通过注解关联订单Id和订单</p><pre><code>private Status  status = Status.CREATED;public enum Status{    CREATED, //等待商家处理 - 商家处理中    CANCELED, //用户取消  - 已取消    FINISHED, //商家同意售后  - 已完成    REJECTED  //商家驳回售后  -  已完成}</code></pre><p>枚举类型 enum Status</p><p>继承一个通用的BaseEntity,包含自动生成的表Id,<br>Timestamp类型的时间,通过注解实现正反的序列化</p><pre><code>@Id@GeneratedValue(    strategy = GenerationType.AUTO)protected Integer id;@CreationTimestamp@JsonSerialize(    using = TimestampSerializer.class)@JsonDeserialize(    using = TimestampDeserializer.class)@Column(    updatable = false)protected Timestamp createTime;@UpdateTimestamp@JsonSerialize(    using = TimestampSerializer.class)@JsonDeserialize(    using = TimestampDeserializer.class)protected Timestamp updateTime;</code></pre><p> Controller </p><p> @RequestBody 接受传递过来的json实体对象</p><pre><code>@PostMapping(&quot;/post_sale/refund_order&quot;)public API&lt;PostSale&gt; refundOrder(@RequestBody @Valid PostSale postSale){    return API.ok(mOrderManageService.refundOrder(postSale));}</code></pre><p> PostSaleService</p><pre><code>   public PostSale refundOrder(PostSale postSale) {    StoreOrder order = orderRepository.findItemById(postSale.getOrderId());    mPayService.refundOrder(order.getNo(),order.getPrice(),order.getPayMethod());    postSale.setStatus(PostSale.Status.FINISHED);    return postSaleRepository.save(postSale);}</code></pre><p>通过postSale实体类中的订单Id查找出订单数据,进行退款操作,退款成功后更改售后状态</p><p>PayService </p><p>传递订单中的编号,价格,订单支付日志中的支付方式</p><pre><code>public void refundOrder(String no, BigDecimal refundPrice, PayLog.PayMethod payMethod) {  getPayService(payMethod).refundOrder(no,refundPrice);}</code></pre><p>抽出getPayService方法.获得支付类型</p><pre><code>public PayService  getPayService(PayLog.PayMethod payMethod){    if (PayLog.PayMethod.WECHAT.equals(payMethod)){        return mWechatPayService;    }    if (PayLog.PayMethod.ROYAL_PAY.equals(payMethod)){        return mRoyalPayService;    }    APIError.CUSTOM.set(400,&quot;暂不支持该支付方式&quot;).expose();    return null;}</code></pre><p>WechatPayService</p><p>申请退款</p><pre><code>public void refundOrder(String orderNo,BigDecimal refundPrice) {        PayLog log = checkIfCanRefund(orderNo);        WxPayRefundRequest refundQueryRequest = new WxPayRefundRequest();        refundQueryRequest.setTransactionId(log.getTransactionId());        refundQueryRequest.setOutRefundNo(generateRefundId());        refundQueryRequest.setRefundFee(transformPrice(refundPrice));        refundQueryRequest.setTotalFee(transformPrice(log.getPrice()));        refundQueryRequest.setNotifyUrl(String.format(&quot;http://%s/pay/wechat/%srefund&quot;,mBasicConfigService.getConfig().getDomain(),callbackPrefix));        try {            WxPayRefundResult data = mWxPayService.refund(refundQueryRequest);            log.setRefundId(data.getRefundId());            log.setRefundPrice(refundPrice);            log.setStatus(PayLog.PayStatus.REFUNDING);            log.setRefundTransactionId(data.getRefundId());            log.setRefundApplyTime(new Timestamp(System.currentTimeMillis()));            mPayLogRepository.save(log);        } catch (WxPayException e) {            mLogger.warning(&quot;refund apply error:&quot;+e.getMessage());            APIError.CUSTOM.set(500,&quot;退款失败:&quot;+e.getMessage()).expose();        }}</code></pre><p>PayLog log = checkIfCanRefund(orderNo); 检查是否可以退款</p><pre><code>protected PayLog checkIfCanRefund(String orderNo){    PayLog log = getPayLog(orderNo);    if (log == null){        APIError.CUSTOM.set(404,&quot;订单不存在&quot;).expose();    }    if (!PayLog.PayStatus.SUCCESS.equals(log.getStatus())){        APIError.CUSTOM.set(404,&quot;当前订单不可退款&quot;).expose();    }    return log;}</code></pre><p> WxPayRefundRequest refundQueryRequest = new WxPayRefundRequest(); </p><p>创建一个微信退款xml实体,满足微信商户申请退款接口中要求的数据 </p><pre><code>&lt;xml&gt;&lt;appid&gt;wx2421b1c4370ec43b&lt;/appid&gt;&lt;mch_id&gt;10000100&lt;/mch_id&gt;    &lt;nonce_str&gt;6cefdb308e1e2e8aabd48cf79e546a02&lt;/nonce_str&gt;    &lt;out_refund_no&gt;1415701182&lt;/out_refund_no&gt;   &lt;out_trade_no&gt;1415757673&lt;/out_trade_no&gt;   &lt;refund_fee&gt;1&lt;/refund_fee&gt;   &lt;total_fee&gt;1&lt;/total_fee&gt;   &lt;transaction_id&gt;&lt;/transaction_id&gt;   &lt;sign&gt;FE56DD4AA85C0EECA82C35595A69E153&lt;/sign&gt;&lt;/xml&gt;</code></pre><p>WxPayRefundRequest 导入jar包<br>package com.github.binarywang.wxpay.bean.request;</p><p>refundQueryRequest.setTransactionId(log.getTransactionId());</p><p>通过支付日志获取交易号</p><p>设置商户自己内部的退款单号</p><p> refundQueryRequest.setOutRefundNo(generateRefundId());</p><pre><code>@Overridepublic String generateRefundId() {    return UUID.randomUUID().toString().replaceAll(&quot;-&quot;,&quot;&quot;);}</code></pre><p>设置退款结果通知url.<br>   refundQueryRequest.setNotifyUrl(String.format(“<a href="http://%s/pay/wechat/%srefund&quot;,mBasicConfigService.getConfig().getDomain(),callbackPrefix" target="_blank" rel="external">http://%s/pay/wechat/%srefund&quot;,mBasicConfigService.getConfig().getDomain(),callbackPrefix</a>));</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;PostSale 表&lt;/p&gt;
&lt;p&gt;用户Id,订单Id,机器Id,订单,用户备注,机器备注,图片,售后状态&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@OneToOne
@JoinColumn(name = &amp;quot;orderId&amp;quot;
,updatable = false

      
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>my blog</title>
    <link href="https://309893147.github.io/2019/07/30/my-blog/"/>
    <id>https://309893147.github.io/2019/07/30/my-blog/</id>
    <published>2019-07-30T10:37:05.000Z</published>
    <updated>2019-07-30T10:42:35.341Z</updated>
    
    <content type="html"><![CDATA[<h1 id="表设计"><a href="#表设计" class="headerlink" title="表设计"></a>表设计</h1><pre><code>@Entity@Getter@Setterpublic class StoreLog  extends BaseEntity {//机器idprivate Integer storeId;//机器状态private LogType type;public enum LogType {    RUN,    OPERATION}private String code;private String content;}</code></pre><p>1.与安卓端交互时,根据返回的type,code来判断,硬件机器是在什么情况发生的错误</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;表设计&quot;&gt;&lt;a href=&quot;#表设计&quot; class=&quot;headerlink&quot; title=&quot;表设计&quot;&gt;&lt;/a&gt;表设计&lt;/h1&gt;&lt;pre&gt;&lt;code&gt;@Entity
@Getter
@Setter
public class StoreLog  extends Bas
      
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>机器日志功能</title>
    <link href="https://309893147.github.io/2019/07/30/test/"/>
    <id>https://309893147.github.io/2019/07/30/test/</id>
    <published>2019-07-30T02:26:00.000Z</published>
    <updated>2019-07-30T10:15:56.443Z</updated>
    
    <content type="html"><![CDATA[<p>表设计</p><pre><code>@Entity@Getter@Setterpublic class StoreLog  extends BaseEntity {//机器idprivate Integer storeId;//机器状态private LogType type;public enum LogType {    RUN,    OPERATION}private String code;private String content;}</code></pre><p>1.与安卓端交互时,根据返回的type,code来判断,硬件机器是在什么情况发生的错误</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;表设计&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@Entity
@Getter
@Setter
public class StoreLog  extends BaseEntity {

//机器id
private Integer storeId;

//机器状态
private L
      
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>【转】如何优雅处理前端异常</title>
    <link href="https://309893147.github.io/2019/05/01/js-exception-handing/"/>
    <id>https://309893147.github.io/2019/05/01/js-exception-handing/</id>
    <published>2019-04-30T19:25:24.000Z</published>
    <updated>2019-07-30T01:31:38.209Z</updated>
    
    <content type="html"><![CDATA[<p>前端一直是距离用户最近的一层，随着产品的日益完善，我们会更加注重用户体验，而前端异常却如鲠在喉，甚是烦人。</p><h1 id="一、为什么要处理异常？"><a href="#一、为什么要处理异常？" class="headerlink" title="一、为什么要处理异常？"></a>一、为什么要处理异常？</h1><p id="div-border-left-green">异常是不可控的，会影响最终的呈现结果，但是我们有充分的理由去做这样的事情。</p><p>1.增强用户体验；<br>2.远程定位问题；<br>3.未雨绸缪，及早发现问题；<br>4.无法复线问题，尤其是移动端，机型，系统都是问题；<br>5.完善的前端方案，前端监控系统；</p><a id="more"></a><p>对于 JS 而言，我们面对的仅仅只是异常，异常的出现不会直接导致 JS 引擎崩溃，最多只会使当前执行的任务终止。</p><h1 id="二、需要处理哪些异常？"><a href="#二、需要处理哪些异常？" class="headerlink" title="二、需要处理哪些异常？"></a>二、需要处理哪些异常？</h1><p>对于前端来说，我们可做的异常捕获还真不少。总结一下，大概如下：</p><ul><li>JS 语法错误、代码异常</li><li>AJAX 请求异常</li><li>静态资源加载异常</li><li>Promise 异常</li><li>Iframe 异常</li><li>跨域 Script error</li><li>崩溃和卡顿</li></ul><p id="div-border-left-yellow">下面我会针对每种具体情况来说明如何处理这些异常。</p><h1 id="三、Try-Catch-的误区"><a href="#三、Try-Catch-的误区" class="headerlink" title="三、Try-Catch 的误区"></a>三、Try-Catch 的误区</h1><p id="div-border-top-blue"><code>try-catch</code> 只能捕获到 <font color="red"><strong>同步</strong></font> 的运行时错误，对 <font color="red">语法</font> 和 <font color="red">异步</font> 错误却无能为力，捕获不到。【不能捕获XHR，AJAX的异常】</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> name = <span class="string">'jartto'</span>;</span><br><span class="line">  <span class="built_in">console</span>.log(nam);</span><br><span class="line">&#125; <span class="keyword">catch</span>(e) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'捕获到异常：'</span>,e);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>输出：<br><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="built_in">ReferenceError</span>: nam is not defined at &lt;anonymous&gt;:<span class="number">3</span>:<span class="number">15</span></span><br></pre></td></tr></table></figure></p><p>2.不能捕获到语法错误，我们修改一下代码，删掉一个单引号：<br><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">try</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> name = <span class="string">'jartto;</span></span><br><span class="line"><span class="string">  console.log(nam);</span></span><br><span class="line"><span class="string">&#125; catch(e) &#123;</span></span><br><span class="line"><span class="string">  console.log('</span>捕获到异常：<span class="string">',e);</span></span><br><span class="line"><span class="string">&#125;</span></span><br></pre></td></tr></table></figure></p><p>输出：<br><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">Uncaught <span class="built_in">SyntaxError</span>: Invalid or unexpected token</span><br></pre></td></tr></table></figure></p><blockquote><font color="#0e8a16">不过语法错误在我们开发阶段就可以看到，应该不会顺利上到线上环境吧。?</font></blockquote><p>3.异步错误<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">  setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="literal">undefined</span>.map(<span class="function"><span class="params">v</span> =&gt;</span> v);</span><br><span class="line">  &#125;, <span class="number">1000</span>)</span><br><span class="line">&#125; <span class="keyword">catch</span>(e) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'捕获到异常：'</span>,e);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>我们看看日志：<br><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">Uncaught <span class="built_in">TypeError</span>: Cannot read property <span class="string">'map'</span> <span class="keyword">of</span> <span class="literal">undefined</span> at setTimeout (<span class="xml"><span class="tag">&lt;<span class="name">anonymous</span>&gt;</span>:3:11)</span></span><br></pre></td></tr></table></figure></p><p>并没有捕获到异常，这是需要我们特别注意的地方。<font color="#b60205">【try catch 无法捕获异步异常】</font></p><h1 id="四、window-onerror-不是万能的"><a href="#四、window-onerror-不是万能的" class="headerlink" title="四、window.onerror 不是万能的"></a>四、window.onerror 不是万能的</h1><p>当 JS 运行时错误发生时，window 会触发一个 ErrorEvent 接口的 error 事件，并执行 <code>window.onerror()</code>。<br><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"><span class="comment">* @param &#123;String&#125;  message    错误信息</span></span><br><span class="line"><span class="comment">* @param &#123;String&#125;  source    出错文件</span></span><br><span class="line"><span class="comment">* @param &#123;Number&#125;  lineno    行号</span></span><br><span class="line"><span class="comment">* @param &#123;Number&#125;  colno    列号</span></span><br><span class="line"><span class="comment">* @param &#123;Object&#125;  error  Error对象（对象）</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="built_in">window</span>.onerror = <span class="function"><span class="keyword">function</span>(<span class="params">message, source, lineno, colno, error</span>) </span>&#123;</span><br><span class="line">   <span class="built_in">console</span>.log(<span class="string">'捕获到异常：'</span>,&#123;message, source, lineno, colno, error&#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>1.首先试试同步运行时错误<br><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="built_in">window</span>.onerror = <span class="function"><span class="keyword">function</span>(<span class="params">message, source, lineno, colno, error</span>) </span>&#123;</span><br><span class="line"><span class="comment">// message：错误信息（字符串）。</span></span><br><span class="line"><span class="comment">// source：发生错误的脚本URL（字符串）</span></span><br><span class="line"><span class="comment">// lineno：发生错误的行号（数字）</span></span><br><span class="line"><span class="comment">// colno：发生错误的列号（数字）</span></span><br><span class="line"><span class="comment">// error：Error对象（对象）</span></span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'捕获到异常：'</span>,&#123;message, source, lineno, colno, error&#125;);</span><br><span class="line">&#125;</span><br><span class="line">Jartto;</span><br></pre></td></tr></table></figure></p><p>可以看到，我们捕获到了异常：<br><img src="/images/js-exception-handing-1.png" alt=""></p><p>2.再试试语法错误呢？<br><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="built_in">window</span>.onerror = <span class="function"><span class="keyword">function</span>(<span class="params">message, source, lineno, colno, error</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'捕获到异常：'</span>,&#123;message, source, lineno, colno, error&#125;);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> name = <span class="string">'Jartto</span></span><br></pre></td></tr></table></figure></p><p>控制台打印出了这样的异常：<br><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">Uncaught <span class="built_in">SyntaxError</span>: Invalid or unexpected token</span><br></pre></td></tr></table></figure></p><p id="div-border-top-red">什么，竟然没有捕获到语法错误？!!</p><p>3.怀着忐忑的心，我们最后来试试异步运行时错误：<br><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="built_in">window</span>.onerror = <span class="function"><span class="keyword">function</span>(<span class="params">message, source, lineno, colno, error</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'捕获到异常：'</span>,&#123;message, source, lineno, colno, error&#125;);</span><br><span class="line">&#125;</span><br><span class="line">setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  Jartto;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p><p>控制台输出了：<br><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 class="attr">message</span>: <span class="string">"Uncaught ReferenceError: Jartto is not defined"</span>, </span><br><span class="line">  source: <span class="string">"http://127.0.0.1:8001/"</span>, </span><br><span class="line">  lineno: <span class="number">36</span>, <span class="attr">colno</span>: <span class="number">5</span>, </span><br><span class="line">  error: <span class="built_in">ReferenceError</span>: Jartto is not defined at setTimeout (http:<span class="comment">//127.0.0.1:8001/:36:5)&#125;</span></span><br></pre></td></tr></table></figure></p><p>4.接着，我们试试网络请求异常的情况：<br><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">&lt;script&gt;</span><br><span class="line"><span class="built_in">window</span>.onerror = <span class="function"><span class="keyword">function</span>(<span class="params">message, source, lineno, colno, error</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'捕获到异常：'</span>,&#123;message, source, lineno, colno, error&#125;);</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">&lt;<span class="regexp">/script&gt;</span></span><br><span class="line"><span class="regexp">&lt;img src="./</span>jartto.png<span class="string">"&gt;</span></span><br></pre></td></tr></table></figure></p><p id="div-border-left-red">我们发现，不论是静态资源异常，或者接口异常，错误都无法捕获到。</p><p>补充一点：<code>window.onerror</code> 函数只有在返回 true 的时候，异常才不会向上抛出，否则即使是知道异常的发生控制台还是会显示 <code>Uncaught Error: xxxxx</code></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">window</span>.onerror = <span class="function"><span class="keyword">function</span>(<span class="params">message, source, lineno, colno, error</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'捕获到异常：'</span>,&#123;message, source, lineno, colno, error&#125;);</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">setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  Jartto;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>控制台就不会再有这样的错误了：<br><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">Uncaught <span class="built_in">ReferenceError</span>: Jartto is not defined</span><br><span class="line">    at setTimeout ((index):<span class="number">36</span>)</span><br></pre></td></tr></table></figure></p><font color="#ff8f00">需要注意：</font><ul><li>onerror 最好写在所有 JS 脚本的前面，否则有可能捕获不到错误；</li><li>onerror 无法捕获语法错误；</li></ul><p id="div-border-top-red">到这里基本就清晰了：在实际的使用过程中，<code>onerror</code> 主要是来捕获预料之外的错误，而 <code>try-catch</code> 则是用来在可预见情况下监控特定的错误，两者结合使用更加高效。</p><p id="div-border-left-yellow">问题又来了，捕获不到静态资源加载异常怎么办？</p><h1 id="五、window-addEventListener"><a href="#五、window-addEventListener" class="headerlink" title="五、window.addEventListener"></a>五、window.addEventListener</h1><p>当一项资源（如图片或脚本）加载失败，加载资源的元素会触发一个 Event 接口的 error 事件，并执行该元素上的 <code>onerror()</code> 处理函数。这些 error 事件不会向上冒泡到 window ，不过（至少在 Firefox 中）能被单一的 <code>window.addEventListener</code> 捕获。<br><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">&lt;scritp&gt;</span><br><span class="line"><span class="built_in">window</span>.addEventListener(<span class="string">'error'</span>, (error) =&gt; &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'捕获到异常：'</span>, error);</span><br><span class="line">&#125;, <span class="literal">true</span>)</span><br><span class="line">&lt;<span class="regexp">/script&gt;</span></span><br><span class="line"><span class="regexp">&lt;img src="./</span>jartto.png<span class="string">"&gt;</span></span><br></pre></td></tr></table></figure></p><p>控制台输出：<br><img src="/images/js-exception-handing-2.png" alt=""></p><p>由于网络请求异常不会事件冒泡，因此必须在捕获阶段将其捕捉到才行，但是这种方式虽然可以捕捉到网络请求的异常，但是无法判断 HTTP 的状态是 404 还是其他比如 500 等等，所以还需要配合服务端日志才进行排查分析才可以。</p><font color="red">需要注意：</font><ul><li>不同浏览器下返回的 error 对象可能不同，需要注意兼容处理。</li><li>需要注意避免 addEventListener 重复监听。</li></ul><h1 id="六、Promise-Catch"><a href="#六、Promise-Catch" class="headerlink" title="六、Promise Catch"></a>六、Promise Catch</h1><p id="div-border-top-blue">在 promise 中使用 catch 可以非常方便的捕获到异步 error ，这个很简单。</p><p>没有写 catch 的 Promise 中抛出的错误无法被 onerror 或 try-catch 捕获到，所以我们务必要在 Promise 中不要忘记写 catch 处理抛出的异常。</p><p>解决方案： 为了防止有漏掉的 Promise 异常，建议在全局增加一个对 <code>unhandledrejection</code>的监听，用来全局监听 <code>Uncaught Promise Error</code>。使用方式：<br><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="built_in">window</span>.addEventListener(<span class="string">"unhandledrejection"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">e</span>)</span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(e);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p><p>我们继续来尝试一下：<br><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="built_in">window</span>.addEventListener(<span class="string">"unhandledrejection"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">e</span>)</span>&#123;</span><br><span class="line">  e.preventDefault()</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'捕获到异常：'</span>, e);</span><br><span class="line">  <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;);</span><br><span class="line"><span class="built_in">Promise</span>.reject(<span class="string">'promise error'</span>);</span><br></pre></td></tr></table></figure></p><p>可以看到如下输出：<br><img src="/images/js-exception-handing-3.png" alt=""></p><p>那如果对 Promise 不进行 catch 呢？<br><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">window</span>.addEventListener(<span class="string">"unhandledrejection"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">e</span>)</span>&#123;</span><br><span class="line">  e.preventDefault()</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'捕获到异常：'</span>, e);</span><br><span class="line">  <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;);</span><br><span class="line"><span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">  reject(<span class="string">'jartto: promise error'</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p><p>嗯，事实证明，也是会被正常捕获到的。</p><p>所以，正如我们上面所说，为了防止有漏掉的 Promise 异常，建议在全局增加一个对 <code>unhandledrejection</code> 的监听，用来全局监听 <code>Uncaught Promise Error</code>。</p><p>补充一点：如果去掉控制台的异常显示，需要加上：<br><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">event.preventDefault();</span><br></pre></td></tr></table></figure></p><h1 id="七、VUE-errorHandler"><a href="#七、VUE-errorHandler" class="headerlink" title="七、VUE errorHandler"></a>七、VUE errorHandler</h1><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">Vue.config.errorHandler = <span class="function">(<span class="params">err, vm, info</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="built_in">console</span>.error(<span class="string">'通过vue errorHandler捕获的错误'</span>);</span><br><span class="line">  <span class="built_in">console</span>.error(err);</span><br><span class="line">  <span class="built_in">console</span>.error(vm);</span><br><span class="line">  <span class="built_in">console</span>.error(info);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="八、React-异常捕获"><a href="#八、React-异常捕获" class="headerlink" title="八、React 异常捕获"></a>八、React 异常捕获</h1><p>React 16 提供了一个内置函数 componentDidCatch，使用它可以非常简单的获取到 react 下的错误信息<br><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">componentDidCatch(error, info) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(error, info);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>除此之外，我们可以了解一下：<code>error boundary</code><br>UI 的某部分引起的 JS 错误不应该破坏整个程序，为了帮 React 的使用者解决这个问题，React 16 介绍了一种关于错误边界（error boundary)的新观念。</p><p id="div-border-left-red">需要注意的是： error boundaries 并不会捕捉下面这些错误。</p><p>1.事件处理器<br>2.异步代码<br>3.服务端的渲染代码<br>4.在 error boundaries 区域内的错误</p><p>我们来举一个小例子，在下面这个 <code>componentDIdCatch(error,info)</code> 里的类会变成一个 <code>error boundary</code>：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ErrorBoundary</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(props) &#123;</span><br><span class="line">    <span class="keyword">super</span>(props);</span><br><span class="line">    <span class="keyword">this</span>.state = &#123; <span class="attr">hasError</span>: <span class="literal">false</span> &#125;;</span><br><span class="line">  &#125;</span><br><span class="line"> </span><br><span class="line">  componentDidCatch(error, info) &#123;</span><br><span class="line">    <span class="comment">// Display fallback UI</span></span><br><span class="line">    <span class="keyword">this</span>.setState(&#123; <span class="attr">hasError</span>: <span class="literal">true</span> &#125;);</span><br><span class="line">    <span class="comment">// You can also log the error to an error reporting service</span></span><br><span class="line">    logErrorToMyService(error, info);</span><br><span class="line">  &#125;</span><br><span class="line"> </span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">this</span>.state.hasError) &#123;</span><br><span class="line">      <span class="comment">// You can render any custom fallback UI</span></span><br><span class="line">      <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">h1</span>&gt;</span>Something went wrong.<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>.props.children;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>然后我们像使用普通组件那样使用它：<br><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">&lt;ErrorBoundary&gt;</span><br><span class="line">  &lt;MyWidget /&gt;</span><br><span class="line">&lt;<span class="regexp">/ErrorBoundary&gt;</span></span><br></pre></td></tr></table></figure></p><p><code>componentDidCatch()</code> 方法像 JS 的 <code>catch{}</code> 模块一样工作，但是对于组件，只有 class 类型的组件<code>( class component )</code>可以成为一个 <code>error boundaries</code>。</p><p>实际上，大多数情况下我们可以在整个程序中定义一个 <code>error boundary</code>组件，之后就可以一直使用它了！</p><h1 id="九、iframe-异常"><a href="#九、iframe-异常" class="headerlink" title="九、iframe 异常"></a>九、iframe 异常</h1><p>对于 iframe 的异常捕获，我们还得借力 <code>window.onerror</code>：<br><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="built_in">window</span>.onerror = <span class="function"><span class="keyword">function</span>(<span class="params">message, source, lineno, colno, error</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'捕获到异常：'</span>,&#123;message, source, lineno, colno, error&#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>一个简单的例子可能如下：<br><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">&lt;iframe src=<span class="string">"./iframe.html"</span> frameborder=<span class="string">"0"</span>&gt;<span class="xml"><span class="tag">&lt;/<span class="name">iframe</span>&gt;</span></span></span><br><span class="line">&lt;script&gt;</span><br><span class="line">  <span class="built_in">window</span>.frames[<span class="number">0</span>].onerror = <span class="function"><span class="keyword">function</span> (<span class="params">message, source, lineno, colno, error</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'捕获到 iframe 异常：'</span>,&#123;message, source, lineno, colno, error&#125;);</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">&lt;<span class="regexp">/script&gt;</span></span><br></pre></td></tr></table></figure></p><h1 id="十、Script-error"><a href="#十、Script-error" class="headerlink" title="十、Script error"></a>十、Script error</h1><p>一般情况，如果出现 <code>script error</code> 这样的错误，基本上可以确定是出现了跨域问题。这时候，是不会有其他太多辅助信息的，但是解决思路无非如下：</p><p id="div-border-left-blue">跨源资源共享机制( CORS )：我们为 script 标签添加 crossOrigin 属性。</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">&lt;script src=<span class="string">"http://jartto.wang/main.js"</span> crossorigin&gt;<span class="xml"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span></span><br></pre></td></tr></table></figure><p>或者动态去添加 js 脚本：<br><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">const</span> script = <span class="built_in">document</span>.createElement(<span class="string">'script'</span>);</span><br><span class="line">script.crossOrigin = <span class="string">'anonymous'</span>;</span><br><span class="line">script.src = url;</span><br><span class="line"><span class="built_in">document</span>.body.appendChild(script);</span><br></pre></td></tr></table></figure></p><p id="div-border-left-yellow">特别注意，服务器端需要设置：Access-Control-Allow-Origin </p><p>此外，我们也可以试试这个 - <a href="https://juejin.im/post/5c00a405f265da610e7fd024" target="_blank" rel="external">解决 Script Error 的另类思路</a>：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> originAddEventListener = EventTarget.prototype.addEventListener;</span><br><span class="line">EventTarget.prototype.addEventListener = <span class="function"><span class="keyword">function</span> (<span class="params">type, listener, options</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> wrappedListener = <span class="function"><span class="keyword">function</span> (<span class="params">...args</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">return</span> listener.apply(<span class="keyword">this</span>, args);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">catch</span> (err) &#123;</span><br><span class="line">      <span class="keyword">throw</span> err;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> originAddEventListener.call(<span class="keyword">this</span>, type, wrappedListener, options);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>简单解释一下：</p><ul><li>改写了 EventTarget 的 addEventListener 方法；</li><li>对传入的 listener 进行包装，返回包装过的 listener，对其执行进行 try-catch；</li><li>浏览器不会对 try-catch 起来的异常进行跨域拦截，所以 catch 到的时候，是有堆栈信息的；</li><li>重新 throw 出来异常的时候，执行的是同域代码，所以 window.onerror 捕获的时候不会丢失堆栈信息；</li></ul><p>利用包装 addEventListener，我们还可以达到「扩展堆栈」的效果：<br><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="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">   <span class="keyword">const</span> originAddEventListener = EventTarget.prototype.addEventListener;</span><br><span class="line">   EventTarget.prototype.addEventListener = <span class="function"><span class="keyword">function</span> (<span class="params">type, listener, options</span>) </span>&#123;</span><br><span class="line">+    <span class="comment">// 捕获添加事件时的堆栈</span></span><br><span class="line">+    <span class="keyword">const</span> addStack = <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">`Event (<span class="subst">$&#123;type&#125;</span>)`</span>).stack;</span><br><span class="line">     <span class="keyword">const</span> wrappedListener = <span class="function"><span class="keyword">function</span> (<span class="params">...args</span>) </span>&#123;</span><br><span class="line">       <span class="keyword">try</span> &#123;</span><br><span class="line">         <span class="keyword">return</span> listener.apply(<span class="keyword">this</span>, args);</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="keyword">catch</span> (err) &#123;</span><br><span class="line">+        <span class="comment">// 异常发生时，扩展堆栈</span></span><br><span class="line">+        err.stack += <span class="string">'\n'</span> + addStack;</span><br><span class="line">         <span class="keyword">throw</span> err;</span><br><span class="line">       &#125;</span><br><span class="line">     &#125;</span><br><span class="line">     <span class="keyword">return</span> originAddEventListener.call(<span class="keyword">this</span>, type, wrappedListener, options);</span><br><span class="line">   &#125;</span><br><span class="line"> &#125;)();</span><br></pre></td></tr></table></figure></p><h1 id="十一、崩溃和卡顿"><a href="#十一、崩溃和卡顿" class="headerlink" title="十一、崩溃和卡顿"></a>十一、崩溃和卡顿</h1><p>卡顿也就是网页暂时响应比较慢， JS 可能无法及时执行。但崩溃就不一样了，网页都崩溃了，JS 都不运行了，还有什么办法可以监控网页的崩溃，并将网页崩溃上报呢？</p><blockquote><p>崩溃和卡顿也是不可忽视的，也许会导致你的用户流失。</p></blockquote><p>1.利用 window 对象的 load 和 beforeunload 事件实现了网页崩溃的监控。<br>不错的文章，推荐阅读：<a href="http://jasonjl.me/blog/2015/06/21/taking-action-on-browser-crashes/" target="_blank" rel="external">Logging Information on Browser Crashes</a>。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">window</span>.addEventListener(<span class="string">'load'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  sessionStorage.setItem(<span class="string">'good_exit'</span>, <span class="string">'pending'</span>);</span><br><span class="line">  setInterval(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">      sessionStorage.setItem(<span class="string">'time_before_crash'</span>, <span class="keyword">new</span> <span class="built_in">Date</span>().toString());</span><br><span class="line">  &#125;, <span class="number">1000</span>);</span><br><span class="line">&#125;);</span><br><span class="line"><span class="built_in">window</span>.addEventListener(<span class="string">'beforeunload'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  sessionStorage.setItem(<span class="string">'good_exit'</span>, <span class="string">'true'</span>);</span><br><span class="line">&#125;);</span><br><span class="line"><span class="keyword">if</span>(sessionStorage.getItem(<span class="string">'good_exit'</span>) &amp;&amp;</span><br><span class="line">  sessionStorage.getItem(<span class="string">'good_exit'</span>) !== <span class="string">'true'</span>) &#123;</span><br><span class="line">  <span class="comment">/*</span></span><br><span class="line"><span class="comment">      insert crash logging code here</span></span><br><span class="line"><span class="comment">  */</span></span><br><span class="line">  alert(<span class="string">'Hey, welcome back from your crash, looks like you crashed on: '</span> + sessionStorage.getItem(<span class="string">'time_before_crash'</span>));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>2.基于以下原因，我们可以使用 <code>Service Worker</code> 来实现 <a href="https://juejin.im/entry/5be158116fb9a049c6434f4a?utm_source=gold_browser_extension" target="_blank" rel="external">网页崩溃的监控</a>：</p><ul><li>Service Worker 有自己独立的工作线程，与网页区分开，网页崩溃了，Service Worker 一般情况下不会崩溃；</li><li>Service Worker 生命周期一般要比网页还要长，可以用来监控网页的状态；</li><li>网页可以通过 <code>navigator.serviceWorker.controller.postMessage</code> API 向掌管自己的 SW 发送消息。</li></ul><h1 id="十二、错误上报"><a href="#十二、错误上报" class="headerlink" title="十二、错误上报"></a>十二、错误上报</h1><p>1.通过 Ajax 发送数据<br>因为 Ajax 请求本身也有可能会发生异常，而且有可能会引发跨域问题，一般情况下更推荐使用动态创建 img 标签的形式进行上报。</p><p>2.动态创建 img 标签的形式<br><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="function"><span class="keyword">function</span> <span class="title">report</span>(<span class="params">error</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> reportUrl = <span class="string">'http://jartto.wang/report'</span>;</span><br><span class="line">  <span class="keyword">new</span> Image().src = <span class="string">`<span class="subst">$&#123;reportUrl&#125;</span>?logs=<span class="subst">$&#123;error&#125;</span>`</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>收集异常信息量太多，怎么办？实际中，我们不得不考虑这样一种情况：如果你的网站访问量很大，那么一个必然的错误发送的信息就有很多条，这时候，我们需要设置采集率，从而减缓服务器的压力：<br><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">Reporter.send = <span class="function"><span class="keyword">function</span>(<span class="params">data</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 只采集 30%</span></span><br><span class="line">  <span class="keyword">if</span>(<span class="built_in">Math</span>.random() &lt; <span class="number">0.3</span>) &#123;</span><br><span class="line">    send(data)      <span class="comment">// 上报错误信息</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>采集率应该通过实际情况来设定，随机数，或者某些用户特征都是不错的选择。<font color="red">【这个随机数用的不错！】</font></p><h1 id="十三、总结"><a href="#十三、总结" class="headerlink" title="十三、总结"></a>十三、总结</h1><p id="div-border-left-yellow">回到我们开头提出的那个问题，如何优雅的处理异常呢？</p><p>1.可疑区域增加 Try-Catch<br>2.全局监控 JS 异常 window.onerror<br>3.全局监控静态资源异常 window.addEventListener<br>4.捕获没有 Catch 的 Promise 异常：unhandledrejection<br>5.VUE errorHandler 和 React componentDidCatch<br>6.监控网页崩溃：window 对象的 load 和 beforeunload<br>7.跨域 crossOrigin 解决</p><p>其实很简单，正如本文所说：采用组合方案，分类型的去捕获异常，这样基本 80%-90% 的问题都化于无形。</p><p><a href="http://jartto.wang/2018/11/20/js-exception-handling/" target="_blank" rel="external">原文</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;前端一直是距离用户最近的一层，随着产品的日益完善，我们会更加注重用户体验，而前端异常却如鲠在喉，甚是烦人。&lt;/p&gt;
&lt;h1 id=&quot;一、为什么要处理异常？&quot;&gt;&lt;a href=&quot;#一、为什么要处理异常？&quot; class=&quot;headerlink&quot; title=&quot;一、为什么要处理异常？&quot;&gt;&lt;/a&gt;一、为什么要处理异常？&lt;/h1&gt;&lt;p id=&quot;div-border-left-green&quot;&gt;异常是不可控的，会影响最终的呈现结果，但是我们有充分的理由去做这样的事情。&lt;/p&gt;

&lt;p&gt;1.增强用户体验；&lt;br&gt;2.远程定位问题；&lt;br&gt;3.未雨绸缪，及早发现问题；&lt;br&gt;4.无法复线问题，尤其是移动端，机型，系统都是问题；&lt;br&gt;5.完善的前端方案，前端监控系统；&lt;/p&gt;
    
    </summary>
    
      <category term="前端" scheme="https://309893147.github.io/categories/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="JavaScript" scheme="https://309893147.github.io/tags/JavaScript/"/>
    
  </entry>
  
  <entry>
    <title>手把手教你 Vue 服务端渲染</title>
    <link href="https://309893147.github.io/2019/03/31/vue-ssr/"/>
    <id>https://309893147.github.io/2019/03/31/vue-ssr/</id>
    <published>2019-03-30T23:25:24.000Z</published>
    <updated>2019-07-30T01:31:38.217Z</updated>
    
    <content type="html"><![CDATA[<h1 id="序"><a href="#序" class="headerlink" title="序"></a>序</h1><p>在写这篇文章之前，我有写一篇 <a href="https://neveryu.github.io/2018/06/18/vue-prerender/" target="_blank" rel="external">Vue 预渲染的教程</a> 以及 <a href="https://neveryu.github.io/prerender-website/index.html" target="_blank" rel="external">在线示例</a>，有需要的可以看一下~</p><hr><font color="green">【下面开始 Vue 服务端渲染】</font><a id="more"></a><p>服务端渲染 = SSR = Server-Side Rendering</p><p><a href="https://ssr.vuejs.org/zh/" target="_blank" rel="external">Vue 服务器渲染</a> 可以说是我们学习 Vue 技术的最后一个环节了；也是上手难度稍为高一点的一个环节。</p><p>目前还没有发现很好的学习资料或者教程，文档也不是特别明白，这也导致了很多人没能拿下 vue 的 ssr。</p><p>所以就想着写一个曲线平滑，循序渐进，明了易懂的 <a href="https://github.com/Neveryu/vue-ssr-lessons" target="_blank" rel="external">教程</a> 来帮助大家找到 Vue SSR 的感觉。</p><h1 id="写在前面"><a href="#写在前面" class="headerlink" title="写在前面"></a>写在前面</h1><p><font color="red">由于内容较多，如果只写一篇文章就想把 Vue SSR 介绍清晰透彻的话，我觉得不太现实；</font>所以就想着把一个完整的 Vue SSR 项目细分开来，每一个小节讲解一个知识点，这样效果应该会好一点吧。这个项目虽然不大，但已经包含了 Vue SSR 的所有内容。</p><p id="div-border-top-green">项目仓库：<a href="https://github.com/Neveryu/vue-ssr-lessons" target="_blank" rel="external">https://github.com/Neveryu/vue-ssr-lessons</a><br></p><p>1、<a href="https://github.com/Neveryu/vue-ssr-lessons" target="_blank" rel="external">这个教程</a> 分为 7 个小节，每个小节都是一个独立的可以运行的小项目，这样可以减少大家出错的概率（如果只给出最终的代码，那万一又跑不起来，岂不凉凉，影响大家学习的心情）；每个小节简单配备了 <strong>运行步骤</strong> 以及 <strong>简要说明</strong> 来帮助大家运行项目以及了解本节的知识点。</p><p><img src="/images/vue-ssr-1.png" alt=""></p><p>2、其次，每个小节都是在前一小节的基础上，继续补充写代码的，这样，大家对比就能知道，这一节具体增加了哪些代码，哪些内容；方便大家学习某一个小节的知识（如果笼统的一次性给出最终代码，这样在找代码之间逻辑关系时，比较吃力）</p><h1 id="章节介绍"><a href="#章节介绍" class="headerlink" title="章节介绍"></a>章节介绍</h1><p>第一节：一个最简单的服务端渲染原型（10 行代码）【难度：<i class="fa fa-star"></i>】<br>第二节：与服务器集成，使用 Express 作为服务器提供服务【难度：<i class="fa fa-star"></i>】<br>第三节：使用一个页面模版【难度：<i class="fa fa-star"></i>】<br>第四节：开发客户端与服务端入口文件，配置webpack【难度：<i class="fa fa-star"></i><i class="fa fa-star"></i><i class="fa fa-star"></i>】<br>第五节：使用vue-router来做路由【难度：<i class="fa fa-star"></i><i class="fa fa-star"></i>】<br>第六节：数据，vuex，状态容器【难度：<i class="fa fa-star"></i><i class="fa fa-star"></i><i class="fa fa-star"></i><i class="fa fa-star"></i><i class="fa fa-star"></i>】<br>第七节：增加一些额外的功能，完善项目【难度：<i class="fa fa-star"></i><i class="fa fa-star"></i>】</p><h1 id="如何学习"><a href="#如何学习" class="headerlink" title="如何学习"></a>如何学习</h1><p>1、建议你先看一遍 Vue SSR 的文档，<em>看不懂的地方不要慌，留个印象也行</em><br>2、学习这个课程的时候，打开 Vue SSR 的文档；找到当前这一小节对应文档中的文字介绍部分<br>3、如果你基础有点薄弱的话，不要太过于着急<br>4、不能保证所有人看一遍就能学会，但是能保证所有人，两遍能拿下<br>5、如果你能跟着动手敲的话，将会事半功倍</p><h1 id="知识点"><a href="#知识点" class="headerlink" title="知识点"></a>知识点</h1><p><strong>1、避免单例状态</strong><br>在 <code>app.js</code> 中，暴露一个可以重复执行的工厂函数，为每个请求创建新的应用程序实例。</p><p><a href="https://ssr.vuejs.org/zh/guide/structure.html#%E9%81%BF%E5%85%8D%E7%8A%B6%E6%80%81%E5%8D%95%E4%BE%8B" target="_blank" rel="external">相关文档</a></p><p><img src="/images/vue-ssr-2.png" alt=""></p><p><strong>2、配置webpack</strong><br><code>webpack</code> 配置文件包含：基本配置(base config)、客户端配置(client config)、服务器配置(server config)。<br>基本配置包含两个环境（客户端环境，服务器环境）共享的配置；然后客户端配置和服务器配置都会通过使用 <code>webpack-merge</code> 来简单的扩展基本配置。</p><p><a href="https://ssr.vuejs.org/zh/guide/build-config.html" target="_blank" rel="external">相关文档</a></p><blockquote><p>教程中的 webpack 相关的配置已经配置好了，你可以直接全部拿过来用就行了</p></blockquote><p><strong>3、createBundleRenderer</strong><br>我们在前三节使用的都是 <code>vue-server-renderer</code> 中的 <code>createRenderer</code> 方法；从第四节开始，我们使用的是 <code>createBundleRenderer</code>，所创建的 <code>bundle renderer</code>，用法和普通 <code>renderer</code> 相同。 <code>createBundleRenderer</code> 接收一个 <code>server bundle</code> 生成的特殊 <code>JSON</code> 文件。但是 <code>bundle renderer</code> 提供以下优点：</p><ul><li>内置的 <code>source map</code> 支持（在 <code>webpack</code> 配置中使用 <code>devtool: &#39;source-map&#39;</code>）</li><li>在开发环境甚至部署过程中热重载（通过读取更新后的 <code>bundle</code>，然后重新创建 <code>renderer</code> 实例）</li><li>关键 <code>CSS(critical CSS)</code> 注入（在使用 <code>*.vue</code> 文件时）：自动内联在渲染过程中用到的组件所需的 <code>CSS</code>。更多细节请查看 <code>CSS</code> 章节。</li><li>使用 <code>clientManifest</code> 进行资源注入：自动推断出最佳的预加载(<code>preload</code>)和预取(<code>prefetch</code>)指令，以及初始渲染所需的代码分割 <code>chunk</code>。</li></ul><p><a href="https://ssr.vuejs.org/zh/guide/bundle-renderer.html" target="_blank" rel="external">相关文档</a></p><h1 id="联系我"><a href="#联系我" class="headerlink" title="联系我"></a>联系我</h1><p>当然，如果你有任何建议或者疑问，欢迎联系我~</p><p>QQ 群：685486827 ，<a target="_blank" style="color: red;" href="//shang.qq.com/wpa/qunwpa?idkey=32da7a18744756b0d8ffdd05b84999afecb5265dbad0fb119033e122abe803f3">一键加群</a></p>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;序&quot;&gt;&lt;a href=&quot;#序&quot; class=&quot;headerlink&quot; title=&quot;序&quot;&gt;&lt;/a&gt;序&lt;/h1&gt;&lt;p&gt;在写这篇文章之前，我有写一篇 &lt;a href=&quot;https://neveryu.github.io/2018/06/18/vue-prerender/&quot;&gt;Vue 预渲染的教程&lt;/a&gt; 以及 &lt;a href=&quot;https://neveryu.github.io/prerender-website/index.html&quot;&gt;在线示例&lt;/a&gt;，有需要的可以看一下~&lt;/p&gt;
&lt;hr&gt;
&lt;font color=&quot;green&quot;&gt;【下面开始 Vue 服务端渲染】&lt;/font&gt;
    
    </summary>
    
      <category term="前端" scheme="https://309893147.github.io/categories/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="Vue" scheme="https://309893147.github.io/tags/Vue/"/>
    
  </entry>
  
  <entry>
    <title>使用 Travis CI 自动更新 GitHub Pages</title>
    <link href="https://309893147.github.io/2019/02/05/travis-ci/"/>
    <id>https://309893147.github.io/2019/02/05/travis-ci/</id>
    <published>2019-02-04T23:25:24.000Z</published>
    <updated>2019-07-30T01:31:38.215Z</updated>
    
    <content type="html"><![CDATA[<p><a href="https://travis-ci.org/" target="_blank" rel="external">Travis CI</a> 提供的是持续集成服务（Continuous Integration，简称 CI）。我们在软件开发过程中，有构建、测试、部署这些必不可少的步骤，而这些会花掉我们很多的时间。为了提高软件开发的效率，现在涌现了很多自动化工具。<a href="https://travis-ci.org/" target="_blank" rel="external">Travis CI</a> 是目前<a href="https://github.blog/2017-11-07-github-welcomes-all-ci-tools/" target="_blank" rel="external">市场份额</a>最大的一个，而且有很详细的文档以及可以和 Github 很好的对接。</p><a id="more"></a><p>Travis CI 还是很强大的，用好这个工具不仅可以提高效率，还能使开发流程更可靠和专业化。</p><p>就我写的 <a href="https://neveryu.github.io/web-bookmarks/" target="_blank" rel="external">web-bookmarks</a> 这个项目来说的话，每次更改完都要手动部署到 GitHub Pages。</p><p>从最开始的手动构建部署：手动敲构建命令，然后手动推到 GitHub。(<em>重复的次数多了就显得很麻烦，出错的几率也会变大。</em>)</p><p>后来写了一个构建部署的脚本：每次开发完，再手动执行脚本，完成构建部署。</p><p>再到现在的使用 CI 自动更新：开发完，我只用将源码 push 到 GitHub 做版本管理，就 ok 了；Travis 监测到代码有变化，然后就会自动执行我们设定好的任务。【优秀～】</p><h1 id="一、什么是持续集成"><a href="#一、什么是持续集成" class="headerlink" title="一、什么是持续集成"></a>一、什么是持续集成</h1><p>Travis CI 提供的持续集成服务（Continuous Integration，简称 CI）。它绑定 Github 上面的项目，只要有新的代码，就会自动抓取。然后，提供一个运行环境，执行测试，完成构建，还能部署到服务器。</p><p>持续集成指的是只要代码有变更，就自动运行构建和测试，反馈运行结果。</p><p id="div-border-top-purple">举一个例子：我们可以在我们的开源项目中，安排一个代码格式检查和测试的任务 <code>npm run test</code>，不管是自己提交代码，还是别人提交的 PR，Travis 监测到代码有新的内容，都会来执行这个任务。【不管代码写的怎样，格式一定不能乱～，哈哈😄】<br></p><p>持续集成的好处在于，每次代码的小幅变更，就能看到运行结果，从而不断累积小的变更，而不是在开发周期结束时，一下子合并一大块代码。</p><h1 id="二、开始使用"><a href="#二、开始使用" class="headerlink" title="二、开始使用"></a>二、开始使用</h1><p>首先打开官方网站 <a href="https://travis-ci.org" target="_blank" rel="external">travis-ci.org</a>，然后使用 Github 账号登入 Travis CI，然后 Travis 中会列出你 Github 上面所有的仓库，以及你所属于的组织。</p><p>然后，勾选你需要 Travis 帮你自动构建的仓库，打开仓库旁边的开关，打开以后，Travis 就会监听这个仓库的所有变化了。</p><p><img src="/images/travis-ci-1.png" alt="travis-ci-1"></p><h1 id="三、-travis-yml"><a href="#三、-travis-yml" class="headerlink" title="三、.travis.yml"></a>三、.travis.yml</h1><p>Travis 要求项目的根目录下面，必须有一个 <code>.travis.yml</code> 文件。这是配置文件，指定了 Travis 的行为。该文件必须保存在 Github 仓库里面，一旦代码仓库有新的 <code>Commit</code>，Travis 就会去找这个文件，执行里面的命令。</p><p>所以呢，我们就可以在这个文件里，配置我们任务（Travis 监测到仓库有 <code>commit</code> 后会自动执行）。</p><p>一个简单的 <code>.travis.yml</code> 文件如下：<br><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">language:</span> <span class="string">node_js</span></span><br><span class="line"><span class="attr">script:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure></p><p>所以呢，我在 <code>.travis.yml</code> 里，配置了一个执行脚本的任务；那么现在 Travis 监测到我仓库有 <code>commit</code> 后就会找到 <code>.travis.yml</code> 这个文件，然后就执行了我的那个脚本了。</p><h2 id="install-字段"><a href="#install-字段" class="headerlink" title="install 字段"></a>install 字段</h2><p><code>install</code> 字段用来指定安装脚本，如果有多个脚本，可以写成下面的形式。<br><figure class="highlight yml"><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="attr">install:</span></span><br><span class="line"><span class="bullet">  -</span> <span class="string">command1</span></span><br><span class="line"><span class="bullet">  -</span> <span class="string">command2</span></span><br></pre></td></tr></table></figure></p><p>上面代码中，如果 <code>command1</code> 失败了，整个构建就会停下来，不再往下进行<br>如果不需要安装，即跳过安装阶段，就直接设为 <code>true</code>。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">install: <span class="literal">true</span></span><br></pre></td></tr></table></figure></p><h2 id="script-字段"><a href="#script-字段" class="headerlink" title="script 字段"></a>script 字段</h2><p><code>script</code> 字段用来配置构建或者测试脚本，如果有多个脚本，可以写成下面的形式。<br><figure class="highlight yml"><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="attr">script:</span></span><br><span class="line"><span class="bullet">  -</span> <span class="string">command1</span></span><br><span class="line"><span class="bullet">  -</span> <span class="string">command2</span></span><br></pre></td></tr></table></figure></p><p>注意，<code>script</code> 与 <code>install</code> 不一样，如果 <code>command1</code> 失败，<code>command2</code> 会继续执行。但是，整个构建阶段的状态是失败。</p><p>如果 <code>command2</code> 只有在 <code>command1</code> 成功后才能执行，就要写成下面这样。<br><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">script:</span> <span class="string">command1</span> <span class="string">&amp;&amp;</span> <span class="string">command2</span></span><br></pre></td></tr></table></figure></p><h1 id="四、部署时面临的问题"><a href="#四、部署时面临的问题" class="headerlink" title="四、部署时面临的问题"></a>四、部署时面临的问题</h1><p id="div-border-left-red">现在脚本是由 Travis CI 来执行的，部署的时候，怎么让 Travis 有权限往 Github 提交代码呢？</p><p>Github 有提供一个 <a href="https://github.blog/2013-05-16-personal-api-tokens/" target="_blank" rel="external">Personal access tokens</a>，这个 Token 与 账号密码 以及 SSH Keys 同样具有 Github 写入能力。</p><p>前往 Github 帐号 Settings 页面，在左侧选择 <code>Personal Access Token</code>，然后在右侧面板点击 <code>“Generate new token”</code> 来新建一个 Token。需要注意的是，创建完的 Token 只有第一次可见，之后再访问就无法看见（只能看见他的名称），因此要保存好这个值。</p><p><img src="/images/travis-ci-2.png" alt="travis-ci-2"></p><p>那么，这个 Token 怎么使用呢。</p><h2 id="方案一、"><a href="#方案一、" class="headerlink" title="方案一、"></a>方案一、</h2><p>一个比较方便快捷的方式，是通过 Travis 网站，写在每个仓库的设置页面里，有一个 <code>Environment Variables</code> 的配置项，给我们的 Token 起一个名字 <code>gh_token</code> 添加进去。这样以来，脚本内部就可以使用这个环境变量了。<br><img src="/images/travis-ci-3.png" alt="travis-ci-1"><br>你可以在你脚本内部使用 <code>${gh_token}</code> 的形式来使用这个 Token 了。【当然了，你还可以添加其他的环境变量进去。】【<a href="https://docs.travis-ci.com/user/environment-variables" target="_blank" rel="external">官方文档在这里</a>】</p><p>使用 <code>Personal access tokens</code> 向 GitHub 提交代码的命令格式如下：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># $&#123;GH_TOKEN&#125; 对应就是 Personal access tokens ， GH_TOKEN 是环境变量名</span></span><br><span class="line"><span class="comment"># $&#123;GH_REF&#125; 对应的是你的 Github 仓库地址，GH_REF 是变量名</span></span><br><span class="line">git push -f <span class="string">"https://<span class="variable">$&#123;GH_TOKEN&#125;</span>@<span class="variable">$&#123;GH_REF&#125;</span>"</span> master:gh-pages</span><br></pre></td></tr></table></figure></p><p id="div-border-left-green">这里需要注意的是：<br>1、GitHub 生成的这个 Token ，只有生成的时候可以看到明文，后面就看不到明文了，所以你使用的时候最好一次操作成功。<br>2、Travis CI 中添加 Token 时，记得用密文，要不然在 <code>build log</code> 中是可以被看到的。<br></p><h2 id="方案二、"><a href="#方案二、" class="headerlink" title="方案二、"></a>方案二、</h2><p>你还可以使用 Travis CI 提供的加密工具来加密我们的这个 Token。加密原理机制如下：</p><p><img src="/images/travis-encrypt.png" alt="travis-ci-encrypt"></p><p>首先，安装 Ruby 的包 <code>travis</code> 。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装 Travis CI 命令行工具</span></span><br><span class="line">$ gem install travis</span><br></pre></td></tr></table></figure></p><p>然后，就可以用 <code>travis encrypt</code> 命令加密信息。<br>在项目的根目录下，执行下面的命令。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ travis encrypt name=secretvalue</span><br></pre></td></tr></table></figure></p><p>上面命令中，<code>gh_token</code> 是要加密的变量名，<code>secretvalue</code> 是要加密的变量值。执行以后，屏幕上会输出如下信息。<br><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">secure: &quot;... encrypted data ...&quot;</span><br></pre></td></tr></table></figure></p><p>现在，就可以把这一行加入 <code>.travis.yml</code> 。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">env:</span><br><span class="line">  global:</span><br><span class="line">    - GH_REF: github.com/Neveryu/xxxxx.git</span><br><span class="line">    - secure: <span class="string">"... entrypted data ..."</span></span><br></pre></td></tr></table></figure></p><p>然后，脚本里面就可以使用环境变量 <code>gh_token</code> 了，Travis 会在运行时自动对它解密。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># $&#123;gh_token&#125; 对应就是 Personal access tokens ， gh_token 是环境变量名</span></span><br><span class="line"><span class="comment"># $&#123;GH_REF&#125; 对应的是你的 Github 仓库地址，GH_REF 是变量名</span></span><br><span class="line">git push -f <span class="string">"https://<span class="variable">$&#123;gh_token&#125;</span>@<span class="variable">$&#123;GH_REF&#125;</span>"</span> master:gh-pages</span><br></pre></td></tr></table></figure><p><code>travis encrypt</code> 命令的 <code>--add</code> 参数会把输出自动写入 <code>.travis.yml</code>，省掉了修改 <code>env</code> 字段的步骤。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ travis encrypt name=secretvalue --add</span><br></pre></td></tr></table></figure></p><p>详细信息请看<a href="https://docs.travis-ci.com/user/encryption-keys/" target="_blank" rel="external">官方文档</a></p><p id="div-border-top-red">可以参考我的 <a href="https://github.com/Neveryu/vue-cms" target="_blank" title="vue-cms">vue-cms</a> 这个项目中的 <code>.travis.yml</code> 文件</p><h1 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h1><h2 id="如何显示-Status-Image"><a href="#如何显示-Status-Image" class="headerlink" title="如何显示 Status Image"></a>如何显示 Status Image</h2><p><a href="https://travis-ci.org/Neveryu/web-bookmarks" target="_blank" rel="external"><img src="https://travis-ci.org/Neveryu/web-bookmarks.svg?branch=master" alt="Build Status"></a></p><p><img src="/images/travis-ci-4.png" alt="travis-ci-4"></p><h2 id="如何跳过自动构建"><a href="#如何跳过自动构建" class="headerlink" title="如何跳过自动构建"></a>如何跳过自动构建</h2><p>如果 commit 不想让 Travis 构建，那么就在 commit message 里加上 [ci skip] 就行了。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git commit -m <span class="string">"[ci skip] commit message"</span></span><br></pre></td></tr></table></figure></p><h2 id="权限问题"><a href="#权限问题" class="headerlink" title="权限问题"></a>权限问题</h2><p>如果遇到脚本权限不够的提示或者问题，你可以给你的脚本加上权限：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">chmod u+x deploy.sh</span><br></pre></td></tr></table></figure></p><p>或者在 <code>.travis.yml</code> 里加：<br><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">before_install:</span></span><br><span class="line"><span class="bullet">  -</span> <span class="string">chmod</span> <span class="string">u+x</span> <span class="string">deploy.sh</span></span><br></pre></td></tr></table></figure></p><h1 id="扩展知识"><a href="#扩展知识" class="headerlink" title="扩展知识"></a>扩展知识</h1><h2 id="Travis-CI-加密文件"><a href="#Travis-CI-加密文件" class="headerlink" title="Travis CI 加密文件"></a>Travis CI 加密文件</h2><p>如果要加密的是文件（比如私钥），Travis 提供了加密文件功能。<br>安装命令行客户端以后，使用下面的命令登入 Travis CI 。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ travis login</span><br></pre></td></tr></table></figure></p><p>然后，进入项目的根目录，使用 <code>travis encrypt-file</code> 命令加密那些想要加密的文件。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">$ travis entrypt-file bacon.txt</span><br><span class="line"></span><br><span class="line">encrypting bacon.txt <span class="keyword">for</span> rkh/travis-encrypt-file-example</span><br><span class="line">storing result as bacon.txt.enc</span><br><span class="line">storing secure env variables <span class="keyword">for</span> decryption</span><br><span class="line"></span><br><span class="line">Please add the following to your build script (before_install stage <span class="keyword">in</span> your .travis.yml, <span class="keyword">for</span> instance):</span><br><span class="line"></span><br><span class="line">    openssl aes-256-cbc -K <span class="variable">$encrypted_0a6446eb3ae3_key</span> -iv <span class="variable">$encrypted_0a6446eb3ae3_key</span> -<span class="keyword">in</span> bacon.txt.enc -out bacon.txt -d</span><br><span class="line"></span><br><span class="line">Pro Tip: You can add it automatically by running with --add.</span><br><span class="line"></span><br><span class="line">Make sure to add bacon.txt.enc to the git repository.</span><br><span class="line">Make sure not to add bacon.txt to the git repository.</span><br><span class="line">Commit all changes to your .travis.yml.</span><br></pre></td></tr></table></figure></p><p>上面的代码对文件 <code>bacon.txt</code> 进行加密，加密后会生成 <code>bacon.txt.enc</code> ，该文件需要提交到代码库。此外，该命令还会生成一个环境变量 <code>$entrypted_0a6446eb3ae3_key</code>，保存密钥，储存在 Travis CI，文件解密时需要这个环境变量。你需要把解密所需的 <code>openssl</code> 命令，写在 <code>.travis.yml</code> 的 <code>before_install</code> 字段里面。这些都写在上面的命令行提示里面。</p><p><code>--add</code> 参数可以自动把环境变量写入 <code>.travis.yml</code> 。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">$ travis encrypt-file bacon.txt --add</span><br><span class="line">entrypting bacon.txt <span class="keyword">for</span> rkh/travis-encrypt-file-example</span><br><span class="line">storing result as bacon.txt.enc</span><br><span class="line">storing secure env variables <span class="keyword">for</span> decryption</span><br><span class="line"></span><br><span class="line">Make sure to add bacon.txt.enc to the git repository.</span><br><span class="line">Make sure not to add bacon.txt to the git repository.</span><br><span class="line">Commit all changes to your .travis.yml.</span><br></pre></td></tr></table></figure><p>详细信息请看<a href="https://docs.travis-ci.com/user/encrypting-files/" target="_blank" rel="external">官方文档</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;a href=&quot;https://travis-ci.org/&quot;&gt;Travis CI&lt;/a&gt; 提供的是持续集成服务（Continuous Integration，简称 CI）。我们在软件开发过程中，有构建、测试、部署这些必不可少的步骤，而这些会花掉我们很多的时间。为了提高软件开发的效率，现在涌现了很多自动化工具。&lt;a href=&quot;https://travis-ci.org/&quot;&gt;Travis CI&lt;/a&gt; 是目前&lt;a href=&quot;https://github.blog/2017-11-07-github-welcomes-all-ci-tools/&quot;&gt;市场份额&lt;/a&gt;最大的一个，而且有很详细的文档以及可以和 Github 很好的对接。&lt;/p&gt;
    
    </summary>
    
      <category term="前端" scheme="https://309893147.github.io/categories/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="CI" scheme="https://309893147.github.io/tags/CI/"/>
    
  </entry>
  
  <entry>
    <title>Hexo-NexT搭建个人博客（六）</title>
    <link href="https://309893147.github.io/2018/10/15/hexo-next-six/"/>
    <id>https://309893147.github.io/2018/10/15/hexo-next-six/</id>
    <published>2018-10-15T15:25:24.000Z</published>
    <updated>2019-07-30T01:31:38.206Z</updated>
    
    <content type="html"><![CDATA[<p>在 hexo 中，我们如何使用自己自定义的 html 页面呢？</p><p>我们知道，在 hexo 中，我们使用的是 markdown 格式的文件，渲染出来的页面是有主题样式的。如果我们不希望我们的页面使用主题样式。那么需要在文件头部加一个 <code>layout: false</code> 的配置。</p><p><strong>使用 md 文件写文章时增加配置不使用 layout </strong></p><a id="more"></a><figure class="highlight md"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><br><span class="line">title: 我来试一下如何禁止解析html</span><br><span class="line">date: 2018-10-04 13:25:24</span><br><span class="line">categories: 综合</span><br><span class="line">tags: [综合]</span><br><span class="line">comments: false</span><br><span class="line">layout: false</span><br><span class="line">---</span><br></pre></td></tr></table></figure><p>这样，我们的文件就不会被主题渲染。</p><hr><p>其实在我们的 hexo 中，是可以直接写 html 文件的，不过也会被渲染，出来的页面还是有主题样式的。如果我们不想要这个主题样式，怎么做呢？</p><p><strong> 使用 <code>skip_render</code> </strong></p><p><code>skip_render</code> 跳过指定文件的渲染，您可使用 <a href="https://github.com/isaacs/node-glob" target="_blank" rel="external">glob 表达式</a> 来匹配路径。   </p><p><code>skip_render</code> 的配置在 <span id="inline-blue">站点配置文件</span> 中。</p><p>只有 <code>source</code> 目录下的文件才会发布到 <code>public</code>（能够在网络上访问到），因此 Hexo 只渲染 <code>source</code> 目录下的文件。<code>skip_render</code> 参数设置的路径是相对于 <code>source</code> 目录的路径。<br><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">skip_render: test.html</span><br></pre></td></tr></table></figure></p><p>注意，千万不要写成<code>/test.html</code>，这里只能填相对于source文件夹的相对路径。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;在 hexo 中，我们如何使用自己自定义的 html 页面呢？&lt;/p&gt;
&lt;p&gt;我们知道，在 hexo 中，我们使用的是 markdown 格式的文件，渲染出来的页面是有主题样式的。如果我们不希望我们的页面使用主题样式。那么需要在文件头部加一个 &lt;code&gt;layout: false&lt;/code&gt; 的配置。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;使用 md 文件写文章时增加配置不使用 layout &lt;/strong&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="Hexo" scheme="https://309893147.github.io/categories/Hexo/"/>
    
    
      <category term="Hexo" scheme="https://309893147.github.io/tags/Hexo/"/>
    
      <category term="Next" scheme="https://309893147.github.io/tags/Next/"/>
    
  </entry>
  
  <entry>
    <title>Yarn安装与使用详细介绍</title>
    <link href="https://309893147.github.io/2018/07/20/yarn/"/>
    <id>https://309893147.github.io/2018/07/20/yarn/</id>
    <published>2018-07-19T19:25:24.000Z</published>
    <updated>2019-07-30T01:31:38.218Z</updated>
    
    <content type="html"><![CDATA[<h1 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h1><p>在 Node 生态系统中，依赖通常安装在项目的 <code>node_modules</code> 文件夹中。然而，这个文件的结构和实际依赖树可能有所区别，因为重复的依赖可以合并到一起。<code>npm</code> 客户端把依赖安装到 <code>node_modules</code> 目录的过程具有不确定性。这意味着当依赖的安装顺序不同时，<code>node_modules</code> 目录的结构可能会发生变化。这种差异可能会导致类似<font color="red">“我的电脑上可以运行，别的电脑上不行”</font>的情况，并且通常需要花费大量时间定为与解决。</p><blockquote><p>有时候就会遇到这种情况，完整可运行的项目上传到 git 上，别人 pull 下来以后，npm install 会报错。</p></blockquote><p><a href="https://github.com/yarnpkg/yarn" target="_blank" rel="external">Yarn</a> 一开始的主要目标是解决由于语义版本控制而导致的 npm 安装的不确定性问题。虽然可以用 <code>npm shrinkwrap</code> 来实现可预测的依赖关系树，但它并不是默认选项，而是取决于所有的开发人员指导并启用这个选项。</p><a id="more"></a><blockquote><p>npm 5+ 以后的版本加入了 package-lock.json 可以用来锁版本，package-lock.json 的名字，一看就懂，更清楚，但是不向后兼容。</p><p>npm-shrinkwrap.json 向后兼容 npm 2-4。</p></blockquote><p><font color="red">举个例子：</font><br>npm 对包引入顺序也十分的敏感，比如在一个空项目里执行以下命令：<br><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">npm init -y</span><br><span class="line">npm install globule@<span class="number">0.1</span><span class="number">.0</span> -S</span><br><span class="line">npm install babel-generator@<span class="number">6.19</span><span class="number">.0</span> -S</span><br><span class="line">npm install babel-helper-define-map@<span class="number">6.18</span><span class="number">.0</span> -S</span><br></pre></td></tr></table></figure></p><p>我们这里安装了 3 个包都依赖于 lodash，不过 globule 依赖 lodash@1.0.3，另外另个依赖 lodash@4.x。<br>现在目录依赖结构如下：<br><img src="https://img-blog.csdn.net/20180824142908356?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NzZG5feXVkb25n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="npm-package"></p><p>这是假设我们在项目里使用 lodash，但是忘记重新安装 lodash<br><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">var</span> lodash = <span class="built_in">require</span>(<span class="string">'lodash'</span>)</span><br><span class="line"><span class="built_in">console</span>.log(lodash.VERSION)  <span class="comment">// v1.0.3</span></span><br></pre></td></tr></table></figure></p><p>另一个同事获取项目代码，执行 <code>npm install</code>，这时的目录依赖结构里面，第一层依赖的 lodash 变成了 4.x 版本，这样就造成了依赖版本不一致的问题。而 yarn 则会保证无论怎样引入的顺序，目录依赖结构都是一致的，确保不会发生这样的BUG。</p><h1 id="什么是-Yarn"><a href="#什么是-Yarn" class="headerlink" title="什么是 Yarn"></a>什么是 Yarn</h1><p>Yarn 就是一个类似于 npm 的包管理工具，它是由 facebook 推出并开源。</p><p>与 npm 相比，yarn 有着众多的优势，主要的优势在于：速度快、离线模式、版本控制。</p><h2 id="速度快"><a href="#速度快" class="headerlink" title="速度快"></a>速度快</h2><p>npm 会等一个包完全安装完才跳到下一个包，但 yarn 会并行执行包，因此速度会快很多。</p><p>Yarn 会缓存它下载的每个包，所以无需重复下载。它还能并行化操作以最大化资源利用率，安装速度之快前所未有。</p><h2 id="离线模式"><a href="#离线模式" class="headerlink" title="离线模式"></a>离线模式</h2><p>之前安装过的包会被保存进缓存目录，以后安装就直接从缓存中复制过来，这样做的本质还是会提高安装下载的速度，避免不必要的网络请求。</p><h2 id="可靠可确定性"><a href="#可靠可确定性" class="headerlink" title="可靠可确定性"></a>可靠可确定性</h2><p>保证各平台依赖的一致性</p><h2 id="网络优化"><a href="#网络优化" class="headerlink" title="网络优化"></a>网络优化</h2><p>力求网络资源最大利用化，让资源下载完美队列执行，避免大量的无用请求，下载失败会自动重新请求，避免整个安装过程失败</p><h2 id="扁平化模式"><a href="#扁平化模式" class="headerlink" title="扁平化模式"></a>扁平化模式</h2><p>对于不匹配的依赖版本的包创立一个独立的包，避免创建重复的。<br>对于多个包依赖同一个子包的情况，yarn 会尽量提取为同一个包，防止出现多处副本，浪费空间。</p><h2 id="版本控制"><a href="#版本控制" class="headerlink" title="版本控制"></a>版本控制</h2><p>npm 用下来比较强的一个痛点就是：当包的依赖层次比较深时，版本控制不够精确。会出现相同 package.json，但不同人的电脑上安装出不同版本的依赖包，出现类似<font color="red">“我的电脑上可以运行，别的电脑上不行”</font>的 bug 很难查找。你可以使用 <a href="https://docs.npmjs.com/cli/shrinkwrap" target="_blank" rel="external">npm-shrinkwrap</a> 来实现版本固化，版本信息会写入 npm-shrinkwrap.json 文件中，但它毕竟不是 npm 的标准配置。</p><p>而 yarn 天生就能实现版本固化。会生成一个类似 npm-shrinkwrap.json 的 yarn.lock 文件，而文件内会描述包自身的版本号，还会锁定所有它依赖的包的版本号：<br><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="string">"@babel/code-frame@7.0.0-beta.47"</span>:</span><br><span class="line">  version <span class="string">"7.0.0-beta.47"</span></span><br><span class="line">  resolved <span class="string">"https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.47.tgz#d18c2f4c4ba8d093a2bcfab5616593bfe2441a27"</span></span><br><span class="line">  dependencies:</span><br><span class="line">    <span class="string">"@babel/highlight"</span> <span class="string">"7.0.0-beta.47"</span></span><br></pre></td></tr></table></figure></p><p>yarn.lock 存储着你的每个包的确切依赖版本，能确保从本地开发到生产环境，所有机器上都有精确相同的依赖版本。</p><h2 id="其他关于-Yarn-的介绍"><a href="#其他关于-Yarn-的介绍" class="headerlink" title="其他关于 Yarn 的介绍"></a>其他关于 Yarn 的介绍</h2><p>我们在使用 Yarn 时，依然要访问 npm 仓库，但 Yarn 能够更快速地安装软件包和管理依赖关系，并且可以在跨机器或者无网络的安全环境中保持代码的一致性。</p><h1 id="Yarn-安装"><a href="#Yarn-安装" class="headerlink" title="Yarn 安装"></a>Yarn 安装</h1><h2 id="windows"><a href="#windows" class="headerlink" title="windows"></a>windows</h2><p>在 Yarn 中文网可以找到 window 下的三种安装方法：</p><p><img src="https://img-blog.csdn.net/20180824120236673?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NzZG5feXVkb25n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="yarn-install"></p><p>不过我觉得这三种方法都不好用，快速好用的安装方法应该还是使用 npm 来安装：<br><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 yarn</span><br></pre></td></tr></table></figure></p><p>关于为什么使用 <code>-g</code>，以及 <code>-g</code> 会带来哪来影响，这个可以看我的这篇文章：<a href="https://neveryu.github.io/2017/04/10/npm/" target="_blank" rel="external">npm详细介绍</a>，里面详细介绍了为什么要使用 <code>-g</code>，以及 <code>-g</code> 的作用。</p><h2 id="mac"><a href="#mac" class="headerlink" title="mac"></a>mac</h2><h3 id="方式一"><a href="#方式一" class="headerlink" title="方式一"></a>方式一</h3><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 yarn</span><br></pre></td></tr></table></figure><p>如果有报： <code>Please try running this command again as root/Administrator.</code>，可能就是权限不足，因此你需要切换到最高权限去执行命令<br><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">sudo -s</span><br><span class="line">npm install yarn -g</span><br></pre></td></tr></table></figure></p><h3 id="方式二"><a href="#方式二" class="headerlink" title="方式二"></a>方式二</h3><p>使用另一种初始化脚本的方法，可能就会比较简单一些：<br><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">curl -o- -L https://yarnpkg.com/install.sh | bash</span><br></pre></td></tr></table></figure></p><h3 id="方式三"><a href="#方式三" class="headerlink" title="方式三"></a>方式三</h3><p>如果你的电脑上面已经安装了 Homebrew 的话，你可以通过 Homebrew 包管理器安装 Yarn<br><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">brew install yarn</span><br></pre></td></tr></table></figure></p><h1 id="Yarn-换源"><a href="#Yarn-换源" class="headerlink" title="Yarn 换源"></a>Yarn 换源</h1><p>Yarn 源仓库包下载不稳定<br><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></pre></td><td class="code"><pre><span class="line">// 查看 yarn 配置</span><br><span class="line">yarn config get registry</span><br><span class="line">或者</span><br><span class="line">yarn config list</span><br><span class="line"></span><br><span class="line">&gt; registry: &apos;https://registry.yarnpkg.com&apos;</span><br></pre></td></tr></table></figure></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">安装淘宝镜像</span><br><span class="line">yarn config set registry https://registry.npm.taobao.org</span><br></pre></td></tr></table></figure><h1 id="Yarn-常用命令"><a href="#Yarn-常用命令" class="headerlink" title="Yarn 常用命令"></a>Yarn 常用命令</h1><ul><li><code>npm install</code> === <code>yarn</code>  —— install安装是默认行为</li><li><code>npm install taco --save</code> === <code>yarn add taco</code>  —— taco包立即被保存到 <code>package.json</code> 中。</li><li><code>npm uninstall taco --save</code> === <code>yarn remove taco</code></li><li><code>npm install taco --save-dev</code> === <code>yarn add taco --dev</code></li><li><code>npm update --save</code> === <code>yarn upgrade</code></li></ul><p>-</p><ul><li><code>npm install taco@latest --save</code> === <code>yarn add taco</code></li><li><code>npm install taco --global</code> === <code>yarn global add taco</code>  —— 一如既往，请谨慎使用 global 标记。</li></ul><blockquote><p>注意：使用yarn或yarn install安装全部依赖时是根据package.json里的”dependencies”字段来决定的</p></blockquote><p>-</p><ul><li><code>npm init</code> === <code>yarn init</code></li><li><code>npm init --yes/-y</code> === <code>yarn init --yes/-y</code></li><li><code>npm link</code> === <code>yarn link</code></li><li><code>npm outdated</code> === <code>yarn outdated</code></li><li><code>npm publish</code> === <code>yarn publish</code></li><li><code>npm run</code> === <code>yarn run</code></li><li><code>npm cache clean</code> === <code>yarn cache clean</code></li><li><code>npm login</code> === <code>yarn login</code></li><li><code>npm test</code> === <code>yarn test</code></li></ul><h2 id="Yarn-独有的命令"><a href="#Yarn-独有的命令" class="headerlink" title="Yarn 独有的命令"></a>Yarn 独有的命令</h2><ul><li><code>yarn licenses ls</code>  —— 允许你检查依赖的许可信息</li><li><code>yarn licenses generate</code>  —— 自动创建依赖免责声明 license</li><li><code>yarn why taco</code>  —— 检查为什么会安装 taco，详细列出依赖它的其他包</li><li><code>yarn why vuepress</code>  —— 检查为什么会安装 vuepress，详细列出依赖它的其他包</li></ul><h1 id="特性"><a href="#特性" class="headerlink" title="特性"></a>特性</h1><p>Yarn 除了让安装过程变得更快与更可靠，还添加了一些额外的特性，从而进一步简化依赖管理的工作流。</p><ul><li>同时兼容 <code>npm</code> 与 <code>bower</code> 工作流，并支持两种软件仓库混合使用</li><li>可以限制已安装模块的协议，并提供方法输出协议信息</li><li>提供一套稳定的共有 JS API，用于记录构建工具的输出信息</li><li>可读、最小化、美观的 CLI 输出信息</li></ul>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;背景&quot;&gt;&lt;a href=&quot;#背景&quot; class=&quot;headerlink&quot; title=&quot;背景&quot;&gt;&lt;/a&gt;背景&lt;/h1&gt;&lt;p&gt;在 Node 生态系统中，依赖通常安装在项目的 &lt;code&gt;node_modules&lt;/code&gt; 文件夹中。然而，这个文件的结构和实际依赖树可能有所区别，因为重复的依赖可以合并到一起。&lt;code&gt;npm&lt;/code&gt; 客户端把依赖安装到 &lt;code&gt;node_modules&lt;/code&gt; 目录的过程具有不确定性。这意味着当依赖的安装顺序不同时，&lt;code&gt;node_modules&lt;/code&gt; 目录的结构可能会发生变化。这种差异可能会导致类似&lt;font color=&quot;red&quot;&gt;“我的电脑上可以运行，别的电脑上不行”&lt;/font&gt;的情况，并且通常需要花费大量时间定为与解决。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;有时候就会遇到这种情况，完整可运行的项目上传到 git 上，别人 pull 下来以后，npm install 会报错。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/yarnpkg/yarn&quot;&gt;Yarn&lt;/a&gt; 一开始的主要目标是解决由于语义版本控制而导致的 npm 安装的不确定性问题。虽然可以用 &lt;code&gt;npm shrinkwrap&lt;/code&gt; 来实现可预测的依赖关系树，但它并不是默认选项，而是取决于所有的开发人员指导并启用这个选项。&lt;/p&gt;
    
    </summary>
    
      <category term="前端" scheme="https://309893147.github.io/categories/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="Yarn" scheme="https://309893147.github.io/tags/Yarn/"/>
    
  </entry>
  
  <entry>
    <title>Vue 服务端渲染 or 预渲染</title>
    <link href="https://309893147.github.io/2018/06/18/vue-prerender/"/>
    <id>https://309893147.github.io/2018/06/18/vue-prerender/</id>
    <published>2018-06-18T08:25:24.000Z</published>
    <updated>2019-07-30T01:31:38.216Z</updated>
    
    <content type="html"><![CDATA[<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>关于 Vue 的 SPA 说的已经太多太多了，它为我们带来了极速的开发体验，极强的开发效率。可能唯一有些许不足的就是，当我们对 SEO 很在乎的时候，我们如何去处理 SEO 的需求。</p><p>关于 SEO ，Vue 也有现成的解决方案： <a href="https://ssr.vuejs.org/zh/" target="_blank" rel="external">Vue 服务端渲染</a></p><a id="more"></a><h2 id="那么"><a href="#那么" class="headerlink" title="那么"></a>那么</h2><h3 id="什么是服务端渲染"><a href="#什么是服务端渲染" class="headerlink" title="什么是服务端渲染"></a>什么是服务端渲染</h3><p>服务端将完整的页面 html 输出到客户端显示，与 SPA （Single-Page-Application）使用 js 渲染页面不同。</p><h3 id="为什么使用服务端渲染"><a href="#为什么使用服务端渲染" class="headerlink" title="为什么使用服务端渲染"></a>为什么使用服务端渲染</h3><ul><li>更好的 SEO</li><li>更快的内容到达时间</li></ul><h3 id="服务端渲染-or-预渲染"><a href="#服务端渲染-or-预渲染" class="headerlink" title="服务端渲染 or 预渲染"></a>服务端渲染 or 预渲染</h3><p>就像官网所说的，如果你调研服务器端渲染(SSR)只是用来改善少数营销页面（例如 /, /about, /contact 等）的 SEO，那么你可能需要<a href="https://github.com/chrisvfritz/prerender-spa-plugin" target="_blank" rel="external">预渲染</a>，一个典型的预渲染使用场景可能类似<a href="https://neveryu.github.io/vue-tour/" target="_blank" rel="external">这个网站</a>。</p><h3 id="区别"><a href="#区别" class="headerlink" title="区别"></a>区别</h3><p><strong>服务端渲染</strong>和<strong>预渲染</strong>的使用场景还是有较明显的区别的。预渲染的使用场景更多是我们所说的静态页面的形式，比如说<a href="https://neveryu.github.io/vue-tour/" target="_blank" rel="external">这个网站</a>。服务端渲染适用于大型的、页面数据处理较多且较为复杂的、与服务端有数据交互的功能型网站，一个明显的使用场景就是电商网站。</p><h2 id="如何使用预渲染"><a href="#如何使用预渲染" class="headerlink" title="如何使用预渲染"></a>如何使用预渲染</h2><p><strong>预渲染</strong>的核心是使用 <a href="https://github.com/chrisvfritz/prerender-spa-plugin" target="_blank" rel="external">prerender-spa-plugin</a>，如何使用它呢？我们还是以<a href="https://neveryu.github.io/vue-tour/" target="_blank" rel="external">这个网站</a>的<a href="https://github.com/Neveryu/prerender-website" target="_blank" rel="external">源代码</a>中的 webpack 配置为例：<br><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="keyword">new</span> PrerenderSPAPlugin(&#123;</span><br><span class="line">  staticDir: path.join(__dirname, <span class="string">'dist'</span>),</span><br><span class="line">  routes: [ <span class="string">'/'</span>, <span class="string">'/home'</span>, <span class="string">'/infomation'</span>, <span class="string">'/ticket'</span>, <span class="string">'/scenery'</span>, <span class="string">'/about'</span> ],</span><br><span class="line">  renderer: <span class="keyword">new</span> Renderer(&#123;</span><br><span class="line">    headless: <span class="literal">false</span>,</span><br><span class="line">    renderAfterDocumentEvent: <span class="string">'render-event'</span></span><br><span class="line">  &#125;)</span><br><span class="line">&#125;),</span><br></pre></td></tr></table></figure></p><p>我们需要简单的配置一下，项目所有的路由，最终生成后有几个页面，都是以这个配置为依据，而不是你在 vue-router 中配置的路由。</p><p>最基础也最核心的配置项也就这几行代码，当然，如果你有更多的需求配置项，你可以去 github 上查看文档，文档中也有很详细的介绍。 </p><h2 id="如何搭建一个预渲染开发环境"><a href="#如何搭建一个预渲染开发环境" class="headerlink" title="如何搭建一个预渲染开发环境"></a>如何搭建一个预渲染开发环境</h2><p>如果你也想要使用<strong>预渲染</strong>来开发你的网站的话，最简单的方法就是克隆<a href="https://github.com/Neveryu/prerender-website" target="_blank" rel="external">这个项目</a>，然后简单删减以后进行二次开发，整个的开发流程和 Vue 是一模一样的。</p><h2 id="Tip"><a href="#Tip" class="headerlink" title="Tip"></a>Tip</h2><p>1、相较于 Vue 的模板中大而全的 webpack 配置项，<strong>预渲染</strong>中的 webpack 配置简单小巧，如果你有一些 webpack 的配置需求的话，你可能需要自己动手。</p><p>2、我的<a href="https://github.com/Neveryu/prerender-website" target="_blank" rel="external">这个项目</a>使用的是 stylus 来作为 css 预编译语言，如果你想使用其他的 css 预编译语言的话，需要额外安装一些插件以及做一些简单配置。当然了，默认的 css 肯定是支持的。</p><p>3、在写这个项目的过程中，也有做一些简单的知识点记录。<a href="https://github.com/Neveryu/prerender-website/blob/master/project-note.md" target="_blank" rel="external">vue-prerender 笔记</a></p><p>4、最后项目打包发布到生产环境，使用 <code>npm run build</code> 一键操作即可。如果你想要部署到子目录下的话，那么，你可能需要做一些简单的修改，具体在 <a href="https://github.com/Neveryu/prerender-website/blob/master/project-note.md" target="_blank" rel="external">vue-prerender 笔记</a> 有提到。</p><h2 id="写在最后"><a href="#写在最后" class="headerlink" title="写在最后"></a>写在最后</h2><p><a href="https://neveryu.github.io/vue-tour/" target="_blank" rel="external">项目预览</a><br><a href="https://github.com/Neveryu/prerender-website" target="_blank" rel="external">项目github地址</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;关于 Vue 的 SPA 说的已经太多太多了，它为我们带来了极速的开发体验，极强的开发效率。可能唯一有些许不足的就是，当我们对 SEO 很在乎的时候，我们如何去处理 SEO 的需求。&lt;/p&gt;
&lt;p&gt;关于 SEO ，Vue 也有现成的解决方案： &lt;a href=&quot;https://ssr.vuejs.org/zh/&quot;&gt;Vue 服务端渲染&lt;/a&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="前端" scheme="https://309893147.github.io/categories/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="vue" scheme="https://309893147.github.io/tags/vue/"/>
    
  </entry>
  
  <entry>
    <title>关于 JavaScript 中 this 的详细总结</title>
    <link href="https://309893147.github.io/2018/06/01/js-this/"/>
    <id>https://309893147.github.io/2018/06/01/js-this/</id>
    <published>2018-05-31T19:25:24.000Z</published>
    <updated>2019-07-30T01:31:38.210Z</updated>
    
    <content type="html"><![CDATA[<p id="div-border-top-blue">在 JavaScript 中，函数中的 this 指向，很多同学总是理不清楚【这必然会带来一些问题】。确实，JavaScript 中，函数的 this 指向比较复杂多变。它和你调用的方式有关系，和 <strong>严格模式</strong> 或者 <strong>非严格模式</strong> 有关系，和你是否使用了箭头函数有关系，和你在使用函数时是否传入了 this 有关系，和你是否主动修改了调用对象有关系。</p><ul><li>在绝大多数情况下，函数的调用方式决定了 <code>this</code> 的值。<code>this</code> 不能再执行期间被赋值，并且在每次函数被调用时 <code>this</code> 的值也可能会不同。<code>ES5</code> 引入了 <code>bind</code> 方法来设置函数的 <code>this</code> 值，<a href="https://blog.csdn.net/csdn_yudong/article/details/78730844" target="_blank" rel="external">关于bind和call可以看我的文章</a>，而不用考虑函数如何被调用的，<code>ES2015</code> 引入了支持 <code>this</code> 词法解析的箭头函数（它在闭合的执行上下文内设置 <code>this</code> 的值）。</li></ul><ul><li>与其他语言相比，<strong>函数的 <code>this</code> 关键字</strong>在 <code>JavaScript</code> 中的表现略有不同，此外，在 严格模式 和 非严格模式之间也会有一些差别。</li></ul><a id="more"></a><h2 id="全局上下文"><a href="#全局上下文" class="headerlink" title="全局上下文"></a>全局上下文</h2><p>无论是否在严格模式下，在全局执行上下文中（在任何函数体外部）<code>this</code> 都指代全局对象。【在全局执行上下文中 <code>this</code> 都是全局对象 <code>window</code>】（浏览器环境）<br><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="keyword">var</span> a = <span class="number">1</span>;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">this</span>);    <span class="comment">// window</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">this</span>.a);  <span class="comment">// 1</span></span><br><span class="line"></span><br><span class="line"><span class="meta">"use strict"</span>;</span><br><span class="line"><span class="keyword">var</span> b = <span class="number">2</span>;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">this</span>);    <span class="comment">// window</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">this</span>.b);  <span class="comment">// 2</span></span><br></pre></td></tr></table></figure></p><h2 id="函数上下文"><a href="#函数上下文" class="headerlink" title="函数上下文"></a>函数上下文</h2><p>在函数内部，<code>this</code> 的值取决于函数被调用的方式。【取决于被调用的方式】</p><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">f1</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 在浏览器中</span></span><br><span class="line">f1() === <span class="built_in">window</span>;  <span class="comment">// 在浏览器中，全局对象是widnow</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 在 Node 中</span></span><br><span class="line">f1() === global;</span><br></pre></td></tr></table></figure><p>【在严格模式下，<code>this</code> 将保持他进入执行上下文时的值】</p><p>在严格模式下，<code>this</code> 将保持他进入执行上下文时的值，所以下面的 <code>this</code> 将会默认为 <code>undefined</code>。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">f2</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line"><span class="meta">  "use strict"</span>; <span class="comment">// 这里是严格模式</span></span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line">f2() === <span class="literal">undefined</span>; <span class="comment">// true</span></span><br></pre></td></tr></table></figure><p>所以，在严格模式下，如果 <code>this</code> 没有被执行上下文（execution context）定义，那它将保持为 <code>undefined</code>。</p><p>因为 <code>f2()</code> 是被直接调用的，而不是作为对象的属性或方法调用的（如<code>window.f2()</code>）。有一些浏览器最初在支持严格模式时没有正确实现这个功能，于是它们错误的返回了 <code>window</code> 对象。</p><p>但是，如果用 <code>window</code> 来调用的话，<code>this</code> 就是 <code>window</code> 了。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">f2</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line"><span class="meta">  "use strict"</span>; <span class="comment">// 这里是严格模式</span></span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">window</span>.f2())  <span class="comment">// window</span></span><br></pre></td></tr></table></figure></p><p>如果要想把 <code>this</code> 的值从一个上下文传到另一个，就要用 <code>call</code> 或者 <code>apply</code> 方法。</p><p>当一个函数在其主体中使用 <code>this</code> 关键字时，可以通过使用函数继承自 <code>Function.prototype</code> 的 <code>call</code> 或 <code>apply</code> 方法将 <code>this</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="function"><span class="keyword">function</span> <span class="title">add</span>(<span class="params">c, d</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">this</span>.a + <span class="keyword">this</span>.b + c + d;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> o = &#123;<span class="attr">a</span>: <span class="number">1</span>, <span class="attr">b</span>: <span class="number">3</span>&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 第一个参数是作为‘this’使用的对象</span></span><br><span class="line"><span class="comment">// 后续参数作为参数传递给函数调用</span></span><br><span class="line">add.call(o, <span class="number">5</span>, <span class="number">7</span>); <span class="comment">// 1 + 3 + 5 + 7 = 16</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 第一个参数也是作为‘this’使用的对象</span></span><br><span class="line"><span class="comment">// 第二个参数是一个数组，数组里的元素用作函数调用中的参数</span></span><br><span class="line">add.apply(o, [<span class="number">10</span>, <span class="number">20</span>]); <span class="comment">// 1 + 3 + 10 + 20 = 34</span></span><br></pre></td></tr></table></figure><blockquote><p>使用 <code>call</code> 和 <code>apply</code> 函数的时候要注意，如果传递给 <code>this</code> 的值不是一个对象，<code>JavaScript</code> 会尝试使用内部 <code>ToObject</code> 操作将其转换为对象。</p></blockquote><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="number">7</span> 或 <span class="string">'foo'</span>，那么就会使用相关构造函数将它转换为对象，所以原始值 <span class="number">7</span> 会被转为对象，像 </span><br><span class="line"><span class="keyword">new</span> <span class="built_in">Number</span>(<span class="number">7</span>) 这样，而字符串 <span class="string">'foo'</span> 转化成 <span class="keyword">new</span> <span class="built_in">String</span>(<span class="string">'foo'</span>) 这样。</span><br></pre></td></tr></table></figure><h2 id="bind-方法"><a href="#bind-方法" class="headerlink" title="bind 方法"></a>bind 方法</h2><p><code>ECMAScript 5</code> 引入了 <code>Function.prototype.bind</code>。调用 <code>f.bind(someObject)</code> 会 <strong>创建</strong>一个与 <code>f</code> 具有相同函数体和作用域的函数，但是在这个新函数中，<code>this</code> 将永久地被绑定到了 <code>bind</code> 的第一个参数，无论这个函数是如何被调用的。</p><p>【<code>this</code> 将永久的被绑定到了 <code>bind</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">f</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">this</span>.a;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> g = f.bind(&#123;<span class="attr">a</span>:<span class="string">"azerty"</span>&#125;);</span><br><span class="line"><span class="built_in">console</span>.log(g()); <span class="comment">// azerty</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> h = g.bind(&#123;<span class="attr">a</span>:<span class="string">'yoo'</span>&#125;); <span class="comment">// bind只生效一次！</span></span><br><span class="line"><span class="built_in">console</span>.log(h()); <span class="comment">// azerty</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> o = &#123;<span class="attr">a</span>:<span class="number">37</span>, <span class="attr">f</span>:f, <span class="attr">g</span>:g, <span class="attr">h</span>:h&#125;;</span><br><span class="line"><span class="built_in">console</span>.log(o.f(), o.g(), o.h()); <span class="comment">// 37, azerty, azerty</span></span><br></pre></td></tr></table></figure><h2 id="箭头函数"><a href="#箭头函数" class="headerlink" title="箭头函数"></a>箭头函数</h2><p>在箭头函数中，<code>this</code> 与封闭词法上下文的 <code>this</code> 保持一致。在全局代码中，它将被设置为全局对象。【封闭词法上下文 是什么意思，你知道吗？】<br><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="keyword">var</span> foo = <span class="function">(<span class="params">(</span>) =&gt;</span> <span class="keyword">this</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 接着上面的代码</span></span><br><span class="line"><span class="comment">// 作为对象的一个方法调用</span></span><br><span class="line"><span class="keyword">var</span> obj = &#123;<span class="attr">foo</span>: foo&#125;;</span><br><span class="line"><span class="built_in">console</span>.log(obj.foo() === <span class="built_in">window</span>); <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 尝试使用call来设定this</span></span><br><span class="line"><span class="built_in">console</span>.log(foo.call(obj) === <span class="built_in">window</span>); <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 尝试使用bind来设定this</span></span><br><span class="line">foo = foo.bind(obj);</span><br><span class="line"><span class="built_in">console</span>.log(foo() === <span class="built_in">window</span>); <span class="comment">// true</span></span><br></pre></td></tr></table></figure></p><p>【无论如何，<code>foo</code> 的 <code>this</code> 被设置为<strong>他被创建时的上下文</strong>（在上面的例子中，就是全局对象）】<br>这同样适用于在其他函数内创建的箭头函数：这些箭头函数的 <code>this</code> 被设置为封闭的词法上下文的。</p><h2 id="作为对象的方法"><a href="#作为对象的方法" class="headerlink" title="作为对象的方法"></a>作为对象的方法</h2><blockquote><p>当函数作为对象里的方法被调用时，它们的 <code>this</code> 是调用该函数的对象</p></blockquote><p><code>this</code> 的绑定只受最靠近的成员引用的影响。在下面的例子中，我们把一个方法 <code>g</code> 当做对象 <code>o.b</code> 的函数调用。在这次执行期间，函数中的 <code>this</code> 将指向 <code>o.b</code> 。事实证明，这与他是对象 <code>o</code> 的成员没有多大关系，最靠近的引用才是最重要的。<br><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">o.b = &#123; <span class="attr">g</span>: independent, <span class="attr">prop</span>: <span class="number">42</span> &#125;</span><br><span class="line"><span class="built_in">console</span>.log(o.b.g())</span><br></pre></td></tr></table></figure></p><h2 id="原型链中的-this"><a href="#原型链中的-this" class="headerlink" title="原型链中的 this"></a>原型链中的 <code>this</code></h2><p>对于在对象原型链上某处定义的方法，同样的概念也适用。如果该方法存在于一个对象的原型链上，那么 <code>this</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="keyword">var</span> o = &#123;</span><br><span class="line">  f: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123; </span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>.a + <span class="keyword">this</span>.b; </span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">var</span> p = <span class="built_in">Object</span>.create(o);</span><br><span class="line">p.a = <span class="number">1</span>;</span><br><span class="line">p.b = <span class="number">4</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(p.f()); <span class="comment">// 5</span></span><br></pre></td></tr></table></figure><p>在这个例子中，对象 <code>p</code> 没有属于它自己的 <code>f</code> 属性，它的 <code>f</code> 属性继承自它的原型。虽然在对 <code>f</code> 的查找过程中，最终是在 <code>o</code> 中找到 <code>f</code> 属性的，这并没有关系；查找过程首先从 <code>p.f</code> 的引用开始，所以函数中的 <code>this</code> 指向 <code>p</code>。也就是说，因为 <code>f</code> 是作为 <code>p</code> 的方法调用的，所以它的 <code>this</code> 指向了 <code>p</code> 。这是 <code>JavaScript</code> 的原型继承中的一个有趣的特性。</p><h2 id="作为构造函数"><a href="#作为构造函数" class="headerlink" title="作为构造函数"></a>作为构造函数</h2><p>当一个函数用作构造函数时（适用 <code>new</code> 关键字），它的 <code>this</code> 被绑定到正在构造的新对象。</p><p>虽然构造器返回的默认值是 <code>this</code> 所指的那个对象，但它仍可以手动返回其他的对象（如果返回值不是一个对象，则返回 <code>this</code> 对象）</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></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="comment"> *</span></span><br><span class="line"><span class="comment"> * function MyConstructor()&#123;</span></span><br><span class="line"><span class="comment"> *   // 函数实体写在这里</span></span><br><span class="line"><span class="comment"> *   // 根据需要在this上创建属性，然后赋值给它们，比如：</span></span><br><span class="line"><span class="comment"> *   this.fum = "nom";</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="comment"> *   // 如果函数具有返回对象的return语句，</span></span><br><span class="line"><span class="comment"> *   // 则该对象将是 new 表达式的结果。 </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"><span class="comment"> * &#125;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">C</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.a = <span class="number">37</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> o = <span class="keyword">new</span> C();</span><br><span class="line"><span class="built_in">console</span>.log(o.a); <span class="comment">// 37</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">C2</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.a = <span class="number">37</span>;</span><br><span class="line">  <span class="keyword">return</span> &#123;<span class="attr">a</span>:<span class="number">38</span>&#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">o = <span class="keyword">new</span> C2();</span><br><span class="line"><span class="built_in">console</span>.log(o.a); <span class="comment">// 38</span></span><br></pre></td></tr></table></figure><p>在刚刚的例子中（C2），因为在调用构造函数的过程中，手动的设置了返回对象，与 <code>this</code> 绑定的默认对象被丢弃了。（这基本上使得语句 <code>this.a = 37;</code> 成了“僵尸”代码，实际上并不是真正的“僵尸”，这条语句执行了，但是对于外部没有任何影响，因此完全可以忽略它）。</p><h2 id="作为一个-DOM-事件处理函数"><a href="#作为一个-DOM-事件处理函数" class="headerlink" title="作为一个 DOM 事件处理函数"></a>作为一个 DOM 事件处理函数</h2><blockquote><p>当函数被用作事件处理函数时，它的 <code>this</code> 指向触发事件的元素（一些浏览器在使用非 <code>addEventListener</code> 的函数动态添加监听函数时不遵守这个约定）。</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></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">bluify</span>(<span class="params">e</span>)</span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="keyword">this</span> === e.currentTarget); <span class="comment">// 总是 true</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 当 currentTarget 和 target 是同一个对象时为 true</span></span><br><span class="line">  <span class="built_in">console</span>.log(<span class="keyword">this</span> === e.target);        </span><br><span class="line">  <span class="keyword">this</span>.style.backgroundColor = <span class="string">'#A5D9F3'</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">var</span> elements = <span class="built_in">document</span>.getElementsByTagName(<span class="string">'*'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 将bluify作为元素的点击监听函数，当元素被点击时，就会变成蓝色</span></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">var</span> i=<span class="number">0</span> ; i&lt;elements.length ; i++)&#123;</span><br><span class="line">  elements[i].addEventListener(<span class="string">'click'</span>, bluify, <span class="literal">false</span>);</span><br></pre></td></tr></table></figure><h2 id="作为一个内联事件处理函数"><a href="#作为一个内联事件处理函数" class="headerlink" title="作为一个内联事件处理函数"></a>作为一个内联事件处理函数</h2><blockquote><p>当代码被内联 <code>on-event</code> 处理函数 调用时，它的 <code>this</code> 指向监听器所在的 <code>DOM</code> 元素</p></blockquote><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">button</span> <span class="attr">onclick</span>=<span class="string">"alert(this.tagName.toLowerCase());"</span>&gt;</span></span><br><span class="line">  Show this</span><br><span class="line"><span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br></pre></td></tr></table></figure><p>上面的 <code>alert</code> 会显示 <code>button</code> 。注意只有外层代码中的 <code>this</code> 是这样设置的：</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">button</span> <span class="attr">onclick</span>=<span class="string">"alert((function()&#123;return this&#125;)());"</span>&gt;</span></span><br><span class="line">  Show inner this</span><br><span class="line"><span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br></pre></td></tr></table></figure><p>在这种情况下，没有设置内部函数的 <code>this</code>，所以它指向 <code>global/window</code> 对象（即非严格模式下调用的函数未设置 <code>this</code> 时指向的默认对象）。</p>]]></content>
    
    <summary type="html">
    
      &lt;p id=&quot;div-border-top-blue&quot;&gt;在 JavaScript 中，函数中的 this 指向，很多同学总是理不清楚【这必然会带来一些问题】。确实，JavaScript 中，函数的 this 指向比较复杂多变。它和你调用的方式有关系，和 &lt;strong&gt;严格模式&lt;/strong&gt; 或者 &lt;strong&gt;非严格模式&lt;/strong&gt; 有关系，和你是否使用了箭头函数有关系，和你在使用函数时是否传入了 this 有关系，和你是否主动修改了调用对象有关系。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;在绝大多数情况下，函数的调用方式决定了 &lt;code&gt;this&lt;/code&gt; 的值。&lt;code&gt;this&lt;/code&gt; 不能再执行期间被赋值，并且在每次函数被调用时 &lt;code&gt;this&lt;/code&gt; 的值也可能会不同。&lt;code&gt;ES5&lt;/code&gt; 引入了 &lt;code&gt;bind&lt;/code&gt; 方法来设置函数的 &lt;code&gt;this&lt;/code&gt; 值，&lt;a href=&quot;https://blog.csdn.net/csdn_yudong/article/details/78730844&quot;&gt;关于bind和call可以看我的文章&lt;/a&gt;，而不用考虑函数如何被调用的，&lt;code&gt;ES2015&lt;/code&gt; 引入了支持 &lt;code&gt;this&lt;/code&gt; 词法解析的箭头函数（它在闭合的执行上下文内设置 &lt;code&gt;this&lt;/code&gt; 的值）。&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;与其他语言相比，&lt;strong&gt;函数的 &lt;code&gt;this&lt;/code&gt; 关键字&lt;/strong&gt;在 &lt;code&gt;JavaScript&lt;/code&gt; 中的表现略有不同，此外，在 严格模式 和 非严格模式之间也会有一些差别。&lt;/li&gt;
&lt;/ul&gt;
    
    </summary>
    
      <category term="前端" scheme="https://309893147.github.io/categories/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="JavaScript" scheme="https://309893147.github.io/tags/JavaScript/"/>
    
  </entry>
  
  <entry>
    <title>vue2.x 做一个音乐app</title>
    <link href="https://309893147.github.io/2018/05/01/vue-music/"/>
    <id>https://309893147.github.io/2018/05/01/vue-music/</id>
    <published>2018-04-30T19:25:24.000Z</published>
    <updated>2019-07-30T01:31:38.215Z</updated>
    
    <content type="html"><![CDATA[<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p id="div-border-top-green">本项目是基于 vue2.4.1 最新的实战项目，vue-cli2.9.3 + vue2.4.1 + axios + vue-router3.0.1 + es6 + vux3.0.1 + webpack + better-scroll + 线上真实接口的一个移动端音乐 app。</p><a id="more"></a><p><img src="/images/vue-music-2.png" alt=""></p><h2 id="实现的功能"><a href="#实现的功能" class="headerlink" title="实现的功能"></a>实现的功能</h2><p>1、音乐列表、歌单、歌手、排行、榜单、推荐<br>2、音乐播放、暂停、上一曲、下一曲、喜欢<br>3、播放列表、添加到播放列表、历史列表<br>4、搜索单曲、歌手、专辑、MV<br>5、查看歌手页面、专辑页面、MV<br>6、热门搜索<br>7、搜索历史记录<br>8、排行榜<br>9、切换播放模式<br>10、歌词<br>11、个人中心<br>12、项目介绍</p><p id="div-border-left-red">现在最流行的开发方式就是前后分离了；<br><a href="/tags/vue">vue</a> 也是现在最流行的前端框架之一。</p><h2 id="截屏演示"><a href="#截屏演示" class="headerlink" title="截屏演示"></a>截屏演示</h2><video src="/images/vue-music.mp4" controls="controls" preload="preload" height="400px"></video><h2 id="移动端演示"><a href="#移动端演示" class="headerlink" title="移动端演示"></a>移动端演示</h2><p>扫二维码在手机上查看效果更好<br><img src="/images/vue-music-1.png" alt=""></p><h2 id="项目代码"><a href="#项目代码" class="headerlink" title="项目代码"></a>项目代码</h2><p><a href="https://github.com/Neveryu/vue-music" target="_blank" rel="external">https://github.com/Neveryu/vue-music</a></p><h2 id="构建"><a href="#构建" class="headerlink" title="构建"></a>构建</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># install dependencies</span></span><br><span class="line">npm install</span><br><span class="line"></span><br><span class="line"><span class="comment"># serve with hot reload at localhost:8080</span></span><br><span class="line">npm run dev</span><br><span class="line"></span><br><span class="line"><span class="comment"># build for production with minification</span></span><br><span class="line">npm run build</span><br><span class="line"></span><br><span class="line"><span class="comment"># build for production and view the bundle analyzer report</span></span><br><span class="line">npm run build --report</span><br><span class="line"></span><br><span class="line"><span class="comment"># run local server</span></span><br><span class="line">npm run prod.server.js</span><br></pre></td></tr></table></figure><h2 id="Tip"><a href="#Tip" class="headerlink" title="Tip"></a>Tip</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 如果 npm install 长时间没有反应或者安装失败，请尝试</span></span><br><span class="line">npm install --registry=https://registry.npm.taobao.org</span><br></pre></td></tr></table></figure><h2 id="详细"><a href="#详细" class="headerlink" title="详细"></a>详细</h2><p>vue 有自己的脚手架构建工具 vue-cli 。使用起来非常方便，使用 webpack 来集成各种开发便捷工具，比如：</p><ul><li>代码热更新，修改代码之后网页无刷新改变，对前端开发来说非常的方便</li><li>Postcss，再也不用去管兼容性的问题了，只针对 chrome 写 css 代码，会自动编译生成支持多款浏览器的 css 代码</li><li>ESlint，统一代码风格</li><li>bable，ES2015 出来已经有一段事件了，但是不少浏览器还没有兼容 ES6。有了 bable，放心使用 ES6 语法，它会自动转义成 ES5 语法</li><li>Stylus，类似于 sass/scss ，但是可以不写 <code>{ }</code> 和 <code>:</code>，使用起来还是很方便的</li><li>better-scroll，很好用的移动端各种滚动场景需求的插件（已支持PC）</li><li>vuex，Vuex是一个专为 Vue.js 应用程序开发的状态管理模式</li><li>vue-router，专为 Vue.js 应用程序开发的路由工具</li></ul><p>除此之外，vue-cli 已经使用 node 配置了一套本地服务器和安装命令等，本地运行和打包只需要一个命令就可以搞定，非常的方便。</p><h2 id="为什么写这个项目"><a href="#为什么写这个项目" class="headerlink" title="为什么写这个项目"></a>为什么写这个项目</h2><p>之前的 <a href="https://neveryu.github.io/2017/11/11/vue-sell/" target="_blank" rel="external">vue-sell</a>，是一个非常好的 vue 的项目教程了，学了 vue 以后，跟着做一遍 vue-sell，应该对 vue 的基本操作都能非常熟练的掌握了。<br>但是如何结合 vuex 和 vue-router，以及其他技术，做一个大型的项目，很多同学还是苦于没有经验和项目实例，所以就有了这个 vue-music。</p><p><a href="https://neveryu.github.io/vue-music/" target="_blank" rel="external">vue-music</a> 里面用到了 vue 全家桶，还有 better-scroll，jsonp 等其他工具，用的也是线上真实的音乐接口数据，而且项目里封装了很多完美的组件，对个人技术的学习和提升有很大的帮助，项目级别上也达到了中大型级别。<br>非常适合 vue 的进阶学习。</p><h2 id="获取教程"><a href="#获取教程" class="headerlink" title="获取教程"></a>获取教程</h2><p>这个项目我从头到尾写了一遍，解决了项目中遇到的所有问题，由于有一些包或者模块升级的原因，会有一些小的问题，我都已经解决过了。想获取教程或者有疑问的，可以加这个群。</p><font color="#f69" size="5">建了一个qq群，可以点击这个logo<a target="_blank" href="//shang.qq.com/wpa/qunwpa?idkey=32da7a18744756b0d8ffdd05b84999afecb5265dbad0fb119033e122abe803f3"><img border="0" width="150" src="https://neveryu.github.io/vue-tour/logo1.png" alt="Vue学习交流" title="Vue学习交流" style="vertical-align: middle"></a>，或者手动search群号：<a target="_blank" href="//shang.qq.com/wpa/qunwpa?idkey=32da7a18744756b0d8ffdd05b84999afecb5265dbad0fb119033e122abe803f3">685486827</a></font>]]></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 id=&quot;div-border-top-green&quot;&gt;本项目是基于 vue2.4.1 最新的实战项目，vue-cli2.9.3 + vue2.4.1 + axios + vue-router3.0.1 + es6 + vux3.0.1 + webpack + better-scroll + 线上真实接口的一个移动端音乐 app。&lt;/p&gt;
    
    </summary>
    
      <category term="前端" scheme="https://309893147.github.io/categories/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="vue" scheme="https://309893147.github.io/tags/vue/"/>
    
  </entry>
  
  <entry>
    <title>vue2.x 做一个外卖app</title>
    <link href="https://309893147.github.io/2017/11/11/vue-sell/"/>
    <id>https://309893147.github.io/2017/11/11/vue-sell/</id>
    <published>2017-11-10T19:25:24.000Z</published>
    <updated>2019-07-30T01:31:38.216Z</updated>
    
    <content type="html"><![CDATA[<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p id="div-border-top-green">本项目是基于 vue2.4 最新的实战项目，vue-cli2.8 + vue2.4 + vue-resource + vue-router2.7 + es6 + mock + webpack 的一个移动端外卖 app。</p><p><img src="/images/vue-sell-1.png" alt=""></p><p id="div-border-left-red">现在最流行的开发方式就是前后分离了；<br><a href="/tags/vue">vue</a> 也是现在最流行的前端框架之一。</p><a id="more"></a><h2 id="截屏演示"><a href="#截屏演示" class="headerlink" title="截屏演示"></a>截屏演示</h2><p><img src="/images/vue-sell-3.gif" alt=""></p><h2 id="移动端演示"><a href="#移动端演示" class="headerlink" title="移动端演示"></a>移动端演示</h2><p>扫二维码在手机上查看效果更好<br><img src="/images/vue-sell-2.png" alt=""></p><h2 id="构建"><a href="#构建" class="headerlink" title="构建"></a>构建</h2><p>vue 有自己的脚手架构建工具 vue-cli 。使用起来非常方便，使用 webpack 来集成各种开发便捷工具，比如：</p><ul><li>代码热更新，修改代码之后网页无刷新改变，对前端开发来说非常的方便</li><li>Postcss，再也不用去管兼容性的问题了，只针对 chrome 写 css 代码，会自动编译生成支持多款浏览器的 css 代码</li><li>ESlint，统一代码风格</li><li>bable，ES2015 出来已经有一段事件了，但是不少浏览器还没有兼容 ES6。有了 bable，放心使用 ES6 语法，它会自动转义成 ES5 语法</li><li>Stylus，类似于 sass/scss ，但是可以不写 <code>{ }</code> 和 <code>:</code>，使用起来还是很方便的</li></ul><p>除此之外，vue-cli 已经使用 node 配置了一套本地服务器和安装命令等，本地运行和打包只需要一个命令就可以搞定，非常的方便。</p><h2 id="为什么写这个项目"><a href="#为什么写这个项目" class="headerlink" title="为什么写这个项目"></a>为什么写这个项目</h2><p>vue 的官网文档写的很好，很多同学在学习完文档以后，很难实际上手做项目，只能做一些 todo-list 的小 demo ,距离上手做项目还有一些差距。<br>这一套 vue 的实战视频可以说是非常适合新手入门进阶，让你可以动手用 vue 来做项目，解决 vue 在实战中的问题才是大家最想学习的，而不是小打小闹的 demo。<br><img src="/images/vue-sell-5.png" alt=""></p><p>而且这套视频教程包含现在最新的前端必备技术点详细教学：webpack、eslint、vue-cli构建、模块打包、mock、vue-resource、axios、以及真实项目中会遇到的各种问题以及解决方案。</p><h2 id="获取教程"><a href="#获取教程" class="headerlink" title="获取教程"></a>获取教程</h2><p>这个项目我从头到尾写了一遍，由于有一些包或者模块升级的原因，会有一些小的问题，我都已经解决过了。想获取教程或者有疑问的，可以加这个群。</p><font color="#f69" size="5">建了一个qq群，可以点击这个logo<a target="_blank" href="//shang.qq.com/wpa/qunwpa?idkey=32da7a18744756b0d8ffdd05b84999afecb5265dbad0fb119033e122abe803f3"><img border="0" width="150" src="https://neveryu.github.io/vue-tour/logo1.png" alt="Vue学习交流" title="Vue学习交流" style="vertical-align: middle"></a>，或者手动search群号：<a target="_blank" href="//shang.qq.com/wpa/qunwpa?idkey=32da7a18744756b0d8ffdd05b84999afecb5265dbad0fb119033e122abe803f3">685486827</a></font>]]></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 id=&quot;div-border-top-green&quot;&gt;本项目是基于 vue2.4 最新的实战项目，vue-cli2.8 + vue2.4 + vue-resource + vue-router2.7 + es6 + mock + webpack 的一个移动端外卖 app。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/vue-sell-1.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p id=&quot;div-border-left-red&quot;&gt;现在最流行的开发方式就是前后分离了；&lt;br&gt;&lt;a href=&quot;/tags/vue&quot;&gt;vue&lt;/a&gt; 也是现在最流行的前端框架之一。&lt;/p&gt;
    
    </summary>
    
      <category term="前端" scheme="https://309893147.github.io/categories/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="vue" scheme="https://309893147.github.io/tags/vue/"/>
    
  </entry>
  
  <entry>
    <title>Hexo-NexT搭建个人博客（五）</title>
    <link href="https://309893147.github.io/2017/07/15/hexo-next-five/"/>
    <id>https://309893147.github.io/2017/07/15/hexo-next-five/</id>
    <published>2017-07-15T15:25:24.000Z</published>
    <updated>2019-07-30T01:31:38.205Z</updated>
    
    <content type="html"><![CDATA[<p>在这之前，我写过四篇关于 Hexo + NexT 构建博客的文章。=》【<a href="/categories/Hexo/">传送门</a>】</p><p>本文将会介绍一些自定义的功能。相较于之前主要是修改配置文件中的内容，现在更多的是动手改源码来实现功能，而且还能帮你搞懂一些 Hexo NexT 的源码。如果你能弄懂源码的一些流程和逻辑，那么，你将能更好的来实现自己的一些想法。</p><h1 id="文章封面"><a href="#文章封面" class="headerlink" title="文章封面"></a>文章封面</h1><p>文章封面的意思就是：在博客首页的时候会显示文章的封面图片，进入这篇文章的详细页面后，将不显示这张图片。</p><p>如果想添加文章封面的话，需要添加一个字段属性：<code>summary_img</code>，<code>summary_img</code> 的值是图片的路径。</p><a id="more"></a><p>例如：<br><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></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">title: CSS 各种Hack手段</span><br><span class="line">date: 2017-06-25 03:25:24</span><br><span class="line">categories: 前端</span><br><span class="line">tags: [CSS]</span><br><span class="line">comments: false</span><br><span class="line">summary_img: /images/css-hack-1.png</span><br><span class="line">---</span><br></pre></td></tr></table></figure></p><p>具体实现细节如下：<br>修改 <code>\themes\next\layout\_macro\post.swing</code> 文件。<br>将代码：<br><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></pre></td><td class="code"><pre><span class="line">&#123;% if post.summary_img  %&#125;</span><br><span class="line">  &lt;div class=&quot;out-img-topic&quot;&gt;</span><br><span class="line">    &lt;img src=&#123;&#123; post.summary_img &#125;&#125; class=&quot;img-topic&quot;&gt;</span><br><span class="line">  &lt;/div&gt;</span><br><span class="line">&#123;% endif %&#125;</span><br></pre></td></tr></table></figure></p><p>添加到下图所示的位置</p><p><img src="/images/hexo-next-five-1.png" alt=""></p><p>这样的话，就可以使用 <code>summary_img: imageurl</code> 来设置文章封面了。</p><p>开启了文章封面的文章，我建议将 <code>&lt;!-- more --&gt;</code> 放在文章内容的开头，像这样：</p><p><img src="/images/hexo-next-five-2.png" alt=""></p><h1 id="网页加载进度条"><a href="#网页加载进度条" class="headerlink" title="网页加载进度条"></a>网页加载进度条</h1><p>打开 <code>/themes/next/layout/_partials/head.swing</code> 文件，在文件末尾添加如下代码：<br><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">&lt;!-- 网页加载条 --&gt;</span><br><span class="line">&lt;script src=&quot;https://neveryu.github.io/js/src/pace.min.js&quot;&gt;&lt;/script&gt;</span><br></pre></td></tr></table></figure></p><p>然后，打开 <code>/themes/source/css/_custom/custom.styl</code> 文件，在文件末尾添加如下代码：<br><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><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></pre></td><td class="code"><pre><span class="line">/*网页加载条*/</span><br><span class="line">/* This is a compiled file, you should be editing the file in the templates directory */</span><br><span class="line">.pace &#123;</span><br><span class="line">  -webkit-pointer-events: none;</span><br><span class="line">  pointer-events: none;</span><br><span class="line">  -webkit-user-select: none;</span><br><span class="line">  -moz-user-select: none;</span><br><span class="line">  user-select: none;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">.pace-inactive &#123;</span><br><span class="line">  display: none;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">.pace .pace-progress &#123;</span><br><span class="line">  background: #1e92fb;</span><br><span class="line">  position: fixed;</span><br><span class="line">  z-index: 2000;</span><br><span class="line">  top: 0;</span><br><span class="line">  right: 100%;</span><br><span class="line">  width: 100%;</span><br><span class="line">  height: 3px;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">.pace .pace-progress-inner &#123;</span><br><span class="line">  display: block;</span><br><span class="line">  position: absolute;</span><br><span class="line">  right: 0px;</span><br><span class="line">  width: 100px;</span><br><span class="line">  height: 100%;</span><br><span class="line">  box-shadow: 0 0 10px #e90f92, 0 0 5px #e90f92;</span><br><span class="line">  opacity: 1.0;</span><br><span class="line">  -webkit-transform: rotate(3deg) translate(0px, -4px);</span><br><span class="line">  -moz-transform: rotate(3deg) translate(0px, -4px);</span><br><span class="line">  -ms-transform: rotate(3deg) translate(0px, -4px);</span><br><span class="line">  -o-transform: rotate(3deg) translate(0px, -4px);</span><br><span class="line">  transform: rotate(3deg) translate(0px, -4px);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">.pace .pace-activity &#123;</span><br><span class="line">  display: block;</span><br><span class="line">  position: fixed;</span><br><span class="line">  z-index: 2000;</span><br><span class="line">  top: 15px;</span><br><span class="line">  right: 15px;</span><br><span class="line">  width: 14px;</span><br><span class="line">  height: 14px;</span><br><span class="line">  border: solid 2px transparent;</span><br><span class="line">  border-top-color: #e90f92;</span><br><span class="line">  border-left-color: #e90f92;</span><br><span class="line">  border-radius: 10px;</span><br><span class="line">  -webkit-animation: pace-spinner 400ms linear infinite;</span><br><span class="line">  -moz-animation: pace-spinner 400ms linear infinite;</span><br><span class="line">  -ms-animation: pace-spinner 400ms linear infinite;</span><br><span class="line">  -o-animation: pace-spinner 400ms linear infinite;</span><br><span class="line">  animation: pace-spinner 400ms linear infinite;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">@-webkit-keyframes pace-spinner &#123;</span><br><span class="line">  0% &#123; -webkit-transform: rotate(0deg); transform: rotate(0deg); &#125;</span><br><span class="line">  100% &#123; -webkit-transform: rotate(360deg); transform: rotate(360deg); &#125;</span><br><span class="line">&#125;</span><br><span class="line">@-moz-keyframes pace-spinner &#123;</span><br><span class="line">  0% &#123; -moz-transform: rotate(0deg); transform: rotate(0deg); &#125;</span><br><span class="line">  100% &#123; -moz-transform: rotate(360deg); transform: rotate(360deg); &#125;</span><br><span class="line">&#125;</span><br><span class="line">@-o-keyframes pace-spinner &#123;</span><br><span class="line">  0% &#123; -o-transform: rotate(0deg); transform: rotate(0deg); &#125;</span><br><span class="line">  100% &#123; -o-transform: rotate(360deg); transform: rotate(360deg); &#125;</span><br><span class="line">&#125;</span><br><span class="line">@-ms-keyframes pace-spinner &#123;</span><br><span class="line">  0% &#123; -ms-transform: rotate(0deg); transform: rotate(0deg); &#125;</span><br><span class="line">  100% &#123; -ms-transform: rotate(360deg); transform: rotate(360deg); &#125;</span><br><span class="line">&#125;</span><br><span class="line">@keyframes pace-spinner &#123;</span><br><span class="line">  0% &#123; transform: rotate(0deg); transform: rotate(0deg); &#125;</span><br><span class="line">  100% &#123; transform: rotate(360deg); transform: rotate(360deg); &#125;</span><br><span class="line">&#125;</span><br><span class="line">/*网页加载条 END*/</span><br></pre></td></tr></table></figure></p><h1 id="开发环境自动刷新"><a href="#开发环境自动刷新" class="headerlink" title="开发环境自动刷新"></a>开发环境自动刷新</h1><p>在 <a href="/2016/09/03/hexo-next-one/">Hexo-NexT搭建个人博客（一）</a> 已经提到了本地调试三部曲：<br><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></pre></td><td class="code"><pre><span class="line">hexo clean</span><br><span class="line">hexo generate</span><br><span class="line">hexo server --debug</span><br></pre></td></tr></table></figure></p><p>然后我在项目的 <code>package.json</code> 中配成了这样：<br><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">&quot;dev&quot;: &quot;hexo clean &amp;&amp; hexo generate &amp;&amp; hexo server --debug&quot;,</span><br></pre></td></tr></table></figure></p><p>这样的话，我执行 <code>npm run dev</code> 就可以启动本地环境了，省去了前面需要分别敲三次命令的步骤。<br>为什么可以这样写，详情看这里：<br><a href="/2017/04/10/npm/">npm 全面介绍</a><br><a href="/2017/05/20/npm-two/">npm 的一个小细节</a></p><p>这样还不爽，我希望在写博客的时候，按下 <code>Ctrl + S</code> 后能自动刷新浏览器，看到实时的效果，省去了自己手动刷新浏览器的过程，在双屏下，真的很好用，一边写一边看。<br>具体的做法是:<br>在项目的根目录下添加一个 <code>gulpfile.js</code> 文件，文件内容参看 <a href="https://github.com/Neveryu/Neveryu.github.io/blob/resource/gulpfile.js" target="_blank" rel="external">源码</a>，这里我就不贴了。<br>然后安装 <code>gulpfile.js</code> 里面的依赖包。</p><p><code>gulpfile.js</code> 里面有一个 dev-proxy 方法，会代理本地的 4000 端口，并且监听文件变化，如有变化就会自动刷新浏览器。</p><p>最后，我们的开发步骤就变成这样了：<br>先打开一个 Terminal ，使用 <code>npm run dev</code> 开启本地的博客服务。<br>然后再开一个 Terminal，使用 <code>gulp</code> 命令来开启监听和代理服务。</p><h1 id="代码压缩"><a href="#代码压缩" class="headerlink" title="代码压缩"></a>代码压缩</h1><p>在项目的根目录下，执行以下命令：<br><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">cnpm install gulp -g</span><br><span class="line">cnpm install gulp-minify-css gulp-uglify gulp-htmlmin gulp --save-dev</span><br></pre></td></tr></table></figure></p><p>然后在 <code>gulpfile.js</code> 里面写上相关代码，详情查看 <a href="https://github.com/Neveryu/Neveryu.github.io/blob/resource/gulpfile.js" target="_blank" rel="external">源码</a> 。</p><p>然后执行 <code>gulp min</code> 就会根据 <code>gulpfile.js</code> 中的配置，对 public 目录中的静态资源文件进行压缩。</p><p>鼠标右键 -&gt; 查看网页源代码，可以看到已经是压缩过的。</p><h1 id="自定义页面与目录"><a href="#自定义页面与目录" class="headerlink" title="自定义页面与目录"></a>自定义页面与目录</h1><p>下面介绍两种方法：</p><p>第一种方法是使用 Hexo 提供的跳过渲染配置，适用于整个目录的设置。</p><p><img src="/images/hexo-next-five-3.png" alt=""></p><p>具体步骤，打开博客根目录_config.yml，找到其中 skip_render 配置项，这个用来配置 /source/ 中需要跳过渲染的文件或目录，例如希望跳过 /source/projects/ 里的所有文件渲染，可以配置为：<br><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">skip_render: projects/**</span><br></pre></td></tr></table></figure></p><p>匹配规则是一种类似正则的规则，官方给出的参考是<a href="https://github.com/isaacs/node-glob" target="_blank" rel="external">这个</a>。另外在测试这个功能的时候发现，Hexo 的内部缓存不是特别好用，有时候你修改了配置但生成出来的内容不一定及时应用了新配置，最好在生成之前执行一下 hexo clean 命令，清除掉旧的生成文件和缓存。</p><p>第二种方法是给单个文件添加不应用模板的标记，适用于个别特殊文件的处理。例如我们的网站如果要使用百度统计，往往需要在根目录放一个 html 格式的验证文件，这个文件默认也会经过用主题模板渲染，避免渲染的办法就是在文件头部添加如下内容：</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></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">layout: false</span><br><span class="line">---</span><br></pre></td></tr></table></figure><p>　　<br>这样，这个文件就不会经过模板渲染，最终发布到 /public/ 里的文件就是去掉标记后的文件的样子。</p><h1 id="关于-categories-和-tags-页面-Cannot-GET-的解决方案"><a href="#关于-categories-和-tags-页面-Cannot-GET-的解决方案" class="headerlink" title="关于 categories 和 tags 页面 Cannot GET  的解决方案"></a>关于 categories 和 tags 页面 Cannot GET  的解决方案</h1><p>有同学反馈在配置文件中配置了 categories 和 tags 后依然没有 categories 和 tags 页面，提示 Cannot GET。<br><img src="/images/hexo-next-five-4.png" alt=""></p><p>其实在配置了 categories 和 tags  后，还需要在 /source/ 目录下新建 categories 目录和 tags 目录，里面的要有 index.md 文件，并且文件开头不能少，也不能写错。<br><img src="/images/hexo-next-five-5.png" alt=""></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;在这之前，我写过四篇关于 Hexo + NexT 构建博客的文章。=》【&lt;a href=&quot;/categories/Hexo/&quot;&gt;传送门&lt;/a&gt;】&lt;/p&gt;
&lt;p&gt;本文将会介绍一些自定义的功能。相较于之前主要是修改配置文件中的内容，现在更多的是动手改源码来实现功能，而且还能帮你搞懂一些 Hexo NexT 的源码。如果你能弄懂源码的一些流程和逻辑，那么，你将能更好的来实现自己的一些想法。&lt;/p&gt;
&lt;h1 id=&quot;文章封面&quot;&gt;&lt;a href=&quot;#文章封面&quot; class=&quot;headerlink&quot; title=&quot;文章封面&quot;&gt;&lt;/a&gt;文章封面&lt;/h1&gt;&lt;p&gt;文章封面的意思就是：在博客首页的时候会显示文章的封面图片，进入这篇文章的详细页面后，将不显示这张图片。&lt;/p&gt;
&lt;p&gt;如果想添加文章封面的话，需要添加一个字段属性：&lt;code&gt;summary_img&lt;/code&gt;，&lt;code&gt;summary_img&lt;/code&gt; 的值是图片的路径。&lt;/p&gt;
    
    </summary>
    
      <category term="Hexo" scheme="https://309893147.github.io/categories/Hexo/"/>
    
    
      <category term="Hexo" scheme="https://309893147.github.io/tags/Hexo/"/>
    
      <category term="Next" scheme="https://309893147.github.io/tags/Next/"/>
    
  </entry>
  
  <entry>
    <title>CSS 各种Hack手段</title>
    <link href="https://309893147.github.io/2017/06/25/css-hack/"/>
    <id>https://309893147.github.io/2017/06/25/css-hack/</id>
    <published>2017-06-24T19:25:24.000Z</published>
    <updated>2019-07-30T01:31:38.203Z</updated>
    
    <content type="html"><![CDATA[<!-- <img src="/images/css-hack-1.png" alt="css-hack"> --><a id="more"></a><p>随着浏览器的发展，css hack 技术的使用应该越来越少了，但是在某些关键时刻以及综合的WEB应用或者老项目中，可能还需要使用 css hack 技术来解决一些问题。</p><h1 id="css-hack-分类"><a href="#css-hack-分类" class="headerlink" title="css hack 分类"></a>css hack 分类</h1><p>css hack 分类大致有 3 种表现形式：<strong>IE条件注释法</strong>、<strong>CSS属性前缀法</strong>以及<strong>选择器前缀法</strong>。</p><p>IE 条件注释法（即 HTML 条件注释 Hack）：<br>针对所有IE(注：IE10+ 已经不再支持条件注释)：<br><code>&lt;!--[if IE]&gt;IE浏览器显示的内容 &lt;![endif]--&gt;</code>；</p><p>针对 IE6 及以下版本：<br><code>&lt;!--[if lt IE 6]&gt;只在IE6-显示的内容 &lt;![endif]--&gt;</code>。<br>这类 Hack 不仅对 CSS 生效，对写在判断语句里面的所有代码都会生效。</p><p>属性前缀法（即类内部 Hack）：例如 IE6 能识别下划线 <code>_</code> 和星号 <code>*</code>，IE7 能识别星号 <code>*</code>，但不能识别下划线 <code>_</code>，IE6~IE10 都认识 <code>\9</code>，但 firefox 前述三个都不能认识。</p><p>选择器前缀法（即选择器 Hack）：例如 IE6 能识别 <code>*html .class{}</code>，IE7 能识别 <code>*+html .class{}</code> 或者 <code>*:first-child+html .class{}</code>。</p><p>css hack 书写顺序，一般是将适用范围广、被识别能力强的 CSS 定义在前面。</p><h1 id="条件注释法"><a href="#条件注释法" class="headerlink" title="条件注释法"></a>条件注释法</h1><p><strong>语法：</strong><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- [if &lt;keywords&gt;? IE &lt;version&gt; ?]&gt;</span></span><br><span class="line"><span class="comment">HTML 代码块</span></span><br><span class="line"><span class="comment">&lt;![endif]--&gt;</span></span><br></pre></td></tr></table></figure></p><p><strong>取值：</strong><br><code>&lt;keywords&gt;</code><br>if 条件共包含 6 种选择方式：是否、大于、大于或等于、小于、小于或等于、非指定版本<br><strong>是否</strong>：指定是否 IE 或 IE 某个版本。关键字：空<br><strong>大于</strong>：选择大鱼指定版本的 IE 版本。关键字：gt<br><strong>大于或等于</strong>：选择大于或等于指定版本的 IE 版本。关键字：gte<br><strong>小于</strong>：选择小于指定版本的IE版本。关键字：lt<br><strong>小于或等于</strong>：选择小于或等于指定版本的IE版本。关键字：lte<br><strong>非指定版本</strong>：选择除指定版本外的所有IE版本。关键字：!</p><p><strong>说明：</strong><br>用于选择 IE 浏览器及IE的不同版本</p><p><strong>示例：</strong></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></pre></td><td class="code"><pre><span class="line">只在IE下生效</span><br><span class="line">&lt;!--[if IE]&gt;</span><br><span class="line">这段文字只在IE浏览器显示</span><br><span class="line">&lt;![endif]--&gt;</span><br><span class="line"></span><br><span class="line">只在IE6下生效</span><br><span class="line">&lt;!--[if IE 6]&gt;</span><br><span class="line">这段文字只在IE6浏览器显示</span><br><span class="line">&lt;![endif]--&gt;</span><br><span class="line"></span><br><span class="line">只在IE6以上版本生效</span><br><span class="line">&lt;!--[if gte IE 6]&gt;</span><br><span class="line">这段文字只在IE6以上(包括)版本IE浏览器显示</span><br><span class="line">&lt;![endif]--&gt;</span><br><span class="line"></span><br><span class="line">只在IE8上不生效</span><br><span class="line">&lt;!--[if ! IE 8]&gt;</span><br><span class="line">这段文字在非IE8浏览器显示</span><br><span class="line">&lt;![endif]--&gt;</span><br><span class="line"></span><br><span class="line">非IE浏览器生效</span><br><span class="line">&lt;!--[if !IE]&gt;</span><br><span class="line">这段文字只在非IE浏览器显示</span><br><span class="line">&lt;![endif]--&gt;</span><br></pre></td></tr></table></figure><p>需要说明的是，IE10和11已经不支持这种条件注释法了。<a href="/yu/css-hack.html" target="_blank">运行上面示例</a></p><h1 id="CSS-属性前缀法"><a href="#CSS-属性前缀法" class="headerlink" title="CSS 属性前缀法"></a>CSS 属性前缀法</h1><p><strong>语法：</strong><br>selector {<hack>?property:value<hack>?;}</hack></hack></p><p><strong>取值：</strong><br><code>_</code>：选择 IE6 及以下。连接线（中划线）（-）亦可使用，为了避免与某些带中划线的属性混淆，所以使用下划线（_）更为合适。<br><code>*</code>：选择 IE7 及以下。诸如：（+）与（#）之类的均可使用，不过业界对（*）的认知度更高。<br><code>\9</code>：选择 IE6+。<br><code>\0</code>：选择 IE8+ 和 Opera。<br><code>[;property:value;];</code>：选择 webkit 核心浏览器（Chrome,Safari）。IE7 及以下也能识别。中括号内外的 3 个分号必须保留，第一个分号前可以是任意规则或任意多个规则。<br><code>[;color:#f00;];</code> 与 <code>[color:#f00;color:#f00;];</code> 与 <code>[margin:0;padding:0;color:#f00;];</code> 是等价的。生效的始终是中括号内的最后一条规则，所以通常选用第一种写法最为简洁。</p><p><strong>说明：</strong><br><strong>选择不同的浏览器及版本</strong>尽可能减少对 CSS Hack 的使用。Hack 有风险，谨慎使用。<br>一些 CSS Hack 由于浏览器存在交叉认识，所以需要通过层层覆盖的方式来实现对不同浏览器进行 Hack 的。如下面这个例子：<br><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></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.test</span>&#123;</span><br><span class="line">  <span class="attribute">color</span>:<span class="number">#090</span>\<span class="number">9</span>; <span class="comment">/* For IE8+ */</span></span><br><span class="line">  *color:#f00;  /* For IE7 and earlier */</span><br><span class="line">  _<span class="selector-tag">color</span>:<span class="selector-id">#ff0</span>;  <span class="comment">/* For IE6 and earlier */</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p id="div-border-left-yellow">上述 Hack 均需运行在标准模式下，若在怪异模式下运行，这些 Hack 将会被不同版本的 IE 相互识别，导致失效。</p><h1 id="选择器前缀法"><a href="#选择器前缀法" class="headerlink" title="选择器前缀法"></a>选择器前缀法</h1><p><strong>语法：</strong><br><code>&lt;hack&gt;selector{sRules}</code></p><p><strong>说明：</strong><br><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></pre></td><td class="code"><pre><span class="line">* <span class="selector-tag">html</span> <span class="selector-class">.test</span>&#123;<span class="attribute">color</span>:<span class="number">#090</span>;&#125;       <span class="comment">/* For IE6 and earlier */</span></span><br><span class="line">* + <span class="selector-tag">html</span> <span class="selector-class">.test</span>&#123;<span class="attribute">color</span>:<span class="number">#ff0</span>;&#125;     <span class="comment">/* For IE7 */</span></span><br><span class="line"><span class="selector-class">.test</span><span class="selector-pseudo">:lang(zh-cn)</span>&#123;<span class="attribute">color</span>:<span class="number">#f00</span>;&#125;  <span class="comment">/* For IE8+ and not IE */</span></span><br><span class="line"><span class="selector-class">.test</span><span class="selector-pseudo">:nth-child(1)</span>&#123;<span class="attribute">color</span>:<span class="number">#0ff</span>;&#125; <span class="comment">/* For IE9+ and not IE */</span></span><br></pre></td></tr></table></figure></p><p id="div-border-left-yellow">上述代码中的3,4两行就是典型的利用能力来进行选择的 CSS Hack。</p>]]></content>
    
    <summary type="html">
    
      &lt;!-- &lt;img src=&quot;/images/css-hack-1.png&quot; alt=&quot;css-hack&quot;&gt; --&gt;
    
    </summary>
    
      <category term="前端" scheme="https://309893147.github.io/categories/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="CSS" scheme="https://309893147.github.io/tags/CSS/"/>
    
  </entry>
  
  <entry>
    <title>使用 Service worker 实现加速/离线访问博客</title>
    <link href="https://309893147.github.io/2017/06/08/service-worker/"/>
    <id>https://309893147.github.io/2017/06/08/service-worker/</id>
    <published>2017-06-08T03:25:24.000Z</published>
    <updated>2019-07-30T01:31:38.213Z</updated>
    
    <content type="html"><![CDATA[<p>有一个困扰 web 用户多年的难题——丢失网络连接。即使是世界上最好的 web app，如果下载不了它，也是非常糟糕的体验。如今虽然已经有很多种技术去尝试着解决这一问题。而随着<strong>离线页面</strong>的出现，一些问题已经得到了解决。有一个叫做 APP Cache 的 API 可以提供离线体验，但它的问题比较多。最重要的问题是，仍然没有一个好的统筹机制对资源缓存和自定义的网络请求进行控制。</p><h1 id="Service-worker"><a href="#Service-worker" class="headerlink" title="Service worker"></a>Service worker</h1><p>于是 HTML5 提出了 Service Worker，Service worker 提供了很多新的能力，使得 web app 拥有与 nativeapp 相同的离线体验、消息推送体验。</p><a id="more"></a><p>Service worker 是一段脚本，它有能力往我们的浏览器中写入缓存，过滤网络请求，将缓存内容作为网络响应结果输出。<br><strong>带来的效果是显而易见的：</strong></p><p id="div-border-top-green">1、当我们缓存了某些资源的时候，当我们再次请求该资源的时候，我们便可以使用缓存的内容，这样的话，就可以减少网络请求了，网站的打开速度明显提升。<br>2、如果我们将网站所需的资源缓存下来了以后，这个时候即使计算机没有网络，依然可以打开这个网站，即离线访问。<br></p><h1 id="Service-worker-使用场景"><a href="#Service-worker-使用场景" class="headerlink" title="Service worker 使用场景"></a>Service worker 使用场景</h1><p>现在很流行基于 GitHub page 和 markdown 的静态 blog ，非常适合技术的思维和习惯，针对不同的语言都有一些优秀的静态 blog 系统出现，如 Jekyll/Ruby，Pelican/Python，Hexo/NodeJs ，由于静态内容的特性非常适合做缓存来加速页面的访问，就利用 Service worker 来实现加速，结果是除了 PageSpeed，CDN 这些常见的服务器和网络加速之外，通过客户端实现了更好的访问体验。</p><h1 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h1><p>你现在可以断开你的网络，或者用浏览器中模拟无网络的情景，继续访问本站。<br><i style="color:red;">如何在浏览器中模拟无网络环境？(在 Network 中选择 offline)</i><br><img src="/images/service-worker-1.png" alt=""></p><h1 id="使用方法"><a href="#使用方法" class="headerlink" title="使用方法"></a>使用方法</h1><h2 id="注册-Service-worker"><a href="#注册-Service-worker" class="headerlink" title="注册 Service worker"></a>注册 Service worker</h2><p>要安装 Service worker，你需要在你的页面上注册它。下面的代码会告诉浏览器你的 Service worker 脚本放在哪里<br><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></pre></td><td class="code"><pre><span class="line">&lt;script&gt;</span><br><span class="line">  <span class="keyword">if</span> (navigator.serviceWorker) &#123;</span><br><span class="line">    <span class="comment">// 注册Service Worker scope表示作用的页面的path</span></span><br><span class="line">    <span class="comment">// register函数返回Promise</span></span><br><span class="line">    navigator.serviceWorker.register(<span class="string">'/service-worker.js'</span>,&#123;<span class="attr">scope</span>: <span class="string">'/'</span>&#125;) </span><br><span class="line">      .then(<span class="function"><span class="keyword">function</span> (<span class="params">registration</span>) </span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(registration);</span><br><span class="line">      &#125;)</span><br><span class="line">      .catch(<span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.error(e);</span><br><span class="line">      &#125;)</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'Service Worker is not supported in this browser.'</span>)</span><br><span class="line">  &#125;</span><br><span class="line">&lt;<span class="regexp">/script&gt;</span></span><br></pre></td></tr></table></figure></p><p>以上代码会先检测 Service worker 在浏览器中是否可用，可用的话一个 Service worker（/service-worker.js）将被注册，如果这个 Service worker 已经注册过了，浏览器这会忽略以上代码。<br><code>{scope: &#39;/&#39;}</code> 表示 Service worker 作用的范围。<br>需要说明的是 service-worker.js 文件被放在这个域的根目录下，这意味着 service worker 是跟网站同源的。换句话说，这个 service worker 将会获取到这个域下的所有 fetch 事件。<br>如果 service worker 文件注册到 /example/service-worker.js ，那么 service worker 只能收到 /example/ 路径下的 fetch 事件（比如： /example/page1/, /example/page2/）。<br>如果 service worker 文件注册到根目录下 /service-worker.js ，同时 <code>{scope: &#39;/example&#39;}</code> ，那么 service worker 也只能收到 /example/ 路径下的 fetch 事件。</p><p>service-worker.js 文件，我建议是放在网站的跟目录下，scope 不作修改，这样 service worker 拥有最大的使用范围。</p><h2 id="安装-Service-worker"><a href="#安装-Service-worker" class="headerlink" title="安装 Service worker"></a>安装 Service worker</h2><p><img src="/images/service-worker-2.png" alt=""></p><p><a href="https://neveryu.github.io/service-worker.js" target="_blank" rel="external">我的 service-worker.js</a><br>关于这个 service-worker.js 怎么写，具体可以查看 API ，<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Service_Worker_API" target="_blank" rel="external">MDN Service Worker API</a>。</p><p id="div-border-left-red">注意：将 service-worker.js 放到域的根目录下哦，这样 Service worker 才能拥有最大的使用范围。</p><p>现在你可以到 chrome://inspect/#service-workers 这里，检查 service worker 是否对你的网站启用了。<br>或者在 chrome://serviceworker-internals/ 中管理你的 Service worker 。<br>或者在浏览器的开发者工具中也可以详细的查看 service worker 的缓存。<br><img src="/images/service-worker-3.png" alt=""></p><h1 id="Service-worker-核心-API"><a href="#Service-worker-核心-API" class="headerlink" title="Service worker 核心 API"></a>Service worker 核心 API</h1><p><img src="/images/service-worker-4.png" alt=""></p><h2 id="install"><a href="#install" class="headerlink" title="install"></a>install</h2><p><img src="/images/service-worker-5.png" alt=""><br>install 是安装一个 service worker 缓存，使用方法可以是这样：<br><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="comment">// Set the callback for the install step</span></span><br><span class="line">self.addEventListener(<span class="string">'install'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// Perform install steps</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p><p>在 install 的 callback 中，我们需要执行一下步骤：<br>1、开启一个缓存<br>2、缓存我们的文件<br>3、确定所有的资源是否要被缓存</p><h2 id="fetch"><a href="#fetch" class="headerlink" title="fetch"></a>fetch</h2><p>fetch 用来监听用户的网络请求，并给出回应。<br><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">self.addEventListener(<span class="string">'fetch'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>&#123;</span><br><span class="line">  event.respondWith(</span><br><span class="line">    caches.match(event.request)</span><br><span class="line">      .then(<span class="function"><span class="keyword">function</span>(<span class="params">response</span>) </span>&#123;</span><br><span class="line">        <span class="comment">// Cache hit - return response</span></span><br><span class="line">        <span class="keyword">if</span> (response) &#123;</span><br><span class="line">          <span class="keyword">return</span> response;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> fetch(event.request);</span><br><span class="line">      &#125;</span><br><span class="line">    )</span><br><span class="line">  );</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p><h2 id="activate"><a href="#activate" class="headerlink" title="activate"></a>activate</h2><p>你的 Service worker 总会有要更新的时候。在那时，你需要按照一下步骤来更新：<br>1、更新你 service worker 的 JavaScript 文件。<br>2、更新后的 service worker 启动并触发 install 事件。<br>3、此时，当前页面生效的依然是老版本的 service worker ，新的 service worker 会进入“waitting”状态。<br>4、当页面关闭后，来的 service worker 会被干掉，新的 service worker 接管页面。<br>5、一旦新的 service worker 生效后会触发 active 事件。</p><p>一个典型的 activete 事件：<br><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></pre></td><td class="code"><pre><span class="line">self.addEventListener(<span class="string">'activate'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">event</span>) </span>&#123; </span><br><span class="line"><span class="comment">// 监听worker的activate事件</span></span><br><span class="line">  event.waitUntil( <span class="comment">// 延迟activate事件直到</span></span><br><span class="line">    caches.keys().then(<span class="function"><span class="keyword">function</span>(<span class="params">keys</span>)</span>&#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">Promise</span>.all(keys.map(<span class="function"><span class="keyword">function</span>(<span class="params">key, i</span>)</span>&#123; <span class="comment">// 清除旧版本缓存</span></span><br><span class="line">        <span class="keyword">if</span>(key !== CACHE_VERSION)&#123;</span><br><span class="line">          <span class="keyword">return</span> caches.delete(keys[i]);</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></p><h1 id="其他使用场景"><a href="#其他使用场景" class="headerlink" title="其他使用场景"></a>其他使用场景</h1><p>在网站 A 中，隐藏一个 iframe ，在这个 iframe 中注册一个 service worker ，这个 service worker 会缓存网站 B 所需的资源。<br>从未访问过网站 B，但网站已经在你的设备上预加载过了，一切仅仅因为你访问过网站 A。<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">iframe</span> <span class="attr">src</span>=<span class="string">"https://B.com/iframe.html"</span> <span class="attr">style</span>=<span class="string">"width: 0; height: 0; border: 0"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">iframe</span>&gt;</span></span><br></pre></td></tr></table></figure></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></pre></td><td class="code"><pre><span class="line">&lt;!DOCTYPE html&gt;</span><br><span class="line">&lt;html lang=<span class="string">"en"</span>&gt;</span><br><span class="line">&lt;head&gt;</span><br><span class="line">&lt;meta charset=<span class="string">"utf-8"</span>&gt;</span><br><span class="line">&lt;title&gt;HTML5 For Web Designers&lt;<span class="regexp">/title&gt;</span></span><br><span class="line"><span class="regexp">&lt;script&gt;</span></span><br><span class="line"><span class="regexp">if ('serviceWorker' in navigator) &#123;</span></span><br><span class="line"><span class="regexp">  navigator.serviceWorker.register('/</span>serviceworker.js<span class="string">');</span></span><br><span class="line"><span class="string">&#125;</span></span><br><span class="line"><span class="string">&lt;/script&gt;</span></span><br><span class="line"><span class="string">&lt;/head&gt;</span></span><br><span class="line"><span class="string">&lt;/html&gt;</span></span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;有一个困扰 web 用户多年的难题——丢失网络连接。即使是世界上最好的 web app，如果下载不了它，也是非常糟糕的体验。如今虽然已经有很多种技术去尝试着解决这一问题。而随着&lt;strong&gt;离线页面&lt;/strong&gt;的出现，一些问题已经得到了解决。有一个叫做 APP Cache 的 API 可以提供离线体验，但它的问题比较多。最重要的问题是，仍然没有一个好的统筹机制对资源缓存和自定义的网络请求进行控制。&lt;/p&gt;
&lt;h1 id=&quot;Service-worker&quot;&gt;&lt;a href=&quot;#Service-worker&quot; class=&quot;headerlink&quot; title=&quot;Service worker&quot;&gt;&lt;/a&gt;Service worker&lt;/h1&gt;&lt;p&gt;于是 HTML5 提出了 Service Worker，Service worker 提供了很多新的能力，使得 web app 拥有与 nativeapp 相同的离线体验、消息推送体验。&lt;/p&gt;
    
    </summary>
    
      <category term="前端" scheme="https://309893147.github.io/categories/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="Service-worker" scheme="https://309893147.github.io/tags/Service-worker/"/>
    
  </entry>
  
  <entry>
    <title>npm 的一个小细节</title>
    <link href="https://309893147.github.io/2017/05/20/npm-two/"/>
    <id>https://309893147.github.io/2017/05/20/npm-two/</id>
    <published>2017-05-19T17:25:24.000Z</published>
    <updated>2019-07-30T01:31:38.211Z</updated>
    
    <content type="html"><![CDATA[<p>在使用 electron 构建桌面应用的时候，在 package.json 里面的 scripts 字段是这样的<br><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></pre></td><td class="code"><pre><span class="line">&quot;scripts&quot;: &#123;</span><br><span class="line">  &quot;start&quot;: &quot;electron .&quot;</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure></p><p>我们可以执行 npm start ，那么它就会执行 <code>electron .</code> 这个命令。<br>那么如果我们直接执行 <code>electron .</code><br>由于我们没有将 electron 加入到全局，所以不行。<br>那么为什么 npm start 可以执行呢？</p><a id="more"></a><p>这就涉及到 npm run 命令的一个小细节了。<br>npm run xxx 可以执行 package.json 里面 scripts 里面对应的命令，并且是 shell 脚本。但是在执行的时候有一个小处理：</p><p><strong>npm run 新建的这个 shell ，会将当前目录的 node_modules/.bin 子目录加入 PATH 变量，执行结束后，再将 PATH 变量恢复原样。</strong></p><p>这就解释了，没有安装全局的 electron ，直接运行 <code>electron .</code> 是不行的，但是使用 <code>npm start</code> 来运行 <code>electron .</code> 可以。</p><p><strong>下面介绍一个使用 npm 的实践：</strong><br>很多朋友使用 hexo 来构建博客；hexo 是基于 Node.js 产物，用它发表博文，很是方便；你只需 hexo clean, hexo g, hexo d三个命令即可；而且每一个命令必须等待前一个命令运行完成。文章数据一多，一套命令打下来，也得 20s+；如果略懂 npm，在 package.js 中加入点命名，例如像这样；<br><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="string">"scripts"</span>: &#123;</span><br><span class="line">  <span class="string">"start"</span>: <span class="string">"sudo hexo clean &amp;&amp; sudo hexo g &amp;&amp; sudo gulp &amp;&amp; sudo hexo d"</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>那么 只需运行 npm start 就好，可将时间消耗缩短至 2s节省时间虽说不多，却也是数量级的提升，而且代价只是那么小，并一劳永逸。所以有必要对此。</p><p>关于 npm 的详细学习，可以查看：<a href="https://neveryu.github.io/2017/04/10/npm/" target="_blank" rel="external">npm 全面介绍</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;在使用 electron 构建桌面应用的时候，在 package.json 里面的 scripts 字段是这样的&lt;br&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;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&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;&amp;quot;scripts&amp;quot;: &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;  &amp;quot;start&amp;quot;: &amp;quot;electron .&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;我们可以执行 npm start ，那么它就会执行 &lt;code&gt;electron .&lt;/code&gt; 这个命令。&lt;br&gt;那么如果我们直接执行 &lt;code&gt;electron .&lt;/code&gt;&lt;br&gt;由于我们没有将 electron 加入到全局，所以不行。&lt;br&gt;那么为什么 npm start 可以执行呢？&lt;/p&gt;
    
    </summary>
    
      <category term="前端" scheme="https://309893147.github.io/categories/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="npm" scheme="https://309893147.github.io/tags/npm/"/>
    
  </entry>
  
  <entry>
    <title>gulp 详解与使用</title>
    <link href="https://309893147.github.io/2017/05/01/gulp/"/>
    <id>https://309893147.github.io/2017/05/01/gulp/</id>
    <published>2017-04-30T17:25:24.000Z</published>
    <updated>2019-07-30T01:31:38.204Z</updated>
    
    <content type="html"><![CDATA[<h1 id="什么是-gulp"><a href="#什么是-gulp" class="headerlink" title="什么是 gulp"></a>什么是 gulp</h1><p><a href="http://gulpjs.com/" target="_blank" rel="external">gulp</a> 是一个前端构建工具，它能通过自动执行常见任务，比如编译预处理 CSS ，压缩 JavaScript 和刷新浏览器，来改进网站开发的过程，从而使开发更加快速高效。</p><h1 id="为什么要用-gulp"><a href="#为什么要用-gulp" class="headerlink" title="为什么要用 gulp"></a>为什么要用 gulp</h1><p>与 grunt 相比，gulp 无需写一大堆繁杂的配置参数，<a href="https://github.com/gulpjs/gulp/blob/master/docs/API.md" target="_blank" rel="external">API</a>（<a href="http://www.gulpjs.com.cn/docs/api/" target="_blank" rel="external">中文 API</a>） 也非常简单，学习起来很容易，而且 gulp 使用的是 nodejs 中 <a href="https://nodejs.org/api/stream.html" target="_blank" rel="external">stream</a> 来读取和操作数据，其速度更快。<br>gulp 有庞大的生态圈，且每天都在发展。依靠成千上万可供选择的插件，你可以利用 gulp 自动完成几乎任何事。</p><h1 id="如何使用-gulp"><a href="#如何使用-gulp" class="headerlink" title="如何使用 gulp"></a>如何使用 gulp</h1><h2 id="Installing-Gulp"><a href="#Installing-Gulp" class="headerlink" title="Installing Gulp"></a>Installing Gulp</h2><p>新版的 gulp 命令行工具已经改名为 gulp-cli 。<br>如果你之前安装了全局的 gulp 。在使用新的 gulp-cli 之前，执行命令<br> <code>npm rm --global gulp</code> ，将之前的全局 gulp 卸掉。</p><a id="more"></a><h3 id="Install-the-gulp-command"><a href="#Install-the-gulp-command" class="headerlink" title="Install the gulp command"></a>Install the gulp command</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install --global gulp-cli</span><br></pre></td></tr></table></figure><h3 id="Install-gulp-in-your-devDependencies"><a href="#Install-gulp-in-your-devDependencies" class="headerlink" title="Install gulp in your devDependencies"></a>Install gulp in your devDependencies</h3><p>Run this command in your project directory<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install --save-dev gulp</span><br></pre></td></tr></table></figure></p><h2 id="Create-a-gulpfile"><a href="#Create-a-gulpfile" class="headerlink" title="Create a gulpfile"></a>Create a gulpfile</h2><p>Create a file called gulpfile.js in your project root with these contents:<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> gulp = <span class="built_in">require</span>(<span class="string">'gulp'</span>);</span><br><span class="line"></span><br><span class="line">gulp.task(<span class="string">'default'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// place code for your default tash here</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p><h2 id="Test-it-out"><a href="#Test-it-out" class="headerlink" title="Test it out"></a>Test it out</h2><p>Run the gulp command in your projct directory:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gulp</span><br></pre></td></tr></table></figure></p><h1 id="gulp-API"><a href="#gulp-API" class="headerlink" title="gulp API"></a>gulp API</h1><p>gulp 的核心 API 有四个：gulp.task() 、 gulp.src() 、 gulp.dest() 、 gulp.watch() 。<br><a href="https://github.com/gulpjs/gulp/blob/master/docs/API.md" target="_blank" rel="external">gulp API</a><br><a href="http://www.gulpjs.com.cn/docs/api/" target="_blank" rel="external">gulp API 中文</a></p><p>下面详细介绍一下：</p><h2 id="gulp-src"><a href="#gulp-src" class="headerlink" title="gulp.src()"></a>gulp.src()</h2><p>gulp.src() 可以读取你需要操作的文件，相比于 Grunt 主要以文件为媒介来运行它的工作流，gulp 使用的是 Nodejs 中的 <a href="https://nodejs.org/api/stream.html" target="_blank" rel="external">stream</a> 流，首先获取到需要的 stream ，然后可以通过 stream 的 pipe() 方法把流导入到你想要的地方，比如 gulp 的插件中，经过插件处理后的流又可以继续导入到其他插件中，当然也可以把流写入到文件中。所以 gulp 是以 stream 为媒介的，它不需要频繁的生成临时文件，这也是 gulp 的速度比 Grunt 快的一个原因。再回到正题上来，gulp.src() 方法正是用来获取流的，但要注意这个流里的内容不是原始的文件流，而是一个虚拟文件对象流（Vinyl files），这个虚拟文件对象中存储着原始文件的路径、文件名、内容等信息，这个我们暂时不用去深入理解，你只需简单的理解可以用这个方法来读取你需要操作的文件就行了。其语法为：<br><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">gulp.src(globs [, options])</span><br></pre></td></tr></table></figure></p><p>gulp 用到的 glob 的匹配规则以及一些文件匹配技巧。<br>gulp 内部使用了 node-glob 模块来实现其文件匹配功能。我们可以使用下面这些特殊的字符来匹配我们想要的文件：</p><ul><li><code>*</code> 匹配文件路径中的 0 个或多个字符，但不会匹配路径分配符，除非路径分隔符出现在末尾</li><li>** 匹配路径中的 0 个或多个目录及其子目录，需要单独出现，即它左右不能有其他东西了。如果出现在末尾，也能匹配文件。</li><li>? 匹配文件路径中的一个字符（不会匹配路径分隔符）</li><li>[…] 匹配方括号中出现的字符中的任意一个，当方括号中第一个字符为 ^ 或 ! 时，则表示不匹配方括号中出现的其他字符中的任意一个，类似 js 正则表达式中的用法。</li><li>!(pattern|pattern|pattern) 匹配任何与括号中给定的任一模式都不匹配的</li><li>?(pattern|pattern|pattern) 匹配括号中给定的任一模式 0 次或 1 次，类似于 js 正则中的(pattern|pattern|pattern)?</li><li>+(pattern|pattern|pattern) 匹配括号中给定的任一模式至少 1 次，类似于正则中的(pattern|pattern|pattern)+</li><li><code>*(pattern|pattern|pattern)</code> 匹配括号中的给定的任一模式 0 次或多次，类似于 js 正则中的 <code>(pattern|pattern|pattern)*</code></li><li>@(pattern|pattern|pattern) 匹配括号中给定的任一模式 1 次，类似于 js 正则中的(pattern|pattern|pattern)</li></ul><p>下面以一系列例子来加深理解</p><ul><li><code>*</code> 能匹配 a.js 、 x.y 、 abc 、 abc/ ，但不能匹配 a/b.js</li><li><code>*.*</code> 能匹配 a.js 、 style.css 、 a.b 、 x.y</li><li><code>*/*/*.js</code> 能匹配 a/b/c.js 、 x/y/z.js ，不能匹配 a/b.js 、a/b/c/d.js</li><li>** 能匹配 abc 、 a/b.js 、 a/b/c.js 、 x/y/z 、x/y/z/a.b ，能用来匹配所有的目录和文件</li><li><em>*/</em>.js 能匹配 foo.js 、 a/foo.js 、 a/b/foo.js 、 a/b/c/foo.js</li><li>a/**/z 能匹配 a/z 、 a/b/z 、 a/b/c/z 、 a/d/g/h/r/z</li><li><code>a/**b/z</code> 能匹配 a/b/z 、 a/fb/z ，但不能匹配 a/x/gb/z ，因为只有单 ** 单独出现才能匹配多级目录</li><li>?.js 能匹配 a.js 、 b.js 、 c.js</li><li>a?? 能匹配 a.b 、 abc ，但不能匹配 ab/ ，因为它不会匹配路径分隔符</li><li>[xyz].js 只能匹配 x.js 、 y.js 、 z.js ，不会匹配 xy.js 、 xyz.js 等，整个中括号只代表一个字符</li><li>[^xyz].js 能匹配 a.js 、 b.js 、 c.js 等，不能匹配 x.js 、 y.js 、 z.js</li></ul><p>当有多种匹配模式时可以使用数组<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用数组的方式来匹配多种文件</span></span><br><span class="line">gulp.src([<span class="string">'js/*.js'</span>,<span class="string">'css/*.css'</span>,<span class="string">'*.html'</span>])</span><br></pre></td></tr></table></figure></p><p>使用数组的方式还有一个好处就是可以很方便的使用排除模式，在数组中的单个匹配模式前加上 ! 即是排除模式，它会在匹配的结果中排除这个匹配，要注意一点的是不能在数组中的第一个元素中使用排除模式<br><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">gulp.src([*.js,<span class="string">'!b*.js'</span>])  <span class="comment">//匹配所有 js 文件，但排除掉以 b 开头的 js 文件</span></span><br><span class="line">gulp.src([<span class="string">'!b*.js'</span>,*.js])  <span class="comment">//不会排除任何文件，因为排除模式不能出现在数组的第一个元素中</span></span><br></pre></td></tr></table></figure></p><p>此外，还可以使用展开模式。展开模式以花括号作为定界符，根据它里面的内容，会展开为多个模式，最后匹配的结果为所有展开的模式想加起来得到的结果。展开的例子如下：</p><ul><li>a{b,c}d 会展开为 abd 、 acd</li><li>a{b,}c 会展开为 abc 、 ac</li><li>a{0..3}d 会展开为 a0d 、 a1d 、 a2d 、 a3d </li><li>a{b,c{d,e}f}g 会展开为 abg 、 acdfg 、 acefg</li><li>a{b,c}d{e,f}g 会展开为 abdeg 、 acdeg 、 abdfg 、 abdeg</li></ul><h2 id="gulp-dest"><a href="#gulp-dest" class="headerlink" title="gulp.dest()"></a>gulp.dest()</h2><p>gulp.dest() 方法是用来写文件的，其语法为：<br><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">gulp.dest(path[, options])</span><br></pre></td></tr></table></figure></p><p><strong>path</strong> 为写入文件的路径<br>我们给 gulp.dest() 传入的路径参数，只能用来指定要生成的文件的目录，而不能指定生成文件的文件名，它生成文件的文件名使用的是导入到它的文件流自身的文件名，所以生成的文件名是由导入到它的文件流决定的，即使我们给它传入一个带有文件名的路径参数，然后它也会把这个文件名当作是目录名，例如：<br><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="keyword">var</span> gulp = <span class="built_in">require</span>(<span class="string">"gulp"</span>);</span><br><span class="line">gulp.src(<span class="string">"script/jquery.js"</span>).pipe(gulp.dest(<span class="string">"dist/foo.js"</span>));</span><br><span class="line"><span class="comment">// 最终生成的文件路径为 dist/foo.js/jquery.js ，而不是 dist/foo.js</span></span><br></pre></td></tr></table></figure></p><p>要想改变文件名，可以使用插件 gulp-rename<br>下面说说生成的文件路径与我们给 gulp.dest() 方法传入的路径参数之间的关系。<br>gulp.dest(path) 生成的文件路径是我们传入的 path 参数后面再加上 gulp.src() 中有通配符开始出现的那部分路径。例如：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> gulp = <span class="built_in">require</span>(<span class="string">"gulp"</span>);</span><br><span class="line"><span class="comment">//有通配符开始出现的那部分路径为 **/*.js</span></span><br><span class="line">gulp.src(<span class="string">"script/**/*.js"</span>).pipe(gulp.dest(<span class="string">"dist"</span>));</span><br><span class="line"><span class="comment">//最后生成的文件路径为 dist/**/*.js</span></span><br><span class="line"><span class="comment">//如果 **/*.js 匹配到的文件为 jquery/jquery.js ，则生成的文件路径为 dist/jquery/jquery.js</span></span><br></pre></td></tr></table></figure></p><p>再举更多一点的例子<br><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></pre></td><td class="code"><pre><span class="line">gulp.src(<span class="string">"script/avalon/avalon.js"</span>).pipe(gulp.dest(<span class="string">"dist"</span>));</span><br><span class="line"><span class="comment">//没有通配符出现的情况，最后生成的文件路径为 dist/avalon.js</span></span><br><span class="line"></span><br><span class="line">gulp.src(<span class="string">"script/**/underscore.js"</span>).pipe(gulp.dest(<span class="string">"dist"</span>));</span><br><span class="line"><span class="comment">//有通配符开始出现的那部分路径为 **/underscore.js</span></span><br><span class="line"><span class="comment">//假设匹配到的文件为script/util/underscore.js</span></span><br><span class="line"><span class="comment">//则最后生成的文件路径为dist/util/underscore.js</span></span><br><span class="line"></span><br><span class="line">gulp.src(<span class="string">"script/*"</span>).pipe(gulp.dest(<span class="string">"dist"</span>));</span><br><span class="line"><span class="comment">//有通配符出现的那部分路径为*</span></span><br><span class="line"><span class="comment">//假设匹配到的文件为script/zepto.js</span></span><br><span class="line"><span class="comment">//则最后生成的文件路径为dist/zepto.js</span></span><br></pre></td></tr></table></figure></p><p>通过指定 gulp.src() 方法配置参数中的 base 属性，我们可以灵活的来改变 gulp.dest() 生成的文件路径。<br>当我们没有在 gulp.src() 方法配置参数中的 base 属性，base 的默认值为通配符开始出现之前那部分路径，例如：<br><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">gulp.src(<span class="string">"app/src/**/*.css"</span>) <span class="comment">//此时base的值为 app/src</span></span><br></pre></td></tr></table></figure></p><p>上面我们说的 gulp.dest() 所生成的文件路径的规则，其实也可以理解成，用我们给 gulp.dest() 传入的路径替换掉 gulp.src() 中的 base 路径，最终得到生成文件的路径。<br><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">gulp.src(<span class="string">"app/src/**/*.css"</span>).pipe(gulp.dest(<span class="string">"dist"</span>));</span><br><span class="line"><span class="comment">//此时base的值为app/src，也就是说它的base路径为app/src</span></span><br><span class="line"><span class="comment">//设该模式匹配到了文件app/src/css/normal.css</span></span><br><span class="line"><span class="comment">//用dist替换掉base路径，最终得到dist/css/normal.css</span></span><br></pre></td></tr></table></figure></p><p>所以改变 base 路径后，gulp.dest() 生成的文件路径也会改变<br><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">gulp.src(<span class="string">"script/lib/*.js"</span>).pipe(gulp.dest(<span class="string">"build"</span>));</span><br><span class="line"><span class="comment">//没有配置base参数，此时默认的base路径为script/lib</span></span><br><span class="line"><span class="comment">//假设匹配到的文件为script/lib/jquery.js</span></span><br><span class="line"><span class="comment">//生成的文件路径为build/jquery.js</span></span><br><span class="line"></span><br><span class="line">gulp.src(<span class="string">"script/lib/*.js"</span>, &#123;<span class="attr">base</span>: <span class="string">"script"</span>&#125;).pipe(gulp.dest(<span class="string">"build"</span>));</span><br><span class="line"><span class="comment">//配置了base参数，此时base路径为script</span></span><br><span class="line"><span class="comment">//假设匹配到的文件为script/lib/jquery.js</span></span><br><span class="line"><span class="comment">//此时生成的文件路径为build/lib/jquery.js</span></span><br></pre></td></tr></table></figure></p><p>用 gulp.dest() 把文件流写入文件后，文件流仍然可以继续使用。</p><h2 id="gulp-task"><a href="#gulp-task" class="headerlink" title="gulp.task()"></a>gulp.task()</h2><p>gulp.task 方法用来定义任务，内部使用的是 Orchestrator ，其语法为：<br><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">gulp.task(name[, deps], fn)</span><br></pre></td></tr></table></figure></p><p><strong>name</strong> 为任务名，如果你需要在命令行中运行你的某些任务，那么，请不要在名字中使用空格。<br><strong>deps</strong> 是当前定义的任务需要依赖的其他任务，为一个数组。当前定义的任务会在所有依赖的任务执行完毕后才开始执行。如果没有依赖，则可省略这个参数。<br><strong>fn</strong> 为任务函数，我们把任务要执行的代码都要写在里面。该参数也是可选的。<br><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">gulp.task(<span class="string">"mytask"</span>, [<span class="string">"array"</span>, <span class="string">"of"</span>, <span class="string">"task"</span>, <span class="string">"names"</span>], <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">//定义一个有依赖的任务</span></span><br><span class="line">  <span class="comment">// Do something</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p><p>关于 gulp.task() ，我们需要知道执行多个任务时怎么来控制任务执行的顺序。<br>gulp 中执行多个任务，可以通过任务依赖来实现。例如我想要执行 one ，two ，three 这三个任务，那我们就可以定义一个空的任务，然后把那三个任务当做这个空的任务的依赖就行了：<br><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">//只要执行default任务，就相当于把one,two,three这三个任务执行了</span></span><br><span class="line">gulp.task(<span class="string">'default'</span>,[<span class="string">'one'</span>,<span class="string">'two'</span>,<span class="string">'three'</span>]);</span><br></pre></td></tr></table></figure></p><p>如果任务相互之间没有依赖，任务会按你书写的顺序来执行，如果有依赖的话则会先执行依赖的任务。<br>但是如果某个任务所依赖的任务是异步的，就要注意了，gulp 并不会等待那个所依赖的异步任务完成，而是会接着执行后续的任务。例如：<br><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">gulp.task(<span class="string">'one'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  <span class="comment">// one是一个异步执行的任务</span></span><br><span class="line">  setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">"one is done"</span>);</span><br><span class="line">  &#125;,<span class="number">3000</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">//two任务虽然依赖于one任务，但并不会等到one任务中的异步操作完成后再执行</span></span><br><span class="line">gulp.task(<span class="string">"two"</span>, [<span class="string">"one"</span>], <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">"two is done"</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p><p><img src="/images/gulp-1.png" alt=""><br>上面的例子中我们执行 two 任务时，会先执行 one 任务，但不会去等待 one 任务中的异步操作完成后再执行 two 任务，而是紧接着执行 two 任务。因为 one 任务耗时 3 秒，所以 two 任务会在 one 任务中的异步操作完成之前就执行了。</p><p>那如果我们想等待异步任务中的异步操作完成后再执行后续的任务，该怎么做呢？<br>有三种方法可以实现：</p><p>第一：在异步操作完成后执行一个回调函数来通知 gulp 这个异步任务已经完成，这个回调函数就是任务函数的第一个参数。<br><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">gulp.task(<span class="string">"one"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">  <span class="comment">//cb为任务函数提供的回调，用来通知任务已经完成</span></span><br><span class="line">  <span class="comment">//one是一个异步执行的任务</span></span><br><span class="line">  setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">"one is done"</span>);</span><br><span class="line">    cb(); <span class="comment">//执行回调，表示这个异步任务已经完成</span></span><br><span class="line">  &#125;,<span class="number">5000</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">//这时two任务会在one任务中的异步操作完成后再执行</span></span><br><span class="line">gulp.task(<span class="string">"two"</span>, [<span class="string">"one"</span>], <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">"two is done"</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p><p>第二：定义任务时返回一个流对象。适用于任务就是操作 gulp.src 获取到的流的情况。<br><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">gulp.task(<span class="string">"one"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> stream = gulp.src(<span class="string">"client/**/*.js"</span>)</span><br><span class="line">      .pipe(dosomething()) <span class="comment">//dosomething()中有某些异步操作</span></span><br><span class="line">      .pipe(gulp.dest(<span class="string">"build"</span>));</span><br><span class="line">  <span class="keyword">return</span> stream;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 这是two任务会在one任务中的异步操作完成后再执行</span></span><br><span class="line">gulp.task(<span class="string">"two"</span>, [<span class="string">"one"</span>], <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">"two is done"</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p><p>第三：返回一个 promise 对象，例如：<br><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="keyword">var</span> Q = <span class="built_in">require</span>(<span class="string">'q'</span>); <span class="comment">//一个著名的异步处理的库 https://github.com/kriskowal/q</span></span><br><span class="line">gulp.task(<span class="string">"one"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">cb</span>)</span>&#123;</span><br><span class="line">  <span class="keyword">var</span> deferred = Q.defer();</span><br><span class="line">  <span class="comment">//做一些异步操作</span></span><br><span class="line">  setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    deferred.resolve();</span><br><span class="line">  &#125;,<span class="number">5000</span>);</span><br><span class="line">  <span class="keyword">return</span> deferred.promise;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">gulp.task(<span class="string">"two"</span>, [<span class="string">"one"</span>], <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">"two is done"</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p><p>关于 gulp.task() ，主要的就是要清除当依赖异步任务时要如何处理。</p><h2 id="gulp-watch"><a href="#gulp-watch" class="headerlink" title="gulp.watch()"></a>gulp.watch()</h2><p>gulp.watch() 用来监视文件的变化，当文件发生变化后，我们可以利用它来执行相应的任务，例如文件压缩等。其语法为：<br><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">gulp.watch(glob[, opts], tasks);</span><br></pre></td></tr></table></figure></p><p><strong>glob</strong> 为要监视的文件匹配模式，规则和用法与 gulp.src() 方法中的 glob 相同。<br><strong>opts</strong> 为一个可选的配置对象，通常不需要用到。<br><strong>tasks</strong> 为文件变化后要执行的任务，为一个数组<br><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">gulp.task(<span class="string">"uglify"</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">//do something</span></span><br><span class="line">&#125;);</span><br><span class="line">gulp.task(<span class="string">"reload"</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">//do something</span></span><br><span class="line">&#125;);</span><br><span class="line">gulp.watch(<span class="string">"js/**/*.js"</span>, [<span class="string">"uglify"</span>,<span class="string">"reload"</span>]);</span><br></pre></td></tr></table></figure></p><p>gulp.watch(glob [,opts, cb])<br><strong>glob</strong> 和 <strong>opts</strong> 参数与第一种用法相同<br><strong>cb</strong> 参数为一个函数。每当监视的文件发生变化时，就会调用这个函数，并且会给它传入一个对象，该对象包含了文件变化的一些信息，type 属性为变化的类型，可以是 added 、changed 、deleted ，path 属性为发生变化的文件的路径<br><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">gulp.watch(<span class="string">"js/**/*.js"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">event</span>)</span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(event.type); <span class="comment">//变化类型added为新增，deleted为删除，changed为改变</span></span><br><span class="line">  <span class="built_in">console</span>.log(event.path); <span class="comment">//变化的文件的路径</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p><h1 id="gulp-的插件"><a href="#gulp-的插件" class="headerlink" title="gulp 的插件"></a>gulp 的插件</h1><p>gulp 本身虽然不能完成很多任务，但它有大量插件可用，我们可以在 <a href="http://gulpjs.com/plugins/" target="_blank" rel="external">插件页面</a> 或者在 npm 搜索 gulpplugin 。<br>列一些很棒的 plugin ：</p><ul><li><a href="https://www.npmjs.com/package/gulp-jshint/" target="_blank" rel="external">JSHint</a> ： js代码检查分析工具</li><li><a href="https://www.npmjs.com/package/gulp-coffee/" target="_blank" rel="external">gulp-coffee</a> ： 编译CoffeeScript</li><li><a href="https://www.npmjs.com/package/gulp-mocha" target="_blank" rel="external">gulp-mocha</a> ： 执行Mocha测试</li><li><a href="https://www.npmjs.com/package/gulp-bump" target="_blank" rel="external">gulp-bump</a> ： 更新版本号</li><li><a href="https://www.npmjs.com/package/gulp-sass" target="_blank" rel="external">gulp-sass</a> ： sass 编译</li><li><a href="http://www.browsersync.cn/docs/gulp/" target="_blank" rel="external">browser-sync</a> ： 浏览器自动刷新</li><li><a href="https://www.npmjs.com/package/gulp-uglify" target="_blank" rel="external">gulp-uglify</a> ： 代码压缩</li><li><a href="https://www.npmjs.com/package/gulp-concat" target="_blank" rel="external">gulp-concat</a> ： 合并</li><li><a href="https://www.npmjs.com/package/gulp-eslint" target="_blank" rel="external">gulp-eslint</a> ： 支持 ES6 JSX</li></ul><h1 id="gulp-命令行参数"><a href="#gulp-命令行参数" class="headerlink" title="gulp 命令行参数"></a>gulp 命令行参数</h1><ul><li><code>-v</code> 或 <code>--version</code> 会显示全局和项目本地所安装的 gulp 版本号</li><li><code>--require &lt;module path&gt;</code> 将会在执行之前 require 一个模块。这对于一些语言编译器或者需要其他应用的情况来说很有用。你可以使用多个 <code>--require</code></li><li><code>--gulpfile &lt;gulpfile path&gt;</code> 手动指定一个 gulpfile 的路径，这在你有很多个 gulpfile 的时候很有用。这也会将 CWD 设置到该 gulpfile 所在目录</li><li><code>--cwd &lt;dir path&gt;</code> 手动指定 CWD 。定义 gulpfile 查找的位置，此外，所有的相应的依赖（require）会从这里开始计算相对路径</li><li><code>-T</code> 或 <code>--tasks</code> 会显示所指定 gulpfile 的 task 依赖树</li><li><code>--tasks-simple</code> 会以纯文本的方式显示所载入的 gulpfile 中的 task 列表</li><li><code>--color</code> 强制 gulp 和 gulp 插件显示颜色，即便没有颜色支持</li><li><code>--no-color</code> 强制不显示颜色，即便检测到有颜色支持</li><li><code>--silent</code> 禁止所有的 gulp 日志</li></ul><p>命令行会在 process.env.INIT_CW 中记录它是从哪里被运行的。</p><h1 id="tip"><a href="#tip" class="headerlink" title="tip"></a>tip</h1><p>1、gulp 写进项目 package.json 文件的依赖有什么作用<br>方便别人查看你项目中有些什么依赖，而且在项目目录下执行 npm install 命令会安装项目 package.json 中的所有依赖模块，这样就能简化项目的安装程序了，不用一个一个模块去安装啊。</p><p>2、gulp 中着重了解 gulp.task() 如何处理依赖任务是耗时操作或者异步操作的情况。</p>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;什么是-gulp&quot;&gt;&lt;a href=&quot;#什么是-gulp&quot; class=&quot;headerlink&quot; title=&quot;什么是 gulp&quot;&gt;&lt;/a&gt;什么是 gulp&lt;/h1&gt;&lt;p&gt;&lt;a href=&quot;http://gulpjs.com/&quot;&gt;gulp&lt;/a&gt; 是一个前端构建工具，它能通过自动执行常见任务，比如编译预处理 CSS ，压缩 JavaScript 和刷新浏览器，来改进网站开发的过程，从而使开发更加快速高效。&lt;/p&gt;
&lt;h1 id=&quot;为什么要用-gulp&quot;&gt;&lt;a href=&quot;#为什么要用-gulp&quot; class=&quot;headerlink&quot; title=&quot;为什么要用 gulp&quot;&gt;&lt;/a&gt;为什么要用 gulp&lt;/h1&gt;&lt;p&gt;与 grunt 相比，gulp 无需写一大堆繁杂的配置参数，&lt;a href=&quot;https://github.com/gulpjs/gulp/blob/master/docs/API.md&quot;&gt;API&lt;/a&gt;（&lt;a href=&quot;http://www.gulpjs.com.cn/docs/api/&quot;&gt;中文 API&lt;/a&gt;） 也非常简单，学习起来很容易，而且 gulp 使用的是 nodejs 中 &lt;a href=&quot;https://nodejs.org/api/stream.html&quot;&gt;stream&lt;/a&gt; 来读取和操作数据，其速度更快。&lt;br&gt;gulp 有庞大的生态圈，且每天都在发展。依靠成千上万可供选择的插件，你可以利用 gulp 自动完成几乎任何事。&lt;/p&gt;
&lt;h1 id=&quot;如何使用-gulp&quot;&gt;&lt;a href=&quot;#如何使用-gulp&quot; class=&quot;headerlink&quot; title=&quot;如何使用 gulp&quot;&gt;&lt;/a&gt;如何使用 gulp&lt;/h1&gt;&lt;h2 id=&quot;Installing-Gulp&quot;&gt;&lt;a href=&quot;#Installing-Gulp&quot; class=&quot;headerlink&quot; title=&quot;Installing Gulp&quot;&gt;&lt;/a&gt;Installing Gulp&lt;/h2&gt;&lt;p&gt;新版的 gulp 命令行工具已经改名为 gulp-cli 。&lt;br&gt;如果你之前安装了全局的 gulp 。在使用新的 gulp-cli 之前，执行命令&lt;br&gt; &lt;code&gt;npm rm --global gulp&lt;/code&gt; ，将之前的全局 gulp 卸掉。&lt;/p&gt;
    
    </summary>
    
      <category term="前端" scheme="https://309893147.github.io/categories/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="gulp" scheme="https://309893147.github.io/tags/gulp/"/>
    
  </entry>
  
  <entry>
    <title>npm 全面介绍</title>
    <link href="https://309893147.github.io/2017/04/10/npm/"/>
    <id>https://309893147.github.io/2017/04/10/npm/</id>
    <published>2017-04-09T19:25:24.000Z</published>
    <updated>2019-07-30T01:31:38.211Z</updated>
    
    <content type="html"><![CDATA[<!-- <img src="http://i1.piimg.com/588926/30e7b49044d5cfc4.png" alt="summary-img-src-npm"> --><a id="more"></a><h1 id="什么是-NPM"><a href="#什么是-NPM" class="headerlink" title="什么是 NPM"></a>什么是 NPM</h1><p>npm 之于 Node.js ，就像 pip 之于 Python， gem 之于 Ruby， pear 之于 PHP 。</p><p>npm 是 Node.js 官方提供的包管理工具，他已经成了 Node.js 包的标准发布平台，用于 Node.js 包的发布、传播、依赖控制。npm 提供了命令行工具，使你可以方便地下载、安装、升级、删除包，也可以让你作为开发者发布并维护包。</p><h1 id="为什么要使用-NPM"><a href="#为什么要使用-NPM" class="headerlink" title="为什么要使用 NPM"></a>为什么要使用 NPM</h1><p>npm 是随同 Node.js 一起安装的包管理工具，能解决 Node.js 代码部署上的很多问题，常见的场景有以下几种：</p><ul><li>允许用户从 npm 服务器下载别人编写的第三方包到本地使用。</li><li>允许用户从 npm 服务器下载并安装别人编写的命令行程序到本地使用。</li><li>允许用户将自己编写的包或命令行程序上传到 npm 服务器供别人使用。</li></ul><p>npm 的背后，是基于 couchdb 的一个数据库，详细记录了每个包的信息，包括作者、版本、依赖、授权信息等。它的一个很重要的作用就是：将开发者从繁琐的包管理工作（版本、依赖等）中解放出来，更加专注于功能的开发。</p><h1 id="如何使用-NPM"><a href="#如何使用-NPM" class="headerlink" title="如何使用 NPM"></a>如何使用 NPM</h1><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><p>npm 不需要单独安装。在安装 Node 的时候，会连带一起安装 npm 。但是，Node 附带的 npm 可能不是最新版本，最后用下面的命令，更新到最新版本。<br><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">$ sudo npm install npm@latest -g</span><br></pre></td></tr></table></figure></p><p>如果是 Window 系统使用以下命令即可：<br><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 npm -g</span><br></pre></td></tr></table></figure></p><p>也就是使用 npm 安装自己。之所以可以这样，是因为 npm 本身与 Node 的其他模块没有区别。</p><p>然后，运行下面的命令，查看各种信息。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看 npm 命令列表</span></span><br><span class="line">$ npm <span class="built_in">help</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看各个命令的简单用法</span></span><br><span class="line">$ npm -l</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看 npm 的版本</span></span><br><span class="line">$ npm -v</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看 npm 的配置</span></span><br><span class="line">$ npm config list -l</span><br></pre></td></tr></table></figure></p><h2 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h2><h3 id="npm-init"><a href="#npm-init" class="headerlink" title="npm init"></a>npm init</h3><p>npm init 用来初始化生成一个新的 package.json 文件。它会向用户提问一系列问题，如果你觉得不用修改默认配置，一路回车就可以了。<br>如果使用了 -f（代表force）、-y（代表yes），则跳过提问阶段，直接生成一个新的 package.json 文件。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm init -y</span><br></pre></td></tr></table></figure></p><h3 id="npm-set"><a href="#npm-set" class="headerlink" title="npm set"></a>npm set</h3><p>npm set 用来设置环境变量<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ npm <span class="built_in">set</span> init-author-name <span class="string">'Your name'</span></span><br><span class="line">$ npm <span class="built_in">set</span> init-author-email <span class="string">'Your email'</span></span><br><span class="line">$ npm <span class="built_in">set</span> init-author-url <span class="string">'http://yourdomain.com'</span></span><br><span class="line">$ npm <span class="built_in">set</span> init-license <span class="string">'MIT'</span></span><br></pre></td></tr></table></figure></p><p>上面命令等于为 npm init 设置了默认值，以后执行 npm init 的时候，package.json 的作者姓名、邮件、主页、许可证字段就会自动写入预设的值。这些信息会存放在用户主目录的 ~/.npmrc文件，使得用户不用每个项目都输入。如果某个项目有不同的设置，可以针对该项目运行 npm config。</p><h3 id="npm-info"><a href="#npm-info" class="headerlink" title="npm info"></a>npm info</h3><p>npm info 命令可以查看每个模块的具体信息。比如，查看 underscore 模块的信息。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm info underscore</span><br></pre></td></tr></table></figure></p><p>上面命令返回一个 JavaScript 对象，包含了 underscore 模块的详细信息。这个对象的每个成员，都可以直接从 info 命令查询。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ npm info underscore description</span><br><span class="line"></span><br><span class="line">$ npm info underscore homepage</span><br><span class="line"></span><br><span class="line">$ npm info underscore version</span><br></pre></td></tr></table></figure></p><h3 id="npm-search"><a href="#npm-search" class="headerlink" title="npm search"></a>npm search</h3><p>npm search 命令用于搜索 npm 仓库，它后面可以跟字符串，也可以跟正则表达式。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm search &lt;搜索词&gt;</span><br></pre></td></tr></table></figure></p><h3 id="npm-list"><a href="#npm-list" class="headerlink" title="npm list"></a>npm list</h3><p>npm list 命令以树形结构列出当前项目安装的所有模块，以及它们依赖的模块。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$ npm list</span><br><span class="line"></span><br><span class="line"><span class="comment"># 加上 global 参数，会列出全局安装的模块</span></span><br><span class="line">$ npm list -global</span><br><span class="line"></span><br><span class="line"><span class="comment"># npm list 命令也可以列出单个模块</span></span><br><span class="line">$ npm list underscore</span><br></pre></td></tr></table></figure></p><h3 id="npm-install"><a href="#npm-install" class="headerlink" title="npm install"></a>npm install</h3><p>使用 npm 安装包的命令格式为：<br><code>npm [install/i] [package_name]</code></p><h4 id="本地模式和全局模式"><a href="#本地模式和全局模式" class="headerlink" title="本地模式和全局模式"></a>本地模式和全局模式</h4><p>npm 在默认情况下会从 <a href="http://npmjs.org" target="_blank" rel="external">http://npmjs.org</a> 搜索或下载包，将包安装到当前目录的 node_modules 子目录下。<br>如果你熟悉 Ruby 的 gem 或者 Python 的 pip，你会发现 npm 与它们的行为不同，gem 或 pip 总是以全局模式安装，使包可以供所有的程序使用，而 npm 默认会把包安装到当前目录下。这反映了 npm 不同的设计哲学。如果把包安装到全局，可以提供程序的重复利用程度，避免同样的内容的多分副本，但坏处是难以处理不同的版本依赖。如果把包安装到当前目录，或者说本地，则不会有不同程序依赖不同版本的包的冲突问题，同时还减轻了包作者的 API 兼容性压力，但缺陷则是同一个包可能会被安装许多次。</p><p>我们在使用 supervisor 的时候使用了 <code>npm install -g supervisor</code> 命令，就是以全局模式安装 supervisor 。</p><p>这里注意一点的就是，supervisor 必须安装到全局，如果你不安装到全局，错误命令会提示你安装到全局。如果不想安装到默认的全局，也可以自己修改全局路径到当前路径 <code>npm config set prefix &quot;路径&quot;</code> 安装完以后就可以用 supervisor 来启动服务了。<br>supervisor 可以帮助你实现这个功能，它会监视你对代码的驱动，并自动重启 Node.js 。</p><p>一般来说，全局安装只适用于工具模块，比如 eslint 和 gulp 。关于使用全局模式，多数时候并不是因为许多程序都有可能用到了它，为了减少多重副本而使用全局模式，而是因为<strong>本地模式不会注册 PATH 环境变量</strong>。<br>“本地安装”指的是将一个模块下载到当前项目的 node_modules 子目录，然后只有在项目目录之中，才能调用这个模块。</p><p>本地模式和全局模式的特点如下：</p><table><thead><tr><th style="text-align:center">模式</th><th style="text-align:center">可通过 require 使用</th><th style="text-align:center">注册 PATH</th></tr></thead><tbody><tr><td style="text-align:center">本地模式</td><td style="text-align:center">是</td><td style="text-align:center">否</td></tr><tr><td style="text-align:center">全局模式</td><td style="text-align:center">否</td><td style="text-align:center">是</td></tr></tbody></table><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 本地安装</span></span><br><span class="line">$ npm install &lt;package name&gt;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 全局安装</span></span><br><span class="line">$ sudo npm install -global &lt;package name&gt;</span><br><span class="line">$ sudo npm install -g &lt;package name&gt;</span><br></pre></td></tr></table></figure><p>npm install 也支持直接输入 Github 代码库地址。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ npm install git://github.com/package/path.git</span><br><span class="line">$ npm install git://github.com/package/path.git<span class="comment">#0.1.0</span></span><br></pre></td></tr></table></figure></p><p>安装之前，npm install 会先检查，node_modules 目录之中是否已经存在指定模块。如果存在，就不再重新安装了，即使远程仓库已经有了一个新版本，也是如此。</p><p>如果你希望，一个模块不管是否安装过， npm 都要强制重新安装，可以使用 -f 或 –force 参数。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm install &lt;packageName&gt; --force</span><br></pre></td></tr></table></figure></p><h4 id="安装不同版本"><a href="#安装不同版本" class="headerlink" title="安装不同版本"></a>安装不同版本</h4><p>install 命令总是安装模块的最新版本，如果要安装模块的特定版本，可以在模块名后面加上 @ 和版本号。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ npm install sax@latest</span><br><span class="line">$ npm install sax@0.1.1</span><br><span class="line">$ npm install sax@<span class="string">"&gt;=0.1.0 &lt;0.2.0"</span></span><br></pre></td></tr></table></figure></p><p>install 命令可以使用不同参数，指定所安装的模块属于哪一种性质的依赖关系，即出现在 packages.json 文件的哪一项中。</p><blockquote><p>–save：模块名将被添加到 dependencies，可以简化为参数-S。<br>–save-dev：模块名将被添加到 devDependencies，可以简化为参数-D。</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ npm install sax --save</span><br><span class="line">$ npm install node-tap --save-dev</span><br><span class="line"><span class="comment"># 或者</span></span><br><span class="line">$ npm install sax -S</span><br><span class="line">$ npm install node-tap -D</span><br></pre></td></tr></table></figure><h5 id="dependencies-依赖"><a href="#dependencies-依赖" class="headerlink" title="dependencies 依赖"></a>dependencies 依赖</h5><p>这个可以说是我们 npm 核心一项内容，依赖管理，这个对象里面的内容就是我们这个项目所依赖的 js 模块包。下面这段代码表示我们依赖了 <code>markdown-it</code> 这个包，版本是 <code>^8.1.0</code> ，代表最小依赖版本是 <code>8.1.0</code> ，如果这个包有更新，那么当我们使用 npm install 命令的时候，npm 会帮我们下载最新的包。当别人引用我们这个包的时候，包内的依赖包也会被下载下来。<br><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="string">"dependencies"</span>: &#123;</span><br><span class="line">    <span class="string">"markdown-it"</span>: <span class="string">"^8.1.0"</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h5 id="devDependencies-开发依赖"><a href="#devDependencies-开发依赖" class="headerlink" title="devDependencies 开发依赖"></a>devDependencies 开发依赖</h5><p>在我们开发的时候会用到的一些包，只是在开发环境中需要用到，但是在别人引用我们包的时候，不会用到这些内容，放在 devDependencies 的包，在别人引用的时候不会被 npm 下载。<br><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="string">"devDependencies"</span>: &#123;</span><br><span class="line">    <span class="string">"autoprefixer"</span>: <span class="string">"^6.4.0"</span>,<span class="number">0</span><span class="string">",</span></span><br><span class="line"><span class="string">    "</span>babel-preset-es2015<span class="string">": "</span>^<span class="number">6.0</span><span class="number">.0</span><span class="string">",</span></span><br><span class="line"><span class="string">    "</span>babel-preset-stage<span class="number">-2</span><span class="string">": "</span>^<span class="number">6.0</span><span class="number">.0</span><span class="string">",</span></span><br><span class="line"><span class="string">    "</span>babel-register<span class="string">": "</span>^<span class="number">6.0</span><span class="number">.0</span><span class="string">",</span></span><br><span class="line"><span class="string">    "</span>webpack<span class="string">": "</span>^<span class="number">1.13</span><span class="number">.2</span><span class="string">",</span></span><br><span class="line"><span class="string">    "</span>webpack-dev-middleware<span class="string">": "</span>^<span class="number">1.8</span><span class="number">.3</span><span class="string">",</span></span><br><span class="line"><span class="string">    "</span>webpack-hot-middleware<span class="string">": "</span>^<span class="number">2.12</span><span class="number">.2</span><span class="string">",</span></span><br><span class="line"><span class="string">    "</span>webpack-merge<span class="string">": "</span>^<span class="number">0.14</span><span class="number">.1</span><span class="string">",</span></span><br><span class="line"><span class="string">    "</span>highlightjs<span class="string">": "</span>^<span class="number">9.8</span><span class="number">.0</span><span class="string">"</span></span><br><span class="line"><span class="string">&#125;</span></span><br></pre></td></tr></table></figure></p><p>当你有了一个完整的 package.json 文件的时候，就可以让人一眼看出来，这个模块的基本信息，和这个模块所需要依赖的包。我们可以通过 npm install 就可以很方便的下载好这个模块所需要的包。</p><p>npm install 默认会安装 dependencies 字段和 devDependencies 字段中的所有模块，如果使用 –production 参数，可以只安装 dependencies 字段的模块。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ npm install --production</span><br><span class="line"><span class="comment"># 或者</span></span><br><span class="line">$ NODE_ENV=production npm install</span><br></pre></td></tr></table></figure></p><p>一旦安装了某个模块，就可以在代码中用 require 命令加载这个模块。<br><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="keyword">var</span> backbone = <span class="built_in">require</span>(<span class="string">'backbone'</span>)</span><br><span class="line"><span class="built_in">console</span>.log(backbone.VERSION)</span><br></pre></td></tr></table></figure></p><h3 id="npm-run"><a href="#npm-run" class="headerlink" title="npm run"></a>npm run</h3><p>npm 不仅可以用于模块管理，还可以用于执行脚本。package.json 文件有一个 scripts 字段，可以用于指定脚本命令，供 npm 直接调用。<br>package.json<br><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></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="string">"name"</span>: <span class="string">"myproject"</span>,</span><br><span class="line">  <span class="string">"devDependencies"</span>: &#123;</span><br><span class="line">    <span class="string">"jshint"</span>: <span class="string">"latest"</span>,</span><br><span class="line">    <span class="string">"browserify"</span>: <span class="string">"latest"</span>,</span><br><span class="line">    <span class="string">"mocha"</span>: <span class="string">"latest"</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="string">"scripts"</span>: &#123;</span><br><span class="line">    <span class="string">"lint"</span>: <span class="string">"jshint **.js"</span>,</span><br><span class="line">    <span class="string">"test"</span>: <span class="string">"mocha test/"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="scripts-脚本"><a href="#scripts-脚本" class="headerlink" title="scripts 脚本"></a>scripts 脚本</h4><p>顾名思义，就是一些脚本代码，可以通过 <code>npm run script-key</code> 来调用，例如在这个 package.json 的文件夹下使用 <code>npm run dev</code> 就相当于运行了 <code>node build/dev-server.js</code> 这一段代码。使用 scripts 的目的就是为了把一些要执行的代码合并到一起，使用 npm run 来快速的运行，方便省事。<br>npm run 是 npm run-script 的缩写，一般都使用前者，但是后者可以更好的反应这个命令的本质。</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="comment">// 脚本</span></span><br><span class="line"><span class="string">"scripts"</span>: &#123;</span><br><span class="line">    <span class="string">"dev"</span>: <span class="string">"node build/dev-server.js"</span>,</span><br><span class="line">    <span class="string">"build"</span>: <span class="string">"node build/build.js"</span>,</span><br><span class="line">    <span class="string">"docs"</span>: <span class="string">"node build/docs.js"</span>,</span><br><span class="line">    <span class="string">"build-docs"</span>: <span class="string">"npm run docs &amp; git checkout gh-pages &amp; xcopy /sy dist\\* . &amp; git add . &amp; git commit -m 'auto-pages' &amp; git push &amp; git checkout master"</span>,</span><br><span class="line">    <span class="string">"build-publish"</span>: <span class="string">"rmdir /S /Q lib &amp; npm run build &amp;git add . &amp; git commit -m auto-build &amp; npm version patch &amp; npm publish &amp; git push"</span>,</span><br><span class="line">    <span class="string">"lint"</span>: <span class="string">"eslint --ext .js,.vue src"</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>npm run 如果不加任何参数，直接运行，会列出 package.json 里面所有可以执行的脚本命令。<br>npm 内置了两个命令简写， npm test 等同于执行 npm run test，npm start 等同于执行 npm run start。</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"><span class="string">"build"</span>: <span class="string">"npm run build-js &amp;&amp; npm run build-css"</span></span><br></pre></td></tr></table></figure><p>上面的写法是先运行 npm run build-js ，然后再运行 npm run build-css ，两个命令中间用 &amp;&amp; 连接。如果希望两个命令同时平行执行，它们中间可以用 &amp; 连接。</p><p>写在 scripts 属性中的命令，也可以在 node_modules/.bin 目录中直接写成 bash 脚本。下面是一个 bash 脚本。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">cd</span> site/main</span><br><span class="line">browserify browser/main.js | uglifyjs -mc &gt; static/bundle.js</span><br></pre></td></tr></table></figure></p><p>假定上面的脚本文件名为 build.sh ，并且权限为可执行，就可以在 scripts 属性中引用该文件。<br><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"><span class="string">"build-js"</span>: <span class="string">"bin/build.sh"</span></span><br></pre></td></tr></table></figure></p><h3 id="pre-和-post-脚本"><a href="#pre-和-post-脚本" class="headerlink" title="pre- 和 post- 脚本"></a>pre- 和 post- 脚本</h3><p>npm run 为每条命令提供了 pre- 和 post- 两个钩子（hook）。以 npm run lint 为例，执行这条命令之前，npm 会先查看有没有定义 prelint 和 postlint 两个钩子，如果有的话，就会先执行 npm run prelint，然后执行 npm run lint，最后执行 npm run postlint。<br><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">&#123;</span><br><span class="line">  <span class="string">"name"</span>: <span class="string">"myproject"</span>,</span><br><span class="line">  <span class="string">"devDependencies"</span>: &#123;</span><br><span class="line">    <span class="string">"eslint"</span>: <span class="string">"latest"</span></span><br><span class="line">    <span class="string">"karma"</span>: <span class="string">"latest"</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="string">"scripts"</span>: &#123;</span><br><span class="line">    <span class="string">"lint"</span>: <span class="string">"eslint --cache --ext .js --ext .jsx src"</span>,</span><br><span class="line">    <span class="string">"test"</span>: <span class="string">"karma start --log-leve=error karma.config.js --single-run=true"</span>,</span><br><span class="line">    <span class="string">"pretest"</span>: <span class="string">"npm run lint"</span>,</span><br><span class="line">    <span class="string">"posttest"</span>: <span class="string">"echo 'Finished running tests'"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>上面代码是一个 package.json 文件的例子。如果执行 npm test，会按下面的顺序执行相应的命令。</p><ol><li>pretest</li><li>test</li><li>posttest</li></ol><p>如果执行过程出错，就不会执行排在后面的脚本，即如果 prelint 脚本执行出错，就不会接着执行 lint 和 postlint 脚本。</p><h3 id="npm-bin"><a href="#npm-bin" class="headerlink" title="npm bin"></a>npm bin</h3><p>npm bin 命令显示相对于当前目录的，Node 模块的可执行脚本所在的目录（即 .bin 目录）。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 项目根目录下执行</span></span><br><span class="line">$ npm bin</span><br><span class="line">./node_modules/.bin</span><br></pre></td></tr></table></figure></p><h1 id="创建全局链接"><a href="#创建全局链接" class="headerlink" title="创建全局链接"></a>创建全局链接</h1><p>npm 提供了一个有趣的命令 npm link，它的功能是在本地包和全局包之间创建符号链接。我们说过使用全局模式安装的包不能直接通过 require 使用。但通过 npm link 命令可以打破这一限制。举个例子，我们已经通过 <code>npm install -g express</code> 安装了 express，这时在工程的目录下运行命令：<br><code>npm link express ./node_modules/express -&gt; /user/local/lib/node_modules/express</code><br>我们可以在 node_modules 子目录中发现一个指向安装到全局的包的符号链接。通过这种方法，我们就可以把全局包当做本地包来使用了。<br>除了将全局的包链接到本地以外，使用 npm link 命令还可以将本地的包链接到全局。使用方法是在包目录（package.json 所在目录）中运行 npm link 命令。如果我们要开发一个包，利用这种方法可以非常方便地在不同的工程间进行测试。</p><h1 id="创建包"><a href="#创建包" class="headerlink" title="创建包"></a>创建包</h1><p>包是在模块基础上更深一步的抽象，Node.js 的包类似于 C/C++ 的函数库或者 Java、.Net 的类库。它将某个独立的功能封装起来，用于发布、更新、依赖管理和版本控制。Node.js 根据 CommonJS 规范实现了包机制，开发了 npm 来解决包的发布和获取需求。<br>Node.js 的包是一个目录，其中包含了一个 JSON 格式的包说明文件 package.json。严格符合 CommonJS 规范的包应该具备以下特征：<br>。package.json 必须在包的顶层目录下；<br>。二进制文件应该在 bin 目录下；<br>。JavaScript 代码应该在 lib 目录下；<br>。文档应该在 doc 目录下；<br>。单元测试应该在 test 目录下。</p><p>Node.js 对包的要求并没有这么严格，只要顶层目录下有 package.json，并符合一些规范即可。当然为了提高兼容性，我们还是建议你在制作包的时候，严格遵守 CommonJS 规范。</p><p>我们也可以把文件夹封装为一个模块，即所谓的包。包通常是一些模块的集合，在模块的基础上提供了更高层的抽象，相当于提供了一些固定接口的函数库。通过定制 package.json，我们可以创建更复杂，更完善，更符合规范的包用于发布。</p><p>Node.js 在调用某个包时，会首先检查包中 packgage.json 文件的 main 字段，将其作为包的接口模块，如果 package.json 或 main 字段不存在，会尝试寻找 index.js 或 index.node 作为包的接口。</p><p>package.json 是 CommonJS 规定的用来描述包的文件，完全符合规范的 package.json 文件应该含有以下字段：<br><span id="inline-yellow">name</span>: 包的名字，必须是唯一的，由小写英文字母、数字和下划线组成，不能包含空格。<br><span id="inline-blue">description</span>: 包的简要说明。<br><span id="inline-green">version</span>: 符合语义化版本识别规范的版本字符串。<br><span id="inline-red">keywords</span>: 关键字数组，通常用于搜索。<br><span id="inline-purple">maintainers</span>: 维护者数组，每个元素要包含 name 、email(可选)、web(可选)字段。<br><span id="inline-yellow">contributors</span>: 贡献者数组，格式与 maintainers 相同。包的作者应该是贡献者数组的第一个元素。<br><span id="inline-blue">bugs</span>: 提交 bug 的地址，可以是网址或者电子邮件地址。<br><span id="inline-green">licenses</span>: 许可证数组，每个元素要包含 type（许可证的名称）和 url（链接到许可证文本的地址）字段。<br><span id="inline-red">repositories</span>: 仓库托管地址数组，每个元素要包含 type（仓库的类型，如 git）、URL（仓库的地址）和 path（相对于仓库的路径，可选）字段。<br><span id="inline-purple">dependencies</span>: 包的依赖，一个关联数组，由包名称和版本号组成。</p><h1 id="包的发布"><a href="#包的发布" class="headerlink" title="包的发布"></a>包的发布</h1><p>通过使用 npm init 可以根据交互式回答产生一个符合标准的 package.json。创建一个 index.js 作为包的接口,一个简单的包就制作完成了。<br>在发布前,我们还需要获得一个账号用于今后维护自己的包,使用 npm adduser 根据提示完成账号的创建<br>完成后可以使用 npm whoami 检测是否已经取得了账号。<br>接下来,在 package.json 所在目录下运行 npm publish，稍等片刻就可以完成发布了，打开浏览器，访问 <a href="http://search.npmjs.org/" target="_blank" rel="external">http://search.npmjs.org/</a> 就可以找到自己刚刚发布的包了。现在我们可以在世界的任意一台计算机上使用 npm install neveryumodule 命令来安装它。<br>如果你的包将来有更新,只需要在 package.json 文件中修改 version 字段,然后重新使用 npm publish 命令就行了。<br>如果你对已发布的包不满意，可以使用 npm unpublish 命令来取消发布。</p><p id="div-border-top-yellow"><em>需要说明的是：json 文件不能有注释</em><br></p><h1 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h1><p><a href="http://javascript.ruanyifeng.com/nodejs/npm.html" target="_blank" rel="external">http://javascript.ruanyifeng.com/nodejs/npm.html</a></p><h1 id="常用命令"><a href="#常用命令" class="headerlink" title="常用命令"></a>常用命令</h1><p>npm version  查看npm和node的版本<br>npm list –depth=0 [-g]  查看[全局]安装的包<br>npm root [-g]  查看[全局的]包的安装路径</p>]]></content>
    
    <summary type="html">
    
      &lt;!-- &lt;img src=&quot;http://i1.piimg.com/588926/30e7b49044d5cfc4.png&quot; alt=&quot;summary-img-src-npm&quot;&gt; --&gt;
    
    </summary>
    
      <category term="前端" scheme="https://309893147.github.io/categories/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="npm" scheme="https://309893147.github.io/tags/npm/"/>
    
  </entry>
  
</feed>
