<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>♌</title>
  
  <subtitle>Mr. Leo</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://xuebin.me/"/>
  <updated>2020-03-08T14:59:54.000Z</updated>
  <id>https://xuebin.me/</id>
  
  <author>
    <name>Leo</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>移动端开发的屏幕、图像、字体与布局的兼容适配</title>
    <link href="https://xuebin.me/posts/366e9f61.html"/>
    <id>https://xuebin.me/posts/366e9f61.html</id>
    <published>2020-03-08T14:59:54.000Z</published>
    <updated>2020-03-08T14:59:54.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p>前端的一大工作内容就是去兼容页面在不同内核的浏览器，不同的设备，不同的分辨率下的行为，使页面的能正常工作在各种各样的宿主环境当中。</p><p>而本文的主题 – 移动端开发的兼容适配与性能优化，就是希望能从一些常见的移动端开发问题出发，厘清 Web 移动端开发的前前后后，一些技术的发展过程，一些问题的优化手段以及给出一些常见的兼容性问题的解决方案。</p><a id="more"></a><h2 id="什么是响应式设计"><a href="#什么是响应式设计" class="headerlink" title="什么是响应式设计"></a>什么是响应式设计</h2><p>首先先聊聊响应式设计，这个与移动端开发有着密切的联系。</p><p>响应式设计即是 RWD，Responsive Web Design。</p><p>这里百度或者谷歌一下会有各种各样的答案。这里一段摘自知乎上我觉得很棒的一个答案：<span class="exturl" data-url="aHR0cHM6Ly93d3cuemhpaHUuY29tL3F1ZXN0aW9uLzIwOTc2NDA1" title="https://www.zhihu.com/question/20976405">什么是响应式布局设计？<i class="fa fa-external-link"></i></span></p><p>根据维基百科及其参考文献，理论上，响应式界面能够适应不同的设备。描述响应式界面最著名的一句话就是 “Content is like water”，翻译成中文便是 “如果将屏幕看作容器，那么内容就像水一样”。</p><h3 id="为什么要设计响应式界面"><a href="#为什么要设计响应式界面" class="headerlink" title="为什么要设计响应式界面"></a>为什么要设计响应式界面</h3><p>为什么要费神地尝试统一所有设备呢？</p><ul><li>即便是 PC 或 Mac 用户，有查显示只有一半的人会将浏览器全屏显示，而剩下的一般人使用多大的浏览器，很难预知；</li><li>台式机、投影、电视、笔记本、手机、平板、手表、VR…… 智能设备正在不断增加，“主流设备” 的概念正在消失；</li><li>屏幕分辨率正飞速发展，同一张图片在不同设备上看起来，大小可能天差地别；</li><li>鼠标、触屏、笔、摄像头手势…… 不可预期的操控方式正在不断出现。</li></ul><h3 id="响应式界面的四个层次"><a href="#响应式界面的四个层次" class="headerlink" title="响应式界面的四个层次"></a>响应式界面的四个层次</h3><ul><li>同一页面在不同大小和比例上看起来都应该是舒适的；</li><li>同一页面在不同分辨率上看起来都应该是合理;</li><li>同一页面在不同操作方式（如鼠标和触屏）下，体验应该是统一的；</li><li>同一页面在不同类型的设备（手机、平板、电脑）上，交互方式应该是符合习惯的。</li></ul><h3 id="响应式界面的基本规则"><a href="#响应式界面的基本规则" class="headerlink" title="响应式界面的基本规则"></a>响应式界面的基本规则</h3><ul><li><p>可伸缩的内容区块：内容区块的在一定程度上能够自动调整，以确保填满整个页面<br><span class="exturl" data-url="aHR0cHM6Ly9jYW1vLmdpdGh1YnVzZXJjb250ZW50LmNvbS9kNGMyYWQ0ZTgwOGYyNTRjZWNiYTkwM2RiMjA1MzE1NjU2MjU2NDhhLzY4NzQ3NDcwNzMzYTJmMmY3MDY5NjMzNDJlN2E2ODY5NmQ2NzJlNjM2ZjZkMmY3NjMyMmQzMzYzMzczNDM3NjU2MzMxMzYzMjMzNjE2NDY2MzU2MzYyMzA2MTM5MzUzOTM3NjYzODY1MzM2MjM2MzQ2MTM0NWY3MjJlNmE3MDY3" title="https://camo.githubusercontent.com/d4c2ad4e808f254cecba903db20531565625648a/68747470733a2f2f706963342e7a68696d672e636f6d2f76322d33633734376563313632336164663563623061393539376638653362363461345f722e6a7067"><img src="https://camo.githubusercontent.com/d4c2ad4e808f254cecba903db20531565625648a/68747470733a2f2f706963342e7a68696d672e636f6d2f76322d33633734376563313632336164663563623061393539376638653362363461345f722e6a7067" alt><i class="fa fa-external-link"></i></span></p></li><li><p>可自由排布的内容区块：当页面尺寸变动较大时，能够减少 / 增加排布的列数<br><span class="exturl" data-url="aHR0cHM6Ly9jYW1vLmdpdGh1YnVzZXJjb250ZW50LmNvbS9kNzE1OGUyMGQ2NTM1MDM3ZGFmMmUzMzhhOGE2NmE0NDdiYTVjOTQxLzY4NzQ3NDcwNzMzYTJmMmY3MDY5NjMzMzJlN2E2ODY5NmQ2NzJlNjM2ZjZkMmY3NjMyMmQ2MzM5NjY2MTY2MzMzNDMxNjMzNTM0MzczMjM0MzAzOTY1NjMzMjM3MzczNzM5MzUzMTM5NjEzMTM4NjUzMjYyNWY3MjJlNmE3MDY3" title="https://camo.githubusercontent.com/d7158e20d6535037daf2e338a8a66a447ba5c941/68747470733a2f2f706963332e7a68696d672e636f6d2f76322d63396661663334316335343732343039656332373737393531396131386532625f722e6a7067"><img src="https://camo.githubusercontent.com/d7158e20d6535037daf2e338a8a66a447ba5c941/68747470733a2f2f706963332e7a68696d672e636f6d2f76322d63396661663334316335343732343039656332373737393531396131386532625f722e6a7067" alt><i class="fa fa-external-link"></i></span></p></li><li><p>适应页面尺寸的边距：到页面尺寸发生更大变化时，区块的边距也应该变化<br><span class="exturl" data-url="aHR0cHM6Ly9jYW1vLmdpdGh1YnVzZXJjb250ZW50LmNvbS9mYWVjMDM1OTEzYzRlMjQ3OTQxZWFjMDRlOGM2ZTAwZjgwZTk4ZTVmLzY4NzQ3NDcwNzMzYTJmMmY3MDY5NjMzMjJlN2E2ODY5NmQ2NzJlNjM2ZjZkMmY3NjMyMmQzOTMyMzYzNDYxNjI2MzM4MzEzMjY2MzQzMDM0MzMzOTYxMzMzNTM2MzEzNzM2NjE2NTYxNjM2NTM2NjI2MzM2NWY3MjJlNmE3MDY3" title="https://camo.githubusercontent.com/faec035913c4e247941eac04e8c6e00f80e98e5f/68747470733a2f2f706963322e7a68696d672e636f6d2f76322d39323634616263383132663430343339613335363137366165616365366263365f722e6a7067"><img src="https://camo.githubusercontent.com/faec035913c4e247941eac04e8c6e00f80e98e5f/68747470733a2f2f706963322e7a68696d672e636f6d2f76322d39323634616263383132663430343339613335363137366165616365366263365f722e6a7067" alt><i class="fa fa-external-link"></i></span></p></li><li><p>能够适应比例变化的图片：对于常见的宽度调整，图片在隐去两侧部分时，依旧保持美观可用<br><span class="exturl" data-url="aHR0cHM6Ly9jYW1vLmdpdGh1YnVzZXJjb250ZW50LmNvbS9mOWJmNGQ0NDVhMWM2ZmFiZjMzYzVlODA1N2I2NDYyMjQ3NzRjM2Q1LzY4NzQ3NDcwNzMzYTJmMmY3MDY5NjMzNDJlN2E2ODY5NmQ2NzJlNjM2ZjZkMmY3NjMyMmQzMjM1MzY2NjMzMzEzOTYxMzQ2NjYyNjYzODMxMzUzODY2MzYzMzM3Mzk2MzMyMzY2NTMwMzA2MzM5NjE2NTYxNWY3MjJlNmE3MDY3" title="https://camo.githubusercontent.com/f9bf4d445a1c6fabf33c5e8057b646224774c3d5/68747470733a2f2f706963342e7a68696d672e636f6d2f76322d32353666333139613466626638313538663633373963323665303063396165615f722e6a7067"><img src="https://camo.githubusercontent.com/f9bf4d445a1c6fabf33c5e8057b646224774c3d5/68747470733a2f2f706963342e7a68696d672e636f6d2f76322d32353666333139613466626638313538663633373963323665303063396165615f722e6a7067" alt><i class="fa fa-external-link"></i></span></p></li><li><p>能够自动隐藏 / 部分显示的内容：如在电脑上显示的的大段描述文本，在手机上就只能少量显示或全部隐藏<br><span class="exturl" data-url="aHR0cHM6Ly9jYW1vLmdpdGh1YnVzZXJjb250ZW50LmNvbS9kNjc4MDIxZTkwZmIzOTcyZDIxNmM2ZGNmNjc3Y2NjYTdiNzg5YjJmLzY4NzQ3NDcwNzMzYTJmMmY3MDY5NjMzNDJlN2E2ODY5NmQ2NzJlNjM2ZjZkMmY3NjMyMmQ2MTM4MzYzMjM1NjUzOTM2MzE2MTMzNjUzMzM0NjEzMTY2MzEzMzMzNjI2NjM5MzEzNzMzNjEzNDM5MzMzMDY0NWY3MjJlNmE3MDY3" title="https://camo.githubusercontent.com/d678021e90fb3972d216c6dcf677ccca7b789b2f/68747470733a2f2f706963342e7a68696d672e636f6d2f76322d61383632356539363161336533346131663133336266393137336134393330645f722e6a7067"><img src="https://camo.githubusercontent.com/d678021e90fb3972d216c6dcf677ccca7b789b2f/68747470733a2f2f706963342e7a68696d672e636f6d2f76322d61383632356539363161336533346131663133336266393137336134393330645f722e6a7067" alt><i class="fa fa-external-link"></i></span></p></li><li><p>能自动折叠的导航和菜单：展开还是收起，应该根据页面尺寸来判断<br><span class="exturl" data-url="aHR0cHM6Ly9jYW1vLmdpdGh1YnVzZXJjb250ZW50LmNvbS85Mzc2Yzc1NzkwMTc2NWYwZjc2YjM2YWExNDEyZDZhMWJlMDlmZWI1LzY4NzQ3NDcwNzMzYTJmMmY3MDY5NjMzMzJlN2E2ODY5NmQ2NzJlNjM2ZjZkMmY3NjMyMmQ2NjY2MzIzODMxNjE2NjYyNjU2NTM3MzgzMjYxMzkzNDY2MzQ2MzM3NjYzNTM4NjUzMDMyNjQzNzY2MzAzNTY0NWY3MjJlNmE3MDY3" title="https://camo.githubusercontent.com/9376c757901765f0f76b36aa1412d6a1be09feb5/68747470733a2f2f706963332e7a68696d672e636f6d2f76322d66663238316166626565373832613934663463376635386530326437663035645f722e6a7067"><img src="https://camo.githubusercontent.com/9376c757901765f0f76b36aa1412d6a1be09feb5/68747470733a2f2f706963332e7a68696d672e636f6d2f76322d66663238316166626565373832613934663463376635386530326437663035645f722e6a7067" alt><i class="fa fa-external-link"></i></span></p></li><li><p>放弃使用像素作为尺寸单位：用 dp(对于前端来说，这里可能是 rem) 尺寸等方法来确保页面在分辨率相差很大的设备上，看起来也能保持一致。同时也要求提供的图片应该比预想的更大，才能适应高分辨率的屏幕</p></li></ul><p>上面一段我觉得已经涵盖了响应式设计的绝大部分，简单总结起来，可以概括为：</p><ol><li>媒体查询，边界断点的规则设定（Media queries &amp;&amp; break point）</li><li>内容的可伸缩性效果（Flexibel visuals）</li><li>流式网格布局 (Fluid grids)</li><li>主要内容呈现及图片的高质量 (Main content and high quality)</li></ol><h2 id="响应式-vs-自适应"><a href="#响应式-vs-自适应" class="headerlink" title="响应式 vs. 自适应"></a>响应式 vs. 自适应</h2><p>响应式设计是 Responsive Web Design（RWD），自适应设计是 Adaptive Web Design（AWD）。经常有人会将两者混为一谈，或者其实根本也区分不了所谓的响应式与自适应。</p><p>其实在我写这篇文章的时候，我也无法很好的去区分两者。</p><p>RWD 和 AWD 两者都是为了适配各种不同的移动设备，致力于提升用户体验所产生的的技术。核心思想是用技术来使网页适应从小到大（现在到超大）的不同分辨率的屏幕。通常认为，RWD 是 AWD 的子集。</p><p>RWD：Ethan Marcote 的文章是大家认为 RWD 的起源。他提出的 RWD 方案是通过 HTML 和 CSS 的媒体查询技术，配合流体布局实现。RWD 倾向于只改变元素的外观布局，而不大幅度改变内容。Jeffrey Zeldman 总结说，我们就把 RWD 定义为一切能用来为各种分辨率和设备性能优化视觉体验的技术。</p><p>AWD：Adaptive Design 是 Aaron Gustafson 的书的标题。他认为 AWD 在包括 RWD 的 CSS 媒体查询技术以外，也要用 Javascript 来操作 HTML 来更适应移动设备的能力。AWD 有可能会针对移动端用户减去内容，减去功能。AWD 可以在服务器端就进行优化，把优化过的内容送到终端上。</p><p>一图胜千言。</p><p><span class="exturl" data-url="aHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vODU1NDE0My82MDAwNDEzNy1hM2UxZTM4MC05NjllLTExZTktODE3NS1jMTAwYjJhN2Q3MTgucG5n" title="https://user-images.githubusercontent.com/8554143/60004137-a3e1e380-969e-11e9-8175-c100b2a7d718.png"><img src="https://user-images.githubusercontent.com/8554143/60004137-a3e1e380-969e-11e9-8175-c100b2a7d718.png" alt><i class="fa fa-external-link"></i></span></p><p>从定义上而言，RWD 是一套代码，适用于所有屏幕。而 AWD 则是多端多套代码。本文不会过多去纠结响应式与自适应区别，我觉得这两者的本质都是致力于适配不同设备，更好地提升用户体验。</p><p><span class="exturl" data-url="aHR0cHM6Ly93d3cucXVvcmEuY29tL1doYXQtaXMtdGhlLWRpZmZlcmVuY2UtYmV0d2Vlbi1yZXNwb25zaXZlLWFuZC1hZGFwdGl2ZS1kZXNpZ24=" title="https://www.quora.com/What-is-the-difference-between-responsive-and-adaptive-design">Quora - Responsive Design vs. Adaptive Design?<i class="fa fa-external-link"></i></span><br><span class="exturl" data-url="aHR0cHM6Ly93d3cuemhpaHUuY29tL3F1ZXN0aW9uLzIwNjI4MDUwP3JmPTI0MzM0MTgx" title="https://www.zhihu.com/question/20628050?rf=24334181">zhihu – Responsive design 和 Adaptive design 的区别<i class="fa fa-external-link"></i></span></p><h2 id="渐进增强-vs-优雅降级"><a href="#渐进增强-vs-优雅降级" class="headerlink" title="渐进增强 vs. 优雅降级"></a>渐进增强 vs. 优雅降级</h2><ul><li><p>渐进增强（progressive enhancement）：针对低版本浏览器进行构建页面，保证最基本的功能，然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验。</p></li><li><p>优雅降级（graceful degradation）：一开始就构建完整的功能，然后再针对低版本浏览器进行兼容。</p></li></ul><p>区别：优雅降级是从复杂的现状开始，并试图减少用户体验的供给，而渐进增强则是从一个非常基础的，能够起作用的版本开始，并不断扩充，以适应未来环境的需要。降级（功能衰减）意味着往回看；而渐进增强则意味着朝前看，同时保证其根基处于安全地带。　</p><p>渐进增强 / 优雅降级通常是 AWD 会牵扯到的另一个技术术语。本质上而言即是随着屏幕的大小的改变，功能会一点一点增强。</p><p>也通常会用在一些高级 CSS3 属性上，我们对一些 CSS 属性进行特性检测，甚至不进行特性检测直接使用。后果是在支持它的网页上该属性正常展示，而不支持它的网页该属性不生效，但也不影响用户的基本使用。</p><p>典型的例子是 CSS3 逐渐被大众认可并被使用，PC 端页面开始由 IE678 向兼容性更好的 IE9+，chrome，firefox 浏览器转变的时期。我们可以对页面元素直接使用阴影，圆角等属性。对于不支持它的低版本 IE 而言，没有什么损失，而对于支持它的高级浏览器而言，带给了用户更好的交互视觉体验，这就是渐进增强。</p><h2 id="移动端屏幕适配方案"><a href="#移动端屏幕适配方案" class="headerlink" title="移动端屏幕适配方案"></a>移动端屏幕适配方案</h2><p>下面会针对一些具体的案例，展开讲讲。第一个是高保真还原设计稿，也就是如何适配移动端繁杂的屏幕大小。</p><p>通常而言，设计师只会给出单一分辨率下的设计稿，而我们要做的，就是以这个设计稿为基准，去适配所有不同大小的移动端设备。</p><p>在此之前，有一些基础概念需要理解。</p><blockquote><p>一些概念性的东西，大部分人很难一次性记住，或者记了又忘，我觉得记忆这个东西比较看技巧，比如关联法，想象法，把这些生硬的概念与一些符合我们常识的知识关联在一起记忆，往往能够事半功倍。</p></blockquote><h3 id="设备独立像素"><a href="#设备独立像素" class="headerlink" title="设备独立像素"></a>设备独立像素</h3><p>以 iPhone6/7/8 为例，这里我们打开 Chrome 开发者工具：</p><p><span class="exturl" data-url="aHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vODU1NDE0My82MDAxNTk4NC02ZjJlNTYwMC05NmI3LTExZTktOGZkNi05ODNmZTQ2MTQ1ZmUucG5n" title="https://user-images.githubusercontent.com/8554143/60015984-6f2e5600-96b7-11e9-8fd6-983fe46145fe.png"><img src="https://user-images.githubusercontent.com/8554143/60015984-6f2e5600-96b7-11e9-8fd6-983fe46145fe.png" alt><i class="fa fa-external-link"></i></span></p><p>这里的 <code>375 * 667</code> 表示的是什么呢，表示的是设备独立像素（DIP），也可以理解为 CSS 像素，也称为逻辑像素：</p><p>设备独立像素 = CSS 像素 = 逻辑像素</p><p>如何记忆呢？这里使用 CSS 像素来记忆，也就是说。我们设定一个宽度为 375px 的 div，刚好可以充满这个设备的一行，配合高度 667px ，则 div 的大小刚好可以充满整个屏幕。</p><h3 id="物理像素"><a href="#物理像素" class="headerlink" title="物理像素"></a>物理像素</h3><p>OK，那么，什么又是物理像素呢。我们到电商网站购买手机，都会看一看手机的参数，以 JD 上的 iPhone7 为例：</p><p><span class="exturl" data-url="aHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vODU1NDE0My82MDgwNzMwMS1lMTI2OTUwMC1hMWI3LTExZTktOGRjMS1lYTYyOTVmZmJjMzcucG5n" title="https://user-images.githubusercontent.com/8554143/60807301-e1269500-a1b7-11e9-8dc1-ea6295ffbc37.png"><img src="https://user-images.githubusercontent.com/8554143/60807301-e1269500-a1b7-11e9-8dc1-ea6295ffbc37.png" alt><i class="fa fa-external-link"></i></span></p><p>可以看到，iPhone7 的分辨率是 <code>1334 x 750</code>，这里描述的就是屏幕实际的物理像素。</p><p>物理像素，又称为设备像素。显示屏是由一个个物理像素点组成的，<code>1334 x 750</code> 表示手机分别在垂直和水平上所具有的像素点数。通过控制每个像素点的颜色，就可以使屏幕显示出不同的图像，屏幕从工厂出来那天起，它上面的物理像素点就固定不变了，单位为 pt。</p><p>设备像素 = 物理像素</p><h2 id="DPR（Device-Pixel-Ratio）-设备像素比"><a href="#DPR（Device-Pixel-Ratio）-设备像素比" class="headerlink" title="DPR（Device Pixel Ratio） 设备像素比"></a>DPR（Device Pixel Ratio） 设备像素比</h2><p>OK，有了上面两个概念，就可以顺理成章引出下一个概念。DPR（Device Pixel Ratio） 设备像素比，这个与我们通常说的视网膜屏（多倍屏，Retina 屏）有关。</p><p>设备像素比描述的是未缩放状态下，物理像素和设备独立像素的初始比例关系。</p><p>简单的计算公式：</p><p>DPR = 物理像素 / 设备独立像素</p><p>我们套用一下上面 iPhone7 的数据（取设备的物理像素宽度与设备独立像素宽度进行计算）：</p><p>iPhone7’s DPR = iPhone7’s 物理像素宽度 / iPhone7’s 设备独立像素宽度 = 2</p><blockquote><p>750 / 375 = 2<br>或者是 1334 / 667 = 2</p></blockquote><p>可以得到 iPhone7 的 dpr 为 2。也就是我们常说的视网膜屏幕。</p><p>视网膜（Retina）屏幕是苹果公司 “发明” 的一个营销术语。 苹果公司将 <code>dpr &gt; 1</code> 的屏幕称为视网膜屏幕。</p><p><span class="exturl" data-url="aHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vODU1NDE0My82MDgwODE1MS0yYmE5MTEwMC1hMWJhLTExZTktODFlYS1jMGZlMGI1MjQ2MjUucG5n" title="https://user-images.githubusercontent.com/8554143/60808151-2ba91100-a1ba-11e9-81ea-c0fe0b524625.png"><img src="https://user-images.githubusercontent.com/8554143/60808151-2ba91100-a1ba-11e9-81ea-c0fe0b524625.png" alt><i class="fa fa-external-link"></i></span></p><p>在视网膜屏幕中，以 dpr = 2 为例，把 4(2x2) 个像素当 1 个像素使用，这样让屏幕看起来更精致，但是元素的大小本身却不会改变：</p><p><span class="exturl" data-url="aHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vODU1NDE0My82MDgwODIyMC01Mzk4NzQ4MC1hMWJhLTExZTktODFhNC04ZTk2MDVkZDY4MTcucG5n" title="https://user-images.githubusercontent.com/8554143/60808220-53987480-a1ba-11e9-81a4-8e9605dd6817.png"><img src="https://user-images.githubusercontent.com/8554143/60808220-53987480-a1ba-11e9-81a4-8e9605dd6817.png" alt><i class="fa fa-external-link"></i></span></p><p>OK，我们再来看看 iPhone XS Max：</p><p><span class="exturl" data-url="aHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vODU1NDE0My82MDgwODY5MS03ZjY4MmEwMC1hMWJiLTExZTktODMwMC0yOTQ0NDM0NDNhOWQucG5n" title="https://user-images.githubusercontent.com/8554143/60808691-7f682a00-a1bb-11e9-8300-294443443a9d.png"><img src="https://user-images.githubusercontent.com/8554143/60808691-7f682a00-a1bb-11e9-8300-294443443a9d.png" alt><i class="fa fa-external-link"></i></span></p><p>它的物理像素如上图是 <code>2688 x 1242</code>，</p><p><span class="exturl" data-url="aHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vODU1NDE0My82MDgwODkwNy1mMjcxYTA4MC1hMWJiLTExZTktOWI1ZC0yMzg5NmFjZDdlNzUucG5n" title="https://user-images.githubusercontent.com/8554143/60808907-f271a080-a1bb-11e9-9b5d-23896acd7e75.png"><img src="https://user-images.githubusercontent.com/8554143/60808907-f271a080-a1bb-11e9-9b5d-23896acd7e75.png" alt><i class="fa fa-external-link"></i></span></p><p>它的 CSS 像素是 <code>896 x 414</code>，很容易得出 iPhone XS Max 的 dpr 为 3。</p><blockquote><p>上面三个概念（CSS 像素、设备独立像素、DPR）是我觉得比较重要的，还有一些其他比较重要的概念 PPI、DPI 不影响后续的内容，可以自行去加深理解。</p></blockquote><p>OK，到这里我们就完成了一个小的里程碑。我们通常说的 H5 手机适配也就是指的这两个维度：</p><ol><li>适配不同屏幕大小，也就是适配不同屏幕下的 CSS 像素</li><li>适配不同像素密度，也就是适配不同屏幕下 dpr 不一致导致的一些问题</li></ol><h2 id="适配不同屏幕大小"><a href="#适配不同屏幕大小" class="headerlink" title="适配不同屏幕大小"></a>适配不同屏幕大小</h2><p>适配不同屏幕大小，也就是适配不同屏幕下的 CSS 像素。最早移动端屏幕 CSS 像素适配方案是 CSS 媒体查询。但是无法做到高保真接近 100% 的还原。</p><p>适配不同屏幕大小其实只需要遵循一条原则，确保页面元素大小的与屏幕大小保持一定比例。也就是：按比例还原设计稿</p><p><span class="exturl" data-url="aHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vODU1NDE0My82MjA0NDgyMC02ZjdkYmMwMC1iMjM2LTExZTktODc3Zi1jMDU4NzQ2MmNhYzgucG5n" title="https://user-images.githubusercontent.com/8554143/62044820-6f7dbc00-b236-11e9-877f-c0587462cac8.png"><img src="https://user-images.githubusercontent.com/8554143/62044820-6f7dbc00-b236-11e9-877f-c0587462cac8.png" alt><i class="fa fa-external-link"></i></span></p><p>假设我们现在拿到标注为 <code>375*667</code> 的大小的设计稿，其中一个元素的标注如下：</p><p><span class="exturl" data-url="aHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vODU1NDE0My82MjA0NTA4OC0wZmQzZTA4MC1iMjM3LTExZTktOTQ1OS0zYWQ4MGYxOGM3MmEucG5n" title="https://user-images.githubusercontent.com/8554143/62045088-0fd3e080-b237-11e9-9459-3ad80f18c72a.png"><img src="https://user-images.githubusercontent.com/8554143/62045088-0fd3e080-b237-11e9-9459-3ad80f18c72a.png" alt><i class="fa fa-external-link"></i></span></p><p><span class="exturl" data-url="aHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vODU1NDE0My82MjA0NTIyNS02NzcyNGMwMC1iMjM3LTExZTktODY5Zi1hNWZjOThkYmI4YWMucG5n" title="https://user-images.githubusercontent.com/8554143/62045225-67724c00-b237-11e9-869f-a5fc98dbb8ac.png"><img src="https://user-images.githubusercontent.com/8554143/62045225-67724c00-b237-11e9-869f-a5fc98dbb8ac.png" alt><i class="fa fa-external-link"></i></span></p><p>以页面宽度为基准的话，那么，</p><ul><li>元素的宽度为：<code>209/375 = 55.73%</code></li><li>元素的高度为：<code>80/375 = 21.33%</code></li><li>元素的上左右边距依次计算…</li></ul><p>这样，无论屏幕的 CSS 像素宽度是 320px 还是 375px 还是 414px，按照等量百分比还原出来的界面总是正确的。</p><p>然而，理想很丰满，现实很骨感。实现上述百分比方案的核心需要一个全局通用的基准单位，让所有百分比展示以它为基准，但是在 CSS 中，根据 <span class="exturl" data-url="aHR0cHM6Ly93d3cudzMub3JnL1RSL2Nzcy12YWx1ZXMtNC8jcGVyY2VudGFnZXM=" title="https://www.w3.org/TR/css-values-4/#percentages">CSS Values and Units Module Level 4<i class="fa fa-external-link"></i></span> 的定义：</p><blockquote><p>百分比值总要相对于另一个量，比如长度。每个允许使用百分比值的属性，同时也要定义百分比值参照的那个量。这个量可以是相同元素的另一个属性的值，也可以是祖先元素的某个属性的值，甚至是格式化上下文的一个度量（比如包含块的宽度）。</p></blockquote><p>具体来说：</p><ul><li><p>宽度（width）、间距（maring/padding）支持百分比值，但默认的相对参考值是包含块的宽度；</p></li><li><p>高度（height）百分比的大小是相对其父级元素高的大小；</p></li><li><p>边框（border）不支持百分值；</p></li><li><p>边框圆角半径（border-radius）支持百分比值，但水平方向相对参考值是盒子的宽度，垂直方向相对参考值是盒子的高度；</p></li><li><p>文本大小（font-size）支持百分比值，但相对参考值是父元素的 font-size 的值；</p></li><li><p>盒阴影（box-shadow）和文本阴影（text-shadow）不支持百分比值；</p></li></ul><p>首先，支持百分比单位的度量属性有其各自的参照基准，其次并非所有度量属性都支持百分比单位。所以我们需要另辟蹊径。</p><h3 id="rem-适配方案"><a href="#rem-适配方案" class="headerlink" title="rem 适配方案"></a>rem 适配方案</h3><p>在 vw 方案出来之前，最被大众接受的就是使用 rem 进行适配的方案，因为 rem 满足上面说的，可以是一个全局性的基准单位。</p><blockquote><p>rem（font size of the root element），在 <span class="exturl" data-url="aHR0cHM6Ly9kcmFmdHMuY3Nzd2cub3JnL2Nzcy12YWx1ZXMtMy8jbGVuZ3Rocw==" title="https://drafts.csswg.org/css-values-3/#lengths">CSS Values and Units Module Level 3<i class="fa fa-external-link"></i></span> 中的定义就是， 根据网页的根元素来设置字体大小，和 em（font size of the element）的区别是，em 是根据其父元素的字体大小来设置，而 rem 是根据网页的跟元素（html）来设置字体大小。</p></blockquote><h4 id="flexible"><a href="#flexible" class="headerlink" title="flexible"></a>flexible</h4><p>基于此，淘宝早年推行的一套以 rem 为基准的适配方案：<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2FtZmUvbGliLWZsZXhpYmxl" title="https://github.com/amfe/lib-flexible">lib-flexible<i class="fa fa-external-link"></i></span>。其核心做法在于：</p><ul><li>根据设备的 dpr 动态改写 <code>&lt;meta&gt;</code> 标签，设置 viewport 的缩放</li><li>给 <code>&lt;html&gt;</code> 元素添加 data-dpr 属性，并且动态改写 data-dpr 的值</li><li>根据 <code>document.documentElement.clientWidth</code> 动态修改 <code>&lt;html&gt;</code> 的 font-size ，页面其他元素使用 rem 作为长度单位进行布局，从而实现页面的等比缩放</li></ul><blockquote><p>关于头两点，其实现在的 <code>lib-flexible</code> 库已经不这样做了，不再去缩放 Viewport，字体大小的设定也直接使用了 rem</p></blockquote><h4 id="hotcss"><a href="#hotcss" class="headerlink" title="hotcss"></a>hotcss</h4><p><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2ltb2NoZW4vaG90Y3Nz" title="https://github.com/imochen/hotcss">hotcss<i class="fa fa-external-link"></i></span> 不是一个库，也不是一个框架。它是一个移动端布局开发解决方案。使用 hotcss 可以让移动端布局开发更容易。本质的思想与 flexible 完全一致。</p><h3 id="对于-rem-方案的一些总结"><a href="#对于-rem-方案的一些总结" class="headerlink" title="对于 rem 方案的一些总结"></a>对于 rem 方案的一些总结</h3><p>使用 flexible/hotcss 作为屏幕宽度适配解决方案，是存在一些问题的：</p><ol><li>动态修改 Viewport 存在一定的风险的，譬如通过 Viewport 改变了页面的缩放之后，获取到的 <code>innerWidth/innerHeight</code> 也会随之发生变化，如果业务逻辑有获取此类高宽进行其他计算的，可能会导致意想不到的错误；</li></ol><blockquote><p>到今天，其实存在很多在 flexible 基础上演化而来的各种 rem 解决方案，有的不会对 Viewport 进行缩放处理，自行处理 1px 边框问题。</p></blockquote><ol start="2"><li>flexible/hotcss 都并非纯 CSS 方案，需要引入一定的 Javascript 代码</li><li>rem 的设计初衷并非是用于解决此类问题，用 rem 进行页面的宽度适配多少有一种 hack 的感觉</li><li>存在一定的兼容性问题，对于安卓 4.4 以下版本系统不支持 viewport 缩放（当然，flexible 处理 Android 系列时，始终认为其 dpr 为 1，没有进行 viewport 缩放）</li></ol><h3 id="vw-适配方案"><a href="#vw-适配方案" class="headerlink" title="vw 适配方案"></a>vw 适配方案</h3><p>严格来说，使用 rem 进行页面适配其实是一种 hack 手段，rem 单位的初衷本身并不是用来进行移动端页面宽度适配的。</p><p>到了今天，有了一种更好的替代方案，使用 vw 进行适配 。</p><p>百分比适配方案的核心需要一个全局通用的基准单位，rem 是不错，但是需要借助 Javascript 进行动态修改根元素的 <code>font-size</code>，而 vw/vh（vmax/vmin） 的出现则很好弥补 rem 需要 JS 辅助的缺点。</p><p>根据 CSS Values and Units Module Level 4：<code>vw</code>等于初始包含块（html 元素）宽度的 1%，也就是</p><ul><li><code>1vw</code> 等于 <code>window.innerWidth</code> 的数值的 1%</li><li><code>1vh</code> 等于<code>window.innerHeight</code> 的数值的 1%</li></ul><p><span class="exturl" data-url="aHR0cHM6Ly9jYW1vLmdpdGh1YnVzZXJjb250ZW50LmNvbS9mOGEzODNjYjIwNzI5NmIxMWM2ODVhM2ZkNTRlOWRmMjI4ZGNjY2E5LzY4NzQ3NDcwNzMzYTJmMmY3NTcwNmM2ZjYxNjQyZDY5NmQ2MTY3NjU3MzJlNmE2OTYxNmU3MzY4NzUyZTY5NmYyZjc1NzA2YzZmNjE2NDVmNjk2ZDYxNjc2NTczMmYzNzMwMzEzNjM2MzEzNzJkNjYzMDY1MzY2NTY0NjUzMTY0NjE2NDMxNjIzMDY1NjYyZTcwNmU2Nw==" title="https://camo.githubusercontent.com/f8a383cb207296b11c685a3fd54e9df228dccca9/68747470733a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f373031363631372d663065366564653164616431623065662e706e67"><img src="https://camo.githubusercontent.com/f8a383cb207296b11c685a3fd54e9df228dccca9/68747470733a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f373031363631372d663065366564653164616431623065662e706e67" alt><i class="fa fa-external-link"></i></span></p><p>再以上面设计稿图的元素为例，那么，</p><ul><li>元素的宽度为：<code>209/375 = 55.73% = 55.73vw</code></li><li>元素的高度为：<code>80/375 = 21.33% = 21.33vw</code></li><li>元素的上左右边距依次计算…</li></ul><p>根据相关的测试，可以使用 vw 进行长度单位的有：</p><ul><li>容器大小适配，可以使用 vw</li><li>文本大小的适配，可以使用 vw</li><li>大于 1px 的边框、圆角、阴影都可以使用 vw</li><li>内距和外距，可以使用 vw</li></ul><p>简单的一个页面，看看效果，完全是等比例缩放的效果：</p><p><span class="exturl" data-url="aHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vODU1NDE0My82NDAwNzM5My1hN2FkM2U4MC1jYjAzLTExZTktOTg4YS05MGE3YTM4OTkwZjYuZ2lm" title="https://user-images.githubusercontent.com/8554143/64007393-a7ad3e80-cb03-11e9-988a-90a7a38990f6.gif"><img src="https://user-images.githubusercontent.com/8554143/64007393-a7ad3e80-cb03-11e9-988a-90a7a38990f6.gif" alt><i class="fa fa-external-link"></i></span></p><p><span class="exturl" data-url="aHR0cHM6Ly9jb2RlcGVuLmlvL0Nob2tjb2NvL3Blbi9vTnZXVllxP2VkaXRvcnM9MTEwMA==" title="https://codepen.io/Chokcoco/pen/oNvWVYq?editors=1100">CodePen Demo（移动端打开）：使用 vw 进行页面适配<i class="fa fa-external-link"></i></span></p><h3 id="自动转换插件"><a href="#自动转换插件" class="headerlink" title="自动转换插件"></a>自动转换插件</h3><p>当我们使用 rem 作为长度单位的时，通常会有借助 Sass/Less 实现一个转换函数，像是这样：</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">// 假设设计稿的宽度是 375px，假设取设计稿宽度下 1rem = 100px</span></span><br><span class="line"><span class="variable">$baseFontSize</span>: <span class="number">100</span>;</span><br><span class="line"> </span><br><span class="line">@function px2rem(<span class="variable">$px</span>) &#123;</span><br><span class="line">    @return <span class="variable">$px</span> / <span class="variable">$baseFontSize</span> * 1rem;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>同理，在 vw 方案下，我们只需要去改写这个方法：</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><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">// 假设设计稿的宽度是 375px</span></span><br><span class="line">@function px2rem(<span class="variable">$px</span>) &#123;</span><br><span class="line">    @return <span class="variable">$px</span> / 375 * 100vw;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>当然，我们还可以借助一些插件包去实现这个自动转换，提高效率，譬如 <span class="exturl" data-url="aHR0cHM6Ly93d3cubnBtanMuY29tL3BhY2thZ2UvcG9zdGNzcy1weC10by12aWV3cG9ydA==" title="https://www.npmjs.com/package/postcss-px-to-viewport">postcss-px-to-viewport<i class="fa fa-external-link"></i></span></p><h3 id="vw-polyfill"><a href="#vw-polyfill" class="headerlink" title="vw polyfill"></a>vw polyfill</h3><p>vw 现在毕竟还是存在兼容问题的，看看兼容性：</p><p><span class="exturl" data-url="aHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vODU1NDE0My82MzgyNjgzMy03ZDNjNzM4MC1jOTkzLTExZTktOWQwZS02ZDMzZjJkNWFhMWEucG5n" title="https://user-images.githubusercontent.com/8554143/63826833-7d3c7380-c993-11e9-9d0e-6d33f2d5aa1a.png"><img src="https://user-images.githubusercontent.com/8554143/63826833-7d3c7380-c993-11e9-9d0e-6d33f2d5aa1a.png" alt><i class="fa fa-external-link"></i></span></p><p>其实已经覆盖了绝大部分设备，那么如果业务使用了且又真的出现了兼容问题，应该怎么处理呢？有两种方式可以进行降级处理：</p><ul><li>CSS Houdini：通过 CSS Houdini 针对 vw 做处理，调用 CSS Typed OM Level1 提供的 CSSUnitValue API。</li><li>CSS Polyfill：通过相应的 Polyfill 做相应的处理，目前针对于 vw 单位的 Polyfill 主要有：vminpoly、Viewport Units Buggyfill、vunits.js 和 Modernizr</li></ul><h2 id="对于-vw-方案的一些总结"><a href="#对于-vw-方案的一些总结" class="headerlink" title="对于 vw 方案的一些总结"></a>对于 vw 方案的一些总结</h2><p>vw 确实看上去很不错，但是也是存在它的一些问题：</p><ol><li>也没能很好的解决 1px 边框在高清屏下的显示问题，需要自行处理</li><li>由于 vw 方案是完全的等比缩放，在完全等比还原设计稿的同时带来的一个问题是无法很好的限定一个最大最小宽度值，由于 rem 方案是借助 Javascript 的，所以这一点 rem 比 vw 会更加的灵活</li></ol><p>当然，两个方案现阶段其实都可以使用甚至一起搭配使用，更多详情可以读读：</p><ul><li><span class="exturl" data-url="aHR0cHM6Ly9qdWVqaW4uaW0vZW50cnkvNWE5ZDA3ZWU2ZmI5YTAyOGMxNDlmNTVi" title="https://juejin.im/entry/5a9d07ee6fb9a028c149f55b">再聊移动端页面的适配<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9tcC53ZWl4aW4ucXEuY29tL3MvTmdHZDl6U202TkVhRjVKeHA1V0JLZw==" title="https://mp.weixin.qq.com/s/NgGd9zSm6NEaF5Jxp5WBKg">H5 必知必会之像素级还原设计稿<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cuc21hc2hpbmdtYWdhemluZS5jb20vMjAxNi8wNS9mbHVpZC10eXBvZ3JhcGh5Lw==" title="https://www.smashingmagazine.com/2016/05/fluid-typography/">Responsive And Fluid Typography With vh And vw Units<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cuY25ibG9ncy5jb20vd2VuZ3h1ZXNvbmcvYXJjaGl2ZS8yMDE2LzA1LzE2LzU0OTc2NTMuaHRtbA==" title="https://www.cnblogs.com/wengxuesong/archive/2016/05/16/5497653.html">使用 VH 和 VW 实现真正的流体排版<i class="fa fa-external-link"></i></span></li></ul><h2 id="1px-线"><a href="#1px-线" class="headerlink" title="1px 线"></a>1px 线</h2><p>上面说到使用 vw 适配屏幕大小方案，其中有一个缺点就是在 Retina 屏下，无法很好的展示真正的 1px 物理像素线条。</p><p>设计师想要的 retina 下 <code>border: 1px</code>，其实是 1 物理像素宽，而不是 1 CSS 像素宽度，对于 CSS 而言：</p><ul><li>在 dpr = 1 时，此时 1 物理像素等于 1 CSS 像素宽度；</li><li>在 dpr = 2 时，此时 1 物理像素等于 0.5 CSS 宽度像素，可以认为 <code>border-width: 1px</code> 这里的 1px 其实是 1 CSS 像素宽度，等于 2 像素物理宽度，设计师其实想要的是 <code>border-width: 0.5px</code>;</li><li>在 dpr = 3 时，此时 1 物理像素等于 0.33 CSS 宽度像素，设计师其实想要的是 border: 0.333px</li></ul><p>然而，并不是所有手机浏览器都能识别 <code>border-width: 0.5px</code>，在 iOS7 以下，Android 等其他系统里，小于 1px 的单位会被当成为 0px 处理，那么如何实现这 0.5px、0.33px 呢？</p><p>这里介绍几种方法：</p><ul><li>渐变实现</li><li>使用缩放实现</li><li>使用图片实现（base64）</li><li>使用 SVG 实现（嵌入 background url）</li></ul><p><span class="exturl" data-url="aHR0cHM6Ly9jb2RlcGVuLmlvL0Nob2tjb2NvL3Blbi9YeU5qcUs=" title="https://codepen.io/Chokcoco/pen/XyNjqK">Retina 屏幕下 1px 线的实现<i class="fa fa-external-link"></i></span></p><h2 id="图片适配及优化"><a href="#图片适配及优化" class="headerlink" title="图片适配及优化"></a>图片适配及优化</h2><p>图像通常占据了网页上下载资源绝的大部分。优化图像通常可以最大限度地减少从网站下载的字节数以及提高网站性能。</p><p>通常可以，有一些通用的优化手段：</p><ol><li>消除多余的图像资源</li><li>尽可能利用 CSS3\SVG 矢量图像替代某些光栅图像</li><li>谨慎使用字体图标，使用网页字体取代在图像中进行文本编码</li><li>选择正确的图片格式</li><li>为不同 DPR 屏幕提供最适合的图片尺寸</li></ol><p>本文重点关注如何在不同的 dpr 屏幕下，让图片看起来都不失真。</p><p>首先就是上述的第二点，尽可能利用 CSS3\SVG 矢量图像替代某些光栅图像。某些简单的几何图标，可以用 CSS3 快速实现的图形，都应该尽量避免使用光栅图像。这样能够保证它们在任何尺寸下都不会失真。</p><p>其次，实在到了必须使用光栅图像的地步，也是有许多方式能保证图像在各种场景下都不失真。</p><h3 id="无脑多倍图"><a href="#无脑多倍图" class="headerlink" title="无脑多倍图"></a>无脑多倍图</h3><p>在移动端假设我们需要一张 CSS 像素为 <code>300 x 200</code> 的图像，考虑到现在已经有了 dpr = 3 的设备，那么要保证图片在 dpr = 3 的设备下也正常高清展示，我们最大可能需要一张 <code>900 x 600</code> 的原图。</p><p>这样，不管设备的 dpr 是否为 3，我们统一都使用 3 倍图。这样即使在 dpr = 1，dpr = 2 的设备上，也能非常好的展示图片。</p><p>当然这样并不可取，会造成大量带宽的浪费。现代浏览器，提供了更好的方式，让我们能够根据设备 dpr 的不同，提供不同尺寸的图片。</p><h3 id="srcset-配合-1x-2x-像素密度描述符"><a href="#srcset-配合-1x-2x-像素密度描述符" class="headerlink" title="srcset 配合 1x 2x 像素密度描述符"></a>srcset 配合 1x 2x 像素密度描述符</h3><p>简单来说，srcset 可以根据不同的 dpr 拉取对应尺寸的图片：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">'illustration'</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">'illustration-small.png'</span></span></span><br><span class="line"><span class="tag">       <span class="attr">srcset</span>=<span class="string">'images/illustration-small.png 1x,</span></span></span><br><span class="line"><span class="tag"><span class="string">               images/illustration-big.png 2x'</span></span></span><br><span class="line"><span class="tag">       <span class="attr">style</span>=<span class="string">'max-width: 500px'</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><p>上面 <code>srcset</code> 里的 1x，2x 表示 像素密度描述符，表示</p><ul><li>当屏幕的 dpr = 1 时，使用 <code>images/illustration-small.png</code> 这张图</li><li>当屏幕的 dpr = 2 时，使用 <code>images/illustration-big.png</code> 这张图</li></ul><h3 id="srcset-属性配合-sizes-属性-w-宽度描述符"><a href="#srcset-属性配合-sizes-属性-w-宽度描述符" class="headerlink" title="srcset 属性配合 sizes 属性 w 宽度描述符"></a>srcset 属性配合 sizes 属性 w 宽度描述符</h3><p>上面 1x，2x 的写法比较容易接受易于理解。</p><p>除此之外，srcset 属性还有一个 w 宽度描述符，配合 sizes 属性一起使用，可以覆盖更多的面。</p><p>以下面这段代码为例子：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">img</span></span></span><br><span class="line"><span class="tag">        <span class="attr">sizes</span> = <span class="string">“(min-width:</span> <span class="attr">600px</span>) <span class="attr">600px</span>, <span class="attr">300px</span>"</span></span><br><span class="line"><span class="tag">        <span class="attr">src</span> = <span class="string">"photo.png"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">srcset</span> = <span class="string">“photo@1x.png</span> <span class="attr">300w</span>,</span></span><br><span class="line"><span class="tag">                       <span class="attr">photo</span>@<span class="attr">2x.png</span> <span class="attr">600w</span>,</span></span><br><span class="line"><span class="tag">                       <span class="attr">photo</span>@<span class="attr">3x.png</span> <span class="attr">1200w</span>,</span></span><br><span class="line"><span class="tag">&gt;</span></span><br></pre></td></tr></table></figure><p>解析一下：</p><p><code>sizes = “(min-width: 600px) 600px, 300px&quot;</code>的意思是，如果屏幕当前的 CSS 像素宽度大于或者等于 600px，则图片的 CSS 宽度为 600px，反之，则图片的 CSS 宽度为 300px。</p><p>也就是 sizes 属性声明了在不同宽度下图片的 CSS 宽度表现。这里可以理解为，大屏幕下图片宽度为 600px，小屏幕下图片宽度为 300px。（具体的媒体查询代码由 CSS 实现）</p><blockquote><p>这里的 sizes 属性只是声明了在不同宽度下图片的 CSS 宽度表现，而具体使图片在大于 600px 的屏幕上展示为 600px 宽度的代码需要另外由 CSS 或者 JS 实现，有点绕。</p></blockquote><p><code>srcset = “photo@1x.png 300w, photo@2x.png 600w, photo@3x.png 1200w</code>里面的 300w，600w，900w 叫宽度描述符。怎么确定当前场景会选取哪张图片呢？</p><h4 id="1-当前屏幕-dpr-2-，CSS-宽度为-375px。"><a href="#1-当前屏幕-dpr-2-，CSS-宽度为-375px。" class="headerlink" title="1. 当前屏幕 dpr = 2 ，CSS 宽度为 375px。"></a>1. 当前屏幕 dpr = 2 ，CSS 宽度为 375px。</h4><p>当前屏幕 CSS 宽度为 375px，则图片 CSS 宽度为 300px。分别用上述 3 个宽度描述符的数值除以 300。</p><ol><li>300 / 300 = 1</li><li>600 / 300 = 2</li><li>1200 / 300 = 4</li></ol><p>上面计算得到的 1、 2、 4 即是算出的有效的像素密度，换算成和 x 描述符等价的值 。这里 600w 算出的 2 即满足 dpr = 2 的情况，选择此张图。 </p><h4 id="2-当前屏幕-dpr-3-，CSS-宽度为-414px。"><a href="#2-当前屏幕-dpr-3-，CSS-宽度为-414px。" class="headerlink" title="2. 当前屏幕 dpr = 3 ，CSS 宽度为 414px。"></a>2. 当前屏幕 dpr = 3 ，CSS 宽度为 414px。</h4><p>当前屏幕 CSS 宽度为 414px，则图片 CSS 宽度仍为 300px。再计算一次：</p><ol><li>300 / 300 = 1</li><li>600 / 300 = 2</li><li>1200 / 300 = 4</li></ol><p>因为 dpr = 3，2 已经不满足了，则此时会选择 1200w 这张图。</p><h4 id="3-当前屏幕-dpr-1-，CSS-宽度为-1920px。"><a href="#3-当前屏幕-dpr-1-，CSS-宽度为-1920px。" class="headerlink" title="3. 当前屏幕 dpr = 1 ，CSS 宽度为 1920px。"></a>3. 当前屏幕 dpr = 1 ，CSS 宽度为 1920px。</h4><p>当前屏幕 CSS 宽度为 1920px，则图片 CSS 宽度变为了 600px。再计算一次：</p><ol><li>300 / 600 = .5</li><li>600 / 600 = 1</li><li>1200 / 600 = 2</li></ol><p>因为 dpr = 1，所以此时会选择 600w 对应的图片。</p><p>具体的可以试下这个 Demo：<span class="exturl" data-url="aHR0cHM6Ly9jb2RlcGVuLmlvL0Nob2tjb2NvL3Blbi9XTmVadk9YP2VkaXRvcnM9MTEwMA==" title="https://codepen.io/Chokcoco/pen/WNeZvOX?editors=1100">CodePen Demo – srcset 属性配合 w 宽度描述符配合 sizes 属性<i class="fa fa-external-link"></i></span></p><p>此方案的意义在于考虑到了响应性布局的复杂性与屏幕的多样性，利用上述规则，可以一次适配 PC 端大屏幕和移动端高清屏，一箭多雕。</p><p>了解更多细节，推荐看看：</p><ul><li><span class="exturl" data-url="aHR0cHM6Ly9kZXZlbG9wZXJzLmdvb2dsZS5jb20vd2ViL2Z1bmRhbWVudGFscy9kZXNpZ24tYW5kLXV4L3Jlc3BvbnNpdmUvaW1hZ2Vz" title="https://developers.google.com/web/fundamentals/design-and-ux/responsive/images">Google Web Fundamentals – Web Responsive Images<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cuemhhbmd4aW54dS5jb20vd29yZHByZXNzLzIwMTQvMTAvcmVzcG9uc2l2ZS1pbWFnZXMtc3Jjc2V0LXNpemUtdy1kZXNjcmlwdG9yLw==" title="https://www.zhangxinxu.com/wordpress/2014/10/responsive-images-srcset-size-w-descriptor/">响应式图片 srcset 全新释义 sizes 属性 w 描述符<i class="fa fa-external-link"></i></span></li></ul><h2 id="字体适配方案"><a href="#字体适配方案" class="headerlink" title="字体适配方案"></a>字体适配方案</h2><h3 id="字体大小"><a href="#字体大小" class="headerlink" title="字体大小"></a>字体大小</h3><p>字体是很多前端开发同学容易忽略的一个点，但是其中也是有很多小知识点。</p><p>首先要知道，浏览器有最小字体限制：</p><ul><li>PC 上最小 font-size=12px</li><li>手机上最小 font-size=8px</li></ul><p>如果小于最小字体，那么字体默认就是最小字体。</p><blockquote><p>其次，很多早期的文章规范都建议不要使用奇数级单位来定义字体大小（如 13px，15px…），容易在一些低端设备上造成字体模糊，出现锯齿。</p></blockquote><h3 id="字体的选择展示"><a href="#字体的选择展示" class="headerlink" title="字体的选择展示"></a>字体的选择展示</h3><p>在字体适配上面，我们需要从性能和展示效果两个维度去考虑。</p><p>完整的一个字体资源实在太大了，所以我们应该尽可能的使用用户设备上已有的字体，而不是额外去下载字体资源，从而使加载时间明显加快。</p><p>而从展示效果层面来说，使用系统字体能更好的与当前操作系统使用的相匹配，得到最佳的展示效果。所以我们在字体使用方面，有一个应该尽量去遵循的原则，也是现在大部分网站在字体适配上使用的策略：</p><p>使用各个支持平台上的默认系统字体。</p><h3 id="兼顾各个操作系统"><a href="#兼顾各个操作系统" class="headerlink" title="兼顾各个操作系统"></a>兼顾各个操作系统</h3><p>常见的操作系统有 Windows、Windows Phone、Mac OS X、iPhone、Android Phone、Linux。当然对于普通用户而言，无须关注 Linux 系统。</p><p>下面就以 <span class="exturl" data-url="aHR0cHM6Ly9jc3MtdHJpY2tzLmNvbS8=" title="https://css-tricks.com/">CSS-Trick<i class="fa fa-external-link"></i></span> 网站最新的 font-family 为例，看看他们是如何在字体选择上做到适配各个操作系统的</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attribute">font-family</span>:</span><br><span class="line">    system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,</span><br><span class="line">    Helvetica,Arial,</span><br><span class="line">    sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="font-family-关键字"><a href="#font-family-关键字" class="headerlink" title="font-family 关键字"></a>font-family 关键字</h3><p>对于 CSS 中的 <code>font-family</code> 而言，它有两类取值。</p><ol><li><p>一类是类似这样的具体的字体族名定义：<code>font-family: Arial</code> 这里定义了一个具体的字体样式，字体族名为 <code>Arial</code>;</p></li><li><p>一类是通用字体族名，它是一种备选机制，用于在指定的字体不可用时给出较好的字体，类似这样：<code>font-family: sans-serif</code> 。</p></li></ol><p>其中，<code>sans-serif</code> 表无衬线字体族，例如， “Open Sans”, “Arial” “微软雅黑” 等等。</p><p>关于通用字体族名，在 <span class="exturl" data-url="aHR0cHM6Ly93d3cudzMub3JnL1RSLzIwMTgvUkVDLWNzcy1mb250cy0zLTIwMTgwOTIwLyNnZW5lcmljLWZvbnQtZmFtaWxpZXM=" title="https://www.w3.org/TR/2018/REC-css-fonts-3-20180920/#generic-font-families">CSS Fonts Module Level 3 – Basic Font Properties<i class="fa fa-external-link"></i></span> 中，定义了 5 个，也就是我们熟知的几个通用字体族名：</p><ol><li>serif 衬线字体族</li><li>sans-serif 非衬线字体族</li><li>monospace 等宽字体，即字体中每个字宽度相同</li><li>cursive 草书字体</li><li>fantasy 主要是那些具有特殊艺术效果的字体</li></ol><h3 id="新增通用字体族关键字"><a href="#新增通用字体族关键字" class="headerlink" title="新增通用字体族关键字"></a>新增通用字体族关键字</h3><p>而在 <span class="exturl" data-url="aHR0cHM6Ly93d3cudzMub3JnL1RSL2Nzcy1mb250cy00LyNnZW5lcmljLWZvbnQtZmFtaWxpZXM=" title="https://www.w3.org/TR/css-fonts-4/#generic-font-families">CSS Fonts Module Level 4 – Generic font families<i class="fa fa-external-link"></i></span> 中，新增了几个关键字：</p><ul><li>system-ui 系统默认字体</li><li>emoji 用于兼容 emoji 表情符号字符</li><li>math 适用于数学表达式</li><li>fangsong 此字体系列用于中文的（仿宋）字体。</li></ul><p>我们看看用的最多的 <code>system-ui</code>。</p><h3 id="system-ui"><a href="#system-ui" class="headerlink" title="system-ui"></a>system-ui</h3><p>简单而言，<code>font-family: system-ui</code> 的目的就是在不同的操作系统的 Web 页面下，自动选择本操作系统下的默认系统字体。</p><p>默认使用特定操作系统的系统字体可以提高性能，因为浏览器或者 webview 不必去下载任何字体文件，而是使用已有的字体文件。 <code>font-family: system-ui</code> 字体设置的优势之处在于它与当前操作系统使用的字体相匹配，对于文本内容而言，它可以得到最恰当的展示。</p><h3 id="San-Francisco-Fonts"><a href="#San-Francisco-Fonts" class="headerlink" title="San Francisco Fonts"></a>San Francisco Fonts</h3><p>OK，简单了解了 <code>system-ui</code>字体族。但是像 <code>-apple-system</code>、<code>BlinkMacSystemFont</code> 没有在最新的标准里出现。它们又代表什么意思呢？</p><p>在此之前，先了解下 <code>San Francisco Fonts</code> 。</p><p><code>San Francisco Fonts</code> 又叫旧金山字体，是一款西文字体。随着 iOS 9 更新面世，在 WatchOS 中随 Apple Watch 一起悄然发售，并且还将在 Apple TV 上的新 tvOS 中使用。</p><p><code>San Francisco Fonts</code> 在 iOS 系统上用于替代升级另外一款西文字体 <code>Helvetica Neue</code>。Apple 做了一些重要的改变，使其成为平台上更好的， 甚至是完美的西文字体。</p><p><span class="exturl" data-url="aHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vODU1NDE0My82MjkyNDYxMi0wNmMwNDMwMC1iZGUzLTExZTktOTM4NS1hZTRkNWE2Y2MwZDkucG5n" title="https://user-images.githubusercontent.com/8554143/62924612-06c04300-bde3-11e9-9385-ae4d5a6cc0d9.png"><img src="https://user-images.githubusercontent.com/8554143/62924612-06c04300-bde3-11e9-9385-ae4d5a6cc0d9.png" alt><i class="fa fa-external-link"></i></span></p><h2 id="apple-system-BlinkMacSystemFont"><a href="#apple-system-BlinkMacSystemFont" class="headerlink" title="-apple-system/BlinkMacSystemFont"></a>-apple-system/BlinkMacSystemFont</h2><p>话说回来。正如每个前端开发人员都知道的那样，将一个功能纳入规范是一回事，将其纳入浏览器又是另一回事。</p><p>幸运的是，<code>system-ui</code> 的普及很快。 Chrome 和 Safari 都可以在各种平台上完全支持它。只有 Mozilla 和 Windows 相对落后。</p><p>看看 <code>system-ui</code> 的兼容性，<span class="exturl" data-url="aHR0cHM6Ly9jYW5pdXNlLmNvbS8jc2VhcmNoPXN5c3RlbQ==" title="https://caniuse.com/#search=system">Can i Use – system-ui<i class="fa fa-external-link"></i></span>(图片截取日 2019-08-13)：</p><p><span class="exturl" data-url="aHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vODU1NDE0My82MjkyNTI5MC05Yjc3NzA4MC1iZGU0LTExZTktOTg2NS0xOTVlOGU0NGZjZDkucG5n" title="https://user-images.githubusercontent.com/8554143/62925290-9b777080-bde4-11e9-9865-195e8e44fcd9.png"><img src="https://user-images.githubusercontent.com/8554143/62925290-9b777080-bde4-11e9-9865-195e8e44fcd9.png" alt><i class="fa fa-external-link"></i></span></p><p>仔细看上图的最后两行：</p><ul><li>Supported as the -apple-system value (only on macOS and iOS)</li><li>Supported as the BlinkMacSystemFont value (only on macOS)</li></ul><p>考虑到不同平台及向后兼容，在 macOS 和 iOS 上，我们需要使用 <code>-apple-system</code> 及 <code>BlinkMacSystemFont</code> 来兼容适配 <code>system-ui</code> 标准。</p><h3 id="Segoe-UI"><a href="#Segoe-UI" class="headerlink" title="Segoe UI"></a>Segoe UI</h3><p>Segoe UI 是 Windows 从 Vista 开始的默认西文字体族，只有西文，不支持汉字，属于无衬线体。</p><p>它也表示一个系列而不是某一款单一字体。使用 <code>font-family: Segoe UI</code> 可以在 Windows 平台及 Windows Phone 上选取最佳的西文字体展示。</p><h3 id="Roboto"><a href="#Roboto" class="headerlink" title="Roboto"></a>Roboto</h3><p>Roboto 是为 Android 操作系统设计的一个无衬线字体家族。Google 描述该字体为 “现代的、但平易近人” 和“有感情”的。</p><p>这个字体家族包含 Thin、Light、Regular、Medium、Bold、Black 六种粗细及相配的斜体。</p><h3 id="总结一下"><a href="#总结一下" class="headerlink" title="总结一下"></a>总结一下</h3><p>到此，我们可以总结一下了。以 <span class="exturl" data-url="aHR0cHM6Ly9jc3MtdHJpY2tzLmNvbS8=" title="https://css-tricks.com/">CSS-Tricks<i class="fa fa-external-link"></i></span> 网站的 font-family 定义为例子：</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attribute">font-family</span>:</span><br><span class="line">    system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,</span><br><span class="line">    Helvetica,Arial,</span><br><span class="line">    sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol><li>system-ui，使用各个支持平台上的默认系统字体</li><li>-apple-system， 在一些稍低版本 Mac OS X 和 iOS 上，它针对旧版上的 Neue Helvetica 和 Lucida Grande 字体，升级使用更为合适的 San Francisco Fonts</li><li>BlinkMacSystemFont，针对一些 Mac OS X 上的 Chrome 浏览器，使用系统默认字体</li><li>segoe ui，在 Windows 及 Windows Phone 上选取系统默认字体</li><li>Roboto，面向 Android 和一些新版的的 Chrome OS</li><li>Helvetica,Arial，在针对不同操作系统不同平台设定采用默认系统字体后，针对一些低版本浏览器的降级方案</li><li>sans-serif，兜底方案，保证字体风格统一，至少也得是无衬线字体</li></ol><p>上述 5 个字体族定义，优先级由高到底，可以看到，它们 5 个都并非某个特定字体，基本的核心思想都是选择对应平台上的默认系统字体。</p><p>涵盖了 iOS、MAC OS X、Android、Windows、Windows Phone 基本所有用户经常使用的主流操作系统。</p><p>使用系统默认字体的主要原因是性能。字体通常是网站上加载的最大 / 最重的资源之一。如果我们可以使用用户机器上已有的字体，我们就完全不需要再去获取字体资源，从而使加载时间明显加快。</p><p>并且系统字体的优点在于它与当前操作系统使用的相匹配，因此它的文本展示必然也是一个让人舒适展示效果。</p><p>当然，上述 <code>font-family</code> 的定义不一定是最佳的。譬如天猫移动端在 <code>font-family</code> 最前面添加了 <code>&quot;PingFang SC&quot;,miui,..</code>必定也有他们的业务上的考虑。但是一些 fallback 方案向后兼容的思想都是一致的，值得参考学习。</p><p>更多的关于字体方面的细节知识，可以看看这几篇文章：</p><ul><li><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2Nob2tjb2NvL2lDU1MvaXNzdWVzLzY=" title="https://github.com/chokcoco/iCSS/issues/6">你该知道的字体 – font-family<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2Nob2tjb2NvL2lDU1MvaXNzdWVzLzY5" title="https://github.com/chokcoco/iCSS/issues/69">Web 字体 font-family 再探秘<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cuc21hc2hpbmdtYWdhemluZS5jb20vMjAxNS8xMS91c2luZy1zeXN0ZW0tdWktZm9udHMtcHJhY3RpY2FsLWd1aWRlLw==" title="https://www.smashingmagazine.com/2015/11/using-system-ui-fonts-practical-guide/">Using UI System Fonts In Web Design: A Quick Practical Guide<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9mdXJiby5vcmcvMjAxOC8wMy8yOC9zeXN0ZW0tZm9udHMtaW4tY3NzLw==" title="https://furbo.org/2018/03/28/system-fonts-in-css/">System Fonts in CSS<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9kZXNpZ25mb3JoYWNrZXJzLmNvbS9ibG9nL3Nhbi1mcmFuY2lzY28tZm9udC8=" title="https://designforhackers.com/blog/san-francisco-font/">Apple’s San Francisco Font<i class="fa fa-external-link"></i></span></li></ul><h2 id="前端布局的兼容适配"><a href="#前端布局的兼容适配" class="headerlink" title="前端布局的兼容适配"></a>前端布局的兼容适配</h2><p>前端工程师的一大工作内容就是页面布局。无论在 PC 端还是移动端，页面布局的兼容适配都是重中之重。在整个前端发展的历程中，布局的方法也在不断的推陈出新。</p><h3 id="布局发展历程"><a href="#布局发展历程" class="headerlink" title="布局发展历程"></a>布局发展历程</h3><p>简单来说，前端的布局发展历程经历了下面几个过程：</p><p>表格布局 –&gt; 定位布局 –&gt; 浮动布局 –&gt; flexbox 布局 –&gt; gridbox 布局</p><p>每一种布局在特定时期都发挥了重要的作用，而每一种新的布局方式的出现，往往都是因为现有的布局方式已经在该时期已经无法很好的满足开发者的需求，无法满足越来越潮流的页面布局的方式。</p><p>以 Flexbox 的出现为例子，在 Flexbox 被大家广为接受使用之前。我们一直在使用定位 + 浮动的布局方式。像下面这个布局：</p><p><span class="exturl" data-url="aHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vODU1NDE0My82MjUzODMwMS0wOTZmZTUwMC1iODg1LTExZTktODJmNS03YWM5MjUwMmY2MTkucG5n" title="https://user-images.githubusercontent.com/8554143/62538301-096fe500-b885-11e9-82f5-7ac92502f619.png"><img src="https://user-images.githubusercontent.com/8554143/62538301-096fe500-b885-11e9-82f5-7ac92502f619.png" alt><i class="fa fa-external-link"></i></span></p><p>容器宽度不定，内部三个元素，均分排列且占满整个空间，并且垂直居中。如果使用定位 + 浮动的布局方式，你无法很快想到最佳的解决方式。三个元素并排那么必然需要浮动或者绝对定位，容器宽度不定且中间元素始终居中，需要顾虑的方面就很多了。也许使用 <code>text-align: justufy</code> 可以 hack 实现，等等等等。</p><p>然而，使用 flexbox 布局的话，只需要： </p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> &#123;</span><br><span class="line">    <span class="attribute">display</span>: flex;</span><br><span class="line">    <span class="attribute">justify-content</span>: space-between;</span><br><span class="line">    <span class="attribute">align-items</span>: center;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>flexbox 的出现，一次性解决了流动布局，弹性布局，排列方式等多个问题。并且它是简洁的，可控的。</p><p>再来看一个例子，水平垂直居中一个元素。使用 flexbox 也许是最便捷的：</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> &#123;</span><br><span class="line">    <span class="attribute">display</span>: flex;</span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line"><span class="selector-class">.item</span> &#123;</span><br><span class="line">    <span class="attribute">margin</span>: auto;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><span class="exturl" data-url="aHR0cHM6Ly9jb2RlcGVuLmlvL0Nob2tjb2NvL3Blbi9HYXJQZXY=" title="https://codepen.io/Chokcoco/pen/GarPev">最便捷的垂直居中方式<i class="fa fa-external-link"></i></span></p><h3 id="CSS-Grid-Layout"><a href="#CSS-Grid-Layout" class="headerlink" title="CSS Grid Layout"></a>CSS Grid Layout</h3><p>OK，flexbox 已经足够优秀了，为什么 gird 网格布局的出现又是为什么？它解决了什么 flex 布局无法很好解决的问题？</p><p>看看下面这张图：</p><p><span class="exturl" data-url="aHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vODU1NDE0My82MjUzOTI3Ny02YmM5ZTUwMC1iODg3LTExZTktOWY3Yi03OGM1MWNkZTMxMmMucG5n" title="https://user-images.githubusercontent.com/8554143/62539277-6bc9e500-b887-11e9-9f7b-78c51cde312c.png"><img src="https://user-images.githubusercontent.com/8554143/62539277-6bc9e500-b887-11e9-9f7b-78c51cde312c.png" alt><i class="fa fa-external-link"></i></span></p><p>flexbox 是一维布局，他只能在一条直线上放置你的内容区块；而 grid 是一个二维布局。它除了可以灵活的控制水平方向之外，还能轻易的控制垂直方向的布局模式。对于上图那样的九宫格布局，它就可以轻而易举的完成。</p><p>一图以蔽之，flexbox：</p><p><span class="exturl" data-url="aHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vODU1NDE0My82MjU5MTc3OC01MWNkZTgwMC1iOTAzLTExZTktOTAzNy05YzViMjVjOWI4OTQucG5n" title="https://user-images.githubusercontent.com/8554143/62591778-51cde800-b903-11e9-9037-9c5b25c9b894.png"><img src="https://user-images.githubusercontent.com/8554143/62591778-51cde800-b903-11e9-9037-9c5b25c9b894.png" alt><i class="fa fa-external-link"></i></span></p><p>gridbox：</p><p><span class="exturl" data-url="aHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vODU1NDE0My82MjU5MTc4NC01Mzk3YWI4MC1iOTAzLTExZTktOWM3MC1lN2Q0MzE2YTNkYTAucG5n" title="https://user-images.githubusercontent.com/8554143/62591784-5397ab80-b903-11e9-9c70-e7d4316a3da0.png"><img src="https://user-images.githubusercontent.com/8554143/62591784-5397ab80-b903-11e9-9c70-e7d4316a3da0.png" alt><i class="fa fa-external-link"></i></span></p><blockquote><p>图片截取自陈慧晶老师在 2019 第五届 CSS 大会上的分享 – <span class="exturl" data-url="aHR0cHM6Ly93d3cuY2hlbmh1aWppbmcuY29tL3NsaWRlcy81My1jc3Njb25mY24tMjAxOS8jLw==" title="https://www.chenhuijing.com/slides/53-cssconfcn-2019/#/">新时代 CSS 布局<i class="fa fa-external-link"></i></span></p></blockquote><p>在现阶段，移动端布局应当更多使用 flexbox 去完成（相对那些还在使用 float 布局的），而考虑到未来页面布局的推陈出新。对于 Grid 布局我们应当像前几年对待 flexbox 一样，重视起来，随着兼容性的普及，Grid 布局也会慢慢成为主流。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;前端的一大工作内容就是去兼容页面在不同内核的浏览器，不同的设备，不同的分辨率下的行为，使页面的能正常工作在各种各样的宿主环境当中。&lt;/p&gt;
&lt;p&gt;而本文的主题 – 移动端开发的兼容适配与性能优化，就是希望能从一些常见的移动端开发问题出发，厘清 Web 移动端开发的前前后后，一些技术的发展过程，一些问题的优化手段以及给出一些常见的兼容性问题的解决方案。&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>几个随机图片API接口</title>
    <link href="https://xuebin.me/posts/3773507a.html"/>
    <id>https://xuebin.me/posts/3773507a.html</id>
    <published>2020-01-01T12:42:04.000Z</published>
    <updated>2020-01-01T12:42:04.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><blockquote class="blockquote-center"><p><span class="exturl" data-url="aHR0cHM6Ly9zb3VyY2UudW5zcGxhc2guY29tLw==" title="https://source.unsplash.com/">Unsplash Source<i class="fa fa-external-link"></i></span></p></blockquote><p><span class="exturl" data-url="aHR0cHM6Ly91bnNwbGFzaC5pdC8xNjAwLzkwMD9yYW5kb20=" title="https://unsplash.it/1600/900?random">https://unsplash.it/1600/900?random<i class="fa fa-external-link"></i></span>（据说国内加载略慢，我试了一下还好都差不多）</p><p><span class="exturl" data-url="aHR0cHM6Ly91cGxvYWRiZXRhLmNvbS9hcGkvcGljdHVyZXMvcmFuZG9tLz9rZXk9QmluZ0V2ZXJ5ZGF5V2FsbHBhcGVyUGljdHVyZQ==" title="https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture">https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture<i class="fa fa-external-link"></i></span>（必应每日图片）<span class="exturl" data-url="aHR0cHM6Ly91cGxvYWRiZXRhLmNvbS9hcGkvcGljdHVyZXMvcmFuZG9t" title="https://uploadbeta.com/api/pictures/random">https://uploadbeta.com/api/pictures/random<i class="fa fa-external-link"></i></span>（必应图库，一些风景图人物图什么的，都是现代风格）</p><p><span class="exturl" data-url="aHR0cHM6Ly9zb3VyY2UudW5zcGxhc2guY29tL3JhbmRvbQ==" title="https://source.unsplash.com/random">https://source.unsplash.com/random<i class="fa fa-external-link"></i></span>（风格同上，但这个的图尺寸不是大图）<br><a id="more"></a></p><blockquote><p>下面是几个二次元的, 都差不多ヽ (´o｀（大部分都在本地测试过，可以放心食用），但是以下这些唯一的问题就是。。有些图片质量参差不齐。。所以要想完全和自己口味的话还是自己做一个吧，有开源代码</p></blockquote><p>首先最牛逼的大概就是<strong><span class="exturl" data-url="aHR0cDovL2lrbW9lLmNvbS84NTQ4Lmh0bWw=" title="http://ikmoe.com/8548.html">「漫月 API」<i class="fa fa-external-link"></i></span></strong>了，一直被模仿从未被超越那种╮(￣▽￣””)╭</p><p><span class="exturl" data-url="aHR0cHM6Ly93d3cueHdib2tlLmNuL2FwaS9hcGkucGhw" title="https://www.xwboke.cn/api/api.php">https://www.xwboke.cn/api/api.php<i class="fa fa-external-link"></i></span> （出处被不小心遗忘在风中了，对不住了 0.0）</p><p><span class="exturl" data-url="aHR0cHM6Ly9pbWcueGpoLm1lL3JhbmRvbV9pbWcucGhw" title="https://img.xjh.me/random_img.php">https://img.xjh.me/random_img.php<i class="fa fa-external-link"></i></span> <span class="exturl" data-url="aHR0cHM6Ly93d3cueGpoLm1lLzMwNjkuaHRtbD9yZXBseXRvY29tPTQ4ODg=" title="https://www.xjh.me/3069.html?replytocom=4888">出处 (・ω・) ノ<i class="fa fa-external-link"></i></span></p><p><span class="exturl" data-url="aHR0cDovL3d3dy5kbW9lLmNjL3JhbmRvbS5waHA=" title="http://www.dmoe.cc/random.php">http://www.dmoe.cc/random.php<i class="fa fa-external-link"></i></span>（1000+）<span class="exturl" data-url="aHR0cDovL3d3dy5kbW9lLmNjLw==" title="http://www.dmoe.cc/">出处 (・ω・) ノ<i class="fa fa-external-link"></i></span></p><p><span class="exturl" data-url="aHR0cDovL2FjZy5iYWtheXVuLmNuL3JhbmRiZy5waHA=" title="http://acg.bakayun.cn/randbg.php">http://acg.bakayun.cn/randbg.php<i class="fa fa-external-link"></i></span>（6000+ | 东方 project 可选 | 多尺寸）<span class="exturl" data-url="aHR0cDovL2FjZy5iYWtheXVuLmNuLw==" title="http://acg.bakayun.cn/">出处 (・ω・) ノ<i class="fa fa-external-link"></i></span></p><p><span class="exturl" data-url="aHR0cHM6Ly9hY2cueWFud3ouY24vYXBpLnBocA==" title="https://acg.yanwz.cn/api.php">https://acg.yanwz.cn/api.php<i class="fa fa-external-link"></i></span> (400+) <span class="exturl" data-url="aHR0cHM6Ly9hY2cueWFud3ouY24v" title="https://acg.yanwz.cn/">出处 (・ω・) ノ<i class="fa fa-external-link"></i></span></p><p><span class="exturl" data-url="aHR0cDovL21vZWNlLmNjL2FwbC9pbWcucGhw" title="http://moece.cc/apl/img.php">http://moece.cc/apl/img.php<i class="fa fa-external-link"></i></span>（433+）<span class="exturl" data-url="aHR0cDovL21vZWNlLmNjLw==" title="http://moece.cc/">出处 (・ω・) ノ<i class="fa fa-external-link"></i></span></p><p><span class="exturl" data-url="aHR0cHM6Ly9yYW5kb20uNTJlY3kuY24vcmFuZG9tLnBocA==" title="https://random.52ecy.cn/random.php">https://random.52ecy.cn/random.php<i class="fa fa-external-link"></i></span> (8000+) <span class="exturl" data-url="aHR0cHM6Ly9yYW5kb20uNTJlY3kuY24v" title="https://random.52ecy.cn/">出处 (・ω・) ノ<i class="fa fa-external-link"></i></span>！！这个我用不了，当前接口需要认证啥的 emmmmm</p><p><span class="exturl" data-url="aHR0cHM6Ly9hY2cubW9lZHJhZ29uLmNvbS9hcGkucGhw" title="https://acg.moedragon.com/api.php">https://acg.moedragon.com/api.php<i class="fa fa-external-link"></i></span> (1000+) <span class="exturl" data-url="aHR0cHM6Ly9hY2cubW9lZHJhZ29uLmNvbS8=" title="https://acg.moedragon.com/">出处 (・ω・) ノ<i class="fa fa-external-link"></i></span></p><p><span class="exturl" data-url="aHR0cDovL2FwaS4zZXdsLmNjL2FjZy9pbWcucGhw" title="http://api.3ewl.cc/acg/img.php">http://api.3ewl.cc/acg/img.php<i class="fa fa-external-link"></i></span>（1000+ | 尺寸可选） <span class="exturl" data-url="aHR0cDovL2FwaS4zZXdsLmNjL2FjZy8=" title="http://api.3ewl.cc/acg/">出处 (・ω・) ノ<i class="fa fa-external-link"></i></span> 下面这篇是作者的博客<span class="exturl" data-url="aHR0cDovL3d3dy4zZXdsLmNjLzMyNC5odG1s" title="http://www.3ewl.cc/324.html">随机图片 api–开源<i class="fa fa-external-link"></i></span></p><p><span class="exturl" data-url="aHR0cHM6Ly9hY2cudG91YmllYy5jbi9yYW5kb20ucGhw" title="https://acg.toubiec.cn/random.php">https://acg.toubiec.cn/random.php<i class="fa fa-external-link"></i></span>（1000+） <span class="exturl" data-url="aHR0cHM6Ly9hY2cudG91YmllYy5jbi8=" title="https://acg.toubiec.cn/">出处 (・ω・) ノ<i class="fa fa-external-link"></i></span> 作者开源了 这篇博客里有介绍和源码 先蟹蟹大佬了<span class="exturl" data-url="aHR0cHM6Ly93d3cudG91YmllYy5jbi8xNjQuaHRtbA==" title="https://www.toubiec.cn/164.html">[项目]随机二次元图片 API - 已经开源<i class="fa fa-external-link"></i></span></p><p>mark 一下慢慢研究➡️ <span class="exturl" data-url="aHR0cHM6Ly93d3cueGhib2tlLmNvbS8xNC5odG1s" title="https://www.xhboke.com/14.html">一个可在 php 中直接输出随机图片的 API<i class="fa fa-external-link"></i></span></p><blockquote><p>转载自： <span class="exturl" data-url="aHR0cHM6Ly9sZWZsYWNvbi5naXRodWIuaW8vNmEyYjk4MWYv" title="https://leflacon.github.io/6a2b981f/">几个随机图片API接口(给hexo设置随机背景图片)<i class="fa fa-external-link"></i></span></p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;blockquote class=&quot;blockquote-center&quot;&gt;&lt;p&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9zb3VyY2UudW5zcGxhc2guY29tLw==&quot; title=&quot;https://source.unsplash.com/&quot;&gt;Unsplash Source&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly91bnNwbGFzaC5pdC8xNjAwLzkwMD9yYW5kb20=&quot; title=&quot;https://unsplash.it/1600/900?random&quot;&gt;https://unsplash.it/1600/900?random&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;（据说国内加载略慢，我试了一下还好都差不多）&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly91cGxvYWRiZXRhLmNvbS9hcGkvcGljdHVyZXMvcmFuZG9tLz9rZXk9QmluZ0V2ZXJ5ZGF5V2FsbHBhcGVyUGljdHVyZQ==&quot; title=&quot;https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture&quot;&gt;https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;（必应每日图片）&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly91cGxvYWRiZXRhLmNvbS9hcGkvcGljdHVyZXMvcmFuZG9t&quot; title=&quot;https://uploadbeta.com/api/pictures/random&quot;&gt;https://uploadbeta.com/api/pictures/random&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;（必应图库，一些风景图人物图什么的，都是现代风格）&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9zb3VyY2UudW5zcGxhc2guY29tL3JhbmRvbQ==&quot; title=&quot;https://source.unsplash.com/random&quot;&gt;https://source.unsplash.com/random&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;（风格同上，但这个的图尺寸不是大图）&lt;br&gt;
    
    </summary>
    
      <category term="api" scheme="https://xuebin.me/categories/api/"/>
    
    
      <category term="转载" scheme="https://xuebin.me/tags/%E8%BD%AC%E8%BD%BD/"/>
    
      <category term="图片" scheme="https://xuebin.me/tags/%E5%9B%BE%E7%89%87/"/>
    
      <category term="api" scheme="https://xuebin.me/tags/api/"/>
    
  </entry>
  
  <entry>
    <title>HTML Email的编写</title>
    <link href="https://xuebin.me/posts/106ada5.html"/>
    <id>https://xuebin.me/posts/106ada5.html</id>
    <published>2019-12-20T17:25:56.000Z</published>
    <updated>2019-12-20T17:25:56.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><blockquote class="blockquote-center"><p><span class="exturl" data-url="aHR0cHM6Ly9qdWVqaW4uaW0vcG9zdC81ZGYwYmRjMjUxODgyNTEyNTcyODY3MzM=" title="https://juejin.im/post/5df0bdc25188251257286733">HTML Email的编写<i class="fa fa-external-link"></i></span></p></blockquote><p>总结回顾</p><ul><li><span class="exturl" data-url="aHR0cHM6Ly9qdWVqaW4uaW0vcG9zdC81ZGU2N2U3NjUxODgyNTEyMjMyMmE5ZTI=" title="https://juejin.im/post/5de67e76518825122322a9e2">这样回答继承，面试官可能更满意<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9qdWVqaW4uaW0vcG9zdC81ZGU3ZWJkNDUxODgyNTEyN2MyNmZiYzE=" title="https://juejin.im/post/5de7ebd4518825127c26fbc1">箭头函数和普通函数的10个区别<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9qdWVqaW4uaW0vcG9zdC81ZGVlMTVhMjUxODgyNTEyNWUxYmE4ZTk=" title="https://juejin.im/post/5dee15a2518825125e1ba8e9">html 空白汉字占位符<i class="fa fa-external-link"></i></span></li><li><p><span class="exturl" data-url="aHR0cHM6Ly9qdWVqaW4uaW0vcG9zdC81ZGVlNjY5MzZmYjlhMDE2MTcxMWFkNTU=" title="https://juejin.im/post/5dee66936fb9a0161711ad55">我想了解更多判断数组的方式<i class="fa fa-external-link"></i></span></p><a id="more"></a><h2 id="一、HTML-Email的编写开发场景"><a href="#一、HTML-Email的编写开发场景" class="headerlink" title="一、HTML Email的编写开发场景"></a>一、HTML Email的编写开发场景</h2></li><li><p>你下单 可能 电商会给你发一份 详情邮件</p></li><li>招行可能给你发下你的账单邮件</li><li>生日了给你发个邮件</li><li>转正了给你发个邮件</li><li>入职周年邮件</li></ul><h2 id="二、遇到的问题"><a href="#二、遇到的问题" class="headerlink" title="二、遇到的问题"></a>二、遇到的问题</h2><h3 id="Email的兼容性很差"><a href="#Email的兼容性很差" class="headerlink" title="Email的兼容性很差"></a>Email的兼容性很差</h3><p>邮件内容所在上下文或者说所在外部容器（以下简称环境）都是由邮箱服务商决定的，不同的邮件服务商对前端样式布局支持都存在在一些差别。这就要求邮件内容需要在任何一种情况下都要正常兼容显示。</p><h3 id="Email容器不同"><a href="#Email容器不同" class="headerlink" title="Email容器不同"></a>Email容器不同</h3><p>你写的邮件前端代码可能被放在一个iframe中，那你的内容是被放在body里面的；可能放在一个div中，你的内容就被放在这个div里面。</p><h3 id="Email自带设置"><a href="#Email自带设置" class="headerlink" title="Email自带设置"></a>Email自带设置</h3><p>邮箱软件本身设置设置了些css，他可能对你产生未知的影响。可能根本没有申明doctype，即使申明了，也不是你想要的doctype。</p><h3 id="避免被嵌套在不正确的容器里"><a href="#避免被嵌套在不正确的容器里" class="headerlink" title="避免被嵌套在不正确的容器里"></a>避免被嵌套在不正确的容器里</h3><p>因为容器可能是body或div，我们邮件内容不应该是一个完整的html。所以邮件内容应该是以div为根节点的html片段。</p><h2 id="三、环境（外部容器）"><a href="#三、环境（外部容器）" class="headerlink" title="三、环境（外部容器）"></a>三、环境（外部容器）</h2><p>我们写的邮件代码在不用的邮件服务商下，对应的外部容器不太一样。</p><p>QQ邮箱：自己编写的内容被嵌套在一个div中</p><p><img src="https://i.loli.net/2019/12/21/aqCZuxpdlKAw2ky.png" alt="16ef47792c6956c9.png"></p><p>outlook邮箱：自己编写的内容不知道被嵌套什么元素里了，它本身的元素加上我编写的元素被混在一起了</p><p><img src="https://i.loli.net/2019/12/21/3L9tIw6h4xNSzH2.png" alt="16ef47aa05a4f633.png"></p><p>其他的邮箱你们可以自己测试一下</p><h2 id="四、开发的Doctype"><a href="#四、开发的Doctype" class="headerlink" title="四、开发的Doctype"></a>四、开发的Doctype</h2><p>一个文档类型标记是一种标准通用标记语言的文档类型声明，它的目的是要告诉标准通用标记语言解析器，它应该使用什么样的文档类型定义（DTD）来解析文档。</p><p>兼容性最好的Doctype是XHTML 1.0 Strict，事实上Gmail和Hotmail会删掉你的Doctype，换上这个Doctype。使用这个Doctype，也就意味着，不能使用HTML5的语法。</p><p><img src="https://i.loli.net/2019/12/21/LHeGnzk9WD5RB2r.png" alt="16ef48167b8d46b2.png"></p><p><img src="https://i.loli.net/2019/12/21/IQv2ZlkR3BXFAsw.png" alt="16ef4820bb240263.png"></p><h2 id="五、开发的布局"><a href="#五、开发的布局" class="headerlink" title="五、开发的布局"></a>五、开发的布局</h2><ul><li>网页的布局（layout）推荐使用表格（table）</li><li>css内嵌，不能在head标签中写style，也不能外联。</li><li>不能用浮动的方式定位。position：absolute;float:left;等都不行，float在qq邮箱客户端中可以识别，但是在outlook中无法识别。</li><li><p>为了保证兼容性，需要把邮件的宽度设置为600px，最大600px；</p><p>  <img src="https://i.loli.net/2019/12/21/2bdzMVDwiEBLHWs.png" alt="16ef484d183ceefe.png"></p></li></ul><blockquote><p>网页的布局（layout）必须使用表格（table）。首先，放置一个最外层的大表格，用来设置背景。 在内层，放置第二个表格。用来展示内容。第二个table的宽度定为600像素，防止超过客户端的显示宽度。</p></blockquote><h2 id="六、开发的样式"><a href="#六、开发的样式" class="headerlink" title="六、开发的样式"></a>六、开发的样式</h2><ul><li>避免css冲突或被覆盖</li><li>尽量使用div、span等无语义标签。</li><li>尽量避免CSS属性值的简写形式</li><li>font-size:0; 很重要，否则qq邮箱 会自动填写很多空格，影响布局</li><li>减少不必要的间隙和空格等存在，影响页面美感  <figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 错误的写法 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">"font: 8px/14px Arial, sans-serif; margin: 1px 0;"</span>&gt;</span> </span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 推荐写法 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin-top: 1px; margin-bottom: 1px; margin-left: 0; margin-right: 0;"</span>&gt;</span></span><br></pre></td></tr></table></figure></li></ul><blockquote><p>所有的CSS规则，最好都采用行内样式。因为放置在网页头部的样式，很可能会被客户端删除。客户端对CSS规则的支持情况 另外，不要采用CSS的简写形式，有些客户端不支持。因为环境中可能已经设置了css，比如一些reset、一些.class。 所以我们只能使用行内style来确保我们的效果，并且在内容根节点上设置基础style，并且尽量使用div、span等无语义标签。</p></blockquote><h2 id="七、开发图片"><a href="#七、开发图片" class="headerlink" title="七、开发图片"></a>七、开发图片</h2><ul><li>少用img，邮箱不会过滤你的img标签，但是系统往往会默认不载入陌生来信的图片</li><li>需要img的话，一定要写好alt和title；</li><li>Outlook中img最高1728px，超过1728 的部分会被截掉</li><li>Outlook可能自动缩小img，使其最高位1728px，（上次测试没缩放，直接裁了）</li><li>建议剪裁img，堆叠在一起</li><li>有些客户端会给img链接加上边框，要去除边框。  <figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">border</span>=<span class="string">"0"</span> <span class="attr">style</span>=<span class="string">"display:block;</span></span></span><br><span class="line"><span class="tag"><span class="string">                    outline:none; </span></span></span><br><span class="line"><span class="tag"><span class="string">                    text-decoration:none; </span></span></span><br><span class="line"><span class="tag"><span class="string">                    -ms-interpolation-mode: bicubic;</span></span></span><br><span class="line"><span class="tag"><span class="string">                    border:none;"</span>&gt;</span></span><br></pre></td></tr></table></figure></li></ul><blockquote><p>图片是唯一可以引用的外部资源。其他的外部资源，比如样式表文件、字体文件、视频文件等，一概不能引用。 有些客户端会给图片链接加上边框，要去除边框。 需要注意的是，不少客户端默认不显示图片（比如Gmail），所以要确保即使没有图片，主要内容也能被阅读。</p></blockquote><h2 id="八、开发的background"><a href="#八、开发的background" class="headerlink" title="八、开发的background"></a>八、开发的background</h2><ul><li>少用background 推荐尽可能使用切割的img</li><li>Gmail也不支持css里面的背景图</li><li><p>在outlook2007、Outlook2010中，背景图片将无法显示</p><p>  <img src="https://i.loli.net/2019/12/21/mviQVBXIrLYPlkn.jpg" alt="16ef7c5af85ceff2.jpg"></p></li></ul><blockquote><p>图片是唯一可以引用的外部资源。其他的外部资源，比如样式表文件、字体文件、视频文件等，一概不能引用。 有些客户端会给图片链接加上边框，要去除边框。 需要注意的是，不少客户端默认不显示图片（比如Gmail），所以要确保即使没有图片，主要内容也能被阅读。</p></blockquote><h2 id="九、email兼容总结"><a href="#九、email兼容总结" class="headerlink" title="九、email兼容总结"></a>九、email兼容总结</h2><ul><li>最好使用TABLE标签布局 ，通过tr td来控制距离 空白区域等</li><li>每个标签设置：margin:0;padding:0;font-size:0;（注意拆开写）</li><li>设置 width, height</li><li>少用img ,少用background</li><li>邮件不支持js，a标签慎用</li><li>在使用行高前建议添加mso-line-height-rule:exactly</li><li>margin:0; padding: 0;colspan=“1” height=“375” 顺序不可换</li></ul><blockquote><p>客户端：foxmail outlook QQ邮箱 为了应付Email的怪癖，花了很多时间测试，确保搞定了所有Outlook的坑洼沟洄 但是….还是不可避免有兼容问题 如果你只要兼容 Foxmail and qq邮箱，那你几乎可以像写web一样写邮件。 由于邮件客户端对css支持各有不同，所以一定要多测试再发送，保证样式的正确。如果出现了不兼容的情况，一定要耐心的使用最简单的方式进行兼容，尽量少用特殊标签及比较复杂的属性。</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;blockquote class=&quot;blockquote-center&quot;&gt;&lt;p&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9qdWVqaW4uaW0vcG9zdC81ZGYwYmRjMjUxODgyNTEyNTcyODY3MzM=&quot; title=&quot;https://juejin.im/post/5df0bdc25188251257286733&quot;&gt;HTML Email的编写&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;总结回顾&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9qdWVqaW4uaW0vcG9zdC81ZGU2N2U3NjUxODgyNTEyMjMyMmE5ZTI=&quot; title=&quot;https://juejin.im/post/5de67e76518825122322a9e2&quot;&gt;这样回答继承，面试官可能更满意&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9qdWVqaW4uaW0vcG9zdC81ZGU3ZWJkNDUxODgyNTEyN2MyNmZiYzE=&quot; title=&quot;https://juejin.im/post/5de7ebd4518825127c26fbc1&quot;&gt;箭头函数和普通函数的10个区别&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9qdWVqaW4uaW0vcG9zdC81ZGVlMTVhMjUxODgyNTEyNWUxYmE4ZTk=&quot; title=&quot;https://juejin.im/post/5dee15a2518825125e1ba8e9&quot;&gt;html 空白汉字占位符&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9qdWVqaW4uaW0vcG9zdC81ZGVlNjY5MzZmYjlhMDE2MTcxMWFkNTU=&quot; title=&quot;https://juejin.im/post/5dee66936fb9a0161711ad55&quot;&gt;我想了解更多判断数组的方式&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="其他" scheme="https://xuebin.me/categories/%E5%85%B6%E4%BB%96/"/>
    
    
      <category term="转载" scheme="https://xuebin.me/tags/%E8%BD%AC%E8%BD%BD/"/>
    
      <category term="email" scheme="https://xuebin.me/tags/email/"/>
    
  </entry>
  
  <entry>
    <title>用 Node.js 把玩一番 Alfred Workflow</title>
    <link href="https://xuebin.me/posts/d2bbb3c7.html"/>
    <id>https://xuebin.me/posts/d2bbb3c7.html</id>
    <published>2019-12-20T17:08:29.000Z</published>
    <updated>2019-12-20T17:08:29.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><ul><li><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3NpbmRyZXNvcmh1cy9hbGZ5" title="https://github.com/sindresorhus/alfy">sindresorhus/alfy<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9qdWVqaW4uaW0vcG9zdC81ZGNlNGMxMWYyNjVkYTBjMDIxMTFjZTk=" title="https://juejin.im/post/5dce4c11f265da0c02111ce9">Node.js库Puppeteer常用API及骚操作总结<i class="fa fa-external-link"></i></span></li></ul><ul><li><span class="exturl" data-url="aHR0cHM6Ly93d3cuYWxmcmVkYXBwLmNvbS9oZWxwL3dvcmtmbG93cy9pbnB1dHMvc2NyaXB0LWZpbHRlci9qc29uLw==" title="https://www.alfredapp.com/help/workflows/inputs/script-filter/json/">Script Filter JSON Format<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cuY25ibG9ncy5jb20vTXVZdW55dW4vcC83MzIzMTI4Lmh0bWw=" title="https://www.cnblogs.com/MuYunyun/p/7323128.html">用 Node.js 把玩一番 Alfred Workflow<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zMzc0NDg1NC9hcnRpY2xlL2RldGFpbHMvODU5OTg4Njc=" title="https://blog.csdn.net/weixin_33744854/article/details/85998867">Node.js 开发 Alfred workflow 初体验<i class="fa fa-external-link"></i></span></li></ul><blockquote class="blockquote-center"><p>原文地址：<span class="exturl" data-url="aHR0cHM6Ly8xOTkxNDIxLmNuLzIwMTkvMDEvMTMvNDUyMjlkMjMv" title="https://1991421.cn/2019/01/13/45229d23/">写个Alfred Workflow，方便看电视剧<i class="fa fa-external-link"></i></span></p></blockquote><blockquote><p>Alfred鼎鼎大名，有人说，Mac自带Spotlight,不也一样吗？不一样，Alfred最厉害的是workflow，利用它你可以查询GitHub项目，查API文档，查单词，查IP等等。</p></blockquote><a id="more"></a><p>当然除了网上一些的workflow之外，我们往往也都有自己的个性化需求，这时就可以自己尝试写几个。我就有这样的需求，所以花了点时间，把玩了下，这里以一个电视剧检索插件为例，记录下编写中要点。</p><h1 id="初始化Workflow"><a href="#初始化Workflow" class="headerlink" title="初始化Workflow"></a>初始化Workflow</h1><p>打开Alfred设定，点击左下角的加号，选择blank Workflow</p><p>按照提示，填写名称，分类，描述等。</p><p><img src="https://i.loli.net/2019/12/21/ZwoEQYfJMP418Lv.png" alt="2019-01-13-035730.png"></p><h2 id="添加ScriptFilter"><a href="#添加ScriptFilter" class="headerlink" title="添加ScriptFilter"></a>添加ScriptFilter</h2><p>我这里因为需要利用脚本去实现，所以选择添加Script filter.<code>右键 =&gt; inputs =&gt; Script Filter</code><br>依次填写信息</p><p>这里的language可还算丰富，我考虑使用NodeJS来实现脚本，所以选择bash,利用bash去执行Node，这里填写脚本如下</p><pre><code>/usr/local/bin/node yy.js &quot;{query}&quot;</code></pre><p>{query}指的是在alfred输入框中参数</p><p><img src="https://i.loli.net/2019/12/21/OSxbHlNZsrfRUw8.png" alt="2019-01-13-035809.png"></p><h3 id="说明"><a href="#说明" class="headerlink" title="说明"></a>说明</h3><ul><li>keyword指的是唤起Alfred，输入的关键词，比如这里我写yy，即输入yy，会进入该插件，参数上，我选择可空</li><li>placeholder指的是还没返回结果前的提示信息</li></ul><h2 id="具体脚本实现"><a href="#具体脚本实现" class="headerlink" title="具体脚本实现"></a>具体脚本实现</h2><p>上面的脚本确保了在用户唤起Alfred，输入yy及参数执行了yy脚本，那么下来就是脚本具体实现，从而返回结果给Alfred了</p><pre><code>const cheerio = require(&apos;cheerio&apos;);const axios = require(&apos;axios&apos;);const fs = require(&apos;fs&apos;);const url = &apos;http://ly6080.com.cn&apos;const keyword = process.argv[2];console.error(keyword);async function searchMovies() {    const res = await axios.get(url + &apos;/index.php?m=vod-search&amp;wd=&apos; + encodeURI(keyword));    const $ = cheerio.load(res.data);    const arr = $(&apos;.index-area&apos;).find(&apos;li&apos;);    const result_array = [];    for (let i = 0; i &lt; arr.length; i++) {        const item = arr.eq(i);        const actors = [];        item.find(&apos;.actor&apos;).each(function (i, elem) {            actors.push($(this).text());        });        const imgUrl = item.find(&apos;img&apos;).attr(&apos;data-original&apos;);        const imgName = imgUrl.slice(imgUrl.lastIndexOf(&apos;/&apos;) + 1);        if (!fs.existsSync(&apos;./thumbs/&apos; + imgName)) {            const imgData = (await axios.get(imgUrl, { responseType:&quot;arraybuffer&quot; })).data;            fs.writeFileSync(&apos;./thumbs/&apos; + imgName,imgData);        }        result_array.push({            title: item.find(&apos;.name&apos;).text(),            subtitle: actors.join(&apos;/&apos;),            arg: url + item.find(&apos;.link-hover&apos;).attr(&apos;href&apos;),            icon: {                path: __dirname + &apos;/thumbs/&apos; + imgName            }        })    }    console.log(JSON.stringify({ items: result_array }));}searchMovies();</code></pre><h3 id="说明-1"><a href="#说明-1" class="headerlink" title="说明"></a>说明</h3><ul><li>Alfred对返回结果的结构是有要求的，所以最后console打印的对象结构是必须如此的，具体结构看<span class="exturl" data-url="aHR0cHM6Ly93d3cuYWxmcmVkYXBwLmNvbS9oZWxwL3dvcmtmbG93cy9pbnB1dHMvc2NyaXB0LWZpbHRlci9qc29uLw==" title="https://www.alfredapp.com/help/workflows/inputs/script-filter/json/">官方文档<i class="fa fa-external-link"></i></span></li><li>js脚本里的结果，是以log形式传给bash命令，也就是流入alfred，假如注掉最后一句话，会失效</li><li>我这里因为想实现电影海报的展示，所以实现了图片的本地缓存化，当然弊端就是延迟明显，为了降低延迟，所以做了点判断，如果有就不再下载。</li></ul><h2 id="添加action-打开URL"><a href="#添加action-打开URL" class="headerlink" title="添加action-打开URL"></a>添加action-打开URL</h2><p>当前面得到了检索的目标影片信息后，那么就应该是打开URL了，所以选中刚才的Script Filter，右键 =&gt; Insert After =&gt; Actions =&gt; Open URL,<code>query就是上一步结果中的arg</code></p><p><img src="https://i.loli.net/2019/12/21/a3gfzOcUuB4ylmV.png" alt="Untitled.png"></p><h2 id="添加通知"><a href="#添加通知" class="headerlink" title="添加通知"></a>添加通知</h2><p>为了进一步提升体验，来个通知，右键 =&gt; Insert After =&gt; Outputs =&gt; Post Notification</p><h2 id="最终效果"><a href="#最终效果" class="headerlink" title="最终效果"></a>最终效果</h2><p><img src="https://i.loli.net/2019/12/21/GoqukdJ6S3AfUie.png" alt="2019-01-13-041740.png"></p><h2 id="写在最后"><a href="#写在最后" class="headerlink" title="写在最后"></a>写在最后</h2><p>以前为了看电视剧，需要打开浏览器，输入网址，搜索电视，点击观看，总共有4部，现在只需要唤起Alfred，输入<code>yy 超人</code>，选择要看的电视，回车即可以观看。节约点时间，这就再开心不过了。</p><p>插件托管进<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2FsYW5oZy9hbGZyZWQtd29ya2Zsb3dz" title="https://github.com/alanhg/alfred-workflows">alfred-workflows<i class="fa fa-external-link"></i></span></p>]]></content>
    
    <summary type="html">
    
      &lt;ul&gt;
&lt;li&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9naXRodWIuY29tL3NpbmRyZXNvcmh1cy9hbGZ5&quot; title=&quot;https://github.com/sindresorhus/alfy&quot;&gt;sindresorhus/alfy&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9qdWVqaW4uaW0vcG9zdC81ZGNlNGMxMWYyNjVkYTBjMDIxMTFjZTk=&quot; title=&quot;https://juejin.im/post/5dce4c11f265da0c02111ce9&quot;&gt;Node.js库Puppeteer常用API及骚操作总结&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly93d3cuYWxmcmVkYXBwLmNvbS9oZWxwL3dvcmtmbG93cy9pbnB1dHMvc2NyaXB0LWZpbHRlci9qc29uLw==&quot; title=&quot;https://www.alfredapp.com/help/workflows/inputs/script-filter/json/&quot;&gt;Script Filter JSON Format&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly93d3cuY25ibG9ncy5jb20vTXVZdW55dW4vcC83MzIzMTI4Lmh0bWw=&quot; title=&quot;https://www.cnblogs.com/MuYunyun/p/7323128.html&quot;&gt;用 Node.js 把玩一番 Alfred Workflow&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zMzc0NDg1NC9hcnRpY2xlL2RldGFpbHMvODU5OTg4Njc=&quot; title=&quot;https://blog.csdn.net/weixin_33744854/article/details/85998867&quot;&gt;Node.js 开发 Alfred workflow 初体验&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote class=&quot;blockquote-center&quot;&gt;&lt;p&gt;原文地址：&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly8xOTkxNDIxLmNuLzIwMTkvMDEvMTMvNDUyMjlkMjMv&quot; title=&quot;https://1991421.cn/2019/01/13/45229d23/&quot;&gt;写个Alfred Workflow，方便看电视剧&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Alfred鼎鼎大名，有人说，Mac自带Spotlight,不也一样吗？不一样，Alfred最厉害的是workflow，利用它你可以查询GitHub项目，查API文档，查单词，查IP等等。&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="工具" scheme="https://xuebin.me/categories/%E5%B7%A5%E5%85%B7/"/>
    
    
      <category term="工具" scheme="https://xuebin.me/tags/%E5%B7%A5%E5%85%B7/"/>
    
      <category term="效率" scheme="https://xuebin.me/tags/%E6%95%88%E7%8E%87/"/>
    
      <category term="Alfred" scheme="https://xuebin.me/tags/Alfred/"/>
    
  </entry>
  
  <entry>
    <title>Vim脱离鼠标</title>
    <link href="https://xuebin.me/posts/31d58333.html"/>
    <id>https://xuebin.me/posts/31d58333.html</id>
    <published>2019-11-15T02:14:26.000Z</published>
    <updated>2019-11-15T02:14:26.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p><img src="https://i.loli.net/2019/11/15/CVtS9vWLOizafFn.jpg" alt></p><p>vim有三种模式，基本模式就是用来输入命令的，比如删除跳转等操作就是在基本模式下面；插入模式不用说；命令模式就是按<code>Esc+:</code>进入的模式，之间的切换很容易我就废话了。下面几乎都在基本模式操作。</p><a id="more"></a><h1 id="基本操作"><a href="#基本操作" class="headerlink" title="基本操作"></a>基本操作</h1><h2 id="移动"><a href="#移动" class="headerlink" title="移动"></a>移动</h2><p><code>h,l,j,k</code>    左右下上<br><code>w,b</code>    移动到下/上一个单词</p><p><img src="https://i.loli.net/2019/11/15/fOLKyCErQGSVZRH.gif" alt></p><h2 id="快速退出"><a href="#快速退出" class="headerlink" title="快速退出"></a>快速退出</h2><p>普通模式下输入<code>Shift+zz</code>即可保存退出vim</p><h2 id="进入插入模式"><a href="#进入插入模式" class="headerlink" title="进入插入模式"></a>进入插入模式</h2><p><code>i</code>    在当前光标处进行编辑<br><code>I</code>，<code>A</code>  在行首/末插入</p><p><img src="https://i.loli.net/2019/11/15/4lsb5qE2KzWBZMP.gif" alt></p><p><code>a</code> 在光标后插入编辑</p><p><code>o</code>，<code>O</code> 在当前行后/前插入一个新行<br><code>cw</code> 删除一个单词，同时进入插入模式</p><p><img src="https://i.loli.net/2019/11/15/UaNBcrA56Y8bzqF.gif" alt></p><h2 id="文档编辑"><a href="#文档编辑" class="headerlink" title="文档编辑"></a>文档编辑</h2><p>文档编辑的所有命令全部符合一个规律，[操作]+[行号]+定位，三个部分完成一个动作</p><p>比如：删除操作代码为<code>d</code>，重复操作代码代表操作当前行，则删除2行就是<code>d2d</code>；</p><p>操作和行号也可以互换位置 <code>2dd</code>，可以省略行号<code>dd</code>代表删除一行，多数时候可以不要操作则代表定位 <code>5G</code>代表移动到第五行。</p><h2 id="删除"><a href="#删除" class="headerlink" title="删除"></a>删除</h2><p><code>x</code>    反退格键（delete键） <code>X</code>   退格键</p><p><img src="https://i.loli.net/2019/11/15/mCqoQVJUlHMzcdi.gif" alt></p><p><code>dw</code> 删除一个单词（不适用中文）类似于上一节说的<code>cw</code>，只是删除后还在普通模式  </p><p><code>d$</code> 删除至行尾,<code>d^</code> 删除至行首</p><p><img src="https://i.loli.net/2019/11/15/V4TzGpsSoAqRHiF.gif" alt></p><p><code>dG</code> 删除到文档结尾处 <code>d1G</code> 删至文档首部</p><p>小结：<br>前缀（操作） ：<code>d</code>代表删除<br>中缀：行号<br>后缀（定位）：<code>d</code>代表行，<code>G</code>代表尽头, <code>$</code>代表行尾，<code>^</code>代表行首，<code>w</code>代表单词</p><h2 id="重复命令"><a href="#重复命令" class="headerlink" title="重复命令"></a>重复命令</h2><p>普通模式下输入dd，删除一行，输入.(小数点)会再次删除一行，除此之外也可以重复其他任意操作</p><p><img src="https://i.loli.net/2019/11/15/gF6xUJG7MSQEmuN.gif" alt></p><p>使用dw删除一个单词，然后用.命令重复删除单词操作</p><p><img src="https://i.loli.net/2019/11/15/DOe1irJRLvFEbXf.gif" alt></p><h2 id="游标"><a href="#游标" class="headerlink" title="游标"></a>游标</h2><p>游标跳转位置，不过是没有去掉操作后输入的命令。<br><code>nG</code>(n Shift+g)    游标移动到第 n 行(如果默认没有显示行号，请先进入命令模式，输入:set nu以显示行号)<br>gg    游标移动到到第一行<br>G(Shift+g)    到最后一行</p><p><img src="https://i.loli.net/2019/11/15/EKgsbIYjvq5p2DS.gif" alt></p><p><code>G</code>代表尽头, <code>$</code>代表行尾，<code>^</code>代表行首，<code>w</code>代表下一个单词，<code>b</code>上一个单词，<code>e</code>当前单词末尾</p><p><img src="https://i.loli.net/2019/11/15/DyXZ6VoUai1HwLe.gif" alt></p><p><code>f+字母</code> 向后搜索 <code>字母</code>并跳转到第一个匹配的位置<br><code>F+字母</code> 向前搜索<code>字母</code>并跳转到第一个匹配的位置</p><p><img src="https://i.loli.net/2019/11/15/Xft1rcjQHNPoesM.gif" alt></p><h2 id="复制与粘贴"><a href="#复制与粘贴" class="headerlink" title="复制与粘贴"></a>复制与粘贴</h2><p>复制为<code>y</code>，后可以接定位符，<br><code>yy</code>复制游标所在的整行（3yy表示复制3行）<br><code>y^</code>复制至行首，或<code>y0</code>。不含光标所在处字符。<code>y$</code> 复制至行尾。含光标所在处字符。</p><p><img src="https://i.loli.net/2019/12/09/UC3thYuZNXlx4vT.gif" alt="UC3thYuZNXlx4vT.gif"></p><p><code>yw</code> 复制一个单词。<br><code>y2w</code> 复制两个单词。<br><code>yG</code> 复制至文本末。<br><code>y1G</code> 复制至文本开头</p><p><img src="https://i.loli.net/2019/12/09/nlHL93PTpV2Ge7c.gif" alt="nlHL93PTpV2Ge7c.gif"></p><p>使用 <code>p</code> 粘贴<br>p(小写)代表粘贴至光标后（下）<br>P(大写)代表粘贴至光标前（上）</p><h2 id="剪切"><a href="#剪切" class="headerlink" title="剪切"></a>剪切</h2><p>我们上面在删除那学到的<code>d</code>操作，其实就是剪切，我们使用<code>ddp</code>就可以交换上下行。</p><p><img src="https://i.loli.net/2019/12/09/12OS3CovRXLDciQ.gif" alt="12OS3CovRXLDciQ.gif"></p><h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>到这里是不是文档操作可以直接脱离鼠标了！<br>移动：<code>h,l,j,k,w,b</code><br>跳转：<code>G</code> <code>nG</code> <code>f/F+字母</code><br>定位：<code>G</code>代表尽头, <code>$</code>代表行尾，<code>^</code>代表行首，<code>w</code>代表下一个单词，<code>b</code>上一个单词，<code>e</code>当前单词末尾<br>操作：<code>y</code> <code>d</code> <code>p</code></p><h1 id="进阶操作-替换、撤销、缩进、查找"><a href="#进阶操作-替换、撤销、缩进、查找" class="headerlink" title="进阶操作 - 替换、撤销、缩进、查找"></a>进阶操作 - 替换、撤销、缩进、查找</h1><h2 id="替换和撤销-Undo"><a href="#替换和撤销-Undo" class="headerlink" title="替换和撤销(Undo)"></a>替换和撤销(Undo)</h2><p>替换和Undo命令都是针对普通模式下的操作,这次用到三个字母，<code>r/R</code>,<code>c/C</code>,<code>u/U</code></p><p><code>r+&lt;待替换字母&gt;</code> 将游标所在的一个字母替换为其他字母（如下#替换成i）<br><img src="https://i.loli.net/2019/11/15/UgKapqSFrlzuPQ5.gif" alt></p><p>当然了，一个一个替换甚是麻烦，不妨使用R连续替换</p><p><code>R</code> 连续替换，直到按下<code>Esc</code><br><img src="https://i.loli.net/2019/11/15/laULmjD9MpyeTr2.gif" alt></p><p><code>cw</code> 删除一个单词，并进入插入模式</p><p><img src="https://i.loli.net/2019/12/09/7NyZmzUu6IhFcAp.gif" alt="7NyZmzUu6IhFcAp.gif"></p><p><code>C</code> 删除从游标处开始到当前行尾，并进入插入模式</p><p><img src="https://i.loli.net/2019/11/15/BS6yRVDfI2nu7JY.gif" alt></p><p><code>u</code> 撤销一次操作（如下，粘贴了Internet单词三次，按u三次，撤销粘贴操作）</p><p><img src="https://i.loli.net/2019/11/15/xDhSZ7lrdUXBmPq.gif" alt></p><p><code>U</code>(大写) 无论当前行修改了多少次，撤销当前行的所有修改</p><p><img src="https://i.loli.net/2019/11/15/O6xgifFTqDJnbep.gif" alt></p><p><code>Ctrl+r</code> redo，即撤销undo的操作（如下，删除一个单词，写入123，之后交替u和Ctrl+r，可以观察到重复撤销123输入，再恢复123输入）</p><p><img src="https://i.loli.net/2019/11/15/aGld3tHJP4bKr9x.gif" alt></p><p>快速缩进在编辑代码时极其有用，快速调整代码缩进非常的爽，缩进操作均在普通模式下有效</p><h2 id="使用命令进行快速调整缩进操作"><a href="#使用命令进行快速调整缩进操作" class="headerlink" title="使用命令进行快速调整缩进操作"></a>使用命令进行快速调整缩进操作</h2><p><code>&gt;&gt;</code> 整行将向右缩进</p><p><img src="https://i.loli.net/2019/11/15/6Vif12MBTbC9xPF.gif" alt></p><p><code>&lt;&lt;</code> 整行向左回退</p><p><img src="https://i.loli.net/2019/11/15/rnqf9uLl1hMcD6g.gif" alt></p><h2 id="设置缩进字符数"><a href="#设置缩进字符数" class="headerlink" title="设置缩进字符数"></a>设置缩进字符数</h2><p>命令行模式下对<code>shiftwidth</code>值进行设置可以控制缩进和回退的字符数 （可以简写成<code>sw</code>,下面我都用简写）</p><p>获取目前的设定值</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:set shiftwidth?</span><br></pre></td></tr></table></figure><p><img src="https://i.loli.net/2019/11/15/UtDiNBlZQMCWPxj.gif" alt></p><p>设置缩进为n个字符</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:set shiftwidth=n</span><br></pre></td></tr></table></figure><p><img src="https://i.loli.net/2019/12/09/h7VaCBOTjYkZcif.gif" alt="h7VaCBOTjYkZcif.gif"></p><p>输入 ESC 回到普通模式，再次尝试 &gt;&gt; 看缩进量是否变化</p><p><img src="https://i.loli.net/2019/12/09/utXGNIy8EKTDcjw.gif" alt="utXGNIy8EKTDcjw.gif"></p><h2 id="调整文本位置"><a href="#调整文本位置" class="headerlink" title="调整文本位置"></a>调整文本位置</h2><p>命令行模式下输入<code>:le</code>(left)命令使本行内容靠左</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:le</span><br></pre></td></tr></table></figure><p>命令行模式下输入<code>:ce</code>(center)命令使本行内容居中</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:ce</span><br></pre></td></tr></table></figure><p>命令行模式下输入<code>:ri</code>(right)命令使本行文本靠右</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:ri</span><br></pre></td></tr></table></figure><p><img src="https://i.loli.net/2019/12/09/7tCSxlNwQPzk5Yr.gif" alt="7tCSxlNwQPzk5Yr.gif"></p><h2 id="快速查找"><a href="#快速查找" class="headerlink" title="快速查找"></a>快速查找</h2><p>普通模式下输入 <code>/</code> 然后键入需要查找的字符串 按回车后就会进行查找。输入<code>n</code> 和 <code>N</code> 可以继续查找。 <code>n</code>是查找下一个内容,<code>N</code>查找上一个内容。</p><p><img src="https://i.loli.net/2019/12/09/1WErUvN7jQGBVhH.gif" alt="1WErUvN7jQGBVhH.gif"></p><p><code>?</code> 与<code>/</code> 功能相同，只不过<code>?</code>是向上而 <code>/</code> 是向下查找。 <code>:noh</code>退出查找模式</p><p><img src="https://i.loli.net/2019/12/09/8MwDn53QUFLNWJf.gif" alt="8MwDn53QUFLNWJf.gif"></p><h2 id="高级查找"><a href="#高级查找" class="headerlink" title="高级查找"></a>高级查找</h2><p>普通模式下输入<code>\*</code>寻找游标所在处的单词<br>普通模式下输入<code>\#</code>同上，但 <code>\#</code>是向前（上）找，<code>\*</code>则是向后（下）找</p><p><img src="https://i.loli.net/2019/12/09/J9PpXDZfBbd4gWO.gif" alt="J9PpXDZfBbd4gWO.gif"></p><p>以上查找<code>n</code>,<code>N</code> 的继续查找命令依然可以用</p><h2 id="小结-1"><a href="#小结-1" class="headerlink" title="小结"></a>小结</h2><p>速记如下：</p><p>替换：<code>r+&lt;待替换字母&gt;</code>,<code>R</code></p><p>删除并插入：<code>cw</code>、<code>C</code></p><p>撤销：<code>u</code>、<code>U</code>、<code>ctrl+r</code></p><p>调整文本位置：<code>:le</code>、<code>:ce</code>、<code>:ri</code></p><p>查找：<code>\文本</code>、<code>?文本</code>、<code>n</code>、<code>N</code></p><h1 id="高级操作"><a href="#高级操作" class="headerlink" title="高级操作"></a>高级操作</h1><ul><li><p>同时修改多个文件、快速在文件之间切换</p></li><li><p>编辑多个文档时分屏操作</p></li><li><p>批量注释多行代码、修改配置、区域复制删除</p></li></ul><h2 id="多文件的编辑"><a href="#多文件的编辑" class="headerlink" title="多文件的编辑"></a>多文件的编辑</h2><p>同时编辑两个文件<code>1.txt</code>,<code>2.txt</code></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim 1.txt 2.txt</span><br></pre></td></tr></table></figure><p>查看同时打开了哪些文件 <code>:ls</code>,其中<code>%a</code>表示正在编辑的文件。</p><p><img src="https://i.loli.net/2019/12/09/nTudgJYMmS9LyvC.gif" alt="nTudgJYMmS9LyvC.gif"></p><p>在文件之间切换 <code>:n</code> , <code>:N</code></p><p><img src="https://i.loli.net/2019/12/09/Qh6WtqsBfNJ1nyF.gif" alt="Qh6WtqsBfNJ1nyF.gif"></p><p>直接切换到想要编辑的文件 <code>:b 1.txt</code></p><p><img src="https://i.loli.net/2019/12/09/OmVtgNLZbAyiTh7.gif" alt="OmVtgNLZbAyiTh7.gif"></p><p>当然了我们为什么跳来跳去，因为这可以完成多个文件之间复制粘贴等组合操作。</p><h2 id="分屏"><a href="#分屏" class="headerlink" title="分屏"></a>分屏</h2><h3 id="创建分屏"><a href="#创建分屏" class="headerlink" title="创建分屏"></a>创建分屏</h3><p><code>:sp 2.txt</code> 打开新的水平分屏(上下)视窗来编辑<code>2.txt</code></p><p><code>:vsp 2.txt</code>打开新的垂直分屏(左右)视窗来编辑<code>2.txt</code></p><p><img src="https://i.loli.net/2019/12/09/HukPtK46N15faSW.gif" alt="HukPtK46N15faSW.gif"></p><h3 id="分屏跳转与移动分屏"><a href="#分屏跳转与移动分屏" class="headerlink" title="分屏跳转与移动分屏"></a>分屏跳转与移动分屏</h3><p>我先打开了三个文件，<code>1.txt</code> <code>2.txt</code> <code>3.txt</code></p><p><img src="https://i.loli.net/2019/12/09/nmwBRMUeVlszdFD.png" alt="nmwBRMUeVlszdFD.png"></p><p>现在我分别跳转到各个文件中，删除文件内容。</p><p>普通模式下<code>Ctrl+w j</code>移至下面视窗</p><p>普通模式下<code>Ctrl+w k</code> 移至上面视窗</p><p>普通模式下<code>Ctrl+w h</code> 移至左边视窗</p><p>普通模式下<code>Ctrl+w l</code> 移至右边视窗</p><p><img src="https://i.loli.net/2019/12/09/zfnJ9dM6eUxubE1.gif" alt="zfnJ9dM6eUxubE1.gif"></p><p>现在我垂直打开了三个文件，使用以下命令移动视窗,先把<code>文件3</code>从最左边移动到最右边，再移动下最下边</p><p>普通模式下<code>Ctrl+w J</code>将当前视窗移至下面</p><p>普通模式下<code>Ctrl+w K</code> 将当前视窗移至上面</p><p>普通模式下<code>Ctrl+w H</code> 将当前视窗移至左边</p><p>普通模式下<code>Ctrl+w L</code> 将当前视窗移至右边</p><p><img src="https://i.loli.net/2019/12/09/1vR7TrWJusl9GF4.gif" alt="1vR7TrWJusl9GF4.gif"></p><p>只要位于相应视窗，<code>:q</code> <code>:wq</code> <code>q!</code>退出就可以；<code>wqa</code>或<code>qa!</code>全部退出</p><h2 id="可视模式"><a href="#可视模式" class="headerlink" title="可视模式"></a>可视模式</h2><p>可视模式分为三种，也是<code>vim</code>的精髓。分别是行，矩形区域选择，字符段落选择。</p><p>和鼠标选择类似，进入可视模式加定位符号就可以快速跳转到想选择的范围，选中以后再进行复制，删除等操作。</p><h3 id="行选择"><a href="#行选择" class="headerlink" title="行选择"></a>行选择</h3><p>行选择 <code>Shift+v（小写）</code> , 整行选取，可以上下移动光标选更多的行，再按一次 <code>Shift+v</code> 就可以取消选取。</p><p>如下，进入行选择模式，输入<code>4G</code>，跳转到第四行，<code>d</code>删除所选区域。</p><p><img src="https://i.loli.net/2019/12/09/JCze4gixO1mIBTs.gif" alt="JCze4gixO1mIBTs.gif"></p><h3 id="矩形区域选择"><a href="#矩形区域选择" class="headerlink" title="矩形区域选择"></a>矩形区域选择</h3><p>矩形区域选择<code>Ctrl+v（小写）</code>，按区域选取，可以上下左右移动光标选更多的区域，再按一次 <code>Ctrl+v</code>就可以取消选取。</p><p>如下:选择所有的行和第一列所在在区域，<code>shift+i</code>进入插入模式，插入<code>#</code>，双击两次<code>esc</code>，批量注释。然后重复选择动作，输入<code>x</code>删除所选区域。</p><p><img src="https://i.loli.net/2019/12/09/CAwQzyofjVR4J8I.gif" alt="CAwQzyofjVR4J8I.gif"></p><h3 id="字符选择-段落选择"><a href="#字符选择-段落选择" class="headerlink" title="字符选择(段落选择)"></a>字符选择(段落选择)</h3><p>字符选择 <code>v（小写）</code>，可以移动光标，光标走过的地方就会选取。再次按下<code>v</code>后就会取消选取。</p><p>如下：<code>v$</code>选中<code>192.168.1.2</code>退一格为了不选中换行符，到第一行粘贴<code>ip</code>再选中不需要的ip删除掉。</p><p><img src="https://i.loli.net/2019/12/09/QNphLTf9uyErmzc.gif" alt="QNphLTf9uyErmzc.gif"></p><p>可视化模式和定位符结合使用效果最佳！</p><h2 id="小结-2"><a href="#小结-2" class="headerlink" title="小结"></a>小结</h2><p>同时编辑多个文件： <code>vim 1.txt 2.txt</code>, 切换文件 <code>:n</code> 、<code>:N</code>、<code>:b 1.txt</code></p><p>分屏 <code>:sp 2.txt</code> <code>:vsp 2.txt</code>，跳转<code>Ctrl+w h|j|k|l</code> 移动分屏位</p><p>置<code>Ctrl+w H|J|K|L</code></p><p>可视模式 <code>Shift+v</code>、<code>Ctrl+v</code>、<code>v</code></p><p><code>vim</code>命令比较多，这里只列举最有用的，足以应对全部的场景了，记的越多速度越快！</p><p><span class="exturl" data-url="aHR0cHM6Ly9tcC53ZWl4aW4ucXEuY29tL3MvZmpfV3FBeXV3NWxpT0hjOTFEeDNOUQ==" title="https://mp.weixin.qq.com/s/fj_WqAyuw5liOHc91Dx3NQ">Vim高手，从来不用鼠标<i class="fa fa-external-link"></i></span>  </p><p><span class="exturl" data-url="aHR0cHM6Ly9tcC53ZWl4aW4ucXEuY29tL3MvSTI1N1ZJeldpc3VIMHRCTEhTNXRRQQ==" title="https://mp.weixin.qq.com/s/I257VIzWisuH0tBLHS5tQA">Vim高手，从来不用鼠标2——替换、撤销、缩进、查找<i class="fa fa-external-link"></i></span></p><p><span class="exturl" data-url="aHR0cHM6Ly9tcC53ZWl4aW4ucXEuY29tL3MvLWJCZ3hway1hMEs1Z2s4ejh3b1NDZw==" title="https://mp.weixin.qq.com/s/-bBgxpk-a0K5gk8z8woSCg">Vim 高级操作一看就会<i class="fa fa-external-link"></i></span></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/11/15/CVtS9vWLOizafFn.jpg&quot; alt&gt;&lt;/p&gt;
&lt;p&gt;vim有三种模式，基本模式就是用来输入命令的，比如删除跳转等操作就是在基本模式下面；插入模式不用说；命令模式就是按&lt;code&gt;Esc+:&lt;/code&gt;进入的模式，之间的切换很容易我就废话了。下面几乎都在基本模式操作。&lt;/p&gt;
    
    </summary>
    
      <category term="工具" scheme="https://xuebin.me/categories/%E5%B7%A5%E5%85%B7/"/>
    
    
      <category term="dev" scheme="https://xuebin.me/tags/dev/"/>
    
      <category term="工具" scheme="https://xuebin.me/tags/%E5%B7%A5%E5%85%B7/"/>
    
      <category term="开发" scheme="https://xuebin.me/tags/%E5%BC%80%E5%8F%91/"/>
    
      <category term="Linux" scheme="https://xuebin.me/tags/Linux/"/>
    
  </entry>
  
  <entry>
    <title>WebP 可用性探测</title>
    <link href="https://xuebin.me/posts/1f6ad7f6.html"/>
    <id>https://xuebin.me/posts/1f6ad7f6.html</id>
    <published>2019-11-06T13:35:54.000Z</published>
    <updated>2019-11-06T13:35:54.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p><span class="exturl" data-url="aHR0cHM6Ly9kZXZlbG9wZXJzLmdvb2dsZS5jb20vc3BlZWQvd2VicA==" title="https://developers.google.com/speed/webp">webp<i class="fa fa-external-link"></i></span> 是目前 Web 比较流行的解决方案，相对于 Jpeg/png, 基于 VP8 的压缩，有着非常不错的压缩率。</p><blockquote><p>WebP lossless images are 26% smaller in size compared to PNGs. WebP lossy images are 25-34% smaller than comparable JPEG images at equivalent SSIM quality index</p></blockquote><p>比较基础的方法，还是检测 UA 白名单来说，毕竟这些版本都是很早就支持。</p><p><img src="http://img1.vued.vanthink.cn/webp.png" alt></p><a id="more"></a><p>这个方法可控性大，而且能够支持 SSR 渲染，在服务端做 UA 判断然后输出对应的图片格式。</p><p>当然，常规的另外一种解决方式是，就是远程加载一张 webp 图片观测是否报错</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">checkWebPSupport</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <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"><span class="keyword">var</span> img = <span class="keyword">new</span> Image();</span><br><span class="line">img.onload = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123; resolve(); &#125;;</span><br><span class="line">    img.onerror = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123; reject(); &#125;;</span><br><span class="line">    img.src = <span class="string">'http://www.gstatic.com/webp/gallery/1.webp'</span>;</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>网络上有一款比较创新的检测方法，利用 canvas 输出图像的方式。</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="function"><span class="keyword">function</span> <span class="title">canUseWebP</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> elem = <span class="built_in">document</span>.createElement(<span class="string">'canvas'</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!!(elem.getContext &amp;&amp; elem.getContext(<span class="string">'2d'</span>))) &#123;</span><br><span class="line">        <span class="keyword">return</span> elem.toDataURL(<span class="string">'image/webp'</span>).indexOf(<span class="string">'data:image/webp'</span>) == <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="CSS-中嵌入的-背景图"><a href="#CSS-中嵌入的-背景图" class="headerlink" title="CSS 中嵌入的 背景图"></a>CSS 中嵌入的 背景图</h2><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.no-webp</span> <span class="selector-class">.elementWithBackgroundImage</span> &#123;</span><br><span class="line">  <span class="attribute">background-image</span>: <span class="built_in">url</span>(<span class="string">"image.jpg"</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.webp</span> <span class="selector-class">.elementWithBackgroundImage</span>&#123;</span><br><span class="line">  <span class="attribute">background-image</span>: <span class="built_in">url</span>(<span class="string">"image.webp"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果用到 背景图的话，我们可以通过跟元素的 class 来进行图片格式的选择</p><h2 id="扩展阅读"><a href="#扩展阅读" class="headerlink" title="扩展阅读"></a>扩展阅读</h2><ul><li><span class="exturl" data-url="aHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNTU3MzA5Ni9kZXRlY3Rpbmctd2VicC1zdXBwb3J0" title="https://stackoverflow.com/questions/5573096/detecting-webp-support">https://stackoverflow.com/questions/5573096/detecting-webp-support<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9jc3MtdHJpY2tzLmNvbS91c2luZy13ZWJwLWltYWdlcy8=" title="https://css-tricks.com/using-webp-images/">Using WebP Images<i class="fa fa-external-link"></i></span></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9kZXZlbG9wZXJzLmdvb2dsZS5jb20vc3BlZWQvd2VicA==&quot; title=&quot;https://developers.google.com/speed/webp&quot;&gt;webp&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt; 是目前 Web 比较流行的解决方案，相对于 Jpeg/png, 基于 VP8 的压缩，有着非常不错的压缩率。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;WebP lossless images are 26% smaller in size compared to PNGs. WebP lossy images are 25-34% smaller than comparable JPEG images at equivalent SSIM quality index&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;比较基础的方法，还是检测 UA 白名单来说，毕竟这些版本都是很早就支持。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://img1.vued.vanthink.cn/webp.png&quot; alt&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="前端" scheme="https://xuebin.me/categories/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="多媒体" scheme="https://xuebin.me/categories/%E5%89%8D%E7%AB%AF/%E5%A4%9A%E5%AA%92%E4%BD%93/"/>
    
    
      <category term="dev" scheme="https://xuebin.me/tags/dev/"/>
    
      <category term="前端" scheme="https://xuebin.me/tags/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="转载" scheme="https://xuebin.me/tags/%E8%BD%AC%E8%BD%BD/"/>
    
      <category term="多媒体" scheme="https://xuebin.me/tags/%E5%A4%9A%E5%AA%92%E4%BD%93/"/>
    
      <category term="webp" scheme="https://xuebin.me/tags/webp/"/>
    
      <category term="图片" scheme="https://xuebin.me/tags/%E5%9B%BE%E7%89%87/"/>
    
  </entry>
  
  <entry>
    <title>Safari和Chrome浏览器真机调试iphone中h5页面</title>
    <link href="https://xuebin.me/posts/6c7d587e.html"/>
    <id>https://xuebin.me/posts/6c7d587e.html</id>
    <published>2019-10-26T15:46:05.000Z</published>
    <updated>2019-10-26T15:46:05.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><h1 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h1><p>现在的前端研发大都采用Mac设备，而移动端app大多沿用混合开发的方式，从而产生了很多原生页面和H5之间的交互问题。经过上次Mac调试安卓app中的H5的经历之后，今天尝试了在mac中调试ios系统的原生浏览器中的页面</p><a id="more"></a><h1 id="环境准备"><a href="#环境准备" class="headerlink" title="环境准备"></a>环境准备</h1><ul><li>mac一台</li><li>iPhone手机一部</li></ul><h1 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h1><h2 id="iPhone设置"><a href="#iPhone设置" class="headerlink" title="iPhone设置"></a>iPhone设置</h2><p>打开设置–&gt;Safari浏览器–&gt;高级–&gt;开启JavaScript和web检查器</p><p><img src="https://tva1.sinaimg.cn/large/006y8mN6ly1g8c1djasx4j30zq0leq9g.jpg" alt></p><p>iPhone端的操作全部操作完成</p><h2 id="Chrome-浏览器"><a href="#Chrome-浏览器" class="headerlink" title="Chrome 浏览器"></a>Chrome 浏览器</h2><p>没有安装HomeBrew的小伙伴，先安装HomeBrew，不了解HomeBrew的同学可以在 <span class="exturl" data-url="aHR0cHM6Ly93d3cuY2FuaXVzZS5jb20v" title="https://www.caniuse.com/">这里<i class="fa fa-external-link"></i></span> 查看</p><ul><li><p>安装完成后,依次执行下面代码</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">brew unlink libimobiledevice ios-webkit-debug-proxy usbmuxd</span><br><span class="line">brew uninstall --force libimobiledevice ios-webkit-debug-proxy usbmuxd</span><br><span class="line">brew install --HEAD usbmuxd</span><br><span class="line">brew install --HEAD libimobiledevice</span><br><span class="line">brew install --HEAD ios-webkit-debug-proxy</span><br></pre></td></tr></table></figure></li><li><p>安装最新版本的adapter</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install remotedebug-ios-webkit-adapter -g</span><br></pre></td></tr></table></figure><p>到这一步，mac端的操作就已经完成了，</p></li><li><p>然后再看iphone的设置</p><p>mac上打开终端，执行命令，开启adapter，设置监听端口为9000</p>  <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">remotedebug_ios_webkit_adapter --port=9000</span><br></pre></td></tr></table></figure><p>  Chrome中打开<code>chrome://inspect/#devices</code>，在configure中添加localhost:9000,可以看到设备出现在列表中。iphone的Safari中的页面，就可以在这里看到，点击inspect，就可以和PC端一样，进行调试啦</p><p>  <img src="https://tva1.sinaimg.cn/large/006y8mN6ly1g8c1i4pnu6j30qs09k3zd.jpg" alt></p></li></ul><h2 id="Safari浏览器"><a href="#Safari浏览器" class="headerlink" title="Safari浏览器"></a>Safari浏览器</h2><p>Safari浏览器中相对就比较简单啦，iphone端的操作完全一样，打开Safari浏览器，选中系统偏好设置–&gt;高级,勾选在菜单中打开“开发”菜单</p><p><img src="https://tva1.sinaimg.cn/large/006y8mN6ly1g8c1j1in1qj314o0pe78k.jpg" alt></p><p>连接手机，打开Safari浏览器，选择开发，选中设备，可看到移动端Safari浏览器中打开的网址，点击，就能打开Safari浏览器的开发者工具了。如图所示：<img src="https://tva1.sinaimg.cn/large/006y8mN6ly1g8c1jmg4g8j30rm0dj786.jpg" alt></p>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;背景&quot;&gt;&lt;a href=&quot;#背景&quot; class=&quot;headerlink&quot; title=&quot;背景&quot;&gt;&lt;/a&gt;背景&lt;/h1&gt;&lt;p&gt;现在的前端研发大都采用Mac设备，而移动端app大多沿用混合开发的方式，从而产生了很多原生页面和H5之间的交互问题。经过上次Mac调试安卓app中的H5的经历之后，今天尝试了在mac中调试ios系统的原生浏览器中的页面&lt;/p&gt;
    
    </summary>
    
      <category term="工具" scheme="https://xuebin.me/categories/%E5%B7%A5%E5%85%B7/"/>
    
    
      <category term="dev" scheme="https://xuebin.me/tags/dev/"/>
    
      <category term="工具" scheme="https://xuebin.me/tags/%E5%B7%A5%E5%85%B7/"/>
    
      <category term="调试" scheme="https://xuebin.me/tags/%E8%B0%83%E8%AF%95/"/>
    
  </entry>
  
  <entry>
    <title>在浏览器输入URL回车之后发生了什么</title>
    <link href="https://xuebin.me/posts/656a0abb.html"/>
    <id>https://xuebin.me/posts/656a0abb.html</id>
    <published>2019-10-02T14:09:12.000Z</published>
    <updated>2019-10-02T14:09:12.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><blockquote class="blockquote-center"><p>本文作者：4Ark<br>本文链接： <span class="exturl" data-url="aHR0cHM6Ly80YXJrLm1lL3Bvc3QvYjZjN2MwYTIuaHRtbA==" title="https://4ark.me/post/b6c7c0a2.html">https://4ark.me/post/b6c7c0a2.html<i class="fa fa-external-link"></i></span></p></blockquote><p>这个问题已经是老生常谈了，更是经常被作为面试的压轴题出现，网上也有很多文章，但最近闲的无聊，然后就自己做了一篇笔记，感觉比之前理解更透彻了。</p><h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>这个问题已经是老生常谈了，更是经常被作为面试的压轴题出现，网上也有很多文章，但最近闲的无聊，然后就自己做了一篇笔记，感觉比之前理解更透彻了。</p><p>这篇笔记是我这两天看了数十篇文章总结出来的，所以相对全面一点，但由于我是做前端的，所以会比较重点分析浏览器渲染页面那一部分，至于其他部分我会罗列出关键词，感兴趣的可以自行查阅，</p><p><strong>注意：</strong>本文的步骤是建立在，请求的是一个简单的 HTTP 请求，没有 HTTPS、HTTP2、最简单的 DNS、没有代理、并且服务器没有任何问题的基础上，尽管这是不切实际的。</p><h1 id="大致流程"><a href="#大致流程" class="headerlink" title="大致流程"></a>大致流程</h1><ol><li>URL 解析</li><li>DNS 查询</li><li>TCP 连接</li><li>处理请求</li><li>接受响应</li><li>渲染页面</li></ol><a id="more"></a><h1 id="一、URL-解析"><a href="#一、URL-解析" class="headerlink" title="一、URL 解析"></a>一、URL 解析</h1><p><strong>地址解析：</strong></p><p>首先判断你输入的是一个合法的 URL 还是一个待搜索的关键词，并且根据你输入的内容进行自动完成、字符编码等操作。</p><p><strong>HSTS</strong></p><p>由于安全隐患，会使用 HSTS 强制客户端使用 HTTPS 访问页面。详见：<span class="exturl" data-url="aHR0cHM6Ly93d3cuYmFycmV0bGVlLmNvbS9ibG9nLzIwMTUvMTAvMjIvaHN0cy1pbnRyby8=" title="https://www.barretlee.com/blog/2015/10/22/hsts-intro/">你所不知道的 HSTS<i class="fa fa-external-link"></i></span>。</p><p><strong>其他操作</strong></p><p>浏览器还会进行一些额外的操作，比如安全检查、访问限制（之前国产浏览器限制 996.icu）。</p><p><strong>检查缓存</strong></p><p><img src="https://gd4ark-1258805822.cos.ap-guangzhou.myqcloud.com/images/20190822181538.png" alt></p><h1 id="二、DNS-查询"><a href="#二、DNS-查询" class="headerlink" title="二、DNS 查询"></a>二、DNS 查询</h1><p><strong>基本步骤</strong></p><p><img src="https://gd4ark-1258805822.cos.ap-guangzhou.myqcloud.com/images/20190822181732.png" alt></p><p><strong>1. 浏览器缓存</strong></p><p>浏览器会先检查是否在缓存中，没有则调用系统库函数进行查询。</p><p><strong>2. 操作系统缓存</strong></p><p>操作系统也有自己的 DNS 缓存，但在这之前，会向检查域名是否存在本地的 Hosts 文件里，没有则向 DNS 服务器发送查询请求。</p><p><strong>3. 路由器缓存</strong></p><p>路由器也有自己的缓存。</p><p><strong>4. ISP DNS 缓存</strong></p><p>ISP DNS 就是在客户端电脑上设置的首选 DNS 服务器，它们在大多数情况下都会有缓存。</p><p><strong>5.根域名服务器查询</strong></p><p>在前面所有步骤没有缓存的情况下，本地 DNS 服务器会将请求转发到互联网上的根域，下面这个图很好的诠释了整个流程：</p><p><img src="https://gd4ark-1258805822.cos.ap-guangzhou.myqcloud.com/images/1604181361-5a1397cca5644_articlex.png" alt></p><blockquote><p>根域名服务器：<span class="exturl" data-url="aHR0cHM6Ly96aC53aWtpcGVkaWEub3JnL3dpa2kvJUU2JUEwJUI5JUU3JUI2JUIyJUU1JTlGJTlGJUU1JTkwJThEJUU3JUE4JUIxJUU0JUJDJUJBJUU2JTlDJThEJUU1JTk5JUE4" title="https://zh.wikipedia.org/wiki/%E6%A0%B9%E7%B6%B2%E5%9F%9F%E5%90%8D%E7%A8%B1%E4%BC%BA%E6%9C%8D%E5%99%A8">维基百科<i class="fa fa-external-link"></i></span></p></blockquote><p><strong>需要注意的点</strong></p><ol><li>递归方式：一路查下去中间不返回，得到最终结果才返回信息（浏览器到本地 DNS 服务器的过程）</li><li>迭代方式，就是本地 DNS 服务器到根域名服务器查询的方式。</li><li>什么是 DNS 劫持</li><li>前端 dns-prefetch 优化</li></ol><h1 id="三、TCP-连接"><a href="#三、TCP-连接" class="headerlink" title="三、TCP 连接"></a>三、TCP 连接</h1><p>TCP/IP 分为四层，在发送数据时，每层都要对数据进行封装：</p><p><img src="https://gd4ark-1258805822.cos.ap-guangzhou.myqcloud.com/images/20190822182400.png" alt></p><h3 id="1-应用层：发送-HTTP-请求"><a href="#1-应用层：发送-HTTP-请求" class="headerlink" title="1. 应用层：发送 HTTP 请求"></a>1. 应用层：发送 HTTP 请求</h3><p>在前面的步骤我们已经得到服务器的 IP 地址，浏览器会开始构造一个 HTTP 报文，其中包括：</p><ul><li>请求报头（Request Header）：请求方法、目标地址、遵循的协议等等</li><li>请求主体（其他参数）</li></ul><p>其中需要注意的点：</p><ul><li>浏览器只能发送 GET、POST 方法，而打开网页使用的是 GET 方法</li></ul><h3 id="2-传输层：TCP-传输报文"><a href="#2-传输层：TCP-传输报文" class="headerlink" title="2. 传输层：TCP 传输报文"></a>2. 传输层：TCP 传输报文</h3><p>传输层会发起一条到达服务器的 TCP 连接，为了方便传输，会对数据进行分割（以报文段为单位），并标记编号，方便服务器接受时能够准确地还原报文信息。</p><p>在建立连接前，会先进行 TCP 三次握手。</p><blockquote><p>关于 TCP/IP 三次握手，网上已经有很多段子和图片生动地描述了。</p><p>相关知识点：</p><ol><li>SYN 泛洪攻击</li></ol></blockquote><h3 id="3-网络层：IP-协议查询-Mac-地址"><a href="#3-网络层：IP-协议查询-Mac-地址" class="headerlink" title="3. 网络层：IP 协议查询 Mac 地址"></a>3. 网络层：IP 协议查询 Mac 地址</h3><p>将数据段打包，并加入源及目标的 IP 地址，并且负责寻找传输路线。</p><p>判断目标地址是否与当前地址处于同一网络中，是的话直接根据 Mac 地址发送，否则使用路由表查找下一跳地址，以及使用 ARP 协议查询它的 Mac 地址。</p><blockquote><p>注意：在 OSI 参考模型中 ARP 协议位于链路层，但在 TCP/IP 中，它位于网络层。</p></blockquote><h3 id="4-链路层：以太网协议"><a href="#4-链路层：以太网协议" class="headerlink" title="4. 链路层：以太网协议"></a>4. 链路层：以太网协议</h3><p><strong>以太网协议</strong></p><p>根据以太网协议将数据分为以“帧”为单位的数据包，每一帧分为两个部分：</p><ul><li>标头：数据包的发送者、接受者、数据类型</li><li>数据：数据包具体内容</li></ul><p><strong>Mac 地址</strong></p><p>以太网规定了连入网络的所有设备都必须具备“网卡”接口，数据包都是从一块网卡传递到另一块网卡，网卡的地址就是 Mac 地址。每一个 Mac 地址都是独一无二的，具备了一对一的能力。</p><p><strong>广播</strong></p><p>发送数据的方法很原始，直接把数据通过 ARP 协议，向本网络的所有机器发送，接收方根据标头信息与自身 Mac 地址比较，一致就接受，否则丢弃。</p><p><strong>注意</strong>：接收方回应是单播。</p><blockquote><p>相关知识点：</p><ol><li>ARP 攻击</li></ol></blockquote><h3 id="服务器接受请求"><a href="#服务器接受请求" class="headerlink" title="服务器接受请求"></a><strong>服务器接受请求</strong></h3><p>接受过程就是把以上步骤逆转过来，参见上图。</p><h1 id="四、服务器处理请求"><a href="#四、服务器处理请求" class="headerlink" title="四、服务器处理请求"></a>四、服务器处理请求</h1><p><strong>大致流程</strong></p><p><img src="https://gd4ark-1258805822.cos.ap-guangzhou.myqcloud.com/images/20190822183251.png" alt></p><p><strong>HTTPD</strong></p><p>最常见的 HTTPD 有 Linux 上常用的 Apache 和 Nginx，以及 Windows 上的 IIS。</p><p>它会监听得到的请求，然后开启一个子进程去处理这个请求。</p><p><strong>处理请求</strong></p><p>接受 TCP 报文后，会对连接进行处理，对 HTTP 协议进行解析（请求方法、域名、路径等），并且进行一些验证：</p><ul><li>验证是否配置虚拟主机</li><li>验证虚拟主机是否接受此方法</li><li>验证该用户可以使用该方法（根据 IP 地址、身份信息等）</li></ul><p><strong>重定向</strong></p><p>假如服务器配置了 HTTP 重定向，就会返回一个 <code>301</code>永久重定向响应，浏览器就会根据响应，重新发送 HTTP 请求（重新执行上面的过程）。</p><blockquote><p>关于更多：<span class="exturl" data-url="aHR0cHM6Ly93d3cuY25ibG9ncy5jb20vd29ya2VzdC9wLzM4OTEzMjEuaHRtbA==" title="https://www.cnblogs.com/workest/p/3891321.html">详见这篇文章<i class="fa fa-external-link"></i></span></p></blockquote><p><strong>URL 重写</strong></p><p>然后会查看 URL 重写规则，如果请求的文件是真实存在的，比如图片、html、css、js 文件等，则会直接把这个文件返回。</p><p>否则服务器会按照规则把请求重写到 一个 REST 风格的 URL 上。</p><p>然后根据动态语言的脚本，来决定调用什么类型的动态文件解释器来处理这个请求。</p><p>以 PHP 语言的 MVC 框架举例，它首先会初始化一些环境的参数，根据 URL 由上到下地去匹配路由，然后让路由所定义的方法去处理请求。</p><h1 id="五、浏览器接受响应"><a href="#五、浏览器接受响应" class="headerlink" title="五、浏览器接受响应"></a>五、浏览器接受响应</h1><p>浏览器接收到来自服务器的响应资源后，会对资源进行分析。</p><p>首先查看 Response header，根据不同状态码做不同的事（比如上面提到的重定向）。</p><p>如果响应资源进行了压缩（比如 gzip），还需要进行解压。</p><p>然后，对响应资源做缓存。</p><p>接下来，根据响应资源里的 <span class="exturl" data-url="aHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvemgtQ04vZG9jcy9XZWIvSFRUUC9CYXNpY3Nfb2ZfSFRUUC9NSU1FX3R5cGVz" title="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Basics_of_HTTP/MIME_types">MIME<i class="fa fa-external-link"></i></span> 类型去解析响应内容（比如 HTML、Image 各有不同的解析方式）。</p><h1 id="六、渲染页面"><a href="#六、渲染页面" class="headerlink" title="六、渲染页面"></a>六、渲染页面</h1><p><strong>浏览器内核</strong></p><p><img src="https://gd4ark-1258805822.cos.ap-guangzhou.myqcloud.com/images/961f7b5d-9e06-4006-ab99-974e8e9e2ba5.png" alt></p><p>不同的浏览器内核，渲染过程也不完全相同，但大致流程都差不多。</p><p><strong>基本流程</strong></p><p><img src="https://gd4ark-1258805822.cos.ap-guangzhou.myqcloud.com/images/20190822183712.png" alt></p><h3 id="1-HTML-解析"><a href="#1-HTML-解析" class="headerlink" title="1.HTML 解析"></a>1.HTML 解析</h3><p>首先要知道浏览器解析是从上往下一行一行地解析的。</p><p>解析的过程可以分为四个步骤：</p><p><strong>1. 解码（encoding）</strong></p><p>传输回来的其实都是一些二进制字节数据，浏览器需要根据文件指定编码（例如 UTF-8）转换成字符串，也就是 HTML 代码。</p><p><strong>2. 预解析（pre-parsing）</strong></p><p>预解析做的事情是提前加载资源，减少处理时间，它会识别一些会请求资源的属性，比如<code>img</code>标签的<code>src</code>属性，并将这个请求加到请求队列中。</p><p><strong>3. 符号化（Tokenization）</strong></p><p>符号化是词法分析的过程，将输入解析成符号，HTML 符号包括，开始标签、结束标签、属性名和属性值。</p><p>它通过一个状态机去识别符号的状态，比如遇到<code>&lt;</code>，<code>&gt;</code>状态都会产生变化。</p><p><strong>4. 构建树（tree construction）</strong></p><blockquote><p>注意：符号化和构建树是并行操作的，也就是说只要解析到一个开始标签，就会创建一个 DOM 节点。</p></blockquote><p>在上一步符号化中，解析器获得这些标记，然后以合适的方法创建<code>DOM</code>对象并把这些符号插入到<code>DOM</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><span class="line">4</span><br><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="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>Web page parsing<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">h1</span>&gt;</span>Web page parsing<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">p</span>&gt;</span>This is an example Web page.<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><p><img src="https://gd4ark-1258805822.cos.ap-guangzhou.myqcloud.com/images/1355879024-573db51949951_articlex.png" alt></p><p><strong>浏览器容错进制</strong></p><p>你从来没有在浏览器看过类似”语法无效”的错误，这是因为浏览器去纠正错误的语法，然后继续工作。</p><p><strong>事件</strong></p><p>当整个解析的过程完成以后，浏览器会通过<code>DOMContentLoaded</code>事件来通知<code>DOM</code>解析完成。</p><h3 id="2-CSS-解析"><a href="#2-CSS-解析" class="headerlink" title="2. CSS 解析"></a>2. CSS 解析</h3><p>一旦浏览器下载了 CSS，CSS 解析器就会处理它遇到的任何 CSS，根据<span class="exturl" data-url="aHR0cHM6Ly9kcmFmdHMuY3Nzd2cub3JnL2Nzcy1zeW50YXgtMy8=" title="https://drafts.csswg.org/css-syntax-3/">语法规范<i class="fa fa-external-link"></i></span>解析出所有的 CSS 并进行标记化，然后我们得到一个规则表。</p><p><strong>CSS 匹配规则</strong></p><p>在匹配一个节点对应的 CSS 规则时，是按照从右到左的顺序的，例如：<code>div p \{ font-size :14px \}</code>会先寻找所有的<code>p</code>标签然后判断它的父元素是否为<code>div</code>。</p><p>所以我们写 CSS 时，尽量用 id 和 class，千万不要过度层叠。</p><h3 id="3-渲染树"><a href="#3-渲染树" class="headerlink" title="3. 渲染树"></a>3. 渲染树</h3><p>其实这就是一个 DOM 树和 CSS 规则树合并的过程。</p><blockquote><p>注意：渲染树会忽略那些不需要渲染的节点，比如设置了<code>display:none</code>的节点。</p></blockquote><p><strong>计算</strong></p><p>通过计算让任何尺寸值都减少到三个可能之一：<code>auto</code>、百分比、px，比如把<code>rem</code>转化为<code>px</code>。</p><p><strong>级联</strong></p><p>浏览器需要一种方法来确定哪些样式才真正需要应用到对应元素，所以它使用一个叫做<code>specificity</code>的公式，这个公式会通过：</p><ol><li>标签名、class、id</li><li>是否内联样式</li><li><code>!important</code></li></ol><p>然后得出一个权重值，取最高的那个。</p><p><strong>渲染阻塞</strong></p><p>当遇到一个<code>script</code>标签时，DOM 构建会被暂停，直至脚本完成执行，然后继续构建 DOM 树。</p><p>但如果 JS 依赖 CSS 样式，而它还没有被下载和构建时，浏览器就会延迟脚本执行，直至 CSS Rules 被构建。</p><p>所有我们知道：</p><ul><li>CSS 会阻塞 JS 执行</li><li>JS 会阻塞后面的 DOM 解析</li></ul><p>为了避免这种情况，应该以下原则：</p><ul><li>CSS 资源排在 JavaScript 资源前面</li><li>JS 放在 HTML 最底部，也就是 <code>&lt;/body&gt;</code>前</li></ul><p>另外，如果要改变阻塞模式，可以使用 defer 与 async，详见：<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3hpYW95dTJlci9ibG9nL2lzc3Vlcy84" title="https://github.com/xiaoyu2er/blog/issues/8">这篇文章<i class="fa fa-external-link"></i></span></p><h3 id="4-布局与绘制"><a href="#4-布局与绘制" class="headerlink" title="4. 布局与绘制"></a>4. 布局与绘制</h3><p>确定渲染树种所有节点的几何属性，比如：位置、大小等等，最后输入一个盒子模型，它能精准地捕获到每个元素在屏幕内的准确位置与大小。</p><p>然后遍历渲染树，调用渲染器的 paint() 方法在屏幕上显示其内容。</p><h3 id="5-合并渲染层"><a href="#5-合并渲染层" class="headerlink" title="5. 合并渲染层"></a>5. 合并渲染层</h3><p>把以上绘制的所有图片合并，最终输出一张图片。</p><h3 id="6-回流与重绘"><a href="#6-回流与重绘" class="headerlink" title="6. 回流与重绘"></a>6. 回流与重绘</h3><p><strong>回流(reflow)</strong></p><p>当浏览器发现某个部分发现变化影响了布局时，需要倒回去重新渲染，会从<code>html</code>标签开始递归往下，重新计算位置和大小。</p><p>reflow 基本是无法避免的，因为当你滑动一下鼠标、resize 窗口，页面就会产生变化。</p><p><strong>重绘(repaint)</strong></p><p>改变了某个元素的背景色、文字颜色等等不会影响周围元素的位置变化时，就会发生重绘。</p><p>每次重绘后，浏览器还需要合并渲染层并输出到屏幕上。</p><p>回流的成本要比重绘高很多，所以我们应该尽量避免产生回流。</p><p>比如：</p><blockquote><p><code>display:none</code> 会触发回流，而 <code>visibility:hidden</code> 只会触发重绘。</p></blockquote><h3 id="7-JavaScript-编译执行"><a href="#7-JavaScript-编译执行" class="headerlink" title="7. JavaScript 编译执行"></a>7. JavaScript 编译执行</h3><p><strong>大致流程</strong></p><p><img src="https://gd4ark-1258805822.cos.ap-guangzhou.myqcloud.com/images/20190822184523.png" alt></p><p>可以分为三个阶段：</p><h4 id="1-词法分析"><a href="#1-词法分析" class="headerlink" title="1. 词法分析"></a>1. 词法分析</h4><p>JS 脚本加载完毕后，会首先进入语法分析阶段，它首先会分析代码块的语法是否正确，不正确则抛出“语法错误”，停止执行。</p><p>几个步骤：</p><ul><li>分词，例如将<code>var a = 2</code>，，分成<code>var</code>、<code>a</code>、<code>=</code>、<code>2</code>这样的词法单元。</li><li>解析，将词法单元转换成抽象语法树（AST）。</li><li>代码生成，将抽象语法树转换成机器指令。</li></ul><h4 id="2-预编译"><a href="#2-预编译" class="headerlink" title="2. 预编译"></a>2. 预编译</h4><p>JS 有三种运行环境：</p><ul><li>全局环境</li><li>函数环境</li><li>eval</li></ul><p>每进入一个不同的运行环境都会创建一个对应的执行上下文，根据不同的上下文环境，形成一个函数调用栈，栈底永远是全局执行上下文，栈顶则永远是当前执行上下文。</p><p><strong>创建执行上下文</strong></p><p>创建执行上下文的过程中，主要做了以下三件事：</p><ul><li>创建变量对象<ul><li>参数、函数、变量</li></ul></li><li>建立作用域链<ul><li>确认当前执行环境是否能访问变量</li></ul></li><li>确定 This 指向</li></ul><h4 id="3-执行"><a href="#3-执行" class="headerlink" title="3. 执行"></a>3. 执行</h4><p><strong>JS 线程</strong></p><p><img src="https://gd4ark-1258805822.cos.ap-guangzhou.myqcloud.com/images/20190822184731.png" alt></p><p>虽然 JS 是单线程的，但实际上参与工作的线程一共有四个：</p><blockquote><p>其中三个只是协助，只有 JS 引擎线程是真正执行的</p></blockquote><ul><li>JS 引擎线程：也叫 JS 内核，负责解析执行 JS 脚本程序的主线程，例如 V8 引擎</li><li>事件触发线程：属于浏览器内核线程，主要用于控制事件，例如鼠标、键盘等，当事件被触发时，就会把事件的处理函数推进事件队列，等待 JS 引擎线程执行</li><li>定时器触发线程：主要控制<code>setInterval</code>和<code>setTimeout</code>，用来计时，计时完毕后，则把定时器的处理函数推进事件队列中，等待 JS 引擎线程。</li><li>HTTP 异步请求线程：通过 XMLHttpRequest 连接后，通过浏览器新开的一个线程，监控 readyState 状态变更时，如果设置了该状态的回调函数，则将该状态的处理函数推进事件队列中，等待 JS 引擎线程执行。</li></ul><p><strong>注：浏览器对同一域名的并发连接数是有限的，通常为 6 个。</strong></p><p><strong>宏任务</strong></p><p>分为：</p><ul><li>同步任务：按照顺序执行，只有前一个任务完成后，才能执行后一个任务</li><li>异步任务：不直接执行，只有满足触发条件时，相关的线程将该异步任务推进任务队列中，等待 JS 引擎主线程上的任务执行完毕时才开始执行，例如异步 Ajax、DOM 事件，setTimeout 等。</li></ul><p><strong>微任务</strong></p><p>微任务是 ES6 和 Node 环境下的，主要 API 有：<code>Promise</code>，<code>process.nextTick</code>。</p><p>微任务的执行在宏任务的同步任务之后，在异步任务之前。</p><p><img src="https://gd4ark-1258805822.cos.ap-guangzhou.myqcloud.com/images/20190822185002.png" alt></p><p><strong>代码例子</strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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="built_in">console</span>.log(<span class="string">'1'</span>) <span class="comment">// 宏任务 同步</span></span><br><span class="line"></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">'2'</span>) <span class="comment">// 宏任务 异步</span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span>(<span class="params">resolve</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'3'</span>) <span class="comment">// 宏任务 同步</span></span><br><span class="line">  resolve()</span><br><span class="line">&#125;).then(<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">'4'</span>) <span class="comment">// 微任务</span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'5'</span>) <span class="comment">// 宏任务 同步</span></span><br></pre></td></tr></table></figure><p>以上代码输出顺序为：1,3,5,4,2</p><h1 id="参考文档"><a href="#参考文档" class="headerlink" title="参考文档"></a>参考文档</h1><ul><li><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3NreWxpbmU3NTQ4OS93aGF0LWhhcHBlbnMtd2hlbi16aF9DTg==" title="https://github.com/skyline75489/what-happens-when-zh_CN">what-happens-when-zh_CN<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9hbGlzdGFwYXJ0LmNvbS9hcnRpY2xlL3RhZ3MtdG8tZG9tLw==" title="https://alistapart.com/article/tags-to-dom/">Tags to DOM<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9oZXlpbmd5ZS5naXRodWIuaW8vMjAxOC8wNC8xNi8lRTUlQkQlQkIlRTUlQkElOTUlRTclOTAlODYlRTglQTclQTMlRTYlQjUlOEYlRTglQTclODglRTUlOTklQTglRTclOUElODQlRTclQkMlOTMlRTUlQUQlOTglRTYlOUMlQkElRTUlODglQjYv" title="https://heyingye.github.io/2018/04/16/%E5%BD%BB%E5%BA%95%E7%90%86%E8%A7%A3%E6%B5%8F%E8%A7%88%E5%99%A8%E7%9A%84%E7%BC%93%E5%AD%98%E6%9C%BA%E5%88%B6/">彻底理解浏览器的缓存机制<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cuaHRtbDVyb2Nrcy5jb20vemgvdHV0b3JpYWxzL2ludGVybmFscy9ob3dicm93c2Vyc3dvcmsvI1RoZV9yZW5kZXJpbmdfZW5naW5l" title="https://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/#The_rendering_engine">浏览器的工作原理：新式网络浏览器幕后揭秘<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9ibG9nLmZ1bmRlYnVnLmNvbS8yMDE5LzAxLzAzL3VuZGVyc3RhbmQtYnJvd3Nlci1yZW5kZXJpbmcv" title="https://blog.fundebug.com/2019/01/03/understand-browser-rendering/">深入浅出浏览器渲染原理<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9oZXlpbmd5ZS5naXRodWIuaW8vMjAxOC8wMy8xOS9qcyVFNSVCQyU5NSVFNiU5MyU4RSVFNyU5QSU4NCVFNiU4OSVBNyVFOCVBMSU4QyVFOCVCRiU4NyVFNyVBOCU4QiVFRiVCQyU4OCVFNCVCOCU4MCVFRiVCQyU4OS8jJUU5JUEyJTg0JUU3JUJDJTk2JUU4JUFGJTkxJUU5JTk4JUI2JUU2JUFFJUI1" title="https://heyingye.github.io/2018/03/19/js%E5%BC%95%E6%93%8E%E7%9A%84%E6%89%A7%E8%A1%8C%E8%BF%87%E7%A8%8B%EF%BC%88%E4%B8%80%EF%BC%89/#%E9%A2%84%E7%BC%96%E8%AF%91%E9%98%B6%E6%AE%B5">js 引擎的执行过程（一）<i class="fa fa-external-link"></i></span></li><li>还有一些找不到了~</li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote class=&quot;blockquote-center&quot;&gt;&lt;p&gt;本文作者：4Ark&lt;br&gt;本文链接： &lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly80YXJrLm1lL3Bvc3QvYjZjN2MwYTIuaHRtbA==&quot; title=&quot;https://4ark.me/post/b6c7c0a2.html&quot;&gt;https://4ark.me/post/b6c7c0a2.html&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&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&gt;这个问题已经是老生常谈了，更是经常被作为面试的压轴题出现，网上也有很多文章，但最近闲的无聊，然后就自己做了一篇笔记，感觉比之前理解更透彻了。&lt;/p&gt;
&lt;p&gt;这篇笔记是我这两天看了数十篇文章总结出来的，所以相对全面一点，但由于我是做前端的，所以会比较重点分析浏览器渲染页面那一部分，至于其他部分我会罗列出关键词，感兴趣的可以自行查阅，&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意：&lt;/strong&gt;本文的步骤是建立在，请求的是一个简单的 HTTP 请求，没有 HTTPS、HTTP2、最简单的 DNS、没有代理、并且服务器没有任何问题的基础上，尽管这是不切实际的。&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;ol&gt;
&lt;li&gt;URL 解析&lt;/li&gt;
&lt;li&gt;DNS 查询&lt;/li&gt;
&lt;li&gt;TCP 连接&lt;/li&gt;
&lt;li&gt;处理请求&lt;/li&gt;
&lt;li&gt;接受响应&lt;/li&gt;
&lt;li&gt;渲染页面&lt;/li&gt;
&lt;/ol&gt;
    
    </summary>
    
      <category term="前端" scheme="https://xuebin.me/categories/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="dev" scheme="https://xuebin.me/tags/dev/"/>
    
      <category term="前端" scheme="https://xuebin.me/tags/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="开发" scheme="https://xuebin.me/tags/%E5%BC%80%E5%8F%91/"/>
    
      <category term="转载" scheme="https://xuebin.me/tags/%E8%BD%AC%E8%BD%BD/"/>
    
  </entry>
  
  <entry>
    <title>滚动吸顶</title>
    <link href="https://xuebin.me/posts/cc24fa55.html"/>
    <id>https://xuebin.me/posts/cc24fa55.html</id>
    <published>2019-08-19T06:43:12.000Z</published>
    <updated>2019-08-19T06:43:12.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><blockquote class="blockquote-center"><p>作者：小生方勤链接：<span class="exturl" data-url="aHR0cHM6Ly9qdWVqaW4uaW0vcG9zdC81Y2FhMGMyZDUxODgyNTQzZmE0MWU0NzjmnaXmupDvvJrmjpjph5HokZfkvZzmnYPlvZLkvZzogIXmiYDmnInjgILllYbkuJrovazovb3or7fogZTns7vkvZzogIXojrflvpfmjojmnYPvvIzpnZ7llYbkuJrovazovb3or7fms6jmmI7lh7rlpITjgII=" title="https://juejin.im/post/5caa0c2d51882543fa41e478来源：掘金著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。">https://juejin.im/post/5caa0c2d51882543fa41e478来源：掘金著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。<i class="fa fa-external-link"></i></span></p></blockquote><p><img src="http://ww1.sinaimg.cn/large/006tNc79ly1g64zmgmgrdg30gm0j01kz.gif" alt="效果图"></p><a id="more"></a><h1 id="position-sticky"><a href="#position-sticky" class="headerlink" title="position:sticky"></a>position:sticky</h1><h2 id="粘性定位是什么？"><a href="#粘性定位是什么？" class="headerlink" title="粘性定位是什么？"></a>粘性定位是什么？</h2><p>粘性定位 <code>sticky</code>相当于相对定位 <code>relative</code>和固定定位 <code>fixed</code>的结合；在页面元素滚动过程中，某个元素距离其父元素的距离达到 <code>sticky</code>粘性定位的要求时；元素的相对定位 <code>relative</code>效果变成固定定位 <code>fixed</code>的效果。</p><p><span class="exturl" data-url="aHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvemgtQ04vZG9jcy9XZWIvQ1NTL3Bvc2l0aW9u" title="https://developer.mozilla.org/zh-CN/docs/Web/CSS/position">MDN 传送门<i class="fa fa-external-link"></i></span></p><h2 id="如何使用？"><a href="#如何使用？" class="headerlink" title="如何使用？"></a>如何使用？</h2><p>使用条件：</p><ol><li>父元素不能 <code>overflow:hidden</code>或者 <code>overflow:auto</code>属性</li><li>必须指定 <code>top、bottom、left、right</code>4 个值之一，否则只会处于相对定位</li><li>父元素的高度不能低于 <code>sticky</code>元素的高度</li><li><code>sticky</code>元素仅在其父元素内生效</li></ol><p>在需要滚动吸顶的元素加上以下样式便可以实现这个效果：</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">.sticky</span> &#123;</span><br><span class="line">    <span class="attribute">position</span>: -webkit-sticky;</span><br><span class="line">    <span class="attribute">position</span>: sticky;</span><br><span class="line">    <span class="attribute">top</span>: <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="这个属性好用吗？"><a href="#这个属性好用吗？" class="headerlink" title="这个属性好用吗？"></a>这个属性好用吗？</h2><p>我们先看下在 Can I use 中看看这个属性的兼容性：<img src="http://ww2.sinaimg.cn/large/006tNc79ly1g64zri8j0yj31b60iajtg.jpg" alt></p><p>可以看出这个属性的兼容性并不是很好，因为这个 API 还只是<strong>实验性的属性</strong>。不过这个 API 在 IOS 系统的兼容性还是比较好的。</p><p>所以我们在生产环境如果使用这个 API 的时候一般会和下面的几种方式结合使用。</p><h1 id="JQuery-的-offset-top"><a href="#JQuery-的-offset-top" class="headerlink" title="JQuery 的 offset().top"></a><strong>JQuery 的</strong> offset().top</h1><p>我们知道 JQuery 中封装了操作 DOM 和读取 DOM 计算属性的 API，基于 <code>offset().top</code> 这个 API 和 <code>scrollTop()</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><br><span class="line"><span class="built_in">window</span>.addEventListener(<span class="string">'scroll'</span>, self.handleScrollOne);</span><br><span class="line">...</span><br><span class="line">handleScrollOne: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> self = <span class="keyword">this</span>;</span><br><span class="line">    <span class="keyword">let</span> scrollTop = $(<span class="string">'html'</span>).scrollTop();</span><br><span class="line">    <span class="keyword">let</span> offsetTop = $(<span class="string">'.title_box'</span>).offset().top;</span><br><span class="line">    self.titleFixed = scrollTop &gt; offsetTop;</span><br><span class="line">&#125;</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>这样实现固然可以，不过由于 JQuery 慢慢的退出历史的舞台，我们在代码中尽量不使用 JQuery 的 API。我们可以基于 <code>offset().top</code> 的源码自己处理原生 <code>offsetTop</code>。于是乎就有了第三种方式。</p><blockquote><p>scrolloTop() 有兼容性问题，在微信浏览器、IE、某些 firefox 版本中 $(‘html’).scrollTop() 的值会为 0，于是乎也就有了第三种方案的兼容性写法。</p></blockquote><h1 id="原生的-offsetTop"><a href="#原生的-offsetTop" class="headerlink" title="原生的 offsetTop"></a><strong>原生的</strong> offsetTop</h1><p>我们知道 <code>offsetTop</code> 是相对定位父级的偏移量，倘若需要滚动吸顶的元素出现定位父级元素，那么 <code>offsetTop</code> 获取的就不是元素距离页面顶部的距离。</p><p>我们可以自己对 <code>offsetTop</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></pre></td><td class="code"><pre><span class="line">getOffset: <span class="function"><span class="keyword">function</span>(<span class="params">obj,direction</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">let</span> offsetL = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">let</span> offsetT = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span>( obj!== <span class="built_in">window</span>.document.body &amp;&amp; obj !== <span class="literal">null</span> )&#123;</span><br><span class="line">        offsetL += obj.offsetLeft;</span><br><span class="line">        offsetT += obj.offsetTop;</span><br><span class="line">        obj = obj.offsetParent;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span>(direction === <span class="string">'left'</span>)&#123;</span><br><span class="line">        <span class="keyword">return</span> offsetL;</span><br><span class="line">    &#125;<span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> offsetT;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>使用：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line"><span class="built_in">window</span>.addEventListener(<span class="string">'scroll'</span>, self.handleScrollTwo);</span><br><span class="line">...</span><br><span class="line">handleScrollTwo: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> self = <span class="keyword">this</span>;</span><br><span class="line">    <span class="keyword">let</span> scrollTop = <span class="built_in">window</span>.pageYOffset || <span class="built_in">document</span>.documentElement.scrollTop || <span class="built_in">document</span>.body.scrollTop;</span><br><span class="line">    <span class="keyword">let</span> offsetTop = self.getOffset(self.$refs.pride_tab_fixed);</span><br><span class="line">    self.titleFixed = scrollTop &gt; offsetTop;</span><br><span class="line">&#125;</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>你是不是看出了以上两种方式的一些问题？</p><blockquote><p>我们一定需要使用 <code>scrollTop - offsetTop</code> 的值来实现滚动吸顶的效果吗？答案是否定的。</p></blockquote><h1 id="obj-getBoundingClientRect-top"><a href="#obj-getBoundingClientRect-top" class="headerlink" title="obj.getBoundingClientRect().top"></a>obj.getBoundingClientRect().top</h1><h2 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h2><p>这个 <code>API</code> 可以告诉你页面中某个元素<strong>相对浏览器视窗</strong>上下左右的距离。</p><h2 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h2><p>tab 吸顶可以使用 <code>obj.getBoundingClientRect().top</code> 代替 <code>scrollTop - offsetTop</code>,代码如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">&lt;template&gt;</span><br><span class="line">&lt;div class=&quot;pride_tab_fixed&quot; ref=&quot;pride_tab_fixed&quot;&gt;</span><br><span class="line">  &lt;div class=&quot;pride_tab&quot; :class=&quot;titleFixed == true ? &apos;isFixed&apos; :&apos;&apos;&quot;&gt;</span><br><span class="line">      // some code</span><br><span class="line">  &lt;/div&gt;</span><br><span class="line">&lt;/div&gt;</span><br><span class="line">&lt;/template&gt;</span><br><span class="line"></span><br><span class="line">&lt;script&gt;</span><br><span class="line">export default &#123;</span><br><span class="line">  data()&#123;</span><br><span class="line">    return&#123;</span><br><span class="line">      titleFixed: false</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  activated()&#123;</span><br><span class="line">    this.titleFixed = false;</span><br><span class="line">    window.addEventListener(&apos;scroll&apos;, this.handleScroll);</span><br><span class="line">  &#125;,</span><br><span class="line">  methods: &#123;</span><br><span class="line">    //滚动监听，头部固定</span><br><span class="line">    handleScroll: function () &#123;</span><br><span class="line">      let offsetTop = this.$refs.pride_tab_fixed.getBoundingClientRect().top;</span><br><span class="line">      this.titleFixed = offsetTop &lt; 0;</span><br><span class="line">      // some code</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">&lt;/script&gt;</span><br></pre></td></tr></table></figure><h1 id="offsetTop-和-getBoundingClientRect-区别"><a href="#offsetTop-和-getBoundingClientRect-区别" class="headerlink" title="offsetTop 和 getBoundingClientRect() 区别"></a>offsetTop 和 getBoundingClientRect() 区别</h1><h2 id="getBoundingClientRect"><a href="#getBoundingClientRect" class="headerlink" title="getBoundingClientRect()"></a>getBoundingClientRect()</h2><p>用于获得页面中某个元素的左，上，右和下分别相对浏览器视窗的位置。不包含文档卷起来的部分。</p><p><img src="http://ww1.sinaimg.cn/large/006tNc79ly1g64zyj0ev4j30lr0dw0sm.jpg" alt="img"></p><p>该函数返回一个 <code>object</code>对象，有8个属性：<code>top, right, buttom, left, width, height, x, y</code></p><p><img src="http://ww3.sinaimg.cn/large/006tNc79ly1g64zz218yaj308a06pmx1.jpg" alt="img"></p><h2 id="offsetTop"><a href="#offsetTop" class="headerlink" title="offsetTop"></a>offsetTop</h2><p>用于获得当前元素到定位父级（ element.offsetParent ）顶部的距离（偏移值）。</p><blockquote><p>定位父级 <code>offsetParent</code>的定义是：与当前元素最近的 position != static 的父级元素。</p></blockquote><p><code>offsetTop</code>和 <code>offsetParent</code>方法相结合可以获得该元素到 <code>body</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></pre></td><td class="code"><pre><span class="line">getOffset: <span class="function"><span class="keyword">function</span>(<span class="params">obj,direction</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">let</span> offsetL = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">let</span> offsetT = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span>( obj!== <span class="built_in">window</span>.document.body &amp;&amp; obj !== <span class="literal">null</span> )&#123;</span><br><span class="line">        offsetL += obj.offsetLeft;</span><br><span class="line">        offsetT += obj.offsetTop;</span><br><span class="line">        obj = obj.offsetParent;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span>(direction === <span class="string">'left'</span>)&#123;</span><br><span class="line">        <span class="keyword">return</span> offsetL;</span><br><span class="line">    &#125;<span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> offsetT;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="延伸知识点"><a href="#延伸知识点" class="headerlink" title="延伸知识点"></a>延伸知识点</h1><h2 id="offsetWidth"><a href="#offsetWidth" class="headerlink" title="offsetWidth"></a>offsetWidth</h2><p>元素在水平方向上占用的空间大小：<br>offsetWidth = border-left + padding-left + width + padding-right + border-right</p><h2 id="offsetHeight"><a href="#offsetHeight" class="headerlink" title="offsetHeight"></a>offsetHeight</h2><p>元素在垂直方向上占用的空间大小：<br>offsetHeight = border-top + padding-top + height + padding-bottom + border-bottom</p><blockquote><p>注：如果存在垂直滚动条，offsetWidth 也包括垂直滚动条的宽度；如果存在水平滚动条，offsetHeight 也包括水平滚动条的高度；</p></blockquote><h2 id="offsetTop-1"><a href="#offsetTop-1" class="headerlink" title="offsetTop"></a>offsetTop</h2><p>元素的上外边框至 <code>offsetParent</code>元素的上内边框之间的像素距离；</p><h2 id="offsetLeft"><a href="#offsetLeft" class="headerlink" title="offsetLeft"></a>offsetLeft</h2><p>元素的左外边框至 <code>offsetParent</code>元素的左内边框之间的像素距离；</p><h2 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h2><ol><li>所有偏移量属性都是只读的；</li><li>如果给元素设置了 <code>display:none</code>，则它的偏移量属性都为 0；</li><li>每次访问偏移量属性都需要重新计算（<strong>保存变量</strong>）；</li><li>在使用的时候可能出现 DOM 没有初始化，就读取了该属性，这个时候会返回 0；对于这个问题我们需要等到 DOM 元素初始化完成后再执行。</li></ol><h1 id="遇到的两个问题"><a href="#遇到的两个问题" class="headerlink" title="遇到的两个问题"></a>遇到的两个问题</h1><h2 id="吸顶的那一刻伴随抖动"><a href="#吸顶的那一刻伴随抖动" class="headerlink" title="吸顶的那一刻伴随抖动"></a>吸顶的那一刻伴随抖动</h2><p>出现抖动的原因是因为：在吸顶元素 position 变为 fixed 的时候，该元素就脱离了文档流，下一个元素就进行了补位。就是这个补位操作造成了抖动。</p><p><strong>解决方案</strong></p><p>为这个吸顶元素添加一个等高的父元素，我们监听这个父元素的 <code>getBoundingClientRect().top</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><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"title_box"</span> <span class="attr">ref</span>=<span class="string">"pride_tab_fixed"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"title"</span> <span class="attr">:class</span>=<span class="string">"titleFixed == true ? 'isFixed' :''"</span>&gt;</span></span><br><span class="line">    使用 `obj.getBoundingClientRect().top` 实现</span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><p>这个方案就可以解决抖动的 Bug 了。</p><h2 id="吸顶效果不能及时响应"><a href="#吸顶效果不能及时响应" class="headerlink" title="吸顶效果不能及时响应"></a>吸顶效果不能及时响应</h2><p>这个问题是我比较头痛，之前我没有在意过这个问题。直到有一天我用美团点外卖的时候，我才开始注意这个问题。</p><p><strong>描述：</strong></p><ol><li>当页面往下滚动时，吸顶元素需要等页面滚动停止之后才会出现吸顶效果</li><li>当页面往上滚动时，滚动到吸顶元素恢复文档流位置时吸顶元素不恢复原样，而等页面停止滚动之后才会恢复原样</li></ol><p><strong>原因：</strong>在 ios 系统上不能实时监听 scroll 滚动监听事件，在滚动停止时才触发其相关的事件。</p><p><strong>解决方案：</strong></p><p>还记得第一种方案中的 <code>position:sticky</code>吗？这个属性在 IOS6 以上的系统中有良好的兼容性，所以我们可以区分 IOS 和 Android 设备做两种处理。</p><blockquote><p>IOS 使用 <code>position:sticky</code>，Android 使用滚动监听 <code>getBoundingClientRect().top</code>的值。</p></blockquote><p>如果 IOS 版本过低呢？这里提供一种思路：<code>window.requestAnimationFrame()</code>。</p><h1 id="性能优化"><a href="#性能优化" class="headerlink" title="性能优化"></a>性能优化</h1><p>到此 4 中滚动吸顶的方式介绍完了，可是这样就真的结束了吗？其实还是有优化的空间的。</p><p>我们从两个方向做性能优化（其实是一个方向）：</p><ol><li>避免过度的 reflow</li><li>优化滚动监听事件</li></ol><h2 id="问题定位过程"><a href="#问题定位过程" class="headerlink" title="问题定位过程"></a>问题定位过程</h2><p>我们知道过度的 reflow 会使页面的性能下降。所以我们需要尽可能的降低 reflow 的次数，给用户更加流畅的感觉。</p><blockquote><p>有的朋友或许会说这个我知道，可是这和滚动吸顶有什么关系呢？</p></blockquote><p>不急，你是否还记得滚动吸顶使用了 offsetTop 或者 getBoundingClientRect().top 来获取响应的偏移量呢？</p><blockquote><p>既然有读取元素的属性就自然会导致页面 reflow。</p></blockquote><p>因此我们优化的方向就是从减少读取元素属性次数下手，查看代码发现一触发屏幕滚动事件就会调用相关方法读取元素的偏移量。</p><h2 id="优化方案"><a href="#优化方案" class="headerlink" title="优化方案"></a>优化方案</h2><p>解决这个问题有以下两个方案：</p><ol><li>牺牲平滑度满足性能，使用节流控制相关方法的调用</li><li>使用 IntersectionObserver 和节流结合，也牺牲了平滑度。</li></ol><h3 id="第一种方案"><a href="#第一种方案" class="headerlink" title="第一种方案"></a>第一种方案</h3><p>这个方案很常见，不过其带来的副作用也很明显，就是在吸顶效果会有些延迟，如果产品可以接受那就不失为一种好方法。</p><p>这样可以控制在一定时间内只读取</p><p>这里节流函数就直接是用 <code>lodash.js</code>封装好的 <code>throttle</code>方法。</p><p>代码如下：</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="built_in">window</span>.addEventListener(<span class="string">'scroll'</span>, _.throttle(self.handleScrollThree, <span class="number">50</span>));</span><br></pre></td></tr></table></figure><h3 id="第二种方案"><a href="#第二种方案" class="headerlink" title="第二种方案"></a>第二种方案</h3><p>第二种方案相对来说容易接受一点，就是支持 IntersectionObserver 就用 IntersectionObserver，否则就用 throttle。</p><p><strong>我们先讲讲 IntersectionObserver</strong></p><blockquote><p>IntersectionObserver 可以用来监听元素是否进入了设备的可视区域之内，而不需要频繁的计算来做这个判断。</p></blockquote><p>通过这个属性我们就可以在元素不在可视范围内，不去读取元素的相对位置，已达到性能优化；当浏览器不支持这个属性的时候就使用 throttle 来处理。</p><p>我们看看这个属性的兼容性怎么样：</p><p><img src="http://ww4.sinaimg.cn/large/006tNc79ly1g6504tspw0j30jp0b50t0.jpg" alt></p><p>大概支持了 60% 以上，在项目中还是可以使用的（你需要做好兼容性处理）。</p><blockquote><p>关于 IntersectionObserver 如何使用，<span class="exturl" data-url="aHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvemgtQ04vZG9jcy9XZWIvQVBJL0ludGVyc2VjdGlvbk9ic2VydmVy" title="https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserver">请看 MDN<i class="fa fa-external-link"></i></span>或者 <span class="exturl" data-url="aHR0cDovL3d3dy5ydWFueWlmZW5nLmNvbS9ibG9nLzIwMTYvMTEvaW50ZXJzZWN0aW9ub2JzZXJ2ZXJfYXBpLmh0bWw=" title="http://www.ruanyifeng.com/blog/2016/11/intersectionobserver_api.html">阮一峰教程<i class="fa fa-external-link"></i></span>。</p></blockquote><p>使用 IntersectionObserver 和 throttle 优化的代码如下：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">IntersectionObserverFun: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> self = <span class="keyword">this</span>;</span><br><span class="line">    <span class="keyword">let</span> ele = self.$refs.pride_tab_fixed;</span><br><span class="line">    <span class="keyword">if</span>( !IntersectionObserver )&#123;</span><br><span class="line">        <span class="keyword">let</span> observer = <span class="keyword">new</span> IntersectionObserver(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">            <span class="keyword">let</span> offsetTop = ele.getBoundingClientRect().top;</span><br><span class="line">            self.titleFixed = offsetTop &lt; <span class="number">0</span>;</span><br><span class="line">        &#125;, &#123;</span><br><span class="line">            threshold: [<span class="number">1</span>]</span><br><span class="line">        &#125;);</span><br><span class="line">        observer.observe(<span class="built_in">document</span>.getElementsByClassName(<span class="string">'title_box'</span>)[<span class="number">0</span>]);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="built_in">window</span>.addEventListener(<span class="string">'scroll'</span>, _.throttle(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">            <span class="keyword">let</span> offsetTop = ele.getBoundingClientRect().top;</span><br><span class="line">            self.titleFixed = offsetTop &lt; <span class="number">0</span>;</span><br><span class="line">        &#125;, <span class="number">50</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure><p><strong>注意</strong></p><blockquote><p>IntersectionObserver API 是异步的，不随着目标元素的滚动同步触发。</p></blockquote><p>规格写明，IntersectionObserver的实现，应该采用 requestIdleCallback()。它不会立即执行回调，它会调用 <code>window.requestIdleCallback()</code>来异步的执行我们指定的回调函数，而且还规定了最大的延迟时间是 100 毫秒。</p><p><strong>总结：</strong></p><blockquote><p>这种 IntersectionObserver 和 throttle 结合的方案不失为一种可选择的方案，这种方案的优点就在于可以有效地减少页面 reflow 的风险，不过缺点也是有的，需要牺牲页面的平滑度。具体该如何取舍，就看业务的需要啦。</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;blockquote class=&quot;blockquote-center&quot;&gt;&lt;p&gt;作者：小生方勤链接：&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9qdWVqaW4uaW0vcG9zdC81Y2FhMGMyZDUxODgyNTQzZmE0MWU0NzjmnaXmupDvvJrmjpjph5HokZfkvZzmnYPlvZLkvZzogIXmiYDmnInjgILllYbkuJrovazovb3or7fogZTns7vkvZzogIXojrflvpfmjojmnYPvvIzpnZ7llYbkuJrovazovb3or7fms6jmmI7lh7rlpITjgII=&quot; title=&quot;https://juejin.im/post/5caa0c2d51882543fa41e478来源：掘金著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。&quot;&gt;https://juejin.im/post/5caa0c2d51882543fa41e478来源：掘金著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;http://ww1.sinaimg.cn/large/006tNc79ly1g64zmgmgrdg30gm0j01kz.gif&quot; alt=&quot;效果图&quot;&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="前端" scheme="https://xuebin.me/categories/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="javascript" scheme="https://xuebin.me/categories/%E5%89%8D%E7%AB%AF/javascript/"/>
    
    
      <category term="dev" scheme="https://xuebin.me/tags/dev/"/>
    
      <category term="前端" scheme="https://xuebin.me/tags/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="开发" scheme="https://xuebin.me/tags/%E5%BC%80%E5%8F%91/"/>
    
      <category term="转载" scheme="https://xuebin.me/tags/%E8%BD%AC%E8%BD%BD/"/>
    
  </entry>
  
  <entry>
    <title>Vue开发技巧</title>
    <link href="https://xuebin.me/posts/4c0ac24d.html"/>
    <id>https://xuebin.me/posts/4c0ac24d.html</id>
    <published>2019-05-23T14:11:18.000Z</published>
    <updated>2019-05-23T14:11:18.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><!-- @import "[TOC]" {cmd="toc" depthFrom=1 depthTo=6 orderedList=false} --><!-- code_chunk_output --><ol><li><a href="#状态共享">状态共享</a></li><li><a href="#长列表性能优化">长列表性能优化</a></li><li><a href="#去除多余的样式">去除多余的样式</a></li><li><a href="#作用域插槽">作用域插槽</a></li><li><a href="#属性事件传递">属性事件传递</a></li><li><a href="#函数式组件">函数式组件</a></li><li><a href="#监听组件的生命周期">监听组件的生命周期</a></li><li><a href="#参考链接">参考链接：</a></li></ol><!-- /code_chunk_output --><a id="more"></a><h1 id="状态共享"><a href="#状态共享" class="headerlink" title="状态共享"></a>状态共享</h1><p>随着组件的细化，就会遇到多组件状态共享的情况，<code>Vuex</code>当然可以解决这类问题，不过就像<code>Vuex</code>官方文档所说的，如果应用不够大，为避免代码繁琐冗余，最好不要使用它，今天我们介绍的是 vue.js 2.6 新增加的<span class="exturl" data-url="aHR0cHM6Ly9saW5rLmp1ZWppbi5pbT90YXJnZXQ9aHR0cHMlM0ElMkYlMkZ2dWVqcy5vcmclMkZ2MiUyRmFwaSUyRiUyM1Z1ZS1vYnNlcnZhYmxl" title="https://link.juejin.im?target=https%3A%2F%2Fvuejs.org%2Fv2%2Fapi%2F%23Vue-observable">Observable API<i class="fa fa-external-link"></i></span> ，通过使用这个 api 我们可以应对一些简单的跨组件数据状态共享的情况。</p><p>如下这个例子，我们将在组件外创建一个<code>store</code>，然后在<code>App.vue</code>组件里面使用 store.js 提供的<code>store</code>和<code>mutation</code>方法，同理其它组件也可以这样使用，从而实现多个组件共享数据状态。</p><p>首先创建一个 store.js，包含一个<code>store</code>和一个<code>mutations</code>，分别用来指向数据和处理方法。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> Vue <span class="keyword">from</span> <span class="string">'vue'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> store = Vue.observable(&#123; <span class="attr">count</span>: <span class="number">0</span> &#125;)</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> mutations = &#123;</span><br><span class="line">  setCount(count) &#123;</span><br><span class="line">    store.count = count</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后在<code>App.vue</code>里面引入这个 store.js，在组件里面使用引入的数据和方法</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">&lt;template&gt;</span><br><span class="line">  &lt;div id=<span class="string">"app"</span>&gt;</span><br><span class="line">    &lt;img width=<span class="string">"25%"</span> src=<span class="string">"./assets/logo.png"</span>&gt;</span><br><span class="line">    &lt;p&gt;count:&#123;&#123;count&#125;&#125;&lt;<span class="regexp">/p&gt;</span></span><br><span class="line"><span class="regexp">    &lt;button @click="setCount(count+1)"&gt;+1&lt;/</span>button&gt;</span><br><span class="line">    &lt;button @click=<span class="string">"setCount(count-1)"</span>&gt;<span class="number">-1</span>&lt;<span class="regexp">/button&gt;</span></span><br><span class="line"><span class="regexp">  &lt;/</span>div&gt;</span><br><span class="line">&lt;<span class="regexp">/template&gt;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">&lt;script&gt;</span></span><br><span class="line"><span class="regexp">import &#123; store, mutations &#125; from "./</span>store<span class="string">";</span></span><br><span class="line"><span class="string">export default &#123;</span></span><br><span class="line"><span class="string">  name: "</span>App<span class="string">",</span></span><br><span class="line"><span class="string">  computed: &#123;</span></span><br><span class="line"><span class="string">    count() &#123;</span></span><br><span class="line"><span class="string">      return store.count;</span></span><br><span class="line"><span class="string">    &#125;</span></span><br><span class="line"><span class="string">  &#125;,</span></span><br><span class="line"><span class="string">  methods: &#123;</span></span><br><span class="line"><span class="string">    setCount: mutations.setCount</span></span><br><span class="line"><span class="string">  &#125;</span></span><br><span class="line"><span class="string">&#125;;</span></span><br><span class="line"><span class="string">&lt;/script&gt;</span></span><br></pre></td></tr></table></figure><p>你可以点击<span class="exturl" data-url="aHR0cHM6Ly9saW5rLmp1ZWppbi5pbT90YXJnZXQ9aHR0cHMlM0ElMkYlMkZjb2Rlc2FuZGJveC5pbyUyRnMlMkY0dzBtbzBreW93" title="https://link.juejin.im?target=https%3A%2F%2Fcodesandbox.io%2Fs%2F4w0mo0kyow">在线 DEMO<i class="fa fa-external-link"></i></span>查看最终效果</p><h1 id="长列表性能优化"><a href="#长列表性能优化" class="headerlink" title="长列表性能优化"></a>长列表性能优化</h1><p>我们应该都知道<code>vue</code>会通过<code>object.defineProperty</code>对数据进行劫持，来实现视图响应数据的变化，然而有些时候我们的组件就是纯粹的数据展示，不会有任何改变，我们就不需要<code>vue</code>来劫持我们的数据，在大量数据展示的情况下，这能够很明显的减少组件初始化的时间，那如何禁止<code>vue</code>劫持我们的数据呢？可以通过<code>object.freeze</code>方法来冻结一个对象，一旦被冻结的对象就再也不能被修改了。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">  data: <span class="function"><span class="params">()</span> =&gt;</span> (&#123;</span><br><span class="line">    users: &#123;&#125;,</span><br><span class="line">  &#125;),</span><br><span class="line">  <span class="keyword">async</span> created() &#123;</span><br><span class="line">    <span class="keyword">const</span> users = <span class="keyword">await</span> axios.get(<span class="string">'/api/users'</span>)</span><br><span class="line">    <span class="keyword">this</span>.users = <span class="built_in">Object</span>.freeze(users)</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>另外需要说明的是，这里只是冻结了<code>users</code>的值，引用不会被冻结，当我们需要<code>reactive</code>数据的时候，我们可以重新给<code>users</code>赋值。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">  data: <span class="function"><span class="params">()</span> =&gt;</span> (&#123;</span><br><span class="line">    users: &#123;&#125;</span><br><span class="line">  &#125;),</span><br><span class="line">  <span class="keyword">async</span> created() &#123;</span><br><span class="line">    <span class="keyword">const</span> users = <span class="keyword">await</span> axios.get(<span class="string">"/api/users"</span>);</span><br><span class="line">    <span class="keyword">this</span>.users = <span class="built_in">Object</span>.freeze(users);</span><br><span class="line">  &#125;,</span><br><span class="line">  methods:&#123;</span><br><span class="line">    <span class="comment">// 改变值不会触发视图响应</span></span><br><span class="line">    <span class="keyword">this</span>.data.users[<span class="number">0</span>] = newValue</span><br><span class="line">    <span class="comment">// 改变引用依然会触发视图响应</span></span><br><span class="line">    <span class="keyword">this</span>.data.users = newArray</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h1 id="去除多余的样式"><a href="#去除多余的样式" class="headerlink" title="去除多余的样式"></a>去除多余的样式</h1><p>随着项目越来越大，书写的不注意，不自然的就会产生一些多余的 css，小项目还好，一旦项目大了以后，多余的 css 会越来越多，导致包越来越大，从而影响项目运行性能，所以有必要在正式环境去除掉这些多余的 css，这里推荐一个库<span class="exturl" data-url="aHR0cHM6Ly9saW5rLmp1ZWppbi5pbT90YXJnZXQ9aHR0cHMlM0ElMkYlMkZ3d3cucHVyZ2Vjc3MuY29tJTJG" title="https://link.juejin.im?target=https%3A%2F%2Fwww.purgecss.com%2F">purgecss<i class="fa fa-external-link"></i></span>，支持 CLI、JavascriptApi、Webpack 等多种方式使用，通过这个库，我们可以很容易的去除掉多余的 css。</p><p>我做了一个测试，<span class="exturl" data-url="aHR0cHM6Ly9saW5rLmp1ZWppbi5pbT90YXJnZXQ9aHR0cHMlM0ElMkYlMkZjb2Rlc2FuZGJveC5pbyUyRnMlMkZ6a3EyNThseTQ=" title="https://link.juejin.im?target=https%3A%2F%2Fcodesandbox.io%2Fs%2Fzkq258ly4">在线 DEMO<i class="fa fa-external-link"></i></span></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">h1</span>&gt;</span>Hello Vanilla!<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span>&gt;</span></span><br><span class="line">  We use Parcel to bundle this sandbox, you can find more info about Parcel</span><br><span class="line">  <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">"https://parceljs.org"</span> <span class="attr">target</span>=<span class="string">"_blank"</span> <span class="attr">rel</span>=<span class="string">"noopener noreferrer"</span>&gt;</span>here<span class="tag">&lt;/<span class="name">a</span>&gt;</span>.</span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">body</span> &#123;</span><br><span class="line">  <span class="attribute">font-family</span>: sans-serif;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-tag">a</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: red;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-tag">ul</span> &#123;</span><br><span class="line">  li &#123;</span><br><span class="line">    <span class="selector-tag">list-style</span>: <span class="selector-tag">none</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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">import</span> Purgecss <span class="keyword">from</span> <span class="string">'purgecss'</span></span><br><span class="line"><span class="keyword">const</span> purgecss = <span class="keyword">new</span> Purgecss(&#123;</span><br><span class="line">  content: [<span class="string">'**/*.html'</span>],</span><br><span class="line">  css: [<span class="string">'**/*.css'</span>],</span><br><span class="line">&#125;)</span><br><span class="line"><span class="keyword">const</span> purgecssResult = purgecss.purge()</span><br></pre></td></tr></table></figure><p>最终产生的<code>purgecssResult</code>结果如下，可以看到多余的<code>a</code>和<code>ul</code>标签的样式都没了</p><p><img src="https://user-gold-cdn.xitu.io/2019/5/21/16ad97db704d6639?imageslim" alt="img"></p><h1 id="作用域插槽"><a href="#作用域插槽" class="headerlink" title="作用域插槽"></a>作用域插槽</h1><p>利用好作用域插槽可以做一些很有意思的事情，比如定义一个基础布局组件 A，只负责布局，不管数据逻辑，然后另外定义一个组件 B 负责数据处理，布局组件 A 需要数据的时候就去 B 里面去取。假设，某一天我们的布局变了，我们只需要去修改组件 A 就行，而不用去修改组件 B，从而就能充分复用组件 B 的数据处理逻辑，关于这块我之前写过一篇实际案例，可以点击<span class="exturl" data-url="aHR0cHM6Ly9qdWVqaW4uaW0vcG9zdC81YzJkNzAzMGYyNjVkYTYxM2E1NDIzNmY=" title="https://juejin.im/post/5c2d7030f265da613a54236f">这里<i class="fa fa-external-link"></i></span>查看。</p><p>这里涉及到的一个最重要的点就是父组件要去获取子组件里面的数据，之前是利用<code>slot-scope</code>，自 vue 2.6.0 起，提供了更好的支持 <code>slot</code> 和 <code>slot-scope</code> 特性的 API 替代方案。</p><p>比如，我们定一个名为<code>current-user</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">span</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">slot</span>&gt;</span>&#123;&#123; user.lastName &#125;&#125;<span class="tag">&lt;/<span class="name">slot</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">span</span>&gt;</span></span><br></pre></td></tr></table></figure><p>父组件引用<code>current-user</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">current-user</span>&gt;</span></span><br><span class="line">  &#123;&#123; user.firstName &#125;&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">current-user</span>&gt;</span></span><br></pre></td></tr></table></figure><p>这种方式不会生效，因为<code>user</code>对象是子组件的数据，在父组件里面我们获取不到，这个时候我们就可以通过<code>v-slot</code> 来实现。</p><p>首先在子组件里面，将<code>user</code>作为一个<code>&lt;slot&gt;</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><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">span</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">slot</span> <span class="attr">v-bind:user</span>=<span class="string">"user"</span>&gt;</span></span><br><span class="line">    &#123;&#123; user.lastName &#125;&#125;</span><br><span class="line">  <span class="tag">&lt;/<span class="name">slot</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">span</span>&gt;</span></span><br></pre></td></tr></table></figure><p>之后，我们就可以在父组件引用的时候，给<code>v-slot</code>带一个值来定义我们提供的插槽 prop 的名字：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">current-user</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">template</span> <span class="attr">v-slot:default</span>=<span class="string">"slotProps"</span>&gt;</span></span><br><span class="line">    &#123;&#123; slotProps.user.firstName &#125;&#125;</span><br><span class="line">  <span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">current-user</span>&gt;</span></span><br></pre></td></tr></table></figure><p>这种方式还有缩写语法，可以查看<span class="exturl" data-url="aHR0cHM6Ly9saW5rLmp1ZWppbi5pbT90YXJnZXQ9aHR0cHMlM0ElMkYlMkZjbi52dWVqcy5vcmclMkZ2MiUyRmd1aWRlJTJGY29tcG9uZW50cy1zbG90cy5odG1sJTIzJUU3JThCJUFDJUU1JThEJUEwJUU5JUJCJTk4JUU4JUFFJUE0JUU2JThGJTkyJUU2JUE3JUJEJUU3JTlBJTg0JUU3JUJDJUE5JUU1JTg2JTk5JUU4JUFGJUFEJUU2JUIzJTk1" title="https://link.juejin.im?target=https%3A%2F%2Fcn.vuejs.org%2Fv2%2Fguide%2Fcomponents-slots.html%23%E7%8B%AC%E5%8D%A0%E9%BB%98%E8%AE%A4%E6%8F%92%E6%A7%BD%E7%9A%84%E7%BC%A9%E5%86%99%E8%AF%AD%E6%B3%95">独占默认插槽的缩写语法<i class="fa fa-external-link"></i></span>，最终我们引用的方式如下：</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">current-user</span> <span class="attr">v-slot</span>=<span class="string">"slotProps"</span>&gt;</span></span><br><span class="line">  &#123;&#123; slotProps.user.firstName &#125;&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">current-user</span>&gt;</span></span><br></pre></td></tr></table></figure><p>相比之前<code>slot-scope</code>代码更清晰，更好理解。</p><h1 id="属性事件传递"><a href="#属性事件传递" class="headerlink" title="属性事件传递"></a>属性事件传递</h1><p>写过高阶组件的童鞋可能都会碰到过将加工过的属性向下传递的情况，如果碰到属性较多时，需要一个个去传递，非常不友好并且费时，有没有一次性传递的呢（比如 react 里面的<code>{...this.props}</code>）？答案就是<code>v-bind</code>和<code>v-on</code>。</p><p>举个例子，假如有一个基础组件<code>BaseList</code>，只有基础的列表展示功能，现在我们想在这基础上增加排序功能，这个时候我们就可以创建一个高阶组件<code>SortList</code>。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">&lt;!-- SortList  --&gt;</span><br><span class="line">&lt;template&gt;</span><br><span class="line">  &lt;BaseList v-bind=&quot;$props&quot; v-on=&quot;$listeners&quot;&gt; &lt;!-- ... --&gt; &lt;/BaseList&gt;</span><br><span class="line">&lt;/template&gt;</span><br><span class="line"></span><br><span class="line">&lt;script&gt;</span><br><span class="line">import BaseList from &apos;./BaseList&apos;</span><br><span class="line">// 包含了基础的属性定义</span><br><span class="line">import BaseListMixin from &apos;./BaseListMixin&apos;</span><br><span class="line">// 封装了排序的逻辑</span><br><span class="line">import sort from &apos;./sort.js&apos;</span><br><span class="line"></span><br><span class="line">export default &#123;</span><br><span class="line">  props: BaseListMixin.props,</span><br><span class="line">  components: &#123;</span><br><span class="line">    BaseList,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;</span><br><span class="line">&lt;/script&gt;</span><br></pre></td></tr></table></figure><p>可以看到传递属性和事件的方便性，而不用一个个去传递</p><h1 id="函数式组件"><a href="#函数式组件" class="headerlink" title="函数式组件"></a>函数式组件</h1><p>函数式组件，即无状态，无法实例化，内部没有任何生命周期处理方法，非常轻量，因而渲染性能高，特别适合用来只依赖外部数据传递而变化的组件。</p><p>写法如下：</p><ol><li>在<code>template</code>标签里面标明<code>functional</code></li><li>只接受<code>props</code>值</li><li>不需要<code>script</code>标签</li></ol><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></pre></td><td class="code"><pre><span class="line">&lt;!-- App.vue --&gt;</span><br><span class="line">&lt;template&gt;</span><br><span class="line">  &lt;div id=&quot;app&quot;&gt;</span><br><span class="line">    &lt;List :items=&quot;[&apos;Wonderwoman&apos;, &apos;Ironman&apos;]&quot; :item-click=&quot;item =&gt; (clicked = item)&quot; /&gt;</span><br><span class="line">    &lt;p&gt;Clicked hero: &#123;&#123; clicked &#125;&#125;&lt;/p&gt;</span><br><span class="line">  &lt;/div&gt;</span><br><span class="line">&lt;/template&gt;</span><br><span class="line"></span><br><span class="line">&lt;script&gt;</span><br><span class="line">import List from &apos;./List&apos;</span><br><span class="line"></span><br><span class="line">export default &#123;</span><br><span class="line">  name: &apos;App&apos;,</span><br><span class="line">  data: () =&gt; (&#123; clicked: &apos;&apos; &#125;),</span><br><span class="line">  components: &#123; List &#125;,</span><br><span class="line">&#125;</span><br><span class="line">&lt;/script&gt;</span><br></pre></td></tr></table></figure><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">&lt;!-- List.vue 函数式组件 --&gt;</span><br><span class="line">&lt;template functional&gt;</span><br><span class="line">  &lt;div&gt;</span><br><span class="line">    &lt;p v-for=&quot;item in props.items&quot; @click=&quot;props.itemClick(item)&quot;&gt;</span><br><span class="line">      &#123;&#123; item &#125;&#125;</span><br><span class="line">    &lt;/p&gt;</span><br><span class="line">  &lt;/div&gt;</span><br><span class="line">&lt;/template&gt;</span><br></pre></td></tr></table></figure><h1 id="监听组件的生命周期"><a href="#监听组件的生命周期" class="headerlink" title="监听组件的生命周期"></a>监听组件的生命周期</h1><p>比如有父组件<code>Parent</code>和子组件<code>Child</code>，如果父组件监听到子组件挂载<code>mounted</code>就做一些逻辑处理，常规的写法可能如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">// Parent.vue</span><br><span class="line">&lt;Child @mounted=&quot;doSomething&quot; /&gt;</span><br><span class="line"></span><br><span class="line">// Child.vue mounted() &#123; this.$emit(&quot;mounted&quot;); &#125;</span><br></pre></td></tr></table></figure><p>这里提供一种特别简单的方式，子组件不需要任何处理，只需要在父组件引用的时候通过<code>@hook</code>来监听即可，代码重写如下：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">Child</span> @<span class="attr">hook:mounted</span>=<span class="string">"doSomething"</span> /&gt;</span></span><br></pre></td></tr></table></figure><p>当然这里不仅仅是可以监听<code>mounted</code>，其它的生命周期事件，例如：<code>created</code>，<code>updated</code>等都可以，是不是特别方便~</p><h1 id="参考链接："><a href="#参考链接：" class="headerlink" title="参考链接："></a>参考链接：</h1><ul><li><span class="exturl" data-url="aHR0cHM6Ly9saW5rLmp1ZWppbi5pbT90YXJnZXQ9aHR0cHMlM0ElMkYlMkZ2dWVkb3NlLnRpcHMlMkZ0aXBz" title="https://link.juejin.im?target=https%3A%2F%2Fvuedose.tips%2Ftips">vueTips<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9saW5rLmp1ZWppbi5pbT90YXJnZXQ9aHR0cHMlM0ElMkYlMkZhbGxpZ2F0b3IuaW8lMkZ2dWVqcyUyRg==" title="https://link.juejin.im?target=https%3A%2F%2Falligator.io%2Fvuejs%2F">vuePost<i class="fa fa-external-link"></i></span></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;!-- @import &quot;[TOC]&quot; {cmd=&quot;toc&quot; depthFrom=1 depthTo=6 orderedList=false} --&gt;
&lt;!-- code_chunk_output --&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;#状态共享&quot;&gt;状态共享&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#长列表性能优化&quot;&gt;长列表性能优化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#去除多余的样式&quot;&gt;去除多余的样式&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#作用域插槽&quot;&gt;作用域插槽&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#属性事件传递&quot;&gt;属性事件传递&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#函数式组件&quot;&gt;函数式组件&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#监听组件的生命周期&quot;&gt;监听组件的生命周期&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#参考链接&quot;&gt;参考链接：&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;!-- /code_chunk_output --&gt;
    
    </summary>
    
      <category term="前端" scheme="https://xuebin.me/categories/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="vue" scheme="https://xuebin.me/categories/%E5%89%8D%E7%AB%AF/vue/"/>
    
    
      <category term="dev" scheme="https://xuebin.me/tags/dev/"/>
    
      <category term="前端" scheme="https://xuebin.me/tags/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="开发" scheme="https://xuebin.me/tags/%E5%BC%80%E5%8F%91/"/>
    
      <category term="vue" scheme="https://xuebin.me/tags/vue/"/>
    
      <category term="转载" scheme="https://xuebin.me/tags/%E8%BD%AC%E8%BD%BD/"/>
    
  </entry>
  
  <entry>
    <title>JS技巧</title>
    <link href="https://xuebin.me/posts/554f088e.html"/>
    <id>https://xuebin.me/posts/554f088e.html</id>
    <published>2019-05-08T15:00:09.000Z</published>
    <updated>2019-05-08T15:00:09.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><!-- @import "[TOC]" {cmd="toc" depthFrom=1 depthTo=6 orderedList=false} --><!-- code_chunk_output --><ol><li><a href="#字符串">字符串</a><ol><li><a href="#格式化金钱">格式化金钱</a></li><li><a href="#生成随机id">生成随机ID</a></li><li><a href="#生成随机hex色值">生成随机HEX色值</a></li><li><a href="#生成星级评分">生成星级评分</a></li><li><a href="#操作url查询参数">操作URL查询参数</a></li></ol></li><li><a href="#数值">数值</a><ol><li><a href="#取整代替正数的mathfloor代替负数的mathceil">取整：代替正数的<code>Math.floor()</code>，代替负数的<code>Math.ceil()</code></a></li><li><a href="#补零">补零</a></li><li><a href="#转数值只对nullfalse数值字符串有效">转数值：只对<code>null、&quot;&quot;、false、数值字符串</code>有效</a></li><li><a href="#时间戳">时间戳</a></li><li><a href="#精确小数">精确小数</a></li><li><a href="#判断奇偶">判断奇偶</a></li><li><a href="#取最小最大值">取最小最大值</a></li></ol></li><li><a href="#函数">函数</a><ol><li><a href="#一次性函数适用于运行一些只需执行一次的初始化代码">一次性函数：<code>适用于运行一些只需执行一次的初始化代码</code></a></li><li><a href="#惰性载入函数函数内判断分支较多较复杂时可大大节约资源开销">惰性载入函数：<code>函数内判断分支较多较复杂时可大大节约资源开销</code></a></li><li><a href="#优雅处理asyncawait参数">优雅处理Async/Await参数</a></li></ol></li><li><a href="#dom">DOM</a><ol><li><a href="#显示全部dom边框调试页面元素边界时使用">显示全部DOM边框：<code>调试页面元素边界时使用</code></a></li><li><a href="#自适应页面页面基于一张设计图但需做多款机型自适应元素尺寸使用rem进行设置">自适应页面：<code>页面基于一张设计图但需做多款机型自适应，元素尺寸使用rem进行设置</code></a></li><li><a href="#阻止冒泡">阻止冒泡</a></li><li><a href="#阻止默认行为">阻止默认行为</a></li></ol></li></ol><!-- /code_chunk_output --><a id="more"></a><h1 id="字符串"><a href="#字符串" class="headerlink" title="字符串"></a>字符串</h1><h2 id="格式化金钱"><a href="#格式化金钱" class="headerlink" title="格式化金钱"></a>格式化金钱</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> thousandNum = <span class="function"><span class="params">num</span> =&gt;</span> num.toString().replace(<span class="regexp">/\B(?=(\d&#123;3&#125;)+(?!\d))/g</span>, <span class="string">","</span>);</span><br><span class="line"><span class="keyword">const</span> money = thousandNum(<span class="number">19941112</span>);</span><br><span class="line"><span class="comment">// money =&gt; "19,941,112"</span></span><br></pre></td></tr></table></figure><h2 id="生成随机ID"><a href="#生成随机ID" class="headerlink" title="生成随机ID"></a>生成随机ID</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> randomId = <span class="function"><span class="params">len</span> =&gt;</span> <span class="built_in">Math</span>.random().toString(<span class="number">36</span>).substr(<span class="number">3</span>, len);</span><br><span class="line"><span class="keyword">const</span> id = randomId(<span class="number">10</span>);</span><br><span class="line"><span class="comment">// id =&gt; "jg7zpgiqva"</span></span><br></pre></td></tr></table></figure><h2 id="生成随机HEX色值"><a href="#生成随机HEX色值" class="headerlink" title="生成随机HEX色值"></a>生成随机HEX色值</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> randomColor = <span class="function"><span class="params">()</span> =&gt;</span> <span class="string">"##"</span> + <span class="built_in">Math</span>.floor(<span class="built_in">Math</span>.random() * <span class="number">0xffffff</span>).toString(<span class="number">16</span>).padEnd(<span class="number">6</span>, <span class="string">"0"</span>);</span><br><span class="line"><span class="keyword">const</span> color = randomColor();</span><br><span class="line"><span class="comment">// color =&gt; "##f03665"</span></span><br></pre></td></tr></table></figure><h2 id="生成星级评分"><a href="#生成星级评分" class="headerlink" title="生成星级评分"></a>生成星级评分</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> startScore = <span class="function"><span class="params">rate</span> =&gt;</span> <span class="string">"★★★★★☆☆☆☆☆"</span>.slice(<span class="number">5</span> - rate, <span class="number">10</span> - rate);</span><br><span class="line"><span class="keyword">const</span> start = startScore(<span class="number">3</span>);</span><br><span class="line"><span class="comment">// start =&gt; "★★★"</span></span><br></pre></td></tr></table></figure><h2 id="操作URL查询参数"><a href="#操作URL查询参数" class="headerlink" title="操作URL查询参数"></a>操作URL查询参数</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// https://developer.mozilla.org/zh-CN/docs/Web/API/URLSearchParams</span></span><br><span class="line"><span class="keyword">const</span> params = <span class="keyword">new</span> URLSearchParams(location.search.replace(<span class="regexp">/\?/ig</span>, <span class="string">""</span>)); </span><br><span class="line"><span class="comment">// location.search = "?name=yajun&amp;sex=female"</span></span><br><span class="line">params.has(<span class="string">"yajun"</span>); <span class="comment">// true</span></span><br><span class="line">params.get(<span class="string">"sex"</span>); <span class="comment">// "female"</span></span><br></pre></td></tr></table></figure><h1 id="数值"><a href="#数值" class="headerlink" title="数值"></a>数值</h1><h2 id="取整：代替正数的Math-floor-，代替负数的Math-ceil"><a href="#取整：代替正数的Math-floor-，代替负数的Math-ceil" class="headerlink" title="取整：代替正数的Math.floor()，代替负数的Math.ceil()"></a>取整：代替正数的<code>Math.floor()</code>，代替负数的<code>Math.ceil()</code></h2><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> num1 = ~~ <span class="number">1.69</span>;</span><br><span class="line"><span class="keyword">const</span> num2 = <span class="number">1.69</span> | <span class="number">0</span>;</span><br><span class="line"><span class="keyword">const</span> num3 = <span class="number">1.69</span> &gt;&gt; <span class="number">0</span>;</span><br><span class="line"><span class="comment">// num1 num2 num3 =&gt; 1 1 1</span></span><br></pre></td></tr></table></figure><h2 id="补零"><a href="#补零" class="headerlink" title="补零"></a>补零</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> fillZero = <span class="function">(<span class="params">num, len</span>) =&gt;</span> num.toString().padStart(len, <span class="string">"0"</span>);</span><br><span class="line"><span class="keyword">const</span> num = fillZero(<span class="number">169</span>, <span class="number">5</span>);</span><br><span class="line"><span class="comment">// num =&gt; "00169"</span></span><br></pre></td></tr></table></figure><h2 id="转数值：只对null、-quot-quot-、false、数值字符串有效"><a href="#转数值：只对null、-quot-quot-、false、数值字符串有效" class="headerlink" title="转数值：只对null、&quot;&quot;、false、数值字符串有效"></a>转数值：只对<code>null、&quot;&quot;、false、数值字符串</code>有效</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> num1 = +<span class="literal">null</span>;</span><br><span class="line"><span class="keyword">const</span> num2 = +<span class="string">""</span>;</span><br><span class="line"><span class="keyword">const</span> num3 = +<span class="literal">false</span>;</span><br><span class="line"><span class="keyword">const</span> num4 = +<span class="string">"169"</span>;</span><br><span class="line"><span class="comment">// num1 num2 num3 num4 =&gt; 0 0 0 169</span></span><br></pre></td></tr></table></figure><h2 id="时间戳"><a href="#时间戳" class="headerlink" title="时间戳"></a>时间戳</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> timestamp = +<span class="keyword">new</span> <span class="built_in">Date</span>(<span class="string">"2019-03-31"</span>);</span><br><span class="line"><span class="comment">// timestamp =&gt; 1553990400000</span></span><br></pre></td></tr></table></figure><h2 id="精确小数"><a href="#精确小数" class="headerlink" title="精确小数"></a>精确小数</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> roundNum = <span class="function">(<span class="params">num, decimal</span>) =&gt;</span> <span class="built_in">Math</span>.round(num * <span class="number">10</span> ** decimal) / <span class="number">10</span> ** decimal;</span><br><span class="line"><span class="keyword">const</span> num = roundNum(<span class="number">1.69</span>, <span class="number">1</span>);</span><br><span class="line"><span class="comment">// num =&gt; 1.7</span></span><br></pre></td></tr></table></figure><h2 id="判断奇偶"><a href="#判断奇偶" class="headerlink" title="判断奇偶"></a>判断奇偶</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> num = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">const</span> odd = !!(num &amp; <span class="number">1</span>);</span><br><span class="line"><span class="comment">// odd =&gt; false</span></span><br></pre></td></tr></table></figure><h2 id="取最小最大值"><a href="#取最小最大值" class="headerlink" title="取最小最大值"></a>取最小最大值</h2><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> arr = [<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>];</span><br><span class="line"><span class="keyword">const</span> min = <span class="built_in">Math</span>.min(...arr);</span><br><span class="line"><span class="keyword">const</span> max = <span class="built_in">Math</span>.max(...arr);</span><br><span class="line"><span class="comment">// min max =&gt; 0 2</span></span><br></pre></td></tr></table></figure><h1 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h1><h2 id="一次性函数：适用于运行一些只需执行一次的初始化代码"><a href="#一次性函数：适用于运行一些只需执行一次的初始化代码" class="headerlink" title="一次性函数：适用于运行一些只需执行一次的初始化代码"></a>一次性函数：<code>适用于运行一些只需执行一次的初始化代码</code></h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Func</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">"x"</span>);</span><br><span class="line">    Func = <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">"y"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="惰性载入函数：函数内判断分支较多较复杂时可大大节约资源开销"><a href="#惰性载入函数：函数内判断分支较多较复杂时可大大节约资源开销" class="headerlink" title="惰性载入函数：函数内判断分支较多较复杂时可大大节约资源开销"></a>惰性载入函数：<code>函数内判断分支较多较复杂时可大大节约资源开销</code></h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Func</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (a === b) &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">"x"</span>);</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">"y"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 换成</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Func</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (a === b) &#123;</span><br><span class="line">        Func = <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">"x"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        Func = <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">"y"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> Func();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="优雅处理Async-Await参数"><a href="#优雅处理Async-Await参数" class="headerlink" title="优雅处理Async/Await参数"></a>优雅处理Async/Await参数</h2><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">AsyncTo</span>(<span class="params">promise</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> promise.then(<span class="function"><span class="params">data</span> =&gt;</span> [<span class="literal">null</span>, data]).catch(<span class="function"><span class="params">err</span> =&gt;</span> [err]);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">const</span> [err, res] = <span class="keyword">await</span> AsyncTo(Func());</span><br></pre></td></tr></table></figure><h1 id="DOM"><a href="#DOM" class="headerlink" title="DOM"></a>DOM</h1><h2 id="显示全部DOM边框：调试页面元素边界时使用"><a href="#显示全部DOM边框：调试页面元素边界时使用" class="headerlink" title="显示全部DOM边框：调试页面元素边界时使用"></a>显示全部DOM边框：<code>调试页面元素边界时使用</code></h2><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">[].forEach.call($$(<span class="string">"*"</span>), dom =&gt; &#123;</span><br><span class="line">    dom.style.outline = <span class="string">"1px solid ##"</span> + (~~(<span class="built_in">Math</span>.random() * (<span class="number">1</span> &lt;&lt; <span class="number">24</span>))).toString(<span class="number">16</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h2 id="自适应页面：页面基于一张设计图但需做多款机型自适应，元素尺寸使用rem进行设置"><a href="#自适应页面：页面基于一张设计图但需做多款机型自适应，元素尺寸使用rem进行设置" class="headerlink" title="自适应页面：页面基于一张设计图但需做多款机型自适应，元素尺寸使用rem进行设置"></a>自适应页面：<code>页面基于一张设计图但需做多款机型自适应，元素尺寸使用rem进行设置</code></h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">AutoResponse</span>(<span class="params">width = <span class="number">750</span></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> target = <span class="built_in">document</span>.documentElement;</span><br><span class="line">    target.clientWidth &gt;= <span class="number">600</span></span><br><span class="line">        ? (target.style.fontSize = <span class="string">"80px"</span>)</span><br><span class="line">        : (target.style.fontSize = target.clientWidth / width * <span class="number">100</span> + <span class="string">"px"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="阻止冒泡"><a href="#阻止冒泡" class="headerlink" title="阻止冒泡"></a>阻止冒泡</h2><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="function"><span class="keyword">function</span> <span class="title">stopBubble</span>(<span class="params">e</span>) </span>&#123; </span><br><span class="line">    <span class="comment">//如果提供了事件对象，则这是一个非IE浏览器 </span></span><br><span class="line">    <span class="keyword">if</span> ( e &amp;&amp; e.stopPropagation ) </span><br><span class="line">        <span class="comment">//因此它支持W3C的stopPropagation()方法 </span></span><br><span class="line">        e.stopPropagation(); </span><br><span class="line">    <span class="keyword">else</span> </span><br><span class="line">        <span class="comment">//否则，我们需要使用IE的方式来取消事件冒泡 </span></span><br><span class="line">        <span class="built_in">window</span>.event.cancelBubble = <span class="literal">true</span>; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="阻止默认行为"><a href="#阻止默认行为" class="headerlink" title="阻止默认行为"></a>阻止默认行为</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//阻止浏览器的默认行为 </span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">stopDefault</span>(<span class="params"> e </span>) </span>&#123; </span><br><span class="line">    <span class="comment">//阻止默认浏览器动作(W3C) </span></span><br><span class="line">    <span class="keyword">if</span> ( e &amp;&amp; e.preventDefault ) </span><br><span class="line">        e.preventDefault(); </span><br><span class="line">    <span class="comment">//IE中阻止函数器默认动作的方式 </span></span><br><span class="line">    <span class="keyword">else</span> </span><br><span class="line">        <span class="built_in">window</span>.event.returnValue = <span class="literal">false</span>; </span><br><span class="line">        </span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;!-- @import &quot;[TOC]&quot; {cmd=&quot;toc&quot; depthFrom=1 depthTo=6 orderedList=false} --&gt;
&lt;!-- code_chunk_output --&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;#字符串&quot;&gt;字符串&lt;/a&gt;&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;#格式化金钱&quot;&gt;格式化金钱&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#生成随机id&quot;&gt;生成随机ID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#生成随机hex色值&quot;&gt;生成随机HEX色值&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#生成星级评分&quot;&gt;生成星级评分&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#操作url查询参数&quot;&gt;操作URL查询参数&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#数值&quot;&gt;数值&lt;/a&gt;&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;#取整代替正数的mathfloor代替负数的mathceil&quot;&gt;取整：代替正数的&lt;code&gt;Math.floor()&lt;/code&gt;，代替负数的&lt;code&gt;Math.ceil()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#补零&quot;&gt;补零&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#转数值只对nullfalse数值字符串有效&quot;&gt;转数值：只对&lt;code&gt;null、&amp;quot;&amp;quot;、false、数值字符串&lt;/code&gt;有效&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#时间戳&quot;&gt;时间戳&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#精确小数&quot;&gt;精确小数&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#判断奇偶&quot;&gt;判断奇偶&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#取最小最大值&quot;&gt;取最小最大值&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#函数&quot;&gt;函数&lt;/a&gt;&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;#一次性函数适用于运行一些只需执行一次的初始化代码&quot;&gt;一次性函数：&lt;code&gt;适用于运行一些只需执行一次的初始化代码&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#惰性载入函数函数内判断分支较多较复杂时可大大节约资源开销&quot;&gt;惰性载入函数：&lt;code&gt;函数内判断分支较多较复杂时可大大节约资源开销&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#优雅处理asyncawait参数&quot;&gt;优雅处理Async/Await参数&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#dom&quot;&gt;DOM&lt;/a&gt;&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;#显示全部dom边框调试页面元素边界时使用&quot;&gt;显示全部DOM边框：&lt;code&gt;调试页面元素边界时使用&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#自适应页面页面基于一张设计图但需做多款机型自适应元素尺寸使用rem进行设置&quot;&gt;自适应页面：&lt;code&gt;页面基于一张设计图但需做多款机型自适应，元素尺寸使用rem进行设置&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#阻止冒泡&quot;&gt;阻止冒泡&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#阻止默认行为&quot;&gt;阻止默认行为&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;!-- /code_chunk_output --&gt;
    
    </summary>
    
      <category term="代码片段" scheme="https://xuebin.me/categories/%E4%BB%A3%E7%A0%81%E7%89%87%E6%AE%B5/"/>
    
      <category term="javascript" scheme="https://xuebin.me/categories/%E4%BB%A3%E7%A0%81%E7%89%87%E6%AE%B5/javascript/"/>
    
    
      <category term="dev" scheme="https://xuebin.me/tags/dev/"/>
    
      <category term="代码片段" scheme="https://xuebin.me/tags/%E4%BB%A3%E7%A0%81%E7%89%87%E6%AE%B5/"/>
    
      <category term="javascript" scheme="https://xuebin.me/tags/javascript/"/>
    
      <category term="技巧" scheme="https://xuebin.me/tags/%E6%8A%80%E5%B7%A7/"/>
    
  </entry>
  
  <entry>
    <title>vue ssr 服务器端渲染</title>
    <link href="https://xuebin.me/posts/fb1b66be.html"/>
    <id>https://xuebin.me/posts/fb1b66be.html</id>
    <published>2019-03-29T14:05:37.000Z</published>
    <updated>2019-03-29T14:05:37.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><h1 id="ssr是什么"><a href="#ssr是什么" class="headerlink" title="ssr是什么"></a>ssr是什么</h1><p>简单来讲，ssr是指vue中的服务端渲染</p><p>默认vue开发打包生成是普通的客户端渲染，将vue打包成js然后在html中引入进而渲染DOM和操作DOM。而服务端渲染是将vue渲染成html后再发送至客户端，中间会有一系列匹配工作</p><h1 id="为什么用ssr"><a href="#为什么用ssr" class="headerlink" title="为什么用ssr"></a>为什么用ssr</h1><p>截两张图你就明白了</p><p>默认方式源代码:</p><p><img src="https://i.loli.net/2019/05/08/5cd23ad12ba7e.jpg" alt></p><p>ssr方式源代码:</p><p><img src="https://i.loli.net/2019/05/08/5cd23ad21b239.jpg" alt></p><p>也就是说使用默认方式，当在搜索引擎搜索你的站点关键词时可能就搜不到你的站点，这对一些需要被erveryone熟知的站点是致命的。</p><p>除此之外服务端渲染也能更好的解决浏览器兼容的问题，并且在性能上也帮客户端做了很多事情</p><a id="more"></a><h1 id="如何开发ssr项目"><a href="#如何开发ssr项目" class="headerlink" title="如何开发ssr项目"></a>如何开发ssr项目</h1><p>两种方式，自己搭建或者使用官方框架<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL251eHQvbnV4dC5qcw==" title="https://github.com/nuxt/nuxt.js">Nuxt.js<i class="fa fa-external-link"></i></span>，我当时是自己搭的，参考了官方示例<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3Z1ZWpzL3Z1ZS1oYWNrZXJuZXdzLTIuMA==" title="https://github.com/vuejs/vue-hackernews-2.0">vue-hackernews-2.0<i class="fa fa-external-link"></i></span>，基本方式都是相同的，打包出对应route的bundle，与template合并，生成html string，展示</p><h1 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h1><h2 id="客户端展示异常，服务端报错-window-alert-document-is-undefined"><a href="#客户端展示异常，服务端报错-window-alert-document-is-undefined" class="headerlink" title="客户端展示异常，服务端报错 window/alert/document is undefined"></a>客户端展示异常，服务端报错 <code>window/alert/document is undefined</code></h2><h3 id="jsdom"><a href="#jsdom" class="headerlink" title="jsdom"></a>jsdom</h3><p>服务端没有<code>window/alert/document</code>这种东西，需要自行定义，建议方式引入第三方包<code>jsdom</code>辅助定义</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//https://github.com/vuejs/vue-hackernews-2.0/issues/52#issuecomment-255594303</span></span><br><span class="line"><span class="keyword">const</span> &#123; JSDOM &#125; = <span class="built_in">require</span>(<span class="string">'jsdom'</span>)</span><br><span class="line"><span class="keyword">const</span> dom = <span class="keyword">new</span> JSDOM(<span class="string">'&lt;!doctype html&gt;&lt;html&gt;&lt;body&gt;&lt;/body&gt;&lt;/html&gt;'</span>,</span><br><span class="line">&#123; <span class="attr">url</span>: <span class="string">'http://localhost'</span> &#125;)</span><br><span class="line"></span><br><span class="line">global.window = dom.window</span><br><span class="line">global.document = <span class="built_in">window</span>.document</span><br><span class="line">global.navigator = <span class="built_in">window</span>.navigator</span><br></pre></td></tr></table></figure><h3 id="官方解决方案"><a href="#官方解决方案" class="headerlink" title="官方解决方案"></a><span class="exturl" data-url="aHR0cHM6Ly96aC5udXh0anMub3JnL2ZhcS93aW5kb3ctZG9jdW1lbnQtdW5kZWZpbmVkLw==" title="https://zh.nuxtjs.org/faq/window-document-undefined/">官方解决方案<i class="fa fa-external-link"></i></span></h3><p><img src="https://i.loli.net/2019/05/08/5cd23ad316b29.jpg" alt="官方解决方案"></p><h3 id="组件二次封装"><a href="#组件二次封装" class="headerlink" title="组件二次封装"></a>组件二次封装</h3><p>创建<code>~/plugins/index.js</code>文件，将组件挂载到全局</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> Vue <span class="keyword">from</span> <span class="string">'vue'</span></span><br><span class="line"><span class="keyword">import</span> MyComponent <span class="keyword">from</span> <span class="string">'~/components/MyComponent'</span></span><br><span class="line"></span><br><span class="line">Vue.component(<span class="string">'MyComponent'</span>, MyComponent)</span><br></pre></td></tr></table></figure><p>然后根据运行环境加载组件</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(<span class="keyword">typeof</span> <span class="built_in">window</span> !== <span class="string">'undefined'</span>)&#123;</span><br><span class="line"><span class="built_in">require</span>(<span class="string">'~/plugins/index.js'</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><ul><li><span class="exturl" data-url="aHR0cHM6Ly9qdWVqaW4uaW0vcG9zdC81YjdlMjZmZWU1MWQ0NTM4ZDEyYzg4MzY=" title="https://juejin.im/post/5b7e26fee51d4538d12c8836">https://juejin.im/post/5b7e26fee51d4538d12c8836<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9taXlhbGVlLmdpdGh1Yi5pby8yMDE4LzAxLzAzL2Jsb2cyMDE4LTAxLTAzLw==" title="https://miyalee.github.io/2018/01/03/blog2018-01-03/">https://miyalee.github.io/2018/01/03/blog2018-01-03/<i class="fa fa-external-link"></i></span></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;ssr是什么&quot;&gt;&lt;a href=&quot;#ssr是什么&quot; class=&quot;headerlink&quot; title=&quot;ssr是什么&quot;&gt;&lt;/a&gt;ssr是什么&lt;/h1&gt;&lt;p&gt;简单来讲，ssr是指vue中的服务端渲染&lt;/p&gt;
&lt;p&gt;默认vue开发打包生成是普通的客户端渲染，将vue打包成js然后在html中引入进而渲染DOM和操作DOM。而服务端渲染是将vue渲染成html后再发送至客户端，中间会有一系列匹配工作&lt;/p&gt;
&lt;h1 id=&quot;为什么用ssr&quot;&gt;&lt;a href=&quot;#为什么用ssr&quot; class=&quot;headerlink&quot; title=&quot;为什么用ssr&quot;&gt;&lt;/a&gt;为什么用ssr&lt;/h1&gt;&lt;p&gt;截两张图你就明白了&lt;/p&gt;
&lt;p&gt;默认方式源代码:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/05/08/5cd23ad12ba7e.jpg&quot; alt&gt;&lt;/p&gt;
&lt;p&gt;ssr方式源代码:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/05/08/5cd23ad21b239.jpg&quot; alt&gt;&lt;/p&gt;
&lt;p&gt;也就是说使用默认方式，当在搜索引擎搜索你的站点关键词时可能就搜不到你的站点，这对一些需要被erveryone熟知的站点是致命的。&lt;/p&gt;
&lt;p&gt;除此之外服务端渲染也能更好的解决浏览器兼容的问题，并且在性能上也帮客户端做了很多事情&lt;/p&gt;
    
    </summary>
    
      <category term="前端" scheme="https://xuebin.me/categories/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="vue" scheme="https://xuebin.me/categories/%E5%89%8D%E7%AB%AF/vue/"/>
    
    
      <category term="dev" scheme="https://xuebin.me/tags/dev/"/>
    
      <category term="前端" scheme="https://xuebin.me/tags/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="开发" scheme="https://xuebin.me/tags/%E5%BC%80%E5%8F%91/"/>
    
      <category term="ssr" scheme="https://xuebin.me/tags/ssr/"/>
    
  </entry>
  
  <entry>
    <title>vue开发项目完全指南</title>
    <link href="https://xuebin.me/posts/65e66eaf.html"/>
    <id>https://xuebin.me/posts/65e66eaf.html</id>
    <published>2019-03-23T02:27:16.000Z</published>
    <updated>2019-03-23T02:27:16.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><blockquote class="blockquote-center"><p>转载自: <span class="exturl" data-url="aHR0cHM6Ly9zZWdtZW50ZmF1bHQuY29tL2EvMTE5MDAwMDAxODYxOTU3Ng==" title="https://segmentfault.com/a/1190000018619576">https://segmentfault.com/a/1190000018619576<i class="fa fa-external-link"></i></span></p></blockquote><p>这篇文章总结了vue项目的所遇到的问题，包括跨域、用户认证、接口统一管理、路由配置、兼容性处理，性能优化等内容。</p><p>项目github地址 :</p><ul><li>前端 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2h1YW5neWFuZ3RlbmcvdnVlMi10ZW1wbGF0ZQ==" title="https://github.com/huangyangteng/vue2-template">https://github.com/huangyangt…<i class="fa fa-external-link"></i></span></li><li>后端: <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2h1YW5neWFuZ3RlbmcvdnVlMi10ZW1wbGF0ZS1zZXJ2ZXI=" title="https://github.com/huangyangteng/vue2-template-server">https://github.com/huangyangt…<i class="fa fa-external-link"></i></span></li></ul><p><img src="https://i.loli.net/2019/05/08/5cd23b0ccd527.jpg" alt></p><a id="more"></a><!-- @import "[TOC]" {cmd="toc" depthFrom=1 depthTo=6 orderedList=false} --><!-- code_chunk_output --><ol><li><a href="#一环境依赖安装">一、环境依赖安装</a><ol><li><a href="#node环境">node环境</a><ol><li><a href="#node和npm环境的安装">node和npm环境的安装</a></li><li><a href="#为npm更改源">为npm更改源</a><ol><li><a href="#使用cnpm代替npm">使用cnpm代替npm</a></li><li><a href="#为npm更换源">为npm更换源</a></li></ol></li><li><a href="#管理更新nodejs的版本">管理(更新)nodejs的版本</a><ol><li><a href="#使用n管理nodejs版本">使用n管理nodejs版本</a></li></ol></li><li><a href="#packagejson文件详解">package.json文件详解</a></li></ol></li><li><a href="#vue脚手架">vue脚手架</a><ol><li><a href="#vue-cli2x安装">vue-cli2.x安装</a></li><li><a href="#vue-cli3x安装及配置仅供参考">vue-cli3.x安装及配置(仅供参考)</a></li></ol></li></ol></li><li><a href="#二开发">二、开发</a><ol><li><a href="#前后端交互报文定义以及数据api接口">前后端交互报文定义以及数据api接口</a><ol><li><a href="#前后端交互报文定义"><strong>前后端交互报文定义</strong></a></li><li><a href="#项目中使用的后台api定义如下">项目中使用的后台api定义如下</a><ol><li><a href="#测试api是否可用"><code>测试api是否可用</code></a></li><li><a href="#注册"><code>注册</code></a></li><li><a href="#登录"><code>登录</code></a></li><li><a href="#获取当前用户信息"><code>获取当前用户信息</code></a></li></ol></li></ol></li><li><a href="#项目文件介绍">项目文件介绍</a><ol><li><a href="#整个文件介绍httpsilolinet201905085cd23b0f68ccejpg">整个文件介绍：<img src="https://i.loli.net/2019/05/08/5cd23b0f68cce.jpg" alt></a></li><li><a href="#src目录介绍">src目录介绍</a></li></ol></li><li><a href="#跨域axios配置与api管理">跨域、axios配置与api管理</a><ol><li><a href="#配置跨域">配置跨域</a></li><li><a href="#生产环境去除apis前缀">生产环境去除/apis前缀</a></li><li><a href="#api统一管理">api统一管理</a></li></ol></li><li><a href="#路由配置">路由配置</a><ol><li><a href="#最简配置">最简配置</a></li><li><a href="#配置路由懒加载">配置路由懒加载</a></li><li><a href="#配置history模式">配置history模式</a></li></ol></li><li><a href="#权限管理">权限管理</a><ol><li><a href="#token验证">token验证</a></li><li><a href="#对页面的访问权限">对页面的访问权限</a></li></ol></li><li><a href="#将界面交给第三方ui库">将界面交给第三方UI库</a><ol><li><a href="#iview的安装与引入">iview的安装与引入</a><ol><li><a href="#安装">安装</a></li><li><a href="#按需引入组件">按需引入组件</a></li><li><a href="#自定义主题">自定义主题</a></li></ol></li></ol></li><li><a href="#兼容性处理">兼容性处理</a><ol><li><a href="#对ie8及以下浏览器的跳转处理">对ie8及以下浏览器的跳转处理</a></li><li><a href="#兼容ie9">兼容ie9</a><ol><li><a href="#es6兼容">ES6兼容</a></li></ol></li></ol></li></ol></li><li><a href="#三优化">三、优化</a><ol><li><a href="#webpack3x优化打包速度">webpack3.x优化打包速度</a><ol><li><a href="#替换代码压缩工具">替换代码压缩工具</a></li><li><a href="#happypack开启多核构建项目">happypack开启多核构建项目</a></li><li><a href="#hardsourcewebpackplugin节省70的时间">hardSourceWebpackPlugin节省70%的时间</a></li></ol></li><li><a href="#webpack3x优化首屏加载速度">webpack3.x优化首屏加载速度</a><ol><li><a href="#maclinux安装">mac/linux安装</a><ol><li><a href="#nginx常用命令">nginx常用命令</a></li><li><a href="#nginx配置静态文件服务器">nginx配置静态文件服务器</a></li><li><a href="#优化首屏加载速度">优化首屏加载速度</a></li></ol></li><li><a href="#预渲染">预渲染</a><ol><li><a href="#预渲染注意事项">预渲染注意事项</a></li></ol></li><li><a href="#配置gzip压缩">配置gzip压缩</a></li></ol></li></ol></li><li><a href="#四部署">四、部署</a><ol><li><a href="#nginx配置反向代理">nginx配置反向代理</a></li><li><a href="#持续部署">持续部署</a><ol><li><a href="#jenkins安装与启动">jenkins安装与启动</a></li><li><a href="#jenkins初始化">jenkins初始化</a></li><li><a href="#创建任务">创建任务</a></li><li><a href="#安装插件">安装插件</a></li><li><a href="#实现git钩子功能">实现git钩子功能</a></li><li><a href="#实现自动化构建">实现自动化构建</a></li></ol></li></ol></li></ol><!-- /code_chunk_output --><h1 id="一、环境依赖安装"><a href="#一、环境依赖安装" class="headerlink" title="一、环境依赖安装"></a>一、环境依赖安装</h1><h2 id="node环境"><a href="#node环境" class="headerlink" title="node环境"></a>node环境</h2><h3 id="node和npm环境的安装"><a href="#node和npm环境的安装" class="headerlink" title="node和npm环境的安装"></a>node和npm环境的安装</h3><blockquote><p>根据以下教程安装，然后设置好环境变量</p></blockquote><p><span class="exturl" data-url="aHR0cDovL3d3dy5ydW5vb2IuY29tL25vZGVqcy9ub2RlanMtaW5zdGFsbC1zZXR1cC5odG1s" title="http://www.runoob.com/nodejs/nodejs-install-setup.html">http://www.runoob.com/nodejs/…<i class="fa fa-external-link"></i></span></p><p>视频教程 <span class="exturl" data-url="aHR0cDovL2dpdGh1Yi5saWFveHVlZmVuZy5jb20vc2luYXdlaWJvcHkvdmlkZW8vbm9kZS9pbnN0YWxsLW5vZGUubXA0" title="http://github.liaoxuefeng.com/sinaweibopy/video/node/install-node.mp4">http://github.liaoxuefeng.com….<i class="fa fa-external-link"></i></span></p><p>centos如果装不上看这里：<span class="exturl" data-url="aHR0cHM6Ly93d3cucm9zZWhvc3RpbmcuY29tL2Jsb2cvaG93LXRvLWluc3RhbGwtbm9kZS1qcy1hbmQtbnBtLW9uLWNlbnRvcy03Lw==" title="https://www.rosehosting.com/blog/how-to-install-node-js-and-npm-on-centos-7/">https://www.rosehosting.com/b…<i class="fa fa-external-link"></i></span></p><h3 id="为npm更改源"><a href="#为npm更改源" class="headerlink" title="为npm更改源"></a>为npm更改源</h3><blockquote><p>npm默认使用的源的服务器在国外下载速度慢，所以需要更换源</p><p>以下两种方法任选一种</p></blockquote><h4 id="使用cnpm代替npm"><a href="#使用cnpm代替npm" class="headerlink" title="使用cnpm代替npm"></a>使用cnpm代替npm</h4><blockquote><p>参考链接：<span class="exturl" data-url="aHR0cHM6Ly9ucG0udGFvYmFvLm9yZy8=" title="https://npm.taobao.org/">https://npm.taobao.org/<i class="fa fa-external-link"></i></span></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"><span class="comment"># 安装</span></span><br><span class="line">npm install -g cnpm --registry=https://registry.npm.taobao.org</span><br><span class="line"></span><br><span class="line"><span class="comment">#安装完cnpm，之后再按照依赖就要使用cnpm</span></span><br><span class="line">cnpm install [包名]</span><br></pre></td></tr></table></figure><h4 id="为npm更换源"><a href="#为npm更换源" class="headerlink" title="为npm更换源"></a>为npm更换源</h4><blockquote><p>参考链接 <span class="exturl" data-url="aHR0cHM6Ly9zZWdtZW50ZmF1bHQuY29tL2EvMTE5MDAwMDAwNDQ0NDI4Mw==" title="https://segmentfault.com/a/1190000004444283">https://segmentfault.com/a/11…<i class="fa fa-external-link"></i></span></p></blockquote><p>修改源为淘宝的源</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm config <span class="built_in">set</span> registry http://registry.npm.taobao.org/</span><br></pre></td></tr></table></figure><p>我们在发布自己包的时候需要将官方的源改回来</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm config <span class="built_in">set</span> registry https://registry.npmjs.org/</span><br></pre></td></tr></table></figure><h3 id="管理-更新-nodejs的版本"><a href="#管理-更新-nodejs的版本" class="headerlink" title="管理(更新)nodejs的版本"></a>管理(更新)nodejs的版本</h3><blockquote><p>切换nodejs版本有两种方式，分别是<code>nvm</code>和<code>n</code>,n更简单推荐使用</p></blockquote><h4 id="使用n管理nodejs版本"><a href="#使用n管理nodejs版本" class="headerlink" title="使用n管理nodejs版本"></a>使用n管理nodejs版本</h4><blockquote><p>参考链接 <span class="exturl" data-url="aHR0cHM6Ly93d3cuamlhbnNodS5jb20vcC9jNjQuLg==" title="https://www.jianshu.com/p/c64..">https://www.jianshu.com/p/c64..<i class="fa fa-external-link"></i></span>.</p><p>官网 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3RqL24=" title="https://github.com/tj/n">https://github.com/tj/n<i class="fa fa-external-link"></i></span></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><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="comment">#安装</span></span><br><span class="line">npm install -g n</span><br><span class="line"></span><br><span class="line"><span class="comment">#使用n下载所需node版本</span></span><br><span class="line">n 版本号</span><br><span class="line"><span class="comment">#下载最新版本</span></span><br><span class="line">n latest</span><br><span class="line"><span class="comment"># 切换版本</span></span><br><span class="line">输入 n，</span><br><span class="line">然后选中所需版本</span><br><span class="line"><span class="comment">#以指定的版本来执行版本</span></span><br><span class="line">n use 7.4.0 index.js</span><br></pre></td></tr></table></figure><p>linux使用n安装新版本nodejs之后，如果<code>node -v</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></pre></td><td class="code"><pre><span class="line">vim .bash_profile</span><br><span class="line"><span class="built_in">export</span> NODE_HOME=/usr/<span class="built_in">local</span>     <span class="comment">#NODE_HOME改成新版本nodejs安装的目录，如果找不到，find / -name node</span></span><br><span class="line"><span class="built_in">export</span> PATH=<span class="variable">$NODE_HOME</span>/bin:<span class="variable">$PATH</span></span><br><span class="line"><span class="built_in">export</span> NODE_PATH=<span class="variable">$NODE_HOME</span>/lib/node_modules:<span class="variable">$PATH</span></span><br></pre></td></tr></table></figure><p>修改环境变量参考：<span class="exturl" data-url="aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lpNDEyL2FydGljbGUvZGV0YWlscy8xMTUyMzUyNQ==" title="https://blog.csdn.net/yi412/article/details/11523525">https://blog.csdn.net/yi412/a…<i class="fa fa-external-link"></i></span></p><h3 id="package-json文件详解"><a href="#package-json文件详解" class="headerlink" title="package.json文件详解"></a>package.json文件详解</h3><blockquote><p>参考文档 <span class="exturl" data-url="aHR0cDovL2phdmFzY3JpcHQucnVhbnlpZmVuZy5jb20vbm9kZWpzL3BhY2thZ2Vqc29uLmh0bWw=" title="http://javascript.ruanyifeng.com/nodejs/packagejson.html">http://javascript.ruanyifeng….<i class="fa fa-external-link"></i></span></p></blockquote><h2 id="vue脚手架"><a href="#vue脚手架" class="headerlink" title="vue脚手架"></a>vue脚手架</h2><blockquote><p>vue-cli目前已经更新到3版本，vue-cli3把webpack相关的配置隐藏起来了，所有的配置都在vue.config.js文件夹中，所以使用vue-cli3需要的webpack水平较高，建议使用vue-cli2</p></blockquote><h3 id="vue-cli2-x安装"><a href="#vue-cli2-x安装" class="headerlink" title="vue-cli2.x安装"></a>vue-cli2.x安装</h3><p>参考链接：<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3Z1ZWpzL3Z1ZS1jbGkvdHJlZS92MiN2dWUtY2xpLS0=" title="https://github.com/vuejs/vue-cli/tree/v2#vue-cli--">https://github.com/vuejs/vue-…<i class="fa fa-external-link"></i></span></p><p>安装：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install -g vue-cli</span><br></pre></td></tr></table></figure><p>用法：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ vue init &lt; template-name &gt; &lt; project-name &gt;</span><br></pre></td></tr></table></figure><p>例：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ vue init webpack my-project</span><br></pre></td></tr></table></figure><p>目前可用的模块包括：</p><ul><li><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3Z1ZWpzLXRlbXBsYXRlcy93ZWJwYWNr" title="https://github.com/vuejs-templates/webpack">webpack<i class="fa fa-external-link"></i></span> - 一个功能齐全的Webpack + vue-loader设置，具有热重载，linting，测试和css提取功能。</li><li><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3Z1ZWpzLXRlbXBsYXRlcy93ZWJwYWNrLXNpbXBsZQ==" title="https://github.com/vuejs-templates/webpack-simple">webpack-simple<i class="fa fa-external-link"></i></span> - 一个简单的Webpack + vue-loader设置，用于快速原型设计。</li><li><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3Z1ZWpzLXRlbXBsYXRlcy9icm93c2VyaWZ5" title="https://github.com/vuejs-templates/browserify">browserify<i class="fa fa-external-link"></i></span> -全功能Browserify + vueify设置用热重装载，linting＆单元测试。</li><li>browserify <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3Z1ZWpzLXRlbXBsYXRlcy9icm93c2VyaWZ5LXNpbXBsZQ==" title="https://github.com/vuejs-templates/browserify-simple">-simple<i class="fa fa-external-link"></i></span> - 一个简单的Browserify + vueify设置，用于快速原型设计。</li><li><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3Z1ZWpzLXRlbXBsYXRlcy9wd2E=" title="https://github.com/vuejs-templates/pwa">pwa<i class="fa fa-external-link"></i></span> - 基于webpack模板的vue-cli的PWA模板</li><li><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3Z1ZWpzLXRlbXBsYXRlcy9zaW1wbGU=" title="https://github.com/vuejs-templates/simple">simple<i class="fa fa-external-link"></i></span> - 单个HTML文件中最简单的Vue设置</li></ul><h3 id="vue-cli3-x安装及配置-仅供参考"><a href="#vue-cli3-x安装及配置-仅供参考" class="headerlink" title="vue-cli3.x安装及配置(仅供参考)"></a>vue-cli3.x安装及配置(仅供参考)</h3><p>vue-cli3x的官方文档：<span class="exturl" data-url="aHR0cHM6Ly9jbGkudnVlanMub3JnLw==" title="https://cli.vuejs.org/">https://cli.vuejs.org/<i class="fa fa-external-link"></i></span></p><p>Vue-cli3 中vue.config.js文件配置参考文档：<span class="exturl" data-url="aHR0cHM6Ly9jbGkudnVlanMub3JnL3poL2NvbmZpZy8jaW50ZWdyaXR5" title="https://cli.vuejs.org/zh/config/#integrity">https://cli.vuejs.org/zh/conf…<i class="fa fa-external-link"></i></span></p><p>Vue CLI 的包名称由 <code>vue-cli</code> 改成了 <code>@vue/cli</code>。 如果你已经全局安装了旧版本的 <code>vue-cli</code>(1.x 或 2.x)，你需要先通过 <code>npm uninstall vue-cli -g</code> 或 <code>yarn global remove vue-cli</code> 卸载它。</p><p>安装</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install -g @vue/cli</span><br></pre></td></tr></table></figure><p>安装了vue-cli3如果还想使用vue-cli2的init功能,需要安装一个桥接功能</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install -g @vue/cli-init</span><br></pre></td></tr></table></figure><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// vue.config.js 配置说明</span></span><br><span class="line"><span class="comment">//官方vue.config.js 参考文档 https://cli.vuejs.org/zh/config/#css-loaderoptions</span></span><br><span class="line"><span class="comment">// 这里只列一部分，具体配置参考文档</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// 部署生产环境和开发环境下的URL。</span></span><br><span class="line">  <span class="comment">// 默认情况下，Vue CLI 会假设你的应用是被部署在一个域名的根路径上</span></span><br><span class="line">  <span class="comment">//例如 https://www.my-app.com/。如果应用被部署在一个子路径上，你就需要用这个选项指定这个子路径。例如，如果你的应用被部署在 https://www.my-app.com/my-app/，则设置 baseUrl 为 /my-app/。</span></span><br><span class="line">  baseUrl: process.env.NODE_ENV === <span class="string">"production"</span> ? <span class="string">"./"</span> : <span class="string">"/"</span>,</span><br><span class="line"> </span><br><span class="line">  <span class="comment">// outputDir: 在npm run build 或 yarn build 时 ，生成文件的目录名称（要和baseUrl的生产环境路径一致）</span></span><br><span class="line">  outputDir: <span class="string">"dist"</span>,</span><br><span class="line">  <span class="comment">//用于放置生成的静态资源 (js、css、img、fonts) 的；（项目打包之后，静态资源会放在这个文件夹下）</span></span><br><span class="line">  assetsDir: <span class="string">"assets"</span>,</span><br><span class="line">  <span class="comment">//指定生成的 index.html 的输出路径  (打包之后，改变系统默认的index.html的文件名)</span></span><br><span class="line">  <span class="comment">// indexPath: "myIndex.html",</span></span><br><span class="line">  <span class="comment">//默认情况下，生成的静态资源在它们的文件名中包含了 hash 以便更好的控制缓存。你可以通过将这个选项设为 false 来关闭文件名哈希。(false的时候就是让原来的文件名不改变)</span></span><br><span class="line">  filenameHashing: <span class="literal">false</span>,</span><br><span class="line"> </span><br><span class="line">  <span class="comment">//   lintOnSave：&#123; type:Boolean default:true &#125; 问你是否使用eslint</span></span><br><span class="line">  <span class="string">`lintOnSave`</span>: <span class="literal">true</span>,</span><br><span class="line">  <span class="comment">//如果你想要在生产构建时禁用 eslint-loader，你可以用如下配置</span></span><br><span class="line">  <span class="comment">// lintOnSave: process.env.NODE_ENV !== 'production',</span></span><br><span class="line"> </span><br><span class="line">  <span class="comment">//是否使用包含运行时编译器的 Vue 构建版本。设置为 true 后你就可以在 Vue 组件中使用 template 选项了，但是这会让你的应用额外增加 10kb 左右。(默认false)</span></span><br><span class="line">  <span class="comment">// runtimeCompiler: false,</span></span><br><span class="line"> </span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * 如果你不需要生产环境的 source map，可以将其设置为 false 以加速生产环境构建。</span></span><br><span class="line"><span class="comment">   *  打包之后发现map文件过大，项目文件体积很大，设置为false就可以不输出map文件</span></span><br><span class="line"><span class="comment">   *  map文件的作用在于：项目打包后，代码都是经过压缩加密的，如果运行时报错，输出的错误信息无法准确得知是哪里的代码报错。</span></span><br><span class="line"><span class="comment">   *  有了map就可以像未加密的代码一样，准确的输出是哪一行哪一列有错。</span></span><br><span class="line"><span class="comment">   * */</span></span><br><span class="line">  productionSourceMap: <span class="literal">false</span>,</span><br><span class="line"> </span><br><span class="line">  <span class="comment">// 它支持webPack-dev-server的所有选项</span></span><br><span class="line">  devServer: &#123;</span><br><span class="line">    host: <span class="string">"localhost"</span>,</span><br><span class="line">    port: <span class="number">1111</span>, <span class="comment">// 端口号</span></span><br><span class="line">    https: <span class="literal">false</span>, <span class="comment">// https:&#123;type:Boolean&#125;</span></span><br><span class="line">    open: <span class="literal">true</span>, <span class="comment">//配置自动启动浏览器</span></span><br><span class="line">    <span class="comment">// proxy: 'http://localhost:4000' // 配置跨域处理,只有一个代理</span></span><br><span class="line"> </span><br><span class="line">    <span class="comment">// 配置多个代理</span></span><br><span class="line">    proxy: &#123;</span><br><span class="line">      <span class="string">"/api"</span>: &#123;</span><br><span class="line">        target: <span class="string">"&lt;url&gt;"</span>,</span><br><span class="line">        ws: <span class="literal">true</span>,</span><br><span class="line">        changeOrigin: <span class="literal">true</span></span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="string">"/foo"</span>: &#123;</span><br><span class="line">        target: <span class="string">"&lt;other_url&gt;"</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h1 id="二、开发"><a href="#二、开发" class="headerlink" title="二、开发"></a>二、开发</h1><blockquote><p>以下内容依赖环境为 ： vue-cli 版本2.9.x</p><p>项目github地址 :</p><ul><li>前端 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2h1YW5neWFuZ3RlbmcvdnVlMi10ZW1wbGF0ZQ==" title="https://github.com/huangyangteng/vue2-template">https://github.com/huangyangt…<i class="fa fa-external-link"></i></span></li><li>后端: <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2h1YW5neWFuZ3RlbmcvdnVlMi10ZW1wbGF0ZS1zZXJ2ZXI=" title="https://github.com/huangyangteng/vue2-template-server">https://github.com/huangyangt…<i class="fa fa-external-link"></i></span></li></ul></blockquote><p>安装完以上依赖后，就可以开始一个项目了,我们先看下后端api的定义</p><h2 id="前后端交互报文定义以及数据api接口"><a href="#前后端交互报文定义以及数据api接口" class="headerlink" title="前后端交互报文定义以及数据api接口"></a>前后端交互报文定义以及数据api接口</h2><h3 id="前后端交互报文定义"><a href="#前后端交互报文定义" class="headerlink" title="前后端交互报文定义"></a><strong>前后端交互报文定义</strong></h3><p>请求</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">http request header&#123; //除登录注册以外的请求，发起请求时要在请求头中加入token</span><br><span class="line">    <span class="attribute">authorization:jwt</span></span><br><span class="line"><span class="attribute">&#125;</span></span><br><span class="line">http request body&#123;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>返回</p><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">http response header&#123;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br><span class="line">http response body&#123;</span><br><span class="line">    <span class="attribute">code:业务处理状态码</span></span><br><span class="line">    msg:业务处理描述</span><br><span class="line">    token:jwt token</span><br><span class="line">    <span class="attribute">data:业务数据</span></span><br><span class="line"><span class="attribute">&#125;</span></span><br></pre></td></tr></table></figure><h3 id="项目中使用的后台api定义如下"><a href="#项目中使用的后台api定义如下" class="headerlink" title="项目中使用的后台api定义如下"></a>项目中使用的后台api定义如下</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">注：服务器端的host为118.24.85.97，端口为22222</span><br></pre></td></tr></table></figure><h4 id="测试api是否可用"><a href="#测试api是否可用" class="headerlink" title="测试api是否可用"></a><code>测试api是否可用</code></h4><ol><li>uri: <span class="exturl" data-url="aHR0cDovLzExOC4yNC44NS45Ny8=" title="http://118.24.85.97/">http://118.24.85.97<i class="fa fa-external-link"></i></span>:22222/api</li><li>描述：测试接口是否能用，能用的话返回 ‘API WORDS’字符串</li><li>请求类型 GET</li><li>请求参数 无</li><li>返回值 {‘Api Works’}</li></ol><h4 id="注册"><a href="#注册" class="headerlink" title="注册"></a><code>注册</code></h4><ol><li>uri: <span class="exturl" data-url="aHR0cDovLzExOC4yNC44NS45Ny8=" title="http://118.24.85.97/">http://118.24.85.97<i class="fa fa-external-link"></i></span>:22222/api/users/reg</li><li>描述:注册</li><li>请求类型 POST</li><li>请求参数</li></ol><table><thead><tr><th>序号</th><th>参数名</th><th>是否必填</th><th>描述</th></tr></thead><tbody><tr><td>1</td><td>name</td><td>y</td><td>用户名</td></tr><tr><td>2</td><td>pass</td><td>y</td><td>密码</td></tr></tbody></table><ol><li>返回参数 不重要</li></ol><h4 id="登录"><a href="#登录" class="headerlink" title="登录"></a><code>登录</code></h4><ol><li>uri: <span class="exturl" data-url="aHR0cDovLzExOC4yNC44NS45Ny8=" title="http://118.24.85.97/">http://118.24.85.97<i class="fa fa-external-link"></i></span>:22222/api/users/login</li><li>描述:登录</li><li>请求类型 POST</li><li>请求参数</li></ol><table><thead><tr><th>序号</th><th>参数名</th><th>是否必填</th><th>描述</th></tr></thead><tbody><tr><td>1</td><td>name</td><td>y</td><td>用户名</td></tr><tr><td>2</td><td>pass</td><td>y</td><td>密码</td></tr></tbody></table><ol><li>返回参数</li></ol><table><thead><tr><th>序号</th><th>参数名</th><th>描述</th></tr></thead><tbody><tr><td>1</td><td>msg</td><td>ok</td></tr><tr><td>2</td><td>token</td><td>用于验证用户身份的token</td></tr></tbody></table><h4 id="获取当前用户信息"><a href="#获取当前用户信息" class="headerlink" title="获取当前用户信息"></a><code>获取当前用户信息</code></h4><ol><li>uri: <span class="exturl" data-url="aHR0cDovLzExOC4yNC44NS45Ny8=" title="http://118.24.85.97/">http://118.24.85.97<i class="fa fa-external-link"></i></span>:22222/api/users/current</li><li>描述:获取用户信息</li><li>请求类型 GET</li><li>请求参数 无</li><li>返回参数</li></ol><table><thead><tr><th>序号</th><th>参数名</th><th>描述</th></tr></thead><tbody><tr><td>1</td><td>id</td><td>用户id</td></tr><tr><td>2</td><td>token</td><td>用于验证用户身份的token</td></tr></tbody></table><p>##初始化项目</p><p>在终端中输入</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vue init webpack vue2_template</span><br></pre></td></tr></table></figure><p>然后会有一些选项让你选,按照项目需求选择，例如我不需要eslint,unit test，就可以选No，现在选no将来如果需要的话也可以自己安装</p><p><img src="https://i.loli.net/2019/05/08/5cd23b0d9e7e9.jpg" alt></p><p>安装完成之后，按照提示切换到相应目录，执行相应指令，然后在浏览器打开网址，这样一个简单的vue项目就启动起来了</p><p><img src="https://i.loli.net/2019/05/08/5cd23b0e7b20b.jpg" alt></p><h2 id="项目文件介绍"><a href="#项目文件介绍" class="headerlink" title="项目文件介绍"></a>项目文件介绍</h2><h3 id="整个文件介绍："><a href="#整个文件介绍：" class="headerlink" title="整个文件介绍："></a>整个文件介绍：<img src="https://i.loli.net/2019/05/08/5cd23b0f68cce.jpg" alt></h3><p>注意：</p><ol><li>开发主要使用src文件夹</li><li>webpack的配置文件配置文件详解看这里：<span class="exturl" data-url="aHR0cHM6Ly9zZWdtZW50ZmF1bHQuY29tL2EvMTE5MDAwMDAxNDgwNDgyNg==" title="https://segmentfault.com/a/1190000014804826">https://segmentfault.com/a/11…<i class="fa fa-external-link"></i></span></li><li>package.json配置详解 <span class="exturl" data-url="aHR0cDovL2phdmFzY3JpcHQucnVhbnlpZmVuZy5jb20vbm9kZWpzL3BhY2thZ2Vqc29uLmh0bWw=" title="http://javascript.ruanyifeng.com/nodejs/packagejson.html">http://javascript.ruanyifeng….<i class="fa fa-external-link"></i></span></li></ol><h3 id="src目录介绍"><a href="#src目录介绍" class="headerlink" title="src目录介绍"></a>src目录介绍</h3><p>首先在src目录下新建一个文件夹views,用来放我们的主要页面，然后在assets文件夹中建立fonts styles imgs，用来存放相应的资源，建完之后，文件夹如下 <img src="https://i.loli.net/2019/05/08/5cd23b101a388.jpg" alt></p><h2 id="跨域、axios配置与api管理"><a href="#跨域、axios配置与api管理" class="headerlink" title="跨域、axios配置与api管理"></a>跨域、axios配置与api管理</h2><p>在这个项目中，我们使用axios进行数据请求</p><blockquote><p>axios中文文档： <span class="exturl" data-url="aHR0cHM6Ly93d3cua2FuY2xvdWQuY24veXVueWUvYXhpb3MvMjM0ODQ1" title="https://www.kancloud.cn/yunye/axios/234845">https://www.kancloud.cn/yunye…<i class="fa fa-external-link"></i></span></p></blockquote><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"># 安装axios</span></span><br><span class="line">npm/cnpm i axios -S      <span class="comment"># -S 指安装到package.json中的dependencies中</span></span><br></pre></td></tr></table></figure><p>安装完成后，我们要在main.js中引入,然后测试一下是否成功引入</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">//main.js文件</span><br><span class="line">import axios from <span class="string">'axios'</span></span><br><span class="line"></span><br><span class="line">axios.get(<span class="string">'https://api.github.com/users?since=10'</span>)   //使用github接口做一下测试</span><br><span class="line">  .<span class="keyword">then</span>(res=&gt;console.log(res))</span><br><span class="line">  .catch(err=&gt;console.log(err))</span><br></pre></td></tr></table></figure><p>浏览器显示以下信息，说明引入成功 <img src="https://i.loli.net/2019/05/08/5cd23b10c2755.jpg" alt></p><p>github提供的接口配置了cors，所以我们能够能够在浏览器正常访问到，但cors兼容性最低到ie10，而且后台不一定会配置cors，所以在开发时我们需要配置一下跨域</p><p>参考链接：</p><ol><li>cors详解 <span class="exturl" data-url="aHR0cDovL3d3dy5ydWFueWlmZW5nLmNvbS9ibG9nLzIwMTYvMDQvY29ycy5odG1s" title="http://www.ruanyifeng.com/blog/2016/04/cors.html">http://www.ruanyifeng.com/blo…<i class="fa fa-external-link"></i></span></li></ol><h3 id="配置跨域"><a href="#配置跨域" class="headerlink" title="配置跨域"></a>配置跨域</h3><blockquote><p>参考文档：<span class="exturl" data-url="aHR0cHM6Ly9zZWdtZW50ZmF1bHQuY29tL2EvMTE5MDAwMDAxNzkwNTAzMA==" title="https://segmentfault.com/a/1190000017905030">https://segmentfault.com/a/11…<i class="fa fa-external-link"></i></span></p></blockquote><p>先找个没有设置cors的api使用axios访问一下</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">axios.get(<span class="string">'http://118.24.85.97:22222/api'</span>)</span><br><span class="line">.then(<span class="function"><span class="params">res</span>=&gt;</span><span class="built_in">console</span>.log(res))</span><br><span class="line">.catch(<span class="function"><span class="params">err</span>=&gt;</span><span class="built_in">console</span>.log(err))</span><br></pre></td></tr></table></figure><p>浏览器会因为同源策略报错 <img src="https://i.loli.net/2019/05/08/5cd23b1181b7c.jpg" alt></p><p>下面进行跨域的配置</p><blockquote><p>配置目录 config/index.js 13行</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></pre></td><td class="code"><pre><span class="line">proxyTable: &#123;</span><br><span class="line">  <span class="string">'/apis'</span>:&#123;</span><br><span class="line">    target:<span class="string">'http://118.24.85.97:22222'</span>,<span class="comment">//后台地址 proxyTable  把/apis映射成target 即 /apis=http://118.24.85.97:22222</span></span><br><span class="line">    changeOrigin:<span class="literal">true</span>,<span class="comment">//是否跨域</span></span><br><span class="line">    pathRewrite:&#123;</span><br><span class="line">      <span class="string">'^/apis'</span>:<span class="string">''</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>再进行访问数据时就要在接口前面加上/apis(/apis就相当于<span class="exturl" data-url="aHR0cDovLzExOC4yNC44NS45Ny8=" title="http://118.24.85.97/">http://118.24.85.97<i class="fa fa-external-link"></i></span>:22222)</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">axios.get(<span class="string">'/apis/api'</span>)</span><br><span class="line">.then(<span class="function"><span class="params">res</span>=&gt;</span><span class="built_in">console</span>.log(res))</span><br><span class="line">.catch(<span class="function"><span class="params">err</span>=&gt;</span><span class="built_in">console</span>.log(err))</span><br></pre></td></tr></table></figure><p>然后就发现浏览器访问成功了 <img src="https://i.loli.net/2019/05/08/5cd23b1211dba.jpg" alt></p><p>proxyTable原理：跨域是浏览器禁止的，服务端并不禁止跨域 ，所以浏览器可以发给自己的服务端然后，由自己的服务端再转发给要跨域的服务端，做一层代理。proxyTable使用的是<code>http-proxy-middleware</code>中间件，内部用的是http-proxy</p><p>以上配置的跨域是开发环境下的，在生产环境就自动失效了，而且这样配置我们开发时访问接口时，都要写成<code>/apis/xxx/xxx</code>格式，在部署到服务器中时，我们要把/apis拿掉，才能访问到正确的url。有两种方法，一种是在开发环境中设置(通过axios的baseURL)，另一种是在服务器上修改nginx的配置设置。</p><h3 id="生产环境去除-apis前缀"><a href="#生产环境去除-apis前缀" class="headerlink" title="生产环境去除/apis前缀"></a>生产环境去除/apis前缀</h3><p>在这里详细说下第一种方式，原理是这样的：</p><p>通过检测是开发环境和生产环境，设置不同的baseURL,使生产环境和开发环境都能正确访问url</p><p>在src目录下新建一个<code>apis</code>目录,然后在apis目录下新建一个<code>api.config.js</code>文件</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//判断是否是生产环境</span></span><br><span class="line"><span class="comment">//webpack在开发环境和生产环境分别执行不同的js文件，process.env.NODE_ENV设置了不同的值，process.env.NODE_ENV在生产环境中值为'production'(这个值是在build/build.js中第4行设置的)</span></span><br><span class="line"><span class="keyword">var</span> isPro = process.env.NODE_ENV=== <span class="string">'production'</span></span><br><span class="line"><span class="comment">// 如果是生产环境 我们就使用服务器的uri，如果是开发环境，我们就添加/apis前缀</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">    baseUrl: isPro ? <span class="string">'http://118.24.85.97:22222'</span> : <span class="string">'/apis'</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在main.js中引入这个文件，然后设置axios的<code>baseURL</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//引入api.config.js文件，然后设置axios的baseURL</span></span><br><span class="line"><span class="keyword">import</span> apiConfig <span class="keyword">from</span> <span class="string">'./apis/api.config'</span></span><br><span class="line">axios.defaults.baseURL=apiConfig.baseUrl</span><br></pre></td></tr></table></figure><p>再来测试一下不加/apis的接口</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">axios.get(<span class="string">'/api'</span>)</span><br><span class="line">.then(<span class="function"><span class="params">res</span>=&gt;</span><span class="built_in">console</span>.log(res))</span><br><span class="line">.catch(<span class="function"><span class="params">err</span>=&gt;</span><span class="built_in">console</span>.log(err))</span><br></pre></td></tr></table></figure><p>浏览器显示是ok的。这样我们以后使用axios访问接口就可以不加/apis了，打包后访问也不用手动去除/apis</p><h3 id="api统一管理"><a href="#api统一管理" class="headerlink" title="api统一管理"></a>api统一管理</h3><blockquote><p>在vue项目开发过程中，会涉及到很多接口的处理，当项目足够大时，就需要统一管理接口。</p><p>具体方法应该挺多的，这里只介绍一种：使用axios+async/await进行接口的统一管理</p></blockquote><p>一般来说，后台的接口是分模块的，例如我们后台的测试接口</p><ul><li>身份认证 /api/login /api/reg</li><li>用户信息 /v1/api/user</li></ul><p>我们首先在src目录下新建一个apis文件夹，后台提供的所有接口都在这里定义</p><p>第二步，按照后台提供的模块新建js文件，我们新建<code>user.js</code> <code>auth.js</code></p><p>第三步，引入axios，做相应的配置</p><p>在apis目录下新建一个http.js，在里面做axios相应的配置</p><ol><li>我们上文中是在main.js文件引入的axios,设置的baseURL，以上代码可以去除，改为在http.js中引入</li><li>我们做的主要是：引入axios,创建一个axios的实例(实例的功能和axios一样)</li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> axios <span class="keyword">from</span> <span class="string">'axios'</span></span><br><span class="line"><span class="keyword">import</span> apiConfig <span class="keyword">from</span> <span class="string">'./api.config'</span></span><br><span class="line"><span class="comment">//创建axios的一个实例</span></span><br><span class="line"><span class="keyword">var</span> instance = axios.create(&#123;</span><br><span class="line">    baseURL:apiConfig.baseUrl,</span><br><span class="line">    timeout: <span class="number">6000</span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//------------------- 一、请求拦截器 后面介绍</span></span><br><span class="line">instance.interceptors.request.use(<span class="function"><span class="keyword">function</span> (<span class="params">config</span>) </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> config;</span><br><span class="line">&#125;, <span class="function"><span class="keyword">function</span> (<span class="params">error</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 对请求错误做些什么</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">Promise</span>.reject(error);</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">instance.interceptors.response.use(<span class="function"><span class="keyword">function</span> (<span class="params">response</span>) </span>&#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> response.data;</span><br><span class="line">&#125;, <span class="function"><span class="keyword">function</span> (<span class="params">error</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 对响应错误做点什么</span></span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">Promise</span>.reject(error);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 使用es6的export default导出了一个函数，导出的函数代替axios去帮我们请求数据，</span></span><br><span class="line"><span class="comment"> * 函数的参数及返回值如下：</span></span><br><span class="line"><span class="comment"> * @param &#123;String&#125; method  请求的方法：get、post、delete、put</span></span><br><span class="line"><span class="comment"> * @param &#123;String&#125; url     请求的url:</span></span><br><span class="line"><span class="comment"> * @param &#123;Object&#125; data    请求的参数</span></span><br><span class="line"><span class="comment"> * @returns &#123;Promise&#125;     返回一个promise对象，其实就相当于axios请求数据的返回值</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> (<span class="params">method, url, data = null</span>) </span>&#123;</span><br><span class="line">    method = method.toLowerCase();</span><br><span class="line">    <span class="keyword">if</span> (method == <span class="string">'post'</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> instance.post(url, data)</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (method == <span class="string">'get'</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> instance.get(url, &#123; <span class="attr">params</span>: data &#125;)</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (method == <span class="string">'delete'</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> instance.delete(url, &#123; <span class="attr">params</span>: data &#125;)</span><br><span class="line">    &#125;<span class="keyword">else</span> <span class="keyword">if</span>(method == <span class="string">'put'</span>)&#123;</span><br><span class="line">        <span class="keyword">return</span> instance.put(url,data)</span><br><span class="line">    &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.error(<span class="string">'未知的method'</span>+method)</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>第四步，在<code>apis/xxx.js</code>文件中引入http.js导出的函数，拿其中一个文件<code>auth.js</code>说明</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//auth.js 用于定义用户的登录、注册、注销等</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> req <span class="keyword">from</span> <span class="string">'./http.js'</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//定义接口</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//在这里定义了一个登陆的接口，把登陆的接口暴露出去给组件使用</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> LOGIN =<span class="function"><span class="params">params</span>=&gt;</span>req(<span class="string">'post'</span>,<span class="string">'/api/users/login'</span>,params)</span><br><span class="line"><span class="comment">//这里使用了箭头函数，转换一下写法：</span></span><br><span class="line"><span class="comment">// export const LOGIN=function(params)&#123;</span></span><br><span class="line"><span class="comment">//   return req('post','/api/login',params)</span></span><br><span class="line"><span class="comment">// &#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//定义注册接口</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> REG =<span class="function"><span class="params">params</span>=&gt;</span>req(<span class="string">'post'</span>,<span class="string">'/api/users/reg'</span>,params)</span><br></pre></td></tr></table></figure><p>最后一步，在需要用的该api的组件中引入并调用,我们在App.vue文件中测试下</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><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></pre></td><td class="code"><pre><span class="line">&lt;template&gt;</span><br><span class="line">  &lt;div&gt;</span><br><span class="line">    &lt;h2&gt;登录&lt;/h2&gt;</span><br><span class="line">    用户名&lt;input type=&quot;text&quot; v-model=&quot;user&quot;&gt;</span><br><span class="line">    密码&lt;input type=&quot;password&quot; v-model=&quot;pass&quot;&gt;</span><br><span class="line">    &lt;input type=&quot;button&quot; @click=&quot;reg&quot; value=&quot;注册&quot;&gt;</span><br><span class="line">    &lt;input type=&quot;button&quot; @click=&quot;login&quot; value=&quot;登录&quot;&gt;</span><br><span class="line">  &lt;/div&gt;</span><br><span class="line">&lt;/template&gt;</span><br><span class="line">&lt;script&gt;</span><br><span class="line">import &#123;LOGIN,REG&#125; from &apos;../../apis/auth.js&apos;</span><br><span class="line">export default &#123;</span><br><span class="line">  data() &#123;</span><br><span class="line">    return &#123;</span><br><span class="line">      user:&apos;&apos;,</span><br><span class="line">      pass:&apos;&apos;,</span><br><span class="line">      err:[]</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  methods: &#123;</span><br><span class="line">    async reg()&#123;</span><br><span class="line">      try &#123;</span><br><span class="line">        const data = await REG(&#123; name: this.user,pass: this.pass &#125;)</span><br><span class="line">        console.log(data)</span><br><span class="line">        alert(JSON.stringify(data))</span><br><span class="line">        this.cleanForm()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">      &#125; catch (error) &#123;</span><br><span class="line">        console.log(error)</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">    &#125;,</span><br><span class="line">    async login()&#123;</span><br><span class="line">      try &#123;</span><br><span class="line">        const data = await LOGIN(&#123; name: this.user,pass: this.pass &#125;)</span><br><span class="line">        alert(JSON.stringify(data))</span><br><span class="line">        this.cleanForm()</span><br><span class="line">      &#125; catch (error) &#123;</span><br><span class="line">        console.log(error)</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    cleanForm()&#123;</span><br><span class="line">      this.user=&apos;&apos;</span><br><span class="line">      this.pass=&apos;&apos;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line">&lt;/script&gt;</span><br></pre></td></tr></table></figure><p>注：如果要打开Login.vue,需要配置对应的路由</p><p>上面的代码引入了<code>auth.js</code>定义的api，并在对应的方法中使用。代码中用到了async/await,其实很简单，可以假设async是个标识，说明这个函数中有异步请求，await翻译为’等’,后面接一个异步请求，等后面的异步请求执行完成之后，会把结果赋给<code>=</code>左边的值</p><blockquote><p>参考链接 <span class="exturl" data-url="aHR0cDovL3d3dy5ydW5vb2IuY29tL3czY25vdGUvZXM2LWFzeW5jLmh0bWw=" title="http://www.runoob.com/w3cnote/es6-async.html">http://www.runoob.com/w3cnote…<i class="fa fa-external-link"></i></span></p></blockquote><p>总结一下，像上面那样定义接口虽然麻烦点，但有两个好处：</p><ol><li>代码看起来规范，所有的接口都在一个文件夹定义，不用分散的各个组件，维护起来简单，例如后台的一些url变了，改起来也方便</li><li>可以做到接口一次定义，到处使用</li></ol><h2 id="路由配置"><a href="#路由配置" class="headerlink" title="路由配置"></a>路由配置</h2><blockquote><p>Vue Router官方文档 <span class="exturl" data-url="aHR0cHM6Ly9yb3V0ZXIudnVlanMub3JnL3poLw==" title="https://router.vuejs.org/zh/">https://router.vuejs.org/zh/<i class="fa fa-external-link"></i></span></p><p>前端路由原理：<span class="exturl" data-url="aHR0cHM6Ly9zZWdtZW50ZmF1bHQuY29tL2EvMTE5MDAwMDAxODIxOTcwNQ==" title="https://segmentfault.com/a/1190000018219705">https://segmentfault.com/a/11…<i class="fa fa-external-link"></i></span></p></blockquote><h3 id="最简配置"><a href="#最简配置" class="headerlink" title="最简配置"></a>最简配置</h3><blockquote><p>路由的配置文件在router/index.js文件中</p><p>先引入文件，再进行配置</p></blockquote><p>首先在<code>views目录中新建以下页面</code>，主页(Home/Home.vue),登录页(Login/Login.vue),测试页(Test/Test.vue)</p><p>然后配置下路由</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> Vue <span class="keyword">from</span> <span class="string">'vue'</span></span><br><span class="line"><span class="keyword">import</span> Router <span class="keyword">from</span> <span class="string">'vue-router'</span></span><br><span class="line"><span class="comment">//@表示 src目录 webpack的配置在webpack.base.conf.js第29行 alias&#123;'@':resolve('src')&#125;</span></span><br><span class="line"><span class="keyword">import</span> Home <span class="keyword">from</span> <span class="string">'@/views/Home/Home.vue'</span></span><br><span class="line"><span class="keyword">import</span> Login <span class="keyword">from</span> <span class="string">'@/views/Login/Login.vue'</span></span><br><span class="line"><span class="keyword">import</span> Test <span class="keyword">from</span> <span class="string">'@/views/Test/Test.vue'</span></span><br><span class="line"></span><br><span class="line">Vue.use(Router)</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">new</span> Router(&#123;</span><br><span class="line">  routes: [<span class="comment">//路由规则</span></span><br><span class="line">    &#123;</span><br><span class="line">      path: <span class="string">'/'</span>,</span><br><span class="line">      name: <span class="string">'Home'</span>,</span><br><span class="line">      component: Home</span><br><span class="line">    &#125;,</span><br><span class="line">    &#123;</span><br><span class="line">      path:<span class="string">'/login'</span>,</span><br><span class="line">      name:<span class="string">'Login'</span>,</span><br><span class="line">      component:Login</span><br><span class="line">    &#125;,</span><br><span class="line">    &#123;</span><br><span class="line">      path:<span class="string">'/test'</span>,</span><br><span class="line">      name:<span class="string">'Test'</span>,</span><br><span class="line">      component:Test</span><br><span class="line">    &#125;</span><br><span class="line">  ]</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><p>路由规则在<code>routes</code>中进行配置,<code>routes</code>是一个数组，接受一系列路由规则，每个路由规则是一个对象，包括路径、路由名字，和路径匹配的组件，建议给每个路由加个名字，在后面可能会用到。</p><p>打开浏览器，输入相应的url查看配置的路由是否正确,不正确的话检查下自己的配置</p><h3 id="配置路由懒加载"><a href="#配置路由懒加载" class="headerlink" title="配置路由懒加载"></a>配置路由懒加载</h3><blockquote><p>参考文档：</p><p>路由懒加载官方文档：<span class="exturl" data-url="aHR0cHM6Ly9yb3V0ZXIudnVlanMub3JnL3poL2d1aWRlL2FkdmFuY2VkL2xhenktbG9hZGluZy5odG1s" title="https://router.vuejs.org/zh/guide/advanced/lazy-loading.html">https://router.vuejs.org/zh/g…<i class="fa fa-external-link"></i></span></p><p>webpack之mainfest解读：<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3lvdW50aC9ibG9nL2lzc3Vlcy8z" title="https://github.com/younth/blog/issues/3">https://github.com/younth/blo…<i class="fa fa-external-link"></i></span></p></blockquote><p>当打包构建应用时，Javascript 包会变得非常大，影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块，然后当路由被访问的时候才加载对应组件，这样就更加高效了。所以，懒加载的含义是当路由被访问时再去加载对应的js代码。</p><p>首先，不做路由懒加载的情况下，我们打包一下（切换到项目目录，执行<code>npm run build</code>）,然后会发现项目下生产了3个js文件</p><p><img src="https://i.loli.net/2019/05/08/5cd23b12ad655.jpg" alt></p><p>简单介绍一下作用：</p><ol><li>vendor.js 第三方库，一般是 node_modules里面的依赖进行打包 体积最大</li><li>app.js 入口js打包的结果，即我们编写的所有代码都会打包进去</li><li>manifest.js 主要是一些异步加载的实现方法（通过建立script方式动态引入js），内容上包含异步js的文件名和路径。</li></ol><p>然后我们实现一下路由懒加载 <code>@/router/router.js</code></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> Vue <span class="keyword">from</span> <span class="string">'vue'</span></span><br><span class="line"><span class="keyword">import</span> Router <span class="keyword">from</span> <span class="string">'vue-router'</span></span><br><span class="line"><span class="comment">// import Home from '@/views/Home/Home.vue'</span></span><br><span class="line"><span class="comment">// import Login from '@/views/Login/Login.vue'</span></span><br><span class="line"><span class="comment">// import Test from '@/views/Test/Test.vue'</span></span><br><span class="line"><span class="comment">// 懒加载方式</span></span><br><span class="line"><span class="keyword">const</span> Home=<span class="function"><span class="params">()</span>=&gt;</span><span class="keyword">import</span>(<span class="string">'@/views/Home/Home.vue'</span>)</span><br><span class="line"><span class="keyword">const</span> Login=<span class="function"><span class="params">()</span>=&gt;</span><span class="keyword">import</span>(<span class="string">'@/views/Login/Login.vue'</span>)</span><br><span class="line"><span class="keyword">const</span> Test=<span class="function"><span class="params">()</span>=&gt;</span><span class="keyword">import</span>(<span class="string">'@/views/Test/Test.vue'</span>)</span><br><span class="line">Vue.use(Router)</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">new</span> Router(&#123;</span><br><span class="line">  routes: [</span><br><span class="line">    &#123;</span><br><span class="line">      path: <span class="string">'/'</span>,</span><br><span class="line">      name: <span class="string">'Home'</span>,</span><br><span class="line">      component: Home</span><br><span class="line">    &#125;,</span><br><span class="line">    &#123;</span><br><span class="line">      path:<span class="string">'/login'</span>,</span><br><span class="line">      name:<span class="string">'Login'</span>,</span><br><span class="line">      component:Login</span><br><span class="line">    &#125;,</span><br><span class="line">    &#123;</span><br><span class="line">      path:<span class="string">'/test'</span>,</span><br><span class="line">      name:<span class="string">'Test'</span>,</span><br><span class="line">      component:Test</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>懒加载只是改变了一下组件的引用方式，由原来的直接引入变成异步引入，当我们访问对应的路由path时，才会加载相应的路由组件。</p><p>配置完成后再执行一次打包，结果如下：<img src="https://i.loli.net/2019/05/08/5cd23b136d294.jpg" alt></p><p>我们会发现目录中多出来3个js文件,并且<code>app.js</code>文件变小了。这说明配置了懒加载之后，app.js中其他组件的内容被抽离出来，分配到各自的js文件中。配置懒加载之后，刚开始打开页面只会加载app.js文件，只有在用户点击相应路由时，才会加载对应的js代码。当我们的业务代码非常多时，懒加载是个很好的选择。</p><h3 id="配置history模式"><a href="#配置history模式" class="headerlink" title="配置history模式"></a>配置history模式</h3><blockquote><p>官方文档：<span class="exturl" data-url="aHR0cHM6Ly9yb3V0ZXIudnVlanMub3JnL3poL2d1aWRlL2Vzc2VudGlhbHMvaGlzdG9yeS1tb2RlLmh0bWw=" title="https://router.vuejs.org/zh/guide/essentials/history-mode.html">https://router.vuejs.org/zh/g…<i class="fa fa-external-link"></i></span></p></blockquote><p>配置history模式有两个原因，一是因为hash模式看很丑，二是因为预加载要用到History模式，配置非常简单,只需要配置属性<code>mode</code>的值为’history’</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> router = <span class="keyword">new</span> VueRouter(&#123;</span><br><span class="line">  mode: <span class="string">'history'</span>,</span><br><span class="line">  routes: [...]</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><p>不过这种方式需要后台的支持，当匹配不到url时，返回url/index.html页面</p><p>nginx配置如下</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">location / &#123;</span><br><span class="line">  try_files $uri /index.html;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="权限管理"><a href="#权限管理" class="headerlink" title="权限管理"></a>权限管理</h2><blockquote><p>参考链接：</p><p>json web token入门教程 <span class="exturl" data-url="aHR0cDovL3d3dy5ydWFueWlmZW5nLmNvbS9ibG9nLzIwMTgvMDcvanNvbl93ZWJfdG9rZW4tdHV0b3JpYWwuaHRtbA==" title="http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html">http://www.ruanyifeng.com/blo…<i class="fa fa-external-link"></i></span></p><p>jwt官网 <span class="exturl" data-url="aHR0cHM6Ly9qd3QuaW8v" title="https://jwt.io/">https://jwt.io/<i class="fa fa-external-link"></i></span></p></blockquote><h3 id="token验证"><a href="#token验证" class="headerlink" title="token验证"></a>token验证</h3><p>我们通过jwt进行用户认证，jwt的原理是：服务器认证以后，生成一个json对象，发回给用户.</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">"id"</span>:<span class="string">"001"</span>,</span><br><span class="line">    <span class="attr">"姓名"</span>:<span class="string">"小明"</span>,</span><br><span class="line">    <span class="attr">"角色"</span>:<span class="string">"管理员"</span>,</span><br><span class="line">    <span class="attr">"到期时间"</span>:<span class="string">"2019年3月3日12时30分"</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>以后用户与服务端通信的时候，都要发回这个json对象。服务器完全靠这个对象认定用户身份(一般是通过这个对象的中id去数据库请求数据)。为了防止用户篡改数据，服务器会在生成这个对象的时候，加上签名。就像这种形式：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c</span><br></pre></td></tr></table></figure><p>关于JWT保存更新的业务流程如下：</p><ol><li>保存：登录后保存token</li><li>添加：每次发送请求之前检查token是否存在，存在，添加到请求头中，发送请求</li><li>更新：每次发送请求服务器返回数据之后更新token</li></ol><p>主要逻辑包括：</p><ol><li>登录之后，在<code>localStorage</code>中保存token</li><li>每次发送请求之前，使用axios请求拦截器将token放到请求头中</li><li>每次发送请求服务器返回数据之后在axios的响应拦截器中更新token</li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//1.登录之后保存token login.vue</span></span><br><span class="line"><span class="keyword">async</span> login()&#123;</span><br><span class="line">    <span class="keyword">const</span> data = <span class="keyword">await</span> LOGIN(&#123; <span class="attr">name</span>: <span class="keyword">this</span>.user,<span class="attr">pass</span>: <span class="keyword">this</span>.pass &#125;)</span><br><span class="line">    <span class="comment">//保存token</span></span><br><span class="line">    localStorage.setItem(<span class="string">'token'</span>,data.token)</span><br><span class="line">    <span class="comment">//查看是否保存成功</span></span><br><span class="line">    <span class="built_in">console</span>.log(localStorage.getItem(<span class="string">'token'</span>))</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//每次发送请求之前，讲token放到请求头中 api/http.js</span></span><br><span class="line"><span class="comment">//---使用axios的请求拦截器，每次发送请求之前拦截一下</span></span><br><span class="line">instance.interceptors.request.use(<span class="function"><span class="keyword">function</span> (<span class="params">config</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 给头添加token</span></span><br><span class="line">    <span class="keyword">if</span> (localStorage.getItem(<span class="string">'token'</span>))&#123;<span class="comment">//存在token,加入头</span></span><br><span class="line">        config.headers.authorization=localStorage.getItem(<span class="string">'token'</span>)</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> config;</span><br><span class="line">&#125;, <span class="function"><span class="keyword">function</span> (<span class="params">error</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 对请求错误做些什么</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">Promise</span>.reject(error);</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">//完成之后，记得发送一个请求，看看是否正确添加token</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//---响应拦截器，服务器响应后先到达这里</span></span><br><span class="line">instance.interceptors.response.use(<span class="function"><span class="keyword">function</span> (<span class="params">response</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(response.data.code==<span class="string">'2000'</span>)&#123;<span class="comment">//成功响应，更新token</span></span><br><span class="line">      <span class="keyword">if</span>(response.data.token)&#123;</span><br><span class="line">        localStorage.setItem(<span class="string">'token'</span>,response.data.token)</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="comment">//错误处理 根据不同的状态码，进行错误处理  </span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> response.data;</span><br><span class="line">&#125;, <span class="function"><span class="keyword">function</span> (<span class="params">error</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 对响应错误做点什么</span></span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">Promise</span>.reject(error);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h3 id="对页面的访问权限"><a href="#对页面的访问权限" class="headerlink" title="对页面的访问权限"></a>对页面的访问权限</h3><p>除了对token的操作，我们还要判断用户有没有权限访问这个页面(有些页面是用户必须登录才能访问的)，具体配置要使用Vue Router的导航守卫</p><blockquote><p>参考链接：<span class="exturl" data-url="aHR0cHM6Ly9yb3V0ZXIudnVlanMub3JnL3poL2d1aWRlL2FkdmFuY2VkL25hdmlnYXRpb24tZ3VhcmRzLmh0bWw=" title="https://router.vuejs.org/zh/guide/advanced/navigation-guards.html">https://router.vuejs.org/zh/g…<i class="fa fa-external-link"></i></span></p></blockquote><p>在全局前置守卫中进行验证</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//在router/index.js进行配置</span></span><br><span class="line"><span class="comment">//在每次进行路由跳转之前进行</span></span><br><span class="line">router.beforeEach(<span class="function">(<span class="params">to,<span class="keyword">from</span>,next</span>)=&gt;</span>&#123;<span class="comment">//增加登录验证</span></span><br><span class="line">  <span class="keyword">const</span> isLogin=localStorage.getItem(<span class="string">'token'</span>)?<span class="literal">true</span>:<span class="literal">false</span>;</span><br><span class="line">  <span class="keyword">if</span>(to.path==<span class="string">'/login'</span>)&#123;<span class="comment">//如果是登录页面，不需要token</span></span><br><span class="line">    next();</span><br><span class="line">  &#125;<span class="keyword">else</span>&#123;<span class="comment">//如果不是登录页面就要判断是否登录</span></span><br><span class="line">    isLogin?next():next(<span class="string">'/login'</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><h2 id="将界面交给第三方UI库"><a href="#将界面交给第三方UI库" class="headerlink" title="将界面交给第三方UI库"></a>将界面交给第三方UI库</h2><blockquote><p>iview官网:<span class="exturl" data-url="aHR0cHM6Ly93d3cuaXZpZXd1aS5jb20v" title="https://www.iviewui.com/">https://www.iviewui.com/<i class="fa fa-external-link"></i></span></p></blockquote><p>为节省开发时间，我们往往会使用一些第三方ui库，比如iview elementui等</p><p>我们在这里只介绍iview,其他ui库大同小异</p><h3 id="iview的安装与引入"><a href="#iview的安装与引入" class="headerlink" title="iview的安装与引入"></a>iview的安装与引入</h3><h4 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cnpm i iview --save</span><br></pre></td></tr></table></figure><h4 id="按需引入组件"><a href="#按需引入组件" class="headerlink" title="按需引入组件"></a>按需引入组件</h4><p>官网说，需要下载插件才能按需引入，<span class="exturl" data-url="aHR0cHM6Ly93d3cuaXZpZXd1aS5jb20vZG9jcy9ndWlkZS9zdGFydCNBWFlZ" title="https://www.iviewui.com/docs/guide/start#AXYY">官网说明<i class="fa fa-external-link"></i></span>，但是不下好像也可以正常引入</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//在main.js文件中引入项目需要的组件</span></span><br><span class="line"><span class="keyword">import</span> &#123;Button,Table,Message&#125; <span class="keyword">from</span> <span class="string">'iview'</span></span><br><span class="line"><span class="comment">//然后注册组件</span></span><br><span class="line">Vue.component(<span class="string">'Button'</span>,Button)</span><br><span class="line">Vue.component(<span class="string">'Table'</span>,Table)</span><br><span class="line">Vue.component(<span class="string">'Message'</span>,Message)</span><br></pre></td></tr></table></figure><p>这样注册的话太繁琐，所以需要优化一下</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//main.js</span></span><br><span class="line"><span class="keyword">import</span> &#123;Button,Table,Message&#125; <span class="keyword">from</span> <span class="string">'iview'</span></span><br><span class="line"><span class="keyword">const</span> iviewComs=&#123;Button,Table,Message&#125;</span><br><span class="line"><span class="built_in">Object</span>.keys(iviewComs).forEach(<span class="function"><span class="params">key</span>=&gt;</span>&#123;Vue.component(key,component[key])&#125;)</span><br></pre></td></tr></table></figure><p>代码都写在main.js中显得太拥挤，我们可以把代码拿出去，写成一个插件</p><p>我们在components文件夹中新建一个文件<code>iview-coms</code>，用来放iview中引入的组件</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//components/iview-coms.js  </span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> &#123;Button,Table,Message&#125; <span class="keyword">from</span> <span class="string">'iview'</span></span><br><span class="line"><span class="keyword">const</span> components=&#123;Button,Table,Message&#125;</span><br><span class="line"><span class="keyword">const</span> install = <span class="function"><span class="keyword">function</span>(<span class="params">Vue, opts = &#123;&#125;</span>)</span>&#123;</span><br><span class="line">  <span class="built_in">Object</span>.keys(components).forEach(<span class="function"><span class="params">key</span>=&gt;</span>&#123;</span><br><span class="line">    Vue.component(key,components[key])</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> install</span><br></pre></td></tr></table></figure><p>然后在main.js中引入,<code>use</code>这个插件</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> iviewComs <span class="keyword">from</span> <span class="string">'./components/iview-coms'</span></span><br><span class="line">Vue.use(iviewComs)</span><br></pre></td></tr></table></figure><p>ok了，接下来看自定义主题</p><h4 id="自定义主题"><a href="#自定义主题" class="headerlink" title="自定义主题"></a>自定义主题</h4><p>官网链接：<span class="exturl" data-url="aHR0cHM6Ly93d3cuaXZpZXd1aS5jb20vZG9jcy9ndWlkZS90aGVtZQ==" title="https://www.iviewui.com/docs/guide/theme">https://www.iviewui.com/docs/…<i class="fa fa-external-link"></i></span></p><p>原理很简单，就是把ivew的less文件引入，并且覆盖掉，然后在main.js文件中引入自己的less文件</p><p>首先，我们需要下载解析less文件的loader ,<code>less</code>和<code>less-loader</code>，这里有个坑，下载less的时候要下载3版本以下的，不然会报一堆错误</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cnpm i less@2.7.2 less-loader -D</span><br></pre></td></tr></table></figure><p>下载完就ok了，不需要在webpack中进行配置，因为已经配置好了</p><p>然后，在assets/styles/base.less(没有需要自己新建)中，引入iview的样式文件,并且覆盖掉</p><p>默认变量列表：<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2l2aWV3L2l2aWV3L2Jsb2IvMi4wL3NyYy9zdHlsZXMvY3VzdG9tLmxlc3M=" title="https://github.com/iview/iview/blob/2.0/src/styles/custom.less">https://github.com/iview/ivie…<i class="fa fa-external-link"></i></span></p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*assets/styles/base.less*/</span></span><br><span class="line"><span class="comment">/*------ 引入iview样式*/</span></span><br><span class="line">@<span class="keyword">import</span> <span class="string">'~iview/src/styles/index.less'</span>;</span><br><span class="line"><span class="comment">/*------ 覆盖iview的样式*/</span></span><br><span class="line">@primary-color: #E91E63;</span><br><span class="line">@<span class="keyword">error</span>-<span class="keyword">color</span> : #FF3300;</span><br></pre></td></tr></table></figure><p>最后在main.js引入该less文件</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//main.js</span></span><br><span class="line"><span class="keyword">import</span> <span class="string">'./assets/styles/base.less'</span></span><br></pre></td></tr></table></figure><p>此时，引入的组件就可以在.vue文件中使用了，看一下效果：<img src="https://i.loli.net/2019/05/08/5cd23b8241ec6.png" alt></p><p>ok了。最后还要补充一下，在项目开发过程中，不可避免的要覆盖iview默认的样式，我们分为两种情况，一种是全局覆盖，一种是局部覆盖。</p><p>全局覆盖的话我们要新建一个less文件，比如叫<code>cover-iview.less</code>所有覆盖iview样式的代码都放在这里,然后在base.less中引入这个文件。</p><p>局部覆盖的话要注意不要影响到别的样式，所以要充分利用less的作用域,例如我们只需要改home页面下的iview按钮样式，我们可以这样：</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">.home</span>&#123;</span><br><span class="line">    <span class="selector-class">.ivu-btn</span>&#123;</span><br><span class="line">        </span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>##开发中注意问题</p><p>###编写自己的工具库插件</p><blockquote><p>参考文档：</p><p>vue插件说明:<span class="exturl" data-url="aHR0cHM6Ly9jbi52dWVqcy5vcmcvdjIvZ3VpZGUvcGx1Z2lucy5odG1s" title="https://cn.vuejs.org/v2/guide/plugins.html">https://cn.vuejs.org/v2/guide…<i class="fa fa-external-link"></i></span></p></blockquote><p>项目中往往会使用一些通用的函数，比如获取当前时间、时间格式转化，防抖，节流等，我们可以把这个公用的部分封装成插件，在main.js中引入。</p><p>首先，在src目录下新建<code>utils</code>文件夹，在里面新建<code>index.js</code>,<code>utils.js</code>文件</p><p>我们在<code>utils.js</code>中编写自己的工具库，然后导出</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Utils</span></span>&#123;</span><br><span class="line">    <span class="keyword">constructor</span>()&#123;</span><br><span class="line">        <span class="keyword">this</span>.d=<span class="keyword">new</span> <span class="built_in">Date</span>();<span class="comment">//date对象</span></span><br><span class="line">        <span class="keyword">this</span>.instance=<span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">static</span> getInstance()&#123;<span class="comment">//单例模式</span></span><br><span class="line">        <span class="keyword">if</span>(!<span class="keyword">this</span>.instance)&#123;</span><br><span class="line">            <span class="keyword">this</span>.instance = <span class="keyword">new</span> Utils();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.instance;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    pick(obj,arr)&#123;<span class="comment">//pick(&#123; a: 1, b: '2', 'c': 3 &#125;, ['a', 'c'])  =&gt;&#123;a:1,c:3&#125;</span></span><br><span class="line">       <span class="keyword">return</span> arr.reduce(<span class="function">(<span class="params">acc,curr</span>)=&gt;</span>&#123;</span><br><span class="line">            <span class="keyword">return</span> (curr <span class="keyword">in</span> obj &amp;&amp; (acc[curr] = obj[curr]), acc)</span><br><span class="line">        &#125;,&#123;&#125;)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    dateFormat(datetime,pattern=<span class="string">""</span>)&#123;</span><br><span class="line">        <span class="keyword">let</span> vWeek = [<span class="string">"星期天"</span>,<span class="string">"星期一"</span>,<span class="string">"星期二"</span>,<span class="string">"星期三"</span>,<span class="string">"星期四"</span>,<span class="string">"星期五"</span>,<span class="string">"星期六"</span>];</span><br><span class="line">        <span class="keyword">let</span> dt=<span class="keyword">new</span> <span class="built_in">Date</span>(datetime);</span><br><span class="line">        <span class="keyword">let</span> y=dt.getFullYear();</span><br><span class="line">        <span class="keyword">let</span> m=(dt.getMonth()+<span class="number">1</span>).toString().padStart(<span class="number">2</span>,<span class="string">'0'</span>);</span><br><span class="line">        <span class="keyword">let</span> d=dt.getDate().toString().padStart(<span class="number">2</span>,<span class="string">'0'</span>);</span><br><span class="line">        <span class="keyword">let</span> hh=dt.getHours().toString().padStart(<span class="number">2</span>,<span class="string">'0'</span>);</span><br><span class="line">        <span class="keyword">let</span> mm=dt.getMinutes().toString().padStart(<span class="number">2</span>,<span class="string">'0'</span>);</span><br><span class="line">        <span class="keyword">let</span> ss=dt.getSeconds().toString().padStart(<span class="number">2</span>,<span class="string">'0'</span>);</span><br><span class="line">        <span class="keyword">let</span> vWeek_s = dt.getDay();<span class="comment">//星期</span></span><br><span class="line">        <span class="keyword">if</span>(pattern.toLowerCase() === <span class="string">'yyyy-mm-dd'</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="string">`<span class="subst">$&#123;y&#125;</span>-<span class="subst">$&#123;m&#125;</span>-<span class="subst">$&#123;d&#125;</span>`</span></span><br><span class="line">        &#125;<span class="keyword">else</span> <span class="keyword">if</span>(pattern.toLowerCase() === <span class="string">'mm-dd'</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="string">`<span class="subst">$&#123;m&#125;</span>-<span class="subst">$&#123;d&#125;</span>`</span></span><br><span class="line">        &#125;<span class="keyword">else</span> <span class="keyword">if</span>(pattern.toLowerCase() === <span class="string">'yyyymmddhhmmss'</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="string">`<span class="subst">$&#123;y&#125;</span><span class="subst">$&#123;m&#125;</span><span class="subst">$&#123;d&#125;</span><span class="subst">$&#123;hh&#125;</span><span class="subst">$&#123;mm&#125;</span><span class="subst">$&#123;ss&#125;</span>`</span></span><br><span class="line">        &#125;<span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="string">`<span class="subst">$&#123;y&#125;</span>-<span class="subst">$&#123;m&#125;</span>-<span class="subst">$&#123;d&#125;</span> <span class="subst">$&#123;hh&#125;</span>:<span class="subst">$&#123;mm&#125;</span>:<span class="subst">$&#123;ss&#125;</span> <span class="subst">$&#123;vWeek[vWeek_s]&#125;</span>`</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> UTIL = Utils.getInstance();</span><br><span class="line"></span><br><span class="line"><span class="comment">// console.log(UTIL.dateFormat(new Date(),'yyyymmddhhmmss')) //=&gt;20190312110722</span></span><br><span class="line"><span class="comment">// console.log(UTIL.dateFormat(new Date()))//=&gt;2019-03-12 11:07:22 星期二</span></span><br><span class="line"><span class="comment">// console.log(UTIL.pick(&#123; a: 1, b: '2', 'c': 3 &#125;, ['a', 'c']))//=&gt;&#123;a:1,c:3&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> UTIL;</span><br></pre></td></tr></table></figure><p>然后在index.js中编写插件，导出</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//utils/index.js</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> UTIL <span class="keyword">from</span> <span class="string">'./utils.js'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> UtilPlugin=&#123;&#125;</span><br><span class="line"></span><br><span class="line">UtilPlugin.install=<span class="function"><span class="keyword">function</span>(<span class="params">Vue,options</span>)</span>&#123;<span class="comment">//插件必须有install方法，接受两个参数，一个是Vue构造器,一个是参数</span></span><br><span class="line">  Vue.prototype.$utils=UTIL<span class="comment">//在vue prototype上添加实例方法</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> UtilPlugin</span><br></pre></td></tr></table></figure><p>最后在main.js中引入并use插件</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// utils</span></span><br><span class="line"><span class="keyword">import</span> Util <span class="keyword">from</span> <span class="string">'./utils/index'</span></span><br><span class="line">Vue.use(Util)</span><br><span class="line"><span class="built_in">console</span>.log(Vue.prototype.$util)<span class="comment">//打印下是否引入成功</span></span><br></pre></td></tr></table></figure><p>之后就可以在组件中通过使用<code>this.$utils</code>调用方法了</p><h2 id="兼容性处理"><a href="#兼容性处理" class="headerlink" title="兼容性处理"></a>兼容性处理</h2><blockquote><p>我们的目标是兼容到ie9,对ie8及以下的浏览器做相应的跳转处理(跳转到浏览器下载界面)</p><p>兼容性对一个程序来说是非常重要的，兼容性测试越早越好</p></blockquote><p><img src="https://i.loli.net/2019/05/08/5cd23b4d084a4.png" alt></p><h3 id="对ie8及以下浏览器的跳转处理"><a href="#对ie8及以下浏览器的跳转处理" class="headerlink" title="对ie8及以下浏览器的跳转处理"></a>对ie8及以下浏览器的跳转处理</h3><p>在项目根目录下中的html中head中加入下面代码</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--[if lte IE 8]&gt;&lt;script&gt;window.location.href="https://support.dmeng.net/upgrade-your-browser.html?referrer="+encodeURIComponent(window.location.href);&lt;/script&gt;&lt;![endif]--&gt;</span></span><br></pre></td></tr></table></figure><p>目的是检测ie浏览器的版本，如果低于&lt;=ie8，就跳转到下面这个页面 <img src="https://i.loli.net/2019/05/08/5cd23b6314ae7.png" alt></p><h3 id="兼容ie9"><a href="#兼容ie9" class="headerlink" title="兼容ie9"></a>兼容ie9</h3><blockquote><p>参考链接：<span class="exturl" data-url="aHR0cHM6Ly9qdWVqaW4uaW0vcG9zdC81YjI4NjhiNDZmYjlhMDBlNmY2NWY4N2U=" title="https://juejin.im/post/5b2868b46fb9a00e6f65f87e">https://juejin.im/post/5b2868…<i class="fa fa-external-link"></i></span></p></blockquote><h4 id="ES6兼容"><a href="#ES6兼容" class="headerlink" title="ES6兼容"></a>ES6兼容</h4><p>我们把浏览器调到ie9,然后看控制台报错信息 <img src="https://i.loli.net/2019/05/08/5cd23b6e9d3ae.png" alt></p><p>报这个错的原因是es6的新对象，新表达式，ie9不支持，为解决这个问题，我们需要引入<code>babel-polyfill</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cnpm i babel-polyfill -D</span><br></pre></td></tr></table></figure><p>安装完成之后，在main.js文件中引入</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="keyword">import</span> <span class="string">'babel-polyfill'</span></span><br></pre></td></tr></table></figure><p>在项目使用 <code>vue-cli</code> 生成的代码中，根目录有一个 <code>.babelrc</code> 文件，这是项目使用 babel 的配置文件。在默认生成的模板内容中，增加 <code>&quot;useBuiltIns&quot;: &quot;entry&quot;</code> 的设置内容，这是一个指定哪些内容需要被 polyfill(兼容) 的设置</p><p>useBuiltIns 有三个设置选项</p><ul><li><code>false</code> - 不做任何操作</li><li><code>entry</code> - 根据浏览器版本的支持，将 polyfill 需求拆分引入，仅引入有浏览器不支持的polyfill</li><li><code>usage</code> - 检测代码中 <code>ES6/7/8</code> 等的使用情况，仅仅加载代码中用到的 polyfill</li></ul><p>####建立自己的polyfill</p><p>加入这些代码后，工程中大部分代码已可以兼容到ie9版本,但还是会有少部分不兼容的特性，例如<code>requestAnimationFrame</code>、<code>classList</code>等。对于这些内容，我们需要自己定义polyfill来解决，在src目录下新建一个文件夹polyfill,然后在polyfill文件夹下面建一个polyfill.js，我们在polyfill.js中加入我们的兼容代码</p><p>然后在main.js中引入这个文件</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="keyword">import</span> <span class="string">'./polyfill/polyfill'</span></span><br></pre></td></tr></table></figure><p>解决兼容方式的正确姿势是：<code>拿到ie9浏览器下的报错信息，去goole或者baidu搜索，得到polyfill,然后加入到自己的polyfill.js文件中</code></p><h1 id="三、优化"><a href="#三、优化" class="headerlink" title="三、优化"></a>三、优化</h1><h2 id="webpack3-x优化打包速度"><a href="#webpack3-x优化打包速度" class="headerlink" title="webpack3.x优化打包速度"></a>webpack3.x优化打包速度</h2><p>我们执行一下<code>npm run build</code>，结果如下：<img src="https://i.loli.net/2019/05/08/5cd23b92a9498.png" alt></p><p>整个打包过程花了32s左右，现在我们的项目只是引入了相关的依赖，一些业务逻辑还没有写，打包速度就那么慢了，等到我们写完整个项目，打包速度还会继续变长，所以我们需要优化一下。</p><blockquote><p>优化打包速度，我们修改的主要是<code>webpack.prod.conf.js</code>文件</p></blockquote><h3 id="替换代码压缩工具"><a href="#替换代码压缩工具" class="headerlink" title="替换代码压缩工具"></a>替换代码压缩工具</h3><p>Webpack 默认提供的 UglifyJS 插件，由于采用单线程压缩，速度慢 ；</p><p><span class="exturl" data-url="aHR0cHM6Ly93d3cubnBtanMuY29tL3BhY2thZ2Uvd2VicGFjay1wYXJhbGxlbC11Z2xpZnktcGx1Z2lu" title="https://www.npmjs.com/package/webpack-parallel-uglify-plugin">webpack-parallel-uglify-plugin<i class="fa fa-external-link"></i></span> 插件可以并行运行 UglifyJS 插件，更加充分而合理的使用 CPU 资源，这可以大大减少的构建时间；</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//安装</span></span><br><span class="line">cnpm i webpack-parallel-uglify-plugin -D</span><br><span class="line"><span class="comment">//配置 webpack.prod.conf.js</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//首先删除项目中的 UglifyJsPlugin插件及配置,第二次打包时提高速度，要把.cache文件加入到gitignore中</span></span><br><span class="line"><span class="comment">// new webpack.optimize.UglifyJsPlugin(&#123;</span></span><br><span class="line"><span class="comment">//   compress: &#123;</span></span><br><span class="line"><span class="comment">//     warnings: false,</span></span><br><span class="line"><span class="comment">//     drop_console: true</span></span><br><span class="line"><span class="comment">//   &#125;,</span></span><br><span class="line"><span class="comment">//   sourceMap: true</span></span><br><span class="line"><span class="comment">// &#125;),</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//然后引入并使用我们刚才装的插件</span></span><br></pre></td></tr></table></figure><p>==注意：版本控制工具提交时，要忽略.<code>cache</code>文件==</p><p>配置完后我们执行<code>npm run build</code>,发现打包速度降到了23s<br><img src="https://i.loli.net/2019/05/08/5cd23bae1c64b.jpg" alt></p><p>再执行一次<code>npm run build</code>,发现打包速度降到了12s<br><img src="https://i.loli.net/2019/05/08/5cd23bae8de3b.jpg" alt></p><p>时间降低那么多是因为文件没有改动，直接利用了缓存中的js文件</p><h3 id="happypack开启多核构建项目"><a href="#happypack开启多核构建项目" class="headerlink" title="happypack开启多核构建项目"></a>happypack开启多核构建项目</h3><p>一般node.js是单线程执行编译，而happypack则是启动node的多线程进行构建，大大提高了构建速度。</p><p>首先安装，</p><p>修改<code>webpack.base.conf.js</code></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> HappyPack = <span class="built_in">require</span>(<span class="string">'happypack'</span>);</span><br><span class="line"><span class="keyword">const</span> os = <span class="built_in">require</span>(<span class="string">'os'</span>);</span><br><span class="line"><span class="keyword">const</span> happyThreadPool = HappyPack.ThreadPool(&#123; <span class="attr">size</span>: os.cpus().length &#125;);</span><br><span class="line">...</span><br><span class="line">...</span><br><span class="line"><span class="comment">// 增加plugins</span></span><br><span class="line"> plugins: [</span><br><span class="line">  <span class="keyword">new</span> HappyPack(&#123;</span><br><span class="line">    id: <span class="string">'happy-babel-js'</span>,</span><br><span class="line">    loaders: [<span class="string">'babel-loader?cacheDirectory=true'</span>],</span><br><span class="line">    threadPool: happyThreadPool,</span><br><span class="line">  &#125;)</span><br><span class="line">]</span><br><span class="line">...</span><br><span class="line">...</span><br><span class="line"><span class="comment">// 修改对应loader</span></span><br><span class="line">&#123;</span><br><span class="line">  test: <span class="regexp">/\.js$/</span>,</span><br><span class="line">  loader: <span class="string">'happypack/loader?id=happy-babel-js'</span>,</span><br><span class="line">  include: [resolve(<span class="string">'src'</span>), resolve(<span class="string">'test'</span>)],</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>配置完成，执行<code>npm run build</code></p><p><img src="https://i.loli.net/2019/05/08/5cd23baf0a13b.jpg" alt></p><p>what??并没有提高速度 不要用这个鬼东西了</p><h3 id="hardSourceWebpackPlugin节省70-的时间"><a href="#hardSourceWebpackPlugin节省70-的时间" class="headerlink" title="hardSourceWebpackPlugin节省70%的时间"></a>hardSourceWebpackPlugin节省70%的时间</h3><blockquote><p><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL216Z29kZGFyZC9oYXJkLXNvdXJjZS13ZWJwYWNrLXBsdWdpbg==" title="https://github.com/mzgoddard/hard-source-webpack-plugin">https://github.com/mzgoddard/…<i class="fa fa-external-link"></i></span></p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#安装</span></span><br><span class="line">cnpm install --save-dev hard-source-webpack-plugin</span><br></pre></td></tr></table></figure><p>使用，在webpack.prod.conf.js中引入并使用</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> HardSourceWebpackPlugin = <span class="built_in">require</span>(<span class="string">'hard-source-webpack-plugin'</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  context: <span class="comment">// ...</span></span><br><span class="line">  entry: <span class="comment">// ...</span></span><br><span class="line">  output: <span class="comment">// ...</span></span><br><span class="line">  plugins: [</span><br><span class="line">    <span class="keyword">new</span> HardSourceWebpackPlugin()</span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>结果：<img src="https://i.loli.net/2019/05/08/5cd23bafa7aef.jpg" alt></p><p>注：要第二次打包才生效</p><p>总结下，使用了三个插件，我们的打包速度从30s降低到4s,awesome!</p><h2 id="webpack3-x优化首屏加载速度"><a href="#webpack3-x优化首屏加载速度" class="headerlink" title="webpack3.x优化首屏加载速度"></a>webpack3.x优化首屏加载速度</h2><p>首先要说明一下，首屏加载速度优化针对的是打包后dist文件。我们如果要在本地进行测试的话，需要本地有个服务器，我们在这里使用nginx。</p><p>###本地安装nginx</p><blockquote><p>下载地址： <span class="exturl" data-url="aHR0cDovL25naW54Lm9yZy9lbi9kb3dubG9hZC5odG1s" title="http://nginx.org/en/download.html">http://nginx.org/en/download….<i class="fa fa-external-link"></i></span></p></blockquote><p>在官网上找到自己系统适合的nginx版本，下载到本地</p><p>####window安装</p><ol><li>解压文件</li><li>双击运行nginx.exe，在任务管理器中出现nginx的进程，则表示安装成功</li></ol><h4 id="mac-linux安装"><a href="#mac-linux安装" class="headerlink" title="mac/linux安装"></a>mac/linux安装</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#1.解压文件</span></span><br><span class="line">tar -xzf nginx-1.14.0.tar.gz  <span class="comment">#mac可以使用解压缩工具解压，不必用命令行</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#2. 配置安装路径   --prefix指定安装路径  假设我要装到/usr/local/nginx文件夹中</span></span><br><span class="line">./configure --prefix=/Users/best9/<span class="built_in">local</span>/nginx</span><br><span class="line"></span><br><span class="line"><span class="comment">#编译</span></span><br><span class="line">make</span><br><span class="line"></span><br><span class="line"><span class="comment">##安装</span></span><br><span class="line">make install</span><br></pre></td></tr></table></figure><p>安装完成后进入到<code>—prefix</code>指定的文件夹中，执行<code>ll</code>,会发现文件夹下有以下目录</p><p><img src="https://i.loli.net/2019/05/08/5cd23bb0751ec.jpg" alt></p><p>我们要关心就是我上面标出来的三个目录</p><p>进到sbin目录中，启动nginx程序</p><p><img src="https://i.loli.net/2019/05/08/5cd23bb11aba5.jpg" alt></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="built_in">cd</span> sbin</span><br><span class="line"><span class="comment">#需要使用root权限，否则会报错  报错信息可以在日志中查看到,错误日志目录 /logs/error.log</span></span><br><span class="line">sudo ./nginx</span><br></pre></td></tr></table></figure><p>正常的话，nginx会默认在localhost:80端口启动，在浏览器访问<code>localhost</code>，就会显示默认界面</p><p><img src="https://i.loli.net/2019/05/08/5cd23bb2094c4.jpg" alt></p><p>如果电脑的80端口被占用的话，在<code>conf/nginx.conf</code>文件中修改端口</p><h3 id="nginx常用命令"><a href="#nginx常用命令" class="headerlink" title="nginx常用命令"></a>nginx常用命令</h3><p>nginx使用-s发送信号操作运行中的进程,常用命令如下：</p><p>注意：使用命令需要在<code>sbin</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"><span class="comment">#启动nginx</span></span><br><span class="line">./nginx</span><br><span class="line"><span class="comment">#立即停止服务 -s stop</span></span><br><span class="line">./nginx -s stop</span><br><span class="line"><span class="comment">#优雅地停止服务 -s quit</span></span><br><span class="line">./nginx -s quit</span><br><span class="line"><span class="comment">#重启服务 -s reload</span></span><br><span class="line">./nginx -s reload</span><br></pre></td></tr></table></figure><h3 id="nginx配置静态文件服务器"><a href="#nginx配置静态文件服务器" class="headerlink" title="nginx配置静态文件服务器"></a>nginx配置静态文件服务器</h3><blockquote><p>我们在这里使用nginx配置一个最简单的静态文件服务器，更复杂的配置稍后再讲</p></blockquote><p>nginx的配置文件地址：<code>conf/nginx.conf</code></p><p>使用vim或者其他编辑器打开该文件，修改配置文件第43-45行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim conf/nginx.conf</span><br></pre></td></tr></table></figure><p><img src="https://i.loli.net/2019/05/08/5cd23bb2c93c9.jpg" alt></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">location / &#123;</span><br><span class="line">  alias /Users/best9/github/vue2_template/dist;  #访问/相当于访问alias配置的目录    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>配置完成后保存，然后重启服务</p><p><code>sudo ./sbin/nginx -s reload</code> 要使用root权限重启</p><p>打开浏览器访问localhost</p><p><img src="https://i.loli.net/2019/05/08/5cd23bb39b8ba.jpg" alt></p><p>因为没有登录，会自动跳转到登录界面</p><p>到这里静态文件服务器就配置好了，但我们刷新下页面，会报错404</p><p><img src="https://i.loli.net/2019/05/08/5cd23bb44e5fb.jpg" alt></p><p>这是因为我们使用了vue router的history模式，我们需要在nginx中加入以下配置</p><p><img src="https://i.loli.net/2019/05/08/5cd23be88d4ef.jpg" alt></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">location / &#123;</span><br><span class="line">  try_files $uri $uri/  <span class="regexp">/index.html;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure><p>然后重启nginx，再刷新页面就没问题了</p><h3 id="优化首屏加载速度"><a href="#优化首屏加载速度" class="headerlink" title="优化首屏加载速度"></a>优化首屏加载速度</h3><p>以上步骤就绪后，我们就可以来优化加载速度了</p><p>打开chrome的devTools面板，切换到<code>Network</code>，禁用浏览器缓存，刷新测试下加载速度，发现整个应用加载大约需要1.97s,如下图：</p><p><img src="https://i.loli.net/2019/05/08/5cd23be980a73.jpg" alt></p><p>把网络环境切换到<code>Fast 3G</code>,再测试一次，发现加载用了7.56s，白屏时间6.89s</p><p><img src="https://i.loli.net/2019/05/08/5cd23bea8f44b.jpg" alt></p><p>我们使用预渲染插件进行优化</p><h4 id="预渲染"><a href="#预渲染" class="headerlink" title="预渲染"></a>预渲染</h4><blockquote><p>使用插件：prerender-spa-plugin</p><p>参考链接：<span class="exturl" data-url="aHR0cHM6Ly9qdWVqaW4uaW0vcG9zdC81OWQ0OWQ5NzZmYjlhMDBhNTcxZDY1MWQ=" title="https://juejin.im/post/59d49d976fb9a00a571d651d">https://juejin.im/post/59d49d…<i class="fa fa-external-link"></i></span></p></blockquote><p>首先，安装 <code>prerender-spa-plugin</code>，安装时件略长，因为其依赖了 <code>phantomjs</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cnpm install prerender-spa-plugin --save-dev</span><br></pre></td></tr></table></figure><p>我们只在生产环境中进行预渲染，修改<code>build/webpack.prod.conf.js</code>，在配置插件的地方加入如下代码。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//引入 预渲染插件</span></span><br><span class="line"><span class="keyword">const</span> PrerenderSpaP=<span class="built_in">require</span>(<span class="string">'prerender-spa-plugin'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">//在plugins中配置</span></span><br><span class="line"><span class="keyword">new</span> PrerenderSpaP(</span><br><span class="line">  <span class="comment">// 输出目录的绝对路径</span></span><br><span class="line">  path.join(__dirname,<span class="string">'../dist'</span>),</span><br><span class="line">  <span class="comment">//预渲染路由</span></span><br><span class="line">  [<span class="string">'/home'</span>,<span class="string">'/login'</span>]</span><br><span class="line">)</span><br></pre></td></tr></table></figure><p>再次执行打包，然后再进行测试：</p><p><img src="https://i.loli.net/2019/05/08/5cd23beb9aa6d.jpg" alt></p><p>发现白屏时间为4.10s，在弱网环境下，使用预渲染，大约能缩减2.5秒的白屏时间</p><h5 id="预渲染注意事项"><a href="#预渲染注意事项" class="headerlink" title="预渲染注意事项"></a>预渲染注意事项</h5><ul><li>预渲染的路由不能是动态加载的，否则会报webpackJsonp is not define的错误，要想解决这个错误，可以看这里 <span class="exturl" data-url="aHR0cHM6Ly9qdWVqaW4uaW0vZW50cnkvNTkxMWE0ODdhYzUwMmU0NTAyODRjYWY4" title="https://juejin.im/entry/5911a487ac502e450284caf8">https://juejin.im/entry/5911a…<i class="fa fa-external-link"></i></span></li><li>预渲染的路由不能是需要权限才能访问的页面。预渲染的机制是在本地跑一个chromium浏览器，然后去爬取你预渲染页面的Html，如果你的页面需要权限(登录)才能进入，就爬不到，也不会报错，最终只会渲染不需要权限的页面</li></ul><p>举个例子：</p><p>插件配置如下：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> PrerenderPlugin(&#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">'/about'</span>,<span class="string">'/login'</span>]</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><p>路由配置如下：</p><p><img src="https://image.xuebin.me/006tKfTcgy1g1cjycq2i8j31320u0jv7.jpg" alt></p><h4 id="配置gzip压缩"><a href="#配置gzip压缩" class="headerlink" title="配置gzip压缩"></a>配置gzip压缩</h4><blockquote><p>gzip官方文档 <span class="exturl" data-url="aHR0cDovL25naW54Lm9yZy9lbi9kb2NzL2h0dHAvbmd4X2h0dHBfZ3ppcF9tb2R1bGUuaHRtbA==" title="http://nginx.org/en/docs/http/ngx_http_gzip_module.html">http://nginx.org/en/docs/http…<i class="fa fa-external-link"></i></span></p></blockquote><p>nginx默认是关闭gzip的，我们需要自己打开，并进行一些配置：</p><p><img src="https://image.xuebin.me/006tKfTcgy1g1cjyuzrfxj30rq02ojrq.jpg" alt></p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">gzip:on;  #打开gzip,关闭为off</span><br><span class="line">gzip_min_length 1;  #小于gzip_min_length，不进行压缩(默认单位为byte)</span><br><span class="line">gzip_comp_level 2;  #压缩级别</span><br><span class="line">gzip_types text/plain text/css application/javascript text/javascript image/jpeg image/gif image/png;#指定类型进行gzip压缩</span><br></pre></td></tr></table></figure><p>配置完成后，我们再测试一下加载速度：</p><p><img src="https://image.xuebin.me/006tKfTcgy1g1cjzaj357j31hc0b7mzs.jpg" alt></p><p>发现白屏时间为1.95s,加载文件的体积也变小了</p><h1 id="四、部署"><a href="#四、部署" class="headerlink" title="四、部署"></a>四、部署</h1><h2 id="nginx配置反向代理"><a href="#nginx配置反向代理" class="headerlink" title="nginx配置反向代理"></a>nginx配置反向代理</h2><blockquote><p>我们要在本地部署测试，所以后台的地址是127.0.0.1:22222</p></blockquote><p>项目开发完成后需要部署到服务器，因为是前后端分离，所以前端的应用部署到nginx,后端的应用部署到自己对应的服务器，所以我们需要配置一下，把后端的服务器变成上游服务，nginx做反向代理服务器</p><blockquote><p>反向代理：服务器根据客户端的请求，从其关系的一组或多组后端服务器上获取资源，然后将这些资源返回给客户端。</p></blockquote><p>由于上游服务器(后台服务器)要处理非常复杂的逻辑，所以性能不怎么样，我们使用nginx作为反向代理服务器后，可以将请求按照负载均衡算法代理给多台上游服务器。配置如下：</p><p><img src="https://image.xuebin.me/006tKfTcly1g1ck018u5cj30r10c6wgn.jpg" alt></p><p>以上配置是将所有的请求转发给上游服务器，但如果我们只想将动态请求转发给上游服务器，静态资源由nginx自己处理，就可以这样做：</p><p>判断是否是后台api(根据location的匹配规则),如果是的话，就进行转发</p><p>匹配规则看这里：<span class="exturl" data-url="aHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNTIzODM3Ny9uZ2lueC1sb2NhdGlvbi1wcmlvcml0eQ==" title="https://stackoverflow.com/questions/5238377/nginx-location-priority">https://stackoverflow.com/que…<i class="fa fa-external-link"></i></span></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></pre></td><td class="code"><pre><span class="line">upstream local&#123;</span><br><span class="line">    server 127.0.0.1:22222;  #假设在本地部署</span><br><span class="line">&#125;</span><br><span class="line">server&#123;</span><br><span class="line">    listen:<span class="number">80</span>;</span><br><span class="line">    server_name localhost;</span><br><span class="line">    location ~ /api/ &#123;  #以`/api/`开头的uri就行转发，否则不转发 ~代表正则表达式匹配</span><br><span class="line">        proxy_set_header: Host $host;</span><br><span class="line">        proxy_set_header: X-Real-IP $remote_addr;</span><br><span class="line">        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;</span><br><span class="line">        proxy_pass http:<span class="comment">//local;</span></span><br><span class="line">    &#125;    </span><br><span class="line">    location / &#123;</span><br><span class="line">        #.. alias index等配置 </span><br><span class="line">          </span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>这里需要注意一个问题</strong>：proxy_pass是转发请求的模块，当你访问<code>localhost:80/api/users/login</code>时，会被转发到<code>local</code>的地址，即<code>127.0.0.1:22222/api/users/login</code>,所以开发环境下访问后台接口的URI要写你部署到nginx的URI，而不是真正的后台地址(因为被转发了)</p><p>前端配置</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//apis/api.config.js</span></span><br><span class="line"><span class="comment">//判断是否是生产环境</span></span><br><span class="line"><span class="keyword">var</span> isPro = process.env.NODE_ENV=== <span class="string">'production'</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">    baseUrl: isPro ? <span class="string">'http://localhost:80'</span> : <span class="string">'/apis'</span><span class="comment">//生产环境下的baseURl是nginx的hoost:port</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="持续部署"><a href="#持续部署" class="headerlink" title="持续部署"></a>持续部署</h2><p>项目做完需要发布到服务器，但每次手动打包，然后ftp传上去的话就太麻烦了，所以我们的需求是：git或者svn提交后，自动打包发布到服务器。使用的工具是jenkins.</p><blockquote><p>参考文档：</p><p><span class="exturl" data-url="aHR0cHM6Ly9qdWVqaW4uaW0vcG9zdC81YWQxOTguLg==" title="https://juejin.im/post/5ad198..">https://juejin.im/post/5ad198..<i class="fa fa-external-link"></i></span>.</p><p>官网：<span class="exturl" data-url="aHR0cHM6Ly9qZW5raW5zLmlvLw==" title="https://jenkins.io/">https://jenkins.io/<i class="fa fa-external-link"></i></span></p></blockquote><h3 id="jenkins安装与启动"><a href="#jenkins安装与启动" class="headerlink" title="jenkins安装与启动"></a>jenkins安装与启动</h3><blockquote><p>jenkins一般情况下会装在服务器，但如果是同一个局域网的话，装在本机也可以</p></blockquote><p>linux:</p><ol><li><span class="exturl" data-url="aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlbmdsYWlsZWEvYXJ0aWNsZS9kZXRhaWxzLzI1OTE5MzY3KHVidW50dQ==" title="https://blog.csdn.net/fenglailea/article/details/25919367(ubuntu">https://blog.csdn.net/fenglai…<i class="fa fa-external-link"></i></span>)</li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cuamlhbnNodS5jb20vcC84YTc3MDEwZGFmYzY=" title="https://www.jianshu.com/p/8a77010dafc6">https://www.jianshu.com/p/8a7…<i class="fa fa-external-link"></i></span> (centos)</li><li>配置文件地址 /etc/sysconfig/jenkins</li><li>工作空间 /var/lib/jenkins</li></ol><p>windows下：</p><ol><li>从Jenkins官网<span class="exturl" data-url="aHR0cHM6Ly9qZW5raW5zLmlvL2Rvd25sb2FkL3RoYW5rLXlvdS1kb3dubG9hZGluZy13aW5kb3dzLWluc3RhbGxlci1zdGFibGUv" title="https://jenkins.io/download/thank-you-downloading-windows-installer-stable/">下载<i class="fa fa-external-link"></i></span>最新war文件。</li><li>运行<code>java -jar jenkins.war</code>即可。</li></ol><p>mac:</p><ol><li>从官网<span class="exturl" data-url="aHR0cHM6Ly9qZW5raW5zLmlvL2Rvd25sb2FkL3RoYW5rLXlvdS1kb3dubG9hZGluZy1vc3gtaW5zdGFsbGVyLw==" title="https://jenkins.io/download/thank-you-downloading-osx-installer/">下载<i class="fa fa-external-link"></i></span>pkg文件</li><li>双击安装，安装之后自己就会启动</li></ol><h3 id="jenkins初始化"><a href="#jenkins初始化" class="headerlink" title="jenkins初始化"></a>jenkins初始化</h3><ol><li>jenkins的默认端口是8080,启动成功后在浏览器打开。</li><li>进入后会让我们输管理员密码，打开网页上提示路径下的文件，复制密码粘贴输入即可。</li><li>然后会让安装需要的插件，此处选默认即可，等待安装完成。</li><li>创建一个管理员账户。</li><li>上面都完成后会看到这个界面。</li></ol><p><img src="https://image.xuebin.me/006tKfTcly1g1ck0fbaj6j30hn0a9mx5.jpg" alt></p><h3 id="创建任务"><a href="#创建任务" class="headerlink" title="创建任务"></a>创建任务</h3><p>在主页上点击<code>创建</code></p><p><img src="https://image.xuebin.me/006tKfTcly1g1ck0y0543j31510jpdip.jpg" alt></p><p>直接点<code>保存</code>，然后去安装插件</p><p><img src="https://image.xuebin.me/006tKfTcly1g1ck1al8ukj315s0osdho.jpg" alt></p><h3 id="安装插件"><a href="#安装插件" class="headerlink" title="安装插件"></a>安装插件</h3><p>首先返回主页，然后点击左侧菜单 <code>系统管理</code>-&gt;<code>插件管理</code></p><p><img src="https://image.xuebin.me/006tKfTcly1g1ck1pyssmj31fy0u0wji.jpg" alt></p><p>需要安装的插件有：</p><ul><li>Generic Webhook Trigger 实现git提交触发更新功能</li><li>Publish Over SSH 实现服务器部署功能</li><li>nvm wrapper 引入node</li></ul><p>安装插件的方式：</p><p><img src="https://image.xuebin.me/006tKfTcly1g1ck2y5ew7j31f40mo77a.jpg" alt></p><p>安装完插件之后重启一下jenkins(安装完插件后，有个重启的选项，勾选即可)</p><h3 id="实现git钩子功能"><a href="#实现git钩子功能" class="headerlink" title="实现git钩子功能"></a>实现git钩子功能</h3><blockquote><p>当我们向github/码云等远程仓库push我们的代码时，jenkins能知道我们提交了代码，这是自动构建自动部署的前提，钩子的实现原理是在远端仓库上配置一个Jenkins服务器的接口地址，当本地向远端仓库发起push时，远端仓库会向配置的Jenkins服务器的接口地址发起一个带参数的请求，jenkins收到后开始工作</p></blockquote><p>打开创建的项目(进入工程-&gt;点击<code>配置</code>)</p><p><img src="https://image.xuebin.me/006tKfTcly1g1ck49b13mj31fj0u0n10.jpg" alt></p><p>构建触发器</p><p>勾选 Generic Webhook Trigger</p><p><img src="https://image.xuebin.me/006tKfTcly1g1ck53a0bxj313w0eidhd.jpg" alt></p><p>github仓库配置钩子：</p><p>进入github项目中该项目页面，点击<code>setting</code>-&gt;<code>webhooks</code>,添加payload URL，</p><p>URL格式为 <code>http://&lt;User ID&gt;:&lt;API Token&gt;@&lt;Jenkins IP地址&gt;:端口/generic-webhook-trigger/invoke</code> userid和api token在jenkins的<code>系统管理</code>-<code>管理用户</code>-<code>选择你的用户点进去</code>-<code>左侧设置</code>里</p><p><img src="https://image.xuebin.me/006tKfTcly1g1ck5m3yxwj31ru0u07ds.jpg" alt></p><h3 id="实现自动化构建"><a href="#实现自动化构建" class="headerlink" title="实现自动化构建"></a>实现自动化构建</h3><p>自动化构建：jenkins实现安装依赖，打包(npm install &amp;&amp; npm run build),此外还可以执行一些测试行为</p><p>点击<code>构建环境</code>,勾选<code>nvm</code>，输入node版本</p><p><img src="https://image.xuebin.me/006tKfTcly1g1ck64qy4sj31gk0rkdjs.jpg" alt></p><p>点击<code>构建</code>,选择<code>执行shell</code>,输入执行命令，多个命令使用&amp;&amp;分开</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">npm config <span class="built_in">set</span> registry http://registry.npm.taobao.org/ &amp;&amp;</span><br><span class="line">npm install &amp;&amp; </span><br><span class="line">npm run build</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;blockquote class=&quot;blockquote-center&quot;&gt;&lt;p&gt;转载自: &lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9zZWdtZW50ZmF1bHQuY29tL2EvMTE5MDAwMDAxODYxOTU3Ng==&quot; title=&quot;https://segmentfault.com/a/1190000018619576&quot;&gt;https://segmentfault.com/a/1190000018619576&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这篇文章总结了vue项目的所遇到的问题，包括跨域、用户认证、接口统一管理、路由配置、兼容性处理，性能优化等内容。&lt;/p&gt;
&lt;p&gt;项目github地址 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;前端 &lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9naXRodWIuY29tL2h1YW5neWFuZ3RlbmcvdnVlMi10ZW1wbGF0ZQ==&quot; title=&quot;https://github.com/huangyangteng/vue2-template&quot;&gt;https://github.com/huangyangt…&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;后端: &lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9naXRodWIuY29tL2h1YW5neWFuZ3RlbmcvdnVlMi10ZW1wbGF0ZS1zZXJ2ZXI=&quot; title=&quot;https://github.com/huangyangteng/vue2-template-server&quot;&gt;https://github.com/huangyangt…&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/05/08/5cd23b0ccd527.jpg&quot; alt&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="前端" scheme="https://xuebin.me/categories/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="vue" scheme="https://xuebin.me/categories/%E5%89%8D%E7%AB%AF/vue/"/>
    
    
      <category term="dev" scheme="https://xuebin.me/tags/dev/"/>
    
      <category term="前端" scheme="https://xuebin.me/tags/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="开发" scheme="https://xuebin.me/tags/%E5%BC%80%E5%8F%91/"/>
    
      <category term="vue" scheme="https://xuebin.me/tags/vue/"/>
    
      <category term="转载" scheme="https://xuebin.me/tags/%E8%BD%AC%E8%BD%BD/"/>
    
  </entry>
  
  <entry>
    <title>webpack4配置</title>
    <link href="https://xuebin.me/posts/81e824f6.html"/>
    <id>https://xuebin.me/posts/81e824f6.html</id>
    <published>2019-03-22T08:01:06.000Z</published>
    <updated>2019-03-22T08:01:06.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><h1 id="webpack-核心概念"><a href="#webpack-核心概念" class="headerlink" title="webpack 核心概念"></a>webpack 核心概念</h1><ul><li><p><strong>Entry</strong>: 入口</p></li><li><p><strong>Module</strong>:模块，webpack中一切皆是模块</p></li><li><p><strong>Chunk</strong>:代码库，一个chunk由十多个模块组合而成，用于代码合并与分割</p></li><li><p><strong>Loader</strong>:模块转换器，用于把模块原内容按照需求转换成新内容</p></li><li><p><strong>Plugin</strong>:扩展插件，在webpack构建流程中的特定时机注入扩展逻辑来改变构建结果或做你想要做的事情</p></li><li><p><strong>Output</strong>: 输出结果</p></li></ul><a id="more"></a><h1 id="webpack流程"><a href="#webpack流程" class="headerlink" title="webpack流程"></a>webpack流程</h1><p>webpack启动后会从 Entry 里配置的 Module 开始递归解析 Entry 依赖的所有Module.每找到一个Module,就会根据配置的Loader去找出对应的转换规则，对Module进行转换后，再解析出当前的Module依赖的Module.这些模块会以Entry为单位进行分组，一个Entry和其所有依赖的Module被分到一个组也就是一个Chunk。最好Webpack会把所有Chunk转换成文件输出。在整个流程中Webpack会在恰当的时机执行Plugin里定义的逻辑。</p><p>下面我们开始从零开始配置一个支持打包图片,CSS,LESS,SASS,支持ES6/ES7和JSX语法，并对代码进行压缩的webpack配置.</p><h2 id="最简webpack配置"><a href="#最简webpack配置" class="headerlink" title="最简webpack配置"></a>最简webpack配置</h2><p>首先初始化npm和安装webpack的依赖:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">npm init -y</span><br><span class="line">npm install --save-dev webpack webpack-cli</span><br></pre></td></tr></table></figure><p>配置 webpack.config.js 文件如下:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">const path = require(<span class="string">'path'</span>);</span><br><span class="line"></span><br><span class="line">module.exports = &#123;</span><br><span class="line">    entry: path.resolve(__dirname, <span class="string">'src/index.js'</span>),</span><br><span class="line">    output: &#123;</span><br><span class="line">        path: path.resolve(__dirname, <span class="string">'dist'</span>),</span><br><span class="line">        filename: <span class="string">'bundle.js'</span>,</span><br><span class="line">        publicPath: <span class="string">'/'</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p><strong>说明:</strong> publicPath 上线时配置的是cdn的地址。</p></blockquote><p>使用命令进行打包:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">webpack --mode production</span><br></pre></td></tr></table></figure><p>也可以将其配置到 package.json 中的 scripts 字段.</p><p>入口文件为 src/index.js, 打包输出到 dist/bundle.js.</p><h2 id="使用模板-html"><a href="#使用模板-html" class="headerlink" title="使用模板 html"></a>使用模板 html</h2><p>html-webpack-plugin 可以指定template模板文件，将会在output目录下，生成html文件，并引入打包后的js.</p><p>安装依赖:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install --save-dev html-webpack-plugin</span><br></pre></td></tr></table></figure><p>在 webpack.config.js 增加 plugins 配置:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">const HtmlWebpackPlugin = require(<span class="string">'html-webpack-plugin'</span>);</span><br><span class="line">module.exports = &#123;</span><br><span class="line">    //...other code</span><br><span class="line">    plugins: [</span><br><span class="line">        new HtmlWebpackPlugin(&#123;</span><br><span class="line">            template: path.resolve(__dirname, <span class="string">'src/index.html'</span>)</span><br><span class="line">        &#125;)</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>HtmlWebpackPlugin 还有一些其它的参数,如title(html的title),minify(是否要压缩),filename(dist中生成的html的文件名)等</p><h2 id="配置-webpack-dev-server"><a href="#配置-webpack-dev-server" class="headerlink" title="配置 webpack-dev-server"></a>配置 webpack-dev-server</h2><p>webpack-dev-server提供了一个简单的Web服务器和实时热更新的能力</p><p>安装依赖:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install --save-dev webpack-dev-server</span><br></pre></td></tr></table></figure><p>在 webpack.config.js 增加 devServer 配置:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">const path = require(<span class="string">'path'</span>);</span><br><span class="line">const HtmlWebpackPlugin = require(<span class="string">'html-webpack-plugin'</span>);</span><br><span class="line">module.exports = &#123;</span><br><span class="line">    //...other code</span><br><span class="line">    devServer: &#123;</span><br><span class="line">        contentBase: <span class="string">'./dist'</span>,</span><br><span class="line">        port: <span class="string">'8080'</span>,</span><br><span class="line">        host: <span class="string">'localhost'</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在 package.json 的 scripts 字段中增加:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">webpack-dev-server --mode development</span><br></pre></td></tr></table></figure><p>之后，我们就可以通过 npm run dev , 来启动服务。</p><p>更多 webpack-dev-server 的知识，请访问: <span class="exturl" data-url="aHR0cHM6Ly93ZWJwYWNrLmpzLm9yZy9jb25maWd1cmF0aW9uL2Rldi1zZXJ2ZXIv" title="https://webpack.js.org/configuration/dev-server/">webpack.js.org/configurati…<i class="fa fa-external-link"></i></span></p><h2 id="支持加载css文件"><a href="#支持加载css文件" class="headerlink" title="支持加载css文件"></a>支持加载css文件</h2><p>通过使用不同的 style-loader 和 css-loader, 可以将 css 文件转换成JS文件类型。</p><p>安装依赖:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install --save-dev style-loader css-loader</span><br></pre></td></tr></table></figure><p>在 webpack.config.js 中增加 loader 的配置。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">    <span class="comment">//other code</span></span><br><span class="line">    <span class="built_in">module</span>: &#123;</span><br><span class="line">        rules: [</span><br><span class="line">            &#123;</span><br><span class="line">                test: <span class="regexp">/\.css/</span>,</span><br><span class="line">                use: [<span class="string">'style-loader'</span>, <span class="string">'css-loader'</span>],</span><br><span class="line">                exclude: <span class="regexp">/node_modules/</span>,</span><br><span class="line">                include: path.resolve(__dirname, <span class="string">'src'</span>)</span><br><span class="line">            &#125;</span><br><span class="line">        ]</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>loader 可以配置以下参数:</p><ul><li>test: 匹配处理文件的扩展名的正则表达式</li><li>use: loader名称</li><li>include/exclude: 手动指定必须处理的文件夹或屏蔽不需要处理的文件夹</li><li>query: 为loader提供额外的设置选项</li></ul><p>如果需要给loader传参，那么可以使用use+loader的方式,如:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">    <span class="comment">//other code</span></span><br><span class="line">    <span class="built_in">module</span>: &#123;</span><br><span class="line">        rules: [</span><br><span class="line">            &#123;</span><br><span class="line">            use: [&#123;</span><br><span class="line">                        loader: <span class="string">'style-loader'</span>,</span><br><span class="line">                        options: &#123;</span><br><span class="line">                            insertAt: <span class="string">'top'</span></span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;,</span><br><span class="line">                    <span class="string">'css-loader'</span></span><br><span class="line">                ],</span><br><span class="line">                <span class="comment">//....</span></span><br><span class="line">            &#125;</span><br><span class="line">        ]</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="支持加载图片"><a href="#支持加载图片" class="headerlink" title="支持加载图片"></a>支持加载图片</h2><ul><li>file-loader: 解决CSS等文件中的引入图片路径问题</li><li>url-loader: 当图片小于limit的时候会把图片Base64编码，大于limit参数的时候还是使用file-loader进行拷贝</li></ul><p><strong>如果希望图片存放在单独的目录下，那么需要指定outputPath</strong></p><p>安装依赖:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install --save-dev url-loder file-loader</span><br></pre></td></tr></table></figure><p>在 webpack.config.js 中增加 loader 的配置(增加在 module.rules 的数组中)。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">    <span class="comment">//other code</span></span><br><span class="line">    <span class="built_in">module</span>: &#123;</span><br><span class="line">        rules: [</span><br><span class="line">            &#123;</span><br><span class="line">                test: <span class="regexp">/\.(gif|jpg|png|bmp|eot|woff|woff2|ttf|svg)/</span>,</span><br><span class="line">                use: [</span><br><span class="line">                    &#123;</span><br><span class="line">                        loader: <span class="string">'url-loader'</span>,</span><br><span class="line">                        options: &#123;</span><br><span class="line">                            limit: <span class="number">8192</span>,</span><br><span class="line">                            outputPath: <span class="string">'images'</span></span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                ]</span><br><span class="line">            &#125;</span><br><span class="line">        ]</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="支持编译less和sass"><a href="#支持编译less和sass" class="headerlink" title="支持编译less和sass"></a>支持编译less和sass</h2><p>有些前端同事可能习惯于使用less或者是sass编写css，那么也需要在 webpack 中进行配置。</p><p>安装对应的依赖:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">npm install --save-dev less less-loader</span><br><span class="line">npm install --save-dev node-sass sass-loader</span><br></pre></td></tr></table></figure><p>在 webpack.config.js 中增加 loader 的配置(module.rules 数组中)。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">    <span class="comment">//other code</span></span><br><span class="line">    <span class="built_in">module</span>: &#123;</span><br><span class="line">        rules: [</span><br><span class="line">            &#123;</span><br><span class="line">                test: <span class="regexp">/\.less/</span>,</span><br><span class="line">                use: [<span class="string">'style-loader'</span>, <span class="string">'css-loader'</span>, <span class="string">'less-loader'</span>],</span><br><span class="line">                exclude: <span class="regexp">/node_modules/</span>,</span><br><span class="line">                include: path.resolve(__dirname, <span class="string">'src'</span>)</span><br><span class="line">            &#125;,</span><br><span class="line">            &#123;</span><br><span class="line">                test: <span class="regexp">/\.scss/</span>,</span><br><span class="line">                use: [<span class="string">'style-loader'</span>, <span class="string">'css-loader'</span>, <span class="string">'sass-loader'</span>],</span><br><span class="line">                exclude: <span class="regexp">/node_modules/</span>,</span><br><span class="line">                include: path.resolve(__dirname, <span class="string">'src'</span>)</span><br><span class="line">            &#125;</span><br><span class="line">        ]</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="支持转义-ES6-ES7-JSX"><a href="#支持转义-ES6-ES7-JSX" class="headerlink" title="支持转义 ES6/ES7/JSX"></a>支持转义 ES6/ES7/JSX</h2><p>ES6/ES7/JSX 转义需要 Babel 的依赖，支持装饰器。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install --save-dev @babel/core babel-loader @babel/preset-env @babel/preset-react @babel/plugin-proposal-decorators @babel/plugin-proposal-object-rest-spread</span><br></pre></td></tr></table></figure><p>在 webpack.config.js 中增加 loader 的配置(module.rules 数组中)。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">    <span class="comment">//other code</span></span><br><span class="line">    <span class="built_in">module</span>: &#123;</span><br><span class="line">        rules: [</span><br><span class="line">            &#123;</span><br><span class="line">                test: <span class="regexp">/\.jsx?$/</span>,</span><br><span class="line">                use: [</span><br><span class="line">                    &#123;</span><br><span class="line">                        loader: <span class="string">'babel-loader'</span>,</span><br><span class="line">                        options: &#123;</span><br><span class="line">                            presets: [<span class="string">'@babel/preset-env'</span>, <span class="string">'@babel/react'</span>],</span><br><span class="line">                            plugins: [</span><br><span class="line">                                [<span class="built_in">require</span>(<span class="string">"@babel/plugin-proposal-decorators"</span>), &#123; <span class="string">"legacy"</span>: <span class="literal">true</span> &#125;]</span><br><span class="line">                            ]</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                ],</span><br><span class="line">                include: path.resolve(__dirname, <span class="string">'src'</span>),</span><br><span class="line">                exclude: <span class="regexp">/node_modules/</span></span><br><span class="line">            &#125;,</span><br><span class="line">        ]</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="压缩JS文件"><a href="#压缩JS文件" class="headerlink" title="压缩JS文件"></a>压缩JS文件</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">npm install --save-dev uglifyjs-webpack-plugin</span><br><span class="line">npm install --save-dev optimize-css-assets-webpack-plugin</span><br></pre></td></tr></table></figure><p>在 webpack.config.js 中增加 optimization 的配置</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="keyword">const</span> UglifyWebpackPlugin = <span class="built_in">require</span>(<span class="string">'uglifyjs-webpack-plugin'</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">    <span class="comment">//other code</span></span><br><span class="line">    optimization: &#123;</span><br><span class="line">        minimizer: [</span><br><span class="line">            <span class="keyword">new</span> UglifyWebpackPlugin(&#123;</span><br><span class="line">                parallel: <span class="number">4</span></span><br><span class="line">            &#125;)</span><br><span class="line">        ]</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="分离CSS-如果CSS文件较大的话"><a href="#分离CSS-如果CSS文件较大的话" class="headerlink" title="分离CSS(如果CSS文件较大的话)"></a>分离CSS(如果CSS文件较大的话)</h2><p>因为CSS的下载和JS可以并行，当一个HTML文件很大的时候，可以把CSS单独提取出来加载</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install --save-dev mini-css-extract-plugin</span><br></pre></td></tr></table></figure><p>在 webpack.config.js 中增加 plugins 的配置,并且将 ‘style-loader’ 修改为 { loader: MiniCssExtractPlugin.loader}。</p><p>CSS打包在单独目录，那么配置filename。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> MiniCssExtractPlugin = <span class="built_in">require</span>(<span class="string">'mini-css-extract-plugin'</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">    <span class="comment">//other code</span></span><br><span class="line">    <span class="built_in">module</span>: &#123;</span><br><span class="line">        rules: [</span><br><span class="line">            &#123;</span><br><span class="line">                test: <span class="regexp">/\.css/</span>,</span><br><span class="line">                use: [&#123; <span class="attr">loader</span>: MiniCssExtractPlugin.loader&#125;, <span class="string">'css-loader'</span>],</span><br><span class="line">                exclude: <span class="regexp">/node_modules/</span>,</span><br><span class="line">                include: path.resolve(__dirname, <span class="string">'src'</span>)</span><br><span class="line">            &#125;,</span><br><span class="line">            &#123;</span><br><span class="line">                test: <span class="regexp">/\.less/</span>,</span><br><span class="line">                use: [&#123; <span class="attr">loader</span>: MiniCssExtractPlugin.loader &#125;, <span class="string">'css-loader'</span>, <span class="string">'less-loader'</span>],</span><br><span class="line">                exclude: <span class="regexp">/node_modules/</span>,</span><br><span class="line">                include: path.resolve(__dirname, <span class="string">'src'</span>)</span><br><span class="line">            &#125;,</span><br><span class="line">            &#123;</span><br><span class="line">                test: <span class="regexp">/\.scss/</span>,</span><br><span class="line">                use: [&#123; <span class="attr">loader</span>: MiniCssExtractPlugin.loader &#125;, <span class="string">'css-loader'</span>, <span class="string">'sass-loader'</span>],</span><br><span class="line">                exclude: <span class="regexp">/node_modules/</span>,</span><br><span class="line">                include: path.resolve(__dirname, <span class="string">'src'</span>)</span><br><span class="line">            &#125;,</span><br><span class="line">        ]</span><br><span class="line">    &#125;,</span><br><span class="line">    plugins: [</span><br><span class="line">        <span class="keyword">new</span> MiniCssExtractPlugin(&#123;</span><br><span class="line">            filename: <span class="string">'css/[name].css'</span></span><br><span class="line">        &#125;)</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="压缩CSS文件"><a href="#压缩CSS文件" class="headerlink" title="压缩CSS文件"></a>压缩CSS文件</h2><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 optimize-css-assets-webpack-plugin</span><br></pre></td></tr></table></figure><p>在 webpack.config.js 中的 optimization 中增加配置</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">const</span> OptimizeCssAssetsWebpackPlugin = <span class="built_in">require</span>(<span class="string">'optimize-css-assets-webpack-plugin'</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">    <span class="comment">//other code</span></span><br><span class="line">    optimization: &#123;</span><br><span class="line">        minimizer: [</span><br><span class="line">            <span class="keyword">new</span> OptimizeCssAssetsWebpackPlugin()</span><br><span class="line">        ]</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="打包前先清空输出目录"><a href="#打包前先清空输出目录" class="headerlink" title="打包前先清空输出目录"></a>打包前先清空输出目录</h2><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 clean-webpack-plugin</span><br></pre></td></tr></table></figure><p>在 webpack.config.js 中增加 plugins 的配置</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> CleanWebpackPlugin = <span class="built_in">require</span>(<span class="string">'clean-webpack-plugin'</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">    <span class="comment">//other code</span></span><br><span class="line">    plugins: [</span><br><span class="line">        <span class="keyword">new</span> CleanWebpackPlugin()</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>至此，webpack配置已经基本能满足需求。</p><h1 id="完整webpack-config-js和package-json文件"><a href="#完整webpack-config-js和package-json文件" class="headerlink" title="完整webpack.config.js和package.json文件"></a>完整webpack.config.js和package.json文件</h1><h2 id="webpack-config-js文件"><a href="#webpack-config-js文件" class="headerlink" title="webpack.config.js文件"></a>webpack.config.js文件</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>);</span><br><span class="line"><span class="keyword">const</span> htmlWebpackPlugin = <span class="built_in">require</span>(<span class="string">'html-webpack-plugin'</span>);</span><br><span class="line"><span class="keyword">const</span> UglifyWebpackPlugin = <span class="built_in">require</span>(<span class="string">'uglifyjs-webpack-plugin'</span>);</span><br><span class="line"><span class="keyword">const</span> MiniCssExtractPlugin = <span class="built_in">require</span>(<span class="string">'mini-css-extract-plugin'</span>);</span><br><span class="line"><span class="keyword">const</span> CleanWebpackPlugin = <span class="built_in">require</span>(<span class="string">'clean-webpack-plugin'</span>);</span><br><span class="line"><span class="keyword">const</span> OptimizeCssAssetsWebpackPlugin = <span class="built_in">require</span>(<span class="string">'optimize-css-assets-webpack-plugin'</span>);</span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">    entry: path.resolve(__dirname, <span class="string">'src/index.js'</span>),</span><br><span class="line">    output: &#123;</span><br><span class="line">        path: path.resolve(__dirname, <span class="string">'dist'</span>),</span><br><span class="line">        filename: <span class="string">'bundle.js'</span>,</span><br><span class="line">        publicPath: <span class="string">'/'</span></span><br><span class="line">    &#125;,</span><br><span class="line">    devServer: &#123;</span><br><span class="line">        contentBase: <span class="string">'./dist'</span>,</span><br><span class="line">        port: <span class="string">'8080'</span>,</span><br><span class="line">        host: <span class="string">'localhost'</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="built_in">module</span>: &#123;</span><br><span class="line">        rules: [</span><br><span class="line">            &#123;</span><br><span class="line">                test: <span class="regexp">/\.jsx?$/</span>,</span><br><span class="line">                use: [</span><br><span class="line">                    &#123;</span><br><span class="line">                        loader: <span class="string">'babel-loader'</span>,</span><br><span class="line">                        options: &#123;</span><br><span class="line">                            presets: [<span class="string">'@babel/preset-env'</span>, <span class="string">'@babel/react'</span>],</span><br><span class="line">                            plugins: [</span><br><span class="line">                                [<span class="built_in">require</span>(<span class="string">"@babel/plugin-proposal-decorators"</span>), &#123; <span class="string">"legacy"</span>: <span class="literal">true</span> &#125;]</span><br><span class="line">                            ]</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                ],</span><br><span class="line">                include: path.resolve(__dirname, <span class="string">'src'</span>),</span><br><span class="line">                exclude: <span class="regexp">/node_modules/</span></span><br><span class="line">            &#125;,</span><br><span class="line">            &#123;</span><br><span class="line">                test: <span class="regexp">/\.css/</span>,</span><br><span class="line">                use: [&#123; <span class="attr">loader</span>: MiniCssExtractPlugin.loader&#125;, <span class="string">'css-loader'</span>],</span><br><span class="line">                exclude: <span class="regexp">/node_modules/</span>,</span><br><span class="line">                include: path.resolve(__dirname, <span class="string">'src'</span>)</span><br><span class="line">            &#125;,</span><br><span class="line">            &#123;</span><br><span class="line">                test: <span class="regexp">/\.less/</span>,</span><br><span class="line">                use: [&#123; <span class="attr">loader</span>: MiniCssExtractPlugin.loader &#125;, <span class="string">'css-loader'</span>, <span class="string">'less-loader'</span>],</span><br><span class="line">                exclude: <span class="regexp">/node_modules/</span>,</span><br><span class="line">                include: path.resolve(__dirname, <span class="string">'src'</span>)</span><br><span class="line">            &#125;,</span><br><span class="line">            &#123;</span><br><span class="line">                test: <span class="regexp">/\.scss/</span>,</span><br><span class="line">                use: [&#123; <span class="attr">loader</span>: MiniCssExtractPlugin.loader &#125;, <span class="string">'css-loader'</span>, <span class="string">'sass-loader'</span>],</span><br><span class="line">                exclude: <span class="regexp">/node_modules/</span>,</span><br><span class="line">                include: path.resolve(__dirname, <span class="string">'src'</span>)</span><br><span class="line">            &#125;,</span><br><span class="line">            &#123;</span><br><span class="line">                test: <span class="regexp">/\.(gif|jpg|png|bmp|eot|woff|woff2|ttf|svg)/</span>,</span><br><span class="line">                use: [</span><br><span class="line">                    &#123;</span><br><span class="line">                        loader: <span class="string">'url-loader'</span>,</span><br><span class="line">                        options: &#123;</span><br><span class="line">                            limit: <span class="number">1024</span>,</span><br><span class="line">                            outputPath: <span class="string">'images'</span></span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                ]</span><br><span class="line">            &#125;</span><br><span class="line">        ]</span><br><span class="line">    &#125;,</span><br><span class="line">    optimization: &#123;</span><br><span class="line">        minimizer: [</span><br><span class="line">            <span class="keyword">new</span> UglifyWebpackPlugin(&#123;</span><br><span class="line">                parallel: <span class="number">4</span></span><br><span class="line">            &#125;),</span><br><span class="line">            <span class="keyword">new</span> OptimizeCssAssetsWebpackPlugin()</span><br><span class="line">        ]</span><br><span class="line">    &#125;,</span><br><span class="line">    plugins: [</span><br><span class="line">        <span class="keyword">new</span> htmlWebpackPlugin(&#123;</span><br><span class="line">            template: path.resolve(__dirname, <span class="string">'src/index.html'</span>),</span><br><span class="line">        &#125;),</span><br><span class="line">        <span class="keyword">new</span> MiniCssExtractPlugin(&#123;</span><br><span class="line">            filename: <span class="string">'css/[name].css'</span></span><br><span class="line">        &#125;),</span><br><span class="line">        <span class="keyword">new</span> CleanWebpackPlugin()</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="package-json文件"><a href="#package-json文件" class="headerlink" title="package.json文件"></a>package.json文件</h2><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"name"</span>: <span class="string">"webpk"</span>,</span><br><span class="line">  <span class="attr">"version"</span>: <span class="string">"1.0.0"</span>,</span><br><span class="line">  <span class="attr">"description"</span>: <span class="string">""</span>,</span><br><span class="line">  <span class="attr">"main"</span>: <span class="string">"webpack.config.js"</span>,</span><br><span class="line">  <span class="attr">"scripts"</span>: &#123;</span><br><span class="line">    <span class="attr">"test"</span>: <span class="string">"echo \"Error: no test specified\" &amp;&amp; exit 1"</span>,</span><br><span class="line">    <span class="attr">"build"</span>: <span class="string">"webpack --mode production"</span>,</span><br><span class="line">    <span class="attr">"dev"</span>: <span class="string">"webpack-dev-server --mode development"</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="attr">"keywords"</span>: [],</span><br><span class="line">  <span class="attr">"author"</span>: <span class="string">""</span>,</span><br><span class="line">  <span class="attr">"license"</span>: <span class="string">"ISC"</span>,</span><br><span class="line">  <span class="attr">"devDependencies"</span>: &#123;</span><br><span class="line">    <span class="attr">"@babel/core"</span>: <span class="string">"^7.4.0"</span>,</span><br><span class="line">    <span class="attr">"@babel/plugin-proposal-decorators"</span>: <span class="string">"^7.4.0"</span>,</span><br><span class="line">    <span class="attr">"@babel/plugin-proposal-object-rest-spread"</span>: <span class="string">"^7.4.0"</span>,</span><br><span class="line">    <span class="attr">"@babel/preset-env"</span>: <span class="string">"^7.4.1"</span>,</span><br><span class="line">    <span class="attr">"@babel/preset-react"</span>: <span class="string">"^7.0.0"</span>,</span><br><span class="line">    <span class="attr">"babel-loader"</span>: <span class="string">"^8.0.5"</span>,</span><br><span class="line">    <span class="attr">"clean-webpack-plugin"</span>: <span class="string">"^2.0.1"</span>,</span><br><span class="line">    <span class="attr">"css-loader"</span>: <span class="string">"^2.1.1"</span>,</span><br><span class="line">    <span class="attr">"file-loader"</span>: <span class="string">"^3.0.1"</span>,</span><br><span class="line">    <span class="attr">"html-webpack-plugin"</span>: <span class="string">"^3.2.0"</span>,</span><br><span class="line">    <span class="attr">"less"</span>: <span class="string">"^3.9.0"</span>,</span><br><span class="line">    <span class="attr">"less-loader"</span>: <span class="string">"^4.1.0"</span>,</span><br><span class="line">    <span class="attr">"mini-css-extract-plugin"</span>: <span class="string">"^0.5.0"</span>,</span><br><span class="line">    <span class="attr">"node-sass"</span>: <span class="string">"^4.11.0"</span>,</span><br><span class="line">    <span class="attr">"optimize-css-assets-webpack-plugin"</span>: <span class="string">"^5.0.1"</span>,</span><br><span class="line">    <span class="attr">"sass-loader"</span>: <span class="string">"^7.1.0"</span>,</span><br><span class="line">    <span class="attr">"style-loader"</span>: <span class="string">"^0.23.1"</span>,</span><br><span class="line">    <span class="attr">"uglifyjs-webpack-plugin"</span>: <span class="string">"^2.1.2"</span>,</span><br><span class="line">    <span class="attr">"url-loader"</span>: <span class="string">"^1.1.2"</span>,</span><br><span class="line">    <span class="attr">"webpack"</span>: <span class="string">"^4.29.6"</span>,</span><br><span class="line">    <span class="attr">"webpack-cli"</span>: <span class="string">"^3.3.0"</span>,</span><br><span class="line">    <span class="attr">"webpack-dev-server"</span>: <span class="string">"^3.2.1"</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="attr">"dependencies"</span>: &#123;</span><br><span class="line">    <span class="attr">"react"</span>: <span class="string">"^16.8.4"</span>,</span><br><span class="line">    <span class="attr">"react-dom"</span>: <span class="string">"^16.8.4"</span>,</span><br><span class="line">    <span class="attr">"react-redux"</span>: <span class="string">"^6.0.1"</span>,</span><br><span class="line">    <span class="attr">"redux"</span>: <span class="string">"^4.0.1"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>更多loader和plugin的参数可以参考: </p><ul><li><span class="exturl" data-url="aHR0cDovL3d3dy53ZWJwYWNranMuY29tL2xvYWRlcnMv" title="http://www.webpackjs.com/loaders/">www.webpackjs.com/loaders/<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cDovL3d3dy53ZWJwYWNranMuY29tL3BsdWdpbnMv" title="http://www.webpackjs.com/plugins/">www.webpackjs.com/plugins/<i class="fa fa-external-link"></i></span></li></ul></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;webpack-核心概念&quot;&gt;&lt;a href=&quot;#webpack-核心概念&quot; class=&quot;headerlink&quot; title=&quot;webpack 核心概念&quot;&gt;&lt;/a&gt;webpack 核心概念&lt;/h1&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Entry&lt;/strong&gt;: 入口&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Module&lt;/strong&gt;:模块，webpack中一切皆是模块&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Chunk&lt;/strong&gt;:代码库，一个chunk由十多个模块组合而成，用于代码合并与分割&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Loader&lt;/strong&gt;:模块转换器，用于把模块原内容按照需求转换成新内容&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Plugin&lt;/strong&gt;:扩展插件，在webpack构建流程中的特定时机注入扩展逻辑来改变构建结果或做你想要做的事情&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;: 输出结果&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
    
    </summary>
    
      <category term="前端" scheme="https://xuebin.me/categories/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="webpack" scheme="https://xuebin.me/categories/%E5%89%8D%E7%AB%AF/webpack/"/>
    
    
      <category term="dev" scheme="https://xuebin.me/tags/dev/"/>
    
      <category term="前端" scheme="https://xuebin.me/tags/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="开发" scheme="https://xuebin.me/tags/%E5%BC%80%E5%8F%91/"/>
    
      <category term="webpack" scheme="https://xuebin.me/tags/webpack/"/>
    
  </entry>
  
  <entry>
    <title>Mac开机启动项</title>
    <link href="https://xuebin.me/posts/c2d2b2bf.html"/>
    <id>https://xuebin.me/posts/c2d2b2bf.html</id>
    <published>2019-03-20T07:47:51.000Z</published>
    <updated>2019-03-20T07:47:51.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p>相比于Linux开机启动设置的简单方便–配置<code>rc.local</code>文件即可，mac设置开机启动略显麻烦。</p><p>如果是使用homebrew安装软件自然不用考虑开机启动这回事，但有时候想执行自己的脚本或者有特殊需求，就不得不自己处理开机启动了。</p><a id="more"></a><h1 id="使用登录项添加可执行脚本-推荐"><a href="#使用登录项添加可执行脚本-推荐" class="headerlink" title="使用登录项添加可执行脚本(推荐)"></a>使用登录项添加可执行脚本(推荐)</h1><p>在用户的家目录编写一个sh脚本，例如：</p><p>test.sh</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 在用户的家目录创建一个文件</span></span><br><span class="line">touch ~/haha.txt</span><br></pre></td></tr></table></figure><p>添加可执行权限</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">chmod 777 test.sh</span><br></pre></td></tr></table></figure><p><img src="https://img2018.cnblogs.com/blog/1219668/201908/1219668-20190819173502114-2052532113.png" alt></p><p>进入<code>设置</code>-&gt;<code>用户与群组</code>-&gt;<code>登录项</code>，点+号，选择test.sh文件即可。</p><p>重启电脑，验证是否新建了文件，如存在说明配置成功，修改该文件内容为你想要的配置。</p><p>该文件将会在开机时执行，可在命令行中执行的开机启动项均可在此文件中配置。</p><blockquote><p>PS: 开机后会显示一个“进程已结束”的终端，不太爽，需要进入终端的<code>偏好设置</code>-&gt;<code>shell</code>选项卡-&gt;<code>当shell退出时</code>, 选择“关闭窗口”。这样开机时就不会出现一个终端了</p></blockquote><h1 id="Creating-a-login-hook"><a href="#Creating-a-login-hook" class="headerlink" title="Creating a login hook"></a>Creating a login hook</h1><blockquote><p><span class="exturl" data-url="aHR0cHM6Ly9zdXBwb3J0LmFwcGxlLmNvbS9lbi11cy9IVDI0MjA=" title="https://support.apple.com/en-us/HT2420">https://support.apple.com/en-us/HT2420<i class="fa fa-external-link"></i></span></p></blockquote><p>打开<code>/etc/ttys</code>文件</p><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">#console &quot;/System/Library/CoreServices/loginwindow.app/Contents/MacOS/loginwindow&quot; vt100 on secure window=/System/Library/CoreServices/WindowServer onoption=&quot;/usr/libexec/getty std.9600&quot;</span><br></pre></td></tr></table></figure></p><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">#console &quot;/System/Library/CoreServices/loginwindow.app/Contents/MacOS/loginwindow -LoginHook /path/to/script&quot; vt100 on secure window=/System/Library/CoreServices/WindowServer onoption=&quot;/usr/libexec/getty std.9600&quot;</span><br></pre></td></tr></table></figure></p><p>其中 <code>/path/to/script</code> 是脚本文件的完整路径</p><h1 id="launchctl加载plist文件"><a href="#launchctl加载plist文件" class="headerlink" title="launchctl加载plist文件"></a>launchctl加载plist文件</h1><blockquote><p><span class="exturl" data-url="aHR0cDovL2xhdW5jaGVkLnplcm93aWR0aC5jb20v" title="http://launched.zerowidth.com/">launchd.plist generator<i class="fa fa-external-link"></i></span></p><p><span class="exturl" data-url="aHR0cHM6Ly93d3cuc29tYS16b25lLmNvbS9MYXVuY2hDb250cm9sLw==" title="https://www.soma-zone.com/LaunchControl/">LaunchControl<i class="fa fa-external-link"></i></span></p></blockquote><p>macOS开机启动一般使用launchctl加载plist文件</p><h2 id="Mac系统-plist文件分布的目录"><a href="#Mac系统-plist文件分布的目录" class="headerlink" title="Mac系统.plist文件分布的目录"></a>Mac系统.plist文件分布的目录</h2><table><thead><tr><th>目录</th><th>说明</th></tr></thead><tbody><tr><td>~/Library/Preferences</td><td>当前用户设置的进程</td></tr><tr><td>~/Library/LaunchAgents</td><td>单用户普通程序</td></tr><tr><td>/Library/LaunchAgents</td><td>单用户管理员程序</td></tr><tr><td>/Library/LaunchDaemons</td><td>多用户管理员程序</td></tr><tr><td>/System/Library/LaunchAgents</td><td>OS X 级单用户程序</td></tr><tr><td>/System/Library/LaunchDaemons</td><td>OS X 级多用户程序</td></tr></tbody></table><p>建议放在 ~/Library/LaunchAgents 下面。</p><p>下面再来理解几个基础概念：</p><ul><li><p>/System/Library和/Library和~/Library目录的区别？</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">/System/Library 目录是存放Apple自己开发的软件。</span><br><span class="line">/Library 目录是系统管理员存放的第三方软件。</span><br><span class="line">~/Library/ 是用户自己存放的第三方软件。</span><br></pre></td></tr></table></figure></li><li><p>LaunchDaemons和LaunchAgents的区别？</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">LaunchDaemons 是用户未登陆前就启动的服务（守护进程）。</span><br><span class="line">LaunchAgents 是用户登陆后启动的服务（守护进程）。</span><br></pre></td></tr></table></figure></li></ul><h2 id="plist文件示例"><a href="#plist文件示例" class="headerlink" title="plist文件示例"></a>plist文件示例</h2><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN</span></span><br><span class="line"><span class="meta">http://www.apple.com/DTDs/PropertyList-1.0.dtd &gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">plist</span> <span class="attr">version</span>=<span class="string">"1.0"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dict</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">key</span>&gt;</span>Label<span class="tag">&lt;/<span class="name">key</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">string</span>&gt;</span>com.example.exampled<span class="tag">&lt;/<span class="name">string</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">key</span>&gt;</span>ProgramArguments<span class="tag">&lt;/<span class="name">key</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">array</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">string</span>&gt;</span>exampled<span class="tag">&lt;/<span class="name">string</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">array</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">key</span>&gt;</span>KeepAlive<span class="tag">&lt;/<span class="name">key</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">true</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">key</span>&gt;</span>RunAtLoad<span class="tag">&lt;/<span class="name">key</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">true</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dict</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">plist</span>&gt;</span></span><br></pre></td></tr></table></figure><p>launchd.plist的部分键值说明，完整版见<span class="exturl" data-url="aHR0cHM6Ly9kZXZlbG9wZXIuYXBwbGUuY29tL2xlZ2FjeS9saWJyYXJ5L2RvY3VtZW50YXRpb24vRGFyd2luL1JlZmVyZW5jZS9NYW5QYWdlcy9tYW41L2xhdW5jaGQucGxpc3QuNS5odG1sIy8vYXBwbGVfcmVmL2RvYy9tYW4vNS9sYXVuY2hkLnBsaXN0" title="https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man5/launchd.plist.5.html#//apple_ref/doc/man/5/launchd.plist">launchd.plist(5)<i class="fa fa-external-link"></i></span></p><h2 id="Label-必须"><a href="#Label-必须" class="headerlink" title="Label (必须)"></a>Label (必须)</h2><p>该项服务的名称</p><h2 id="ProgramArguments"><a href="#ProgramArguments" class="headerlink" title="ProgramArguments"></a>ProgramArguments</h2><p>指定可执行文件路径及其参数，比如执行<code>ls -a</code>，对应到该配置中，应该写作：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">key</span>&gt;</span>ProgramArguments<span class="tag">&lt;/<span class="name">key</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">array</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">string</span>&gt;</span>ls<span class="tag">&lt;/<span class="name">string</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">string</span>&gt;</span>-a<span class="tag">&lt;/<span class="name">string</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">array</span>&gt;</span></span><br></pre></td></tr></table></figure><h2 id="RunAtLoad-可选"><a href="#RunAtLoad-可选" class="headerlink" title="RunAtLoad (可选)"></a>RunAtLoad (可选)</h2><p>标识launchd在加载完该项服务之后立即启动路径指定的可执行文件。默认值为 false,设置为 true 即可实现开机运行脚本文件。</p><h2 id="StartCalendarInterval-可选"><a href="#StartCalendarInterval-可选" class="headerlink" title="StartCalendarInterval (可选)"></a>StartCalendarInterval (可选)</h2><p>该关键字可以用来设置定时执行可执行程序，可使用 Month, Day, Hour, Minute, Second等子关键字，它可以指定脚本在多少月，天，小时，分钟，秒，星期几等时间上执行，若缺少某个关键字则表示任意该时间点，类似于 Unix 的 Crontab 计划任务的设置方式，比如在该例子中设置为每小时的20分的时候执行该命令。</p><h2 id="KeepAlive（可选）"><a href="#KeepAlive（可选）" class="headerlink" title="KeepAlive（可选）"></a>KeepAlive（可选）</h2><p>是否保持持续运行</p><p>所有key关键字详细使用说明可以在Mac OS X终端下使用命令 man launchd.plist 查询</p><h1 id="命令行操作"><a href="#命令行操作" class="headerlink" title="命令行操作"></a>命令行操作</h1><h2 id="检查plist语法是否正确"><a href="#检查plist语法是否正确" class="headerlink" title="检查plist语法是否正确"></a>检查plist语法是否正确</h2><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">plutil ~/Library/LaunchAgents/example.plist</span><br></pre></td></tr></table></figure><h2 id="载入配置-使配置生效"><a href="#载入配置-使配置生效" class="headerlink" title="载入配置, 使配置生效"></a>载入配置, 使配置生效</h2><blockquote><p>-w选项会将plist文件中无效的key覆盖掉，建议加上</p></blockquote><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">launchctl load -w ~/Library/LaunchAgents/example.plist</span><br></pre></td></tr></table></figure><h2 id="卸载配置"><a href="#卸载配置" class="headerlink" title="卸载配置"></a>卸载配置</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">launchctl unload -w ~/Library/LaunchAgents/example.plist</span><br></pre></td></tr></table></figure><h2 id="查看服务运行状态"><a href="#查看服务运行状态" class="headerlink" title="查看服务运行状态"></a>查看服务运行状态</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">launchctl list</span><br></pre></td></tr></table></figure><h2 id="修改文件权限"><a href="#修改文件权限" class="headerlink" title="修改文件权限"></a>修改<span class="exturl" data-url="aHR0cHM6Ly9zczY0LmNvbS9iYXNoL2NobW9kLmh0bWw=" title="https://ss64.com/bash/chmod.html">文件权限<i class="fa fa-external-link"></i></span></h2><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 644 ~/Library/LaunchAgents/example.plist</span><br></pre></td></tr></table></figure><h2 id="启动自启动项"><a href="#启动自启动项" class="headerlink" title="启动自启动项"></a>启动自启动项</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">launchctl start example</span><br></pre></td></tr></table></figure><h2 id="结束自启动项"><a href="#结束自启动项" class="headerlink" title="结束自启动项"></a>结束自启动项</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">launchctl stop example</span><br></pre></td></tr></table></figure><h1 id="Homebrew安装的程序可使用Homebrew-Services设置开机启动"><a href="#Homebrew安装的程序可使用Homebrew-Services设置开机启动" class="headerlink" title="Homebrew安装的程序可使用Homebrew Services设置开机启动"></a>Homebrew安装的程序可使用<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL0hvbWVicmV3L2hvbWVicmV3LXNlcnZpY2Vz" title="https://github.com/Homebrew/homebrew-services">Homebrew Services<i class="fa fa-external-link"></i></span>设置开机启动</h1><h2 id="设置开机启动"><a href="#设置开机启动" class="headerlink" title="设置开机启动"></a>设置开机启动</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 如果用Homebrew安装了mysq</span></span><br><span class="line">brew services start mysql</span><br></pre></td></tr></table></figure><h2 id="只运行不开机启动"><a href="#只运行不开机启动" class="headerlink" title="只运行不开机启动"></a>只运行不开机启动</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew services run mysql</span><br></pre></td></tr></table></figure><h2 id="停止运行不再开机启动-存疑，未验证）"><a href="#停止运行不再开机启动-存疑，未验证）" class="headerlink" title="停止运行不再开机启动(存疑，未验证）"></a>停止运行不再开机启动(存疑，未验证）</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew services stop mysql</span><br></pre></td></tr></table></figure><p>注意Homebrew安装的程序不全都支持这种方式，如果命令执行失败，请手动配置plist文件，使用launchctl工具添加启动项。</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><ul><li><span class="exturl" data-url="aHR0cHM6Ly93d3cuamlhbnNodS5jb20vcC80OWRhYmQ4ZWM5YmI=" title="https://www.jianshu.com/p/49dabd8ec9bb">https://www.jianshu.com/p/49dabd8ec9bb<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cuamlhbnNodS5jb20vcC9lZWU4YTdkZTE3OWM=" title="https://www.jianshu.com/p/eee8a7de179c">https://www.jianshu.com/p/eee8a7de179c<i class="fa fa-external-link"></i></span></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;相比于Linux开机启动设置的简单方便–配置&lt;code&gt;rc.local&lt;/code&gt;文件即可，mac设置开机启动略显麻烦。&lt;/p&gt;
&lt;p&gt;如果是使用homebrew安装软件自然不用考虑开机启动这回事，但有时候想执行自己的脚本或者有特殊需求，就不得不自己处理开机启动了。&lt;/p&gt;
    
    </summary>
    
      <category term="mac" scheme="https://xuebin.me/categories/mac/"/>
    
    
      <category term="mac" scheme="https://xuebin.me/tags/mac/"/>
    
      <category term="开机启动" scheme="https://xuebin.me/tags/%E5%BC%80%E6%9C%BA%E5%90%AF%E5%8A%A8/"/>
    
  </entry>
  
  <entry>
    <title>nginx</title>
    <link href="https://xuebin.me/posts/9de3336.html"/>
    <id>https://xuebin.me/posts/9de3336.html</id>
    <published>2019-03-20T07:25:07.000Z</published>
    <updated>2019-03-20T07:25:07.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><h1 id="nginx-是什么？"><a href="#nginx-是什么？" class="headerlink" title="nginx 是什么？"></a>nginx 是什么？</h1><p>nginx 是俄罗斯人 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的一个十分轻量级的 HTTP 服务器。它是一个高性能的 HTTP 和反向代理服务器，同时也可以作为 IMAP/POP3/SMTP 的代理服务器。nginx 使用的是 BSD 许可。</p><p>Nginx 以事件驱动的方式编写，所以有非常好的性能，同时也是一个非常高效的反向代理、负载平衡。</p><p>Nginx 因为它的稳定性、丰富的模块库、灵活的配置和低系统资源的消耗而闻名。</p><p>nginx 适合用来做 mongrel clusters 的前端 HTTP 响应。</p><a id="more"></a><h1 id="为什么要用-nginx，nginx-有什么特点？"><a href="#为什么要用-nginx，nginx-有什么特点？" class="headerlink" title="为什么要用 nginx，nginx 有什么特点？"></a>为什么要用 nginx，nginx 有什么特点？</h1><p><span class="exturl" data-url="aHR0cHM6Ly93d3cuY3RvbGliLmNvbS90b3BpY3MtMTAxMDAwLmh0bWw=" title="https://www.ctolib.com/topics-101000.html">nginx 的特点<i class="fa fa-external-link"></i></span>：</p><ul><li>核心特点：高并发请求的同时保持高效的服务</li><li>热部署</li><li>低内存消耗</li><li>处理响应请求很快</li><li>具有很高的可靠性</li></ul><p>同时，nginx 也可以实现高效的反向代理、负载均衡。</p><p>前端可以用 nginx 做些什么？</p><ul><li>搭建静态资源服务器</li><li>反向代理分发后端服务（可以和 nodejs 搭配实现前后端分离）和跨域问题</li><li>根据 User Agent 来重定向站点</li><li>开发环境或测试环境切换（切换 host）</li><li>url 重写，使用 rewrie 规则本地映射</li><li>资源内容篡改</li><li>获取 cookie 做分流</li><li>资源合并</li><li>gzip 压缩</li><li>压缩图片</li><li>sourceMap 调试</li></ul><h1 id="如何安装-nginx？"><a href="#如何安装-nginx？" class="headerlink" title="如何安装 nginx？"></a>如何安装 nginx？</h1><h2 id="mac-安装"><a href="#mac-安装" class="headerlink" title="mac 安装"></a>mac 安装</h2><p>安装<span class="exturl" data-url="aHR0cHM6Ly9icmV3LnNoLw==" title="https://brew.sh/">brew<i class="fa fa-external-link"></i></span>之后，执行命令：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ sudo brew install nginx</span><br></pre></td></tr></table></figure><h2 id="windows-安装"><a href="#windows-安装" class="headerlink" title="windows 安装"></a>windows 安装</h2><p>下载： <span class="exturl" data-url="aHR0cDovL25naW54Lm9yZy8=" title="http://nginx.org/">nginx 官网<i class="fa fa-external-link"></i></span></p><ol><li>解压运行：解压至<code>c:\nginx</code>，运行<code>nginx.exe</code>(即<code>nginx -c conf\nginx.conf</code>)，默认使用 80 端口，日志见文件夹<code>C:\nginx\logs</code></li><li>关闭：<code>nginx -s stop</code> 或<code>taskkill /F /IM nginx.exe &gt; nul</code></li></ol><p><em>【注意】以下皆以 mac 为例。</em></p><h1 id="nginx-如何启动、重启、关闭？"><a href="#nginx-如何启动、重启、关闭？" class="headerlink" title="nginx 如何启动、重启、关闭？"></a>nginx 如何启动、重启、关闭？</h1><h2 id="查看-nginx-版本"><a href="#查看-nginx-版本" class="headerlink" title="查看 nginx 版本"></a>查看 nginx 版本</h2><p><code>nginx -v</code></p><h2 id="启动-nginx-服务"><a href="#启动-nginx-服务" class="headerlink" title="启动 nginx 服务"></a>启动 nginx 服务</h2><ul><li>方法一：运行命令：<code>sudo brew services start nginx</code></li><li>方法二：运行命令：<code>nginx</code></li></ul><p>访问<span class="exturl" data-url="aHR0cDovL2xvY2FsaG9zdC8=" title="http://localhost/">http://localhost<i class="fa fa-external-link"></i></span>:8080<br>出现如下界面则表示安装成功：<img src="https://image.xuebin.me/006tKfTcly1g19bjj2al3j30yg0bnmzd.jpg" alt></p><h2 id="关闭-nginx-服务"><a href="#关闭-nginx-服务" class="headerlink" title="关闭 nginx 服务"></a>关闭 nginx 服务</h2><ul><li>方法一：运行命令：<code>sudo brew services stop nginx</code></li><li>方法二：运行命令： <code>nginx -s stop</code></li><li>方法三：<br>运行命令：<code>ps -ef | grep nginx</code>，找到 master 对应的进程号。<br>快速停止：<code>kill -TERM nginx进程号</code>或<code>kill -INT nginx进程号</code><br>从容停止： <code>kill -QUIT nginx进程号</code><br>强制停止所有 nginx 进程：<code>pkill -9 nginx</code></li></ul><h2 id="重启-nginx-服务"><a href="#重启-nginx-服务" class="headerlink" title="重启 nginx 服务"></a>重启 nginx 服务</h2><p>方法一：<code>nginx -s reload</code><br>方法二： 平滑重启命令： <code>kill -HUP nginx进程号</code></p><h2 id="nginx-信号控制"><a href="#nginx-信号控制" class="headerlink" title="nginx 信号控制"></a>nginx 信号控制</h2><ul><li><code>TERM,INT</code> 快速关闭</li><li><code>QUIT</code> 从容关闭</li><li><code>HUP</code> 平滑重启，重新加载配置文件</li><li><code>USR1</code> 重新打开日志文件，在切割日志时用途较大</li><li><code>USR2</code> 平滑升级可执行程序</li><li><code>WINCH</code> 从容关闭工作进程</li></ul><h1 id="如何查看-nginx-的配置文件-nginx-conf-的路径和安装路径？"><a href="#如何查看-nginx-的配置文件-nginx-conf-的路径和安装路径？" class="headerlink" title="如何查看 nginx 的配置文件 nginx.conf 的路径和安装路径？"></a>如何查看 nginx 的配置文件 nginx.conf 的路径和安装路径？</h1><p>查看配置文件位置和测试配置文件语法，运行命令<code>nginx -t</code>:<img src="https://image.xuebin.me/006tKfTcly1g19bm00hknj30uo030glv.jpg" alt></p><p>查看 nginx 安装路径：<br>因为是使用 brew 安装的，所以使用 brew 命令：<code>brew info nginx</code>:<img src="https://image.xuebin.me/006tKfTcly1g19bnnssyqj30v90u0q8h.jpg" alt></p><h1 id="nginx-conf-基本配置有哪些？"><a href="#nginx-conf-基本配置有哪些？" class="headerlink" title="nginx.conf 基本配置有哪些？"></a>nginx.conf 基本配置有哪些？</h1><p>nginx 配置文件主要分成四个部分：</p><ul><li>main，全局设置，影响其它部分所有设置</li><li>server，主机服务相关设置，主要用于指定虚拟主机域名、IP 和端口</li><li>location，URL 匹配特定位置后的设置，反向代理、内容篡改相关设置</li><li>upstream，上游服务器设置，负载均衡相关配置</li></ul><p>他们之间的关系式：server 继承 main，location 继承 server；upstream 既不会继承指令也不会被继承。</p><p>如下是一份通用的配置和详解：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br></pre></td><td class="code"><pre><span class="line">#定义 Nginx 运行的用户和用户组,默认由 nobody 账号运行, windows 下面可以注释掉。</span><br><span class="line">user  nobody;</span><br><span class="line"></span><br><span class="line">#nginx进程数，建议设置为等于CPU总核心数。可以和worker_cpu_affinity配合</span><br><span class="line">worker_processes  1;</span><br><span class="line"></span><br><span class="line">#全局错误日志定义类型，[ debug | info | notice | warn | error | crit ]</span><br><span class="line">#error_log  logs/error.log;</span><br><span class="line">#error_log  logs/error.log  notice;</span><br><span class="line">#error_log  logs/error.log  info;</span><br><span class="line"></span><br><span class="line">#进程文件，window下可以注释掉</span><br><span class="line">#pid        logs/nginx.pid;</span><br><span class="line"></span><br><span class="line"># 一个nginx进程打开的最多文件描述符(句柄)数目，理论值应该是最多打开文件数（系统的值ulimit -n）与nginx进程数相除，</span><br><span class="line"># 但是nginx分配请求并不均匀，所以建议与ulimit -n的值保持一致。</span><br><span class="line">worker_rlimit_nofile 65535;</span><br><span class="line"></span><br><span class="line">#工作模式与连接数上限</span><br><span class="line">events &#123;</span><br><span class="line">    # 参考事件模型，use [ kqueue | rtsig | epoll | /dev/poll | select | poll ];</span><br><span class="line">    # epoll模型是Linux 2.6以上版本内核中的高性能网络I/O模型，如果跑在FreeBSD上面，就用kqueue模型。</span><br><span class="line">   #use epoll;</span><br><span class="line">   #connections 20000;  # 每个进程允许的最多连接数</span><br><span class="line">   # 单个进程最大连接数（最大连接数=连接数*进程数）该值受系统进程最大打开文件数限制，需要使用命令ulimit -n 查看当前设置</span><br><span class="line">   worker_connections 65535;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">#设定http服务器</span><br><span class="line">http &#123;</span><br><span class="line">    #文件扩展名与文件类型映射表</span><br><span class="line">    #include 是个主模块指令，可以将配置文件拆分并引用，可以减少主配置文件的复杂度</span><br><span class="line">    include       mime.types;</span><br><span class="line">    #默认文件类型</span><br><span class="line">    default_type  application/octet-stream;</span><br><span class="line">    #charset utf-8; #默认编码</span><br><span class="line"></span><br><span class="line">    #定义虚拟主机日志的格式</span><br><span class="line">    # 日志格式设置:</span><br><span class="line">    #     $remote_addr与$http_x_forwarded_for用以记录客户端的ip地址；</span><br><span class="line">    #     $remote_user：用来记录客户端用户名称；</span><br><span class="line">    #     $time_local： 用来记录访问时间与时区；</span><br><span class="line">    #     $request： 用来记录请求的url与http协议；</span><br><span class="line">    #     $status： 用来记录请求状态；成功是200，</span><br><span class="line">    #     $body_bytes_sent ：记录发送给客户端文件主体内容大小；</span><br><span class="line">    #     $http_referer：用来记录从那个页面链接访问过来的；</span><br><span class="line">    #     $http_user_agent：记录客户浏览器的相关信息；</span><br><span class="line">    #log_format  main  &apos;$remote_addr - $remote_user [$time_local] &quot;$request&quot; &apos;</span><br><span class="line">    #                  &apos;$status $body_bytes_sent &quot;$http_referer&quot; &apos;</span><br><span class="line">    #                  &apos;&quot;$http_user_agent&quot; &quot;$http_x_forwarded_for&quot;&apos;;</span><br><span class="line"></span><br><span class="line">    #定义虚拟主机访问日志</span><br><span class="line">    #access_log  logs/access.log  main;</span><br><span class="line"></span><br><span class="line">    #开启高效文件传输模式，sendfile指令指定nginx是否调用sendfile函数来输出文件，对于普通应用设为 on，如果用来进行下载等应用磁盘IO重负载应用，可设置为off，以平衡磁盘与网络I/O处理速度，降低系统的负载。注意：如果图片显示不正常把这个改成off。</span><br><span class="line">    sendfile        on;</span><br><span class="line">    #autoindex on; #开启目录列表访问，合适下载服务器，默认关闭。</span><br><span class="line"></span><br><span class="line">    #防止网络阻塞</span><br><span class="line">    #tcp_nopush     on;</span><br><span class="line"></span><br><span class="line">    #长连接超时时间，单位是秒，默认为0</span><br><span class="line">    keepalive_timeout  65;</span><br><span class="line"></span><br><span class="line">    # gzip压缩功能设置</span><br><span class="line">    gzip on; #开启gzip压缩输出</span><br><span class="line">    gzip_min_length 1k; #最小压缩文件大小</span><br><span class="line">    gzip_buffers    4 16k; #压缩缓冲区</span><br><span class="line">    gzip_http_version 1.0; #压缩版本（默认1.1，前端如果是squid2.5请使用1.0）</span><br><span class="line">    gzip_comp_level 6; #压缩等级</span><br><span class="line">    #压缩类型，默认就已经包含text/html，所以下面就不用再写了，写上去也不会有问题，但是会有一个warn。</span><br><span class="line">    gzip_types text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml;</span><br><span class="line">    gzip_vary on; //和http头有关系，加个vary头，给代理服务器用的，有的浏览器支持压缩，有的不支持，所以避免浪费不支持的也压缩，所以根据客户端的HTTP头来判断，是否需要压缩</span><br><span class="line">    #limit_zone crawler $binary_remote_addr 10m; #开启限制IP连接数的时候需要使用</span><br><span class="line"></span><br><span class="line">    # http_proxy服务全局设置</span><br><span class="line">    client_max_body_size   10m;</span><br><span class="line">    client_body_buffer_size   128k;</span><br><span class="line">    proxy_connect_timeout   75;</span><br><span class="line">    proxy_send_timeout   75;</span><br><span class="line">    proxy_read_timeout   75;</span><br><span class="line">    proxy_buffer_size   4k;</span><br><span class="line">    proxy_buffers   4 32k;</span><br><span class="line">    proxy_busy_buffers_size   64k;</span><br><span class="line">    proxy_temp_file_write_size  64k;</span><br><span class="line">    proxy_temp_path   /usr/local/nginx/proxy_temp 1 2;</span><br><span class="line"></span><br><span class="line">   # 设定负载均衡后台服务器列表</span><br><span class="line">    upstream  backend.com  &#123;</span><br><span class="line">        #ip_hash; # 指定支持的调度算法</span><br><span class="line">        # upstream 的负载均衡，weight 是权重，可以根据机器配置定义权重。weigth 参数表示权值，权值越高被分配到的几率越大。</span><br><span class="line">        server   192.168.10.100:8080 max_fails=2 fail_timeout=30s ;</span><br><span class="line">        server   192.168.10.101:8080 max_fails=2 fail_timeout=30s ;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    #虚拟主机的配置</span><br><span class="line">    server &#123;</span><br><span class="line">        #监听端口</span><br><span class="line">        listen       80;</span><br><span class="line">        #域名可以有多个，用空格隔开</span><br><span class="line">        server_name  localhost fontend.com;</span><br><span class="line">        # Server Side Include，通常称为服务器端嵌入</span><br><span class="line">        #ssi on;</span><br><span class="line">        #默认编码</span><br><span class="line">        #charset utf-8;</span><br><span class="line">        #定义本虚拟主机的访问日志</span><br><span class="line">        #access_log  logs/host.access.log  main;</span><br><span class="line"></span><br><span class="line">        # 因为所有的地址都以 / 开头，所以这条规则将匹配到所有请求</span><br><span class="line">        location / &#123;</span><br><span class="line">            root   html;</span><br><span class="line">            index  index.html index.htm;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        #error_page  404              /404.html;</span><br><span class="line"></span><br><span class="line">        # redirect server error pages to the static page /50x.html</span><br><span class="line">        #</span><br><span class="line">        error_page   500 502 503 504  /50x.html;</span><br><span class="line">        location = /50x.html &#123;</span><br><span class="line">            root   html;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">       # 图片缓存时间设置</span><br><span class="line">       location ~ .*.(gif|jpg|jpeg|png|bmp|swf)$ &#123;</span><br><span class="line">          expires 10d;</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       # JS和CSS缓存时间设置</span><br><span class="line">       location ~ .*.(js|css)?$ &#123;</span><br><span class="line">          expires 1h;</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">        #代理配置</span><br><span class="line">        # proxy the PHP scripts to Apache listening on 127.0.0.1:80</span><br><span class="line">        #location /proxy/ &#123;</span><br><span class="line">        #    proxy_pass   http://127.0.0.1;</span><br><span class="line">        #&#125;</span><br><span class="line"></span><br><span class="line">        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000</span><br><span class="line">        #</span><br><span class="line">        #location ~ \.php$ &#123;</span><br><span class="line">        #    root           html;</span><br><span class="line">        #    fastcgi_pass   127.0.0.1:9000;</span><br><span class="line">        #    fastcgi_index  index.php;</span><br><span class="line">        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;</span><br><span class="line">        #    include        fastcgi_params;</span><br><span class="line">        #&#125;</span><br><span class="line"></span><br><span class="line">        # deny access to .htaccess files, if Apache&apos;s document root</span><br><span class="line">        # concurs with nginx&apos;s one</span><br><span class="line">        #</span><br><span class="line">        #location ~ /\.ht &#123;</span><br><span class="line">        #    deny  all;</span><br><span class="line">        #&#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    # another virtual host using mix of IP-, name-, and port-based configuration</span><br><span class="line">    #</span><br><span class="line">    #server &#123;</span><br><span class="line">    #    listen       8000;</span><br><span class="line">    #    listen       somename:8080;</span><br><span class="line">    #    server_name  somename  alias  another.alias;</span><br><span class="line"></span><br><span class="line">    #    location / &#123;</span><br><span class="line">    #        root   html;</span><br><span class="line">    #        index  index.html index.htm;</span><br><span class="line">    #    &#125;</span><br><span class="line">    #&#125;</span><br><span class="line"></span><br><span class="line">    # HTTPS server</span><br><span class="line">    #</span><br><span class="line">    #server &#123;</span><br><span class="line">    #    listen       443 ssl;</span><br><span class="line">    #    server_name  localhost;</span><br><span class="line"></span><br><span class="line">    #    ssl_certificate      cert.pem;</span><br><span class="line">    #    ssl_certificate_key  cert.key;</span><br><span class="line"></span><br><span class="line">    #    ssl_session_cache    shared:SSL:1m;</span><br><span class="line">    #    ssl_session_timeout  5m;</span><br><span class="line"></span><br><span class="line">    #    ssl_ciphers  HIGH:!aNULL:!MD5;</span><br><span class="line">    #    ssl_prefer_server_ciphers  on;</span><br><span class="line"></span><br><span class="line">    #    location / &#123;</span><br><span class="line">    #        root   html;</span><br><span class="line">    #        index  index.html index.htm;</span><br><span class="line">    #    &#125;</span><br><span class="line">    #&#125;</span><br><span class="line"></span><br><span class="line">    # 导入多个外部server</span><br><span class="line">    include servers/*;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="匹配-location"><a href="#匹配-location" class="headerlink" title="匹配 location"></a>匹配 location</h2><p>示例：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><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></pre></td><td class="code"><pre><span class="line">location  = / &#123;</span><br><span class="line">  # 精确匹配 / ，主机名后面不能带任何字符串</span><br><span class="line">  [ configuration A ]</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">location  / &#123;</span><br><span class="line">  # 因为所有的地址都以 / 开头，所以这条规则将匹配到所有请求</span><br><span class="line">  # 但是正则和最长字符串会优先匹配</span><br><span class="line">  [ configuration B ]</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">location /documents/ &#123;</span><br><span class="line">  # 匹配任何以 /documents/ 开头的地址，匹配符合以后，还要继续往下搜索</span><br><span class="line">  # 只有后面的正则表达式没有匹配到时，这一条才会采用这一条</span><br><span class="line">  [ configuration C ]</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">location ~ /documents/Abc &#123;</span><br><span class="line">  # 匹配任何以 /documents/ 开头的地址，匹配符合以后，还要继续往下搜索</span><br><span class="line">  # 只有后面的正则表达式没有匹配到时，这一条才会采用这一条</span><br><span class="line">  [ configuration CC ]</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">location ^~ /images/ &#123;</span><br><span class="line">  # 匹配任何以 /images/ 开头的地址，匹配符合以后，停止往下搜索正则，采用这一条。</span><br><span class="line">  [ configuration D ]</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">location ~* \.(gif|jpg|jpeg)$ &#123;</span><br><span class="line">  # 匹配所有以 gif,jpg或jpeg 结尾的请求</span><br><span class="line">  # 然而，所有请求 /images/ 下的图片会被 config D 处理，因为 ^~ 到达不了这一条正则</span><br><span class="line">  [ configuration E ]</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">location /images/ &#123;</span><br><span class="line">  # 字符匹配到 /images/，继续往下，会发现 ^~ 存在</span><br><span class="line">  [ configuration F ]</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">location /images/abc &#123;</span><br><span class="line">  # 最长字符匹配到 /images/abc，继续往下，会发现 ^~ 存在</span><br><span class="line">  # F与G的放置顺序是没有关系的</span><br><span class="line">  [ configuration G ]</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">location ~ /images/abc/ &#123;</span><br><span class="line">  # 只有去掉 config D 才有效：先最长匹配 config G 开头的地址，继续往下搜索，匹配到这一条正则，采用</span><br><span class="line">    [ configuration H ]</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">location ~* /js/.*/\.js</span><br></pre></td></tr></table></figure><ul><li>以<code>=</code>开头表示精确匹配</li><li><code>^~</code> 开头表示 uri 以某个常规字符串开头，不是正则匹配</li><li><code>~</code>开头表示区分大小写的正则匹配;</li><li><code>~*</code> 开头表示不区分大小写的正则匹配</li><li><code>/</code> 通用匹配, 如果没有其它匹配,任何请求都会匹配到</li></ul><p>优先级：<br>(location =) &gt; (location 完整路径) &gt; (location ^~ 路径) &gt; (location ~,~* 正则顺序) &gt; (location 部分起始路径) &gt; (/)</p><h2 id="配置反向代理"><a href="#配置反向代理" class="headerlink" title="配置反向代理"></a>配置反向代理</h2><p>详解：</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></pre></td><td class="code"><pre><span class="line"># 对 “/” 启用反向代理</span><br><span class="line">location / &#123;</span><br><span class="line">  proxy_pass http://127.0.0.1:3000;  # 设置要代理的 uri，注意最后的 /。可以是 Unix 域套接字路径，也可以是正则表达式。</span><br><span class="line">  proxy_redirect off; # 设置后端服务器“Location”响应头和“Refresh”响应头的替换文本</span><br><span class="line">  proxy_set_header X-Real-IP $remote_addr; # 获取用户的真实 IP 地址</span><br><span class="line">  #后端的Web服务器可以通过 X-Forwarded-For 获取用户真实IP，多个 nginx 反代的情况下，例如 CDN。参见：http://gong1208.iteye.com/blog/1559835 和 http://bbs.linuxtone.org/thread-9050-1-1.html</span><br><span class="line">  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;</span><br><span class="line">  #以下是一些反向代理的配置，可选。</span><br><span class="line">  proxy_set_header Host $host; # 允许重新定义或者添加发往后端服务器的请求头。</span><br><span class="line"></span><br><span class="line">  client_max_body_size 10m; #允许客户端请求的最大单文件字节数</span><br><span class="line">  client_body_buffer_size 128k; #缓冲区代理缓冲用户端请求的最大字节数，</span><br><span class="line">  proxy_connect_timeout 90; #nginx跟后端服务器连接超时时间(代理连接超时)</span><br><span class="line">  proxy_send_timeout 90; #后端服务器数据回传时间(代理发送超时)</span><br><span class="line">  proxy_read_timeout 90; #连接成功后，后端服务器响应时间(代理接收超时)</span><br><span class="line">  proxy_buffer_size 4k; #设置代理服务器（nginx）保存用户头信息的缓冲区大小</span><br><span class="line">  proxy_buffers 4 32k; #proxy_buffers缓冲区，网页平均在32k以下的设置</span><br><span class="line">  proxy_busy_buffers_size 64k; #高负荷下缓冲大小（proxy_buffers*2）</span><br><span class="line">  proxy_temp_file_write_size 64k;</span><br><span class="line">  #设定缓存文件夹大小，大于这个值，将从upstream服务器传</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>举例：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">location ^~ /service/ &#123;</span><br><span class="line">  proxy_pass http://192.168.60.245:8080/;</span><br><span class="line">  proxy_redirect      default;</span><br><span class="line">  proxy_set_header    Host $host</span><br><span class="line">  proxy_set_header    X-Real-IP $remote_addr;</span><br><span class="line">  proxy_set_header    X-Forwarded-For  $proxy_add_x_forwarded_for;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>简化：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">location /proxy/ &#123;</span><br><span class="line">  proxy_pass http://backend.com/;</span><br><span class="line">  proxy_redirect      default;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="配置-rewrite"><a href="#配置-rewrite" class="headerlink" title="配置 rewrite"></a>配置 rewrite</h2><p>rewrite 功能就是集合正则表达式和标志位实现 url 重写和重定向。rewrite 只能放在 server{}、location{}、if(){}块中，并且只能对域名后边的出去传递参数外的字符串起作用。如 URL：<br><code>http://microloan-sms-platform.yxapp.xyz/proxy/sms/task/querydeleted?page=1&amp;pagesize=10</code><br>只对/proxy/sms/task/querydeleted 进行重写。</p><p>如果相对域名或参数字符串起作用，可以使用全局变量匹配，也可以使用 proxy_pass 反向代理。</p><p>表明看 rewrite 和 location 功能有点像，都能实现跳转，主要区别在于 rewrite 是在同一域名内更改获取资源的路径，而 location 是对一类路径做控制访问或反向代理，可以 proxy_pass 到其他机器。很多情况下 rewrite 也会写在 location 里，它们的执行顺序是：</p><ul><li>执行 server 块的 rewrite 指令</li><li>执行 location 匹配</li><li>执行选定的 location 中的 rewrite 指令</li></ul><p>如果其中某步 URI 被重写，则重新循环执行 1-3，直到找到真实存在的文件；循环超过 10 次，则返回 500 Internal Server Error 错误。</p><p>rewrite 规则后边，通常会带有 flag 标志位：</p><ul><li><code>last</code> : 相当于 Apache 的[L]标记，表示完成 rewrite</li><li><code>break</code> : 停止执行当前虚拟主机的后续 rewrite 指令集</li><li><code>redirect</code> : 返回<code>302</code>临时重定向，地址栏会显示跳转后的地址</li><li><code>permanent</code> : 返回<code>301</code>永久重定向，地址栏会显示跳转后的地址</li></ul><p><code>last</code> 和 <code>break</code> 区别：</p><ul><li><code>last</code>一般写在<code>server</code>和<code>if</code>中，而<code>break</code>一般使用在<code>location</code>中</li><li><code>last</code>不终止重写后的 url 匹配，即新的 url 会再从<code>server</code>走一遍匹配流程，而<code>break</code>终止重写后的匹配</li><li><code>break</code>和<code>last</code>都能组织继续执行后面的 rewrite 指令</li></ul><p>rewrite 常用正则：</p><ul><li><code>.</code> ： 匹配除换行符以外的任意字符</li><li><code>?</code> ： 重复 0 次或 1 次</li><li><code>+</code> ： 重复 1 次或更多次</li><li><code>*</code> ： 重复 0 次或更多次</li><li><code>\d</code> ：匹配数字</li><li><code>^</code> ： 匹配字符串的开始</li><li><code>$</code> ： 匹配字符串的介绍</li><li><code>{n}</code> ： 重复 n 次</li><li><code>{n,}</code> ： 重复 n 次或更多次</li><li><code>[c]</code> ： 匹配单个字符 c</li><li><code>[a-z]</code> ： 匹配 a-z 小写字母的任意一个</li></ul><p>可以使用<code>()</code>来进行分组，可以通过<code>$1</code>的形式来引用。</p><p>示例：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">location /proxy/ &#123;</span><br><span class="line">        proxy_pass http://microloan-notification-web.yxapp.in;</span><br><span class="line">        rewrite /proxy/(.*)$ /$1 break;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="配置负载均衡"><a href="#配置负载均衡" class="headerlink" title="配置负载均衡"></a>配置负载均衡</h2><p>示例：</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></pre></td><td class="code"><pre><span class="line">upstream test.net&#123;</span><br><span class="line">   ip_hash;</span><br><span class="line">   server 192.168.11.1:80;</span><br><span class="line">   server 192.168.11.11:80  down;</span><br><span class="line">   server 192.168.11.123:8009  max_fails=3  fail_timeout=20s;</span><br><span class="line">   server 192.168.11.1234:8080;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>upstream 是 Nginx 的 HTTP Upstream 模块，这个模块通过一个简单的调度算法来实现客户端 IP 到后端服务器的负载均衡。<br>Nginx 的负载均衡模块目前支持 4 种调度算法：</p><ul><li><code>轮询（默认）</code>。每个请求按时间顺序逐一分配到不同的后端服务器，如果后端某台服务器宕机，故障系统被自动剔除，使用户访问不受影响。Weight 指定轮询权值，Weight 值越大，分配到的访问机率越高，主要用于后端每个服务器性能不均的情况下。</li><li><code>ip_hash</code>。每个请求按访问 IP 的 hash 结果分配，这样来自同一个 IP 的访客固定访问一个后端服务器，有效解决了动态网页存在的 session 共享问题。</li><li><code>fair</code>。这是比上面两个更加智能的负载均衡算法。此种算法可以依据页面大小和加载时间长短智能地进行负载均衡，也就是根据后端服务器的响应时间来分配请求，响应时间短的优先分配。Nginx 本身是不支持 fair 的，如果需要使用这种调度算法，必须下载 Nginx 的 upstream_fair 模块。</li><li><code>url_hash</code>。此方法按访问 url 的 hash 结果来分配请求，使每个 url 定向到同一个后端服务器，可以进一步提高后端缓存服务器的效率。Nginx 本身是不支持 url_hash 的，如果需要使用这种调度算法，必须安装 Nginx 的 hash 软件包。</li></ul><p>upstream 可以设定每个后端服务器在负载均衡调度中的状态，支持的状态参数:</p><ul><li><code>down</code>，表示当前的 server 暂时不参与负载均衡</li><li><code>backup</code>，预留的备份机器。当其他所有的非 backup 机器出现故障或者忙的时候，才会请求<code>backup</code>机器，因此这台机器的压力最轻。</li><li><code>max_fails</code>，允许请求失败的次数，默认为<code>1</code>。当超过最大次数时，返回<code>proxy_next_upstream</code> 模块定义的错误。</li><li><code>fail_timeout</code>，在经历了<code>max_fails</code>次失败后，暂停服务的时间。<code>max_fails</code>可以和<code>fail_timeout</code>一起使用。</li></ul><p><strong>注，当负载调度算法为 ip_hash 时，后端服务器在负载均衡调度中的状态不能是 weight 和 backup。</strong></p><h2 id="设置页面缓存"><a href="#设置页面缓存" class="headerlink" title="设置页面缓存"></a>设置页面缓存</h2><p>页面缓存设置指令：</p><ul><li><p><code>proxy_cache_path</code>: 指定缓存的路径和一些其他参数，缓存的数据存储在文件中，并且使用代理 url 的哈希值作为关键字与文件名。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">proxy_cache_path /data/nginx/cache/webserver levels=1:2 keys_zone=webserver:20m max_size=1g;</span><br></pre></td></tr></table></figure><p><code>levels</code>参数指定缓存的子目录数。<code>keys_zone</code>指定活动的 key 和元数据存储在共享池（webserver 为共享池名称，20m 位共享池大小），<code>inactive</code>参数指定的时间内缓存的数据没有被请求则被删除，默认 inactive 为 10 分钟<code>·max_size</code>指定缓存空间的大小。</p></li><li><p><code>proxy_cache</code>: 设置一个缓存区域的名称，一个相同的区域可以在不同的地方使用。</p></li><li><code>proxy_cache_valid</code>: 为不同的应答设置不同的缓存时间。</li></ul><h2 id="设置读写分离"><a href="#设置读写分离" class="headerlink" title="设置读写分离"></a>设置读写分离</h2><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></pre></td><td class="code"><pre><span class="line">server &#123;</span><br><span class="line">        listen       80;</span><br><span class="line">        server_name  localhost;</span><br><span class="line">        #charset koi8-r;</span><br><span class="line">        #access_log  logs/host.access.log  main;</span><br><span class="line">        location / &#123;</span><br><span class="line">                proxy_pass http://192.128.133.202;</span><br><span class="line">                if ($request_method = &quot;PUT&quot;)&#123;</span><br><span class="line">                        proxy_pass http://192.128.18.201;</span><br><span class="line">                &#125;</span><br><span class="line">        &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="开机启动"><a href="#开机启动" class="headerlink" title="开机启动"></a>开机启动</h1><blockquote><p>当我们在 Mac 上使用 homebrew 安装 nginx，mysql，php 的时候，遗憾的是 nginx 默认监听的是 8080 端口，而当我们将端口修改为 Web 服务器通用的 80 端口时，却导致 nginx 无法在开机的时候自启。。。</p></blockquote><ul><li><p>通过 homebrew 安装 nginx 后，我们可以在 nginx 软件的安装目录<code>/usr/local/Cellar/nginx</code>下看到有一个<code>homebrew.mxcl.nginx.plist</code>文件。在 Mac 系统上, <code>.plist</code>文件就是服务开机启动的配置文件。</p></li><li><p>由于 nginx 监听<code>80</code>端口，必须在开机启动的时候，以管理员权限执行，所以需要将此文件复制一份置于<code>/Library/LaunchDaemons</code>目录之下。注意权限需要和<code>/Library/LaunchDaemons</code>目录下的其他文件权限保持一致，通常是<code>644</code>, 文件所有者为<code>root:wheel</code>，否则真的会报错误的。</p></li><li>然后使用<code>launchctl load -w /Library/LaunchDaemons/homebrew.mxcl.nginx.plist</code> 加载 nginx 服务到系统启动服务中即可。</li></ul><p><strong>命令：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># [可选]先查看此目录是否存在，不存在执行此命令新建</span></span><br><span class="line">mkdir -p ~/Library/LaunchDaemons</span><br><span class="line"></span><br><span class="line"><span class="comment"># 拷贝homebrew.mxcl.nginx.plist文件</span></span><br><span class="line">sudo cp /usr/<span class="built_in">local</span>/Cellar/nginx/*.plist /Library/LaunchDaemons</span><br><span class="line"></span><br><span class="line"><span class="comment"># 加载启动服务</span></span><br><span class="line">sudo launchctl load -w /Library/LaunchDaemons/homebrew.mxcl.nginx.plist</span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置nginx权限</span></span><br><span class="line">sudo chown root:wheel /usr/<span class="built_in">local</span>/Cellar/nginx/&#123;nginx版本&#125;/bin/nginx</span><br></pre></td></tr></table></figure><h1 id="完整-nginx-配置举例"><a href="#完整-nginx-配置举例" class="headerlink" title="完整 nginx 配置举例"></a>完整 nginx 配置举例</h1><blockquote><p>nginx 配置主文件: /usr/local/etc/nginx/nginx.conf</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><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br></pre></td><td class="code"><pre><span class="line">user root owner;</span><br><span class="line"><span class="comment">#user  nobody;</span></span><br><span class="line">worker_processes  1;</span><br><span class="line"></span><br><span class="line"><span class="comment"># pid        /logs/nginx/nginx.pid;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># events-模块（工作模式&amp;连接上限）: https://www.zybuluo.com/phper/note/89391#events-%E6%A8%A1%E5%9D%97</span></span><br><span class="line">events &#123;</span><br><span class="line">    use kqueue; <span class="comment">#mac平台</span></span><br><span class="line">    worker_connections  1024;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># # 这个将为打开文件指定缓存，默认是没有启用的，max指定缓存数量，建议和打开文件数一致，inactive是指经过多长时间文件没被请求后删除缓存。</span></span><br><span class="line"><span class="comment"># open_file_cache max=65535 inactive=60s;</span></span><br><span class="line"><span class="comment"># # 检查缓存有效信息的频率。</span></span><br><span class="line"><span class="comment"># open_file_cache_valid 80s;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">http &#123;</span><br><span class="line">    include       mime.types;</span><br><span class="line">    default_type  application/octet-stream;</span><br><span class="line"></span><br><span class="line">    underscores_in_headers on; <span class="comment"># 开启变量下划线支持</span></span><br><span class="line"></span><br><span class="line">    client_max_body_size 24M;</span><br><span class="line">    client_body_buffer_size 256k;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 日志格式设置: https://juejin.im/post/59f94f626fb9a045023af34c</span></span><br><span class="line">    <span class="comment"># $remote_addr             客户端地址                                    211.28.65.253</span></span><br><span class="line">    <span class="comment"># $remote_user             客户端用户名称                                --</span></span><br><span class="line">    <span class="comment"># $time_local              访问时间和时区                                18/Jul/2012:17:00:01 +0800</span></span><br><span class="line">    <span class="comment"># $request                 请求的URI和HTTP协议                           "GET /article-10000.html HTTP/1.1"</span></span><br><span class="line">    <span class="comment"># $http_host               请求地址，即浏览器中你输入的地址（IP或域名）       www.wang.com 192.168.100.100</span></span><br><span class="line">    <span class="comment"># $status                  HTTP请求状态                                  200</span></span><br><span class="line">    <span class="comment"># $upstream_status         upstream状态                                 200</span></span><br><span class="line">    <span class="comment"># $body_bytes_sent         发送给客户端文件内容大小                        1547</span></span><br><span class="line">    <span class="comment"># $http_referer            url跳转来源                                   https://www.baidu.com/</span></span><br><span class="line">    <span class="comment"># $http_user_agent         用户终端浏览器等信息                           "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; SV1; GTB7.0; .NET4.0C;</span></span><br><span class="line">    <span class="comment"># $ssl_protocol            SSL协议版本                                   TLSv1</span></span><br><span class="line">    <span class="comment"># $ssl_cipher              交换数据中的算法                               RC4-SHA</span></span><br><span class="line">    <span class="comment"># $upstream_addr           后台upstream的地址，即真正提供服务的主机地址      10.10.10.100:80</span></span><br><span class="line">    <span class="comment"># $request_time            整个请求的总时间                               0.205</span></span><br><span class="line">    <span class="comment"># $upstream_response_time  请求过程中，upstream响应时间                    0.002</span></span><br><span class="line">    log_format  main  <span class="string">'remote_addr:                     $remote_addr\n'</span></span><br><span class="line">                        <span class="string">'remote_user:                   $remote_user\n'</span></span><br><span class="line">                        <span class="string">'time_local:                    $time_local\n'</span></span><br><span class="line">                        <span class="string">'request:                       $request\n'</span></span><br><span class="line">                        <span class="string">'status:                        $status\n'</span></span><br><span class="line">                        <span class="string">'body_bytes_sent:               $body_bytes_sent\n'</span></span><br><span class="line">                        <span class="string">'http_referer:                  $http_referer\n'</span></span><br><span class="line">                        <span class="string">'http_user_agent:               $http_user_agent\n'</span></span><br><span class="line">                        <span class="string">'http_x_forwarded_for:          $http_x_forwarded_for\n'</span></span><br><span class="line">                        <span class="string">'request_body:                  $request_body\n'</span></span><br><span class="line">                        <span class="string">'------------------------------------------------------------------------'</span>;</span><br><span class="line"></span><br><span class="line">    log_format  porxy  <span class="string">'http_x_forwarded_for:           $http_x_forwarded_for\n'</span></span><br><span class="line">                        <span class="string">'remote_user:                   $remote_user\n'</span></span><br><span class="line">                        <span class="string">'time_local:                    $time_local\n'</span></span><br><span class="line">                        <span class="string">'http_host:                     $http_host\n'</span></span><br><span class="line">                        <span class="string">'request:                       $request\n'</span></span><br><span class="line">                        <span class="string">'status:                        $status\n'</span></span><br><span class="line">                        <span class="string">'body_bytes_sent:               $body_bytes_sent\n'</span></span><br><span class="line">                        <span class="string">'http_referer:                  $http_referer\n'</span></span><br><span class="line">                        <span class="string">'http_user_agent:               $http_user_agent\n'</span></span><br><span class="line">                        <span class="string">'proxy_protocol_addr:           $proxy_protocol_addr\n'</span></span><br><span class="line">                        <span class="string">'request_body:                  $request_body\n'</span></span><br><span class="line">                        <span class="string">'porxy_url:                     $porxy_url\n'</span></span><br><span class="line">                        <span class="string">'------------------------------------------------------------------------'</span>;</span><br><span class="line"></span><br><span class="line">    log_format log404 <span class="string">'$status [$time_local] $remote_addr $host$request_uri $sent_http_location'</span>;</span><br><span class="line"></span><br><span class="line">    access_log  /logs/nginx/access.log  main;</span><br><span class="line">    <span class="comment"># error_log  /logs/nginx/error.log;</span></span><br><span class="line">    <span class="comment"># error_log  /logs/nginx/error.log  notice;</span></span><br><span class="line">    error_log  /logs/nginx/error.log  info;</span><br><span class="line"></span><br><span class="line">    access_log      on;</span><br><span class="line">    sendfile        on;</span><br><span class="line">    <span class="comment"># tcp_nopush     on;</span></span><br><span class="line">    <span class="comment"># fastcgi_intercept_errors on;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># keepalive_timeout  0;</span></span><br><span class="line">    keepalive_timeout  65;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># # gzip压缩功能设置</span></span><br><span class="line">    <span class="comment"># gzip on;</span></span><br><span class="line"></span><br><span class="line">    proxy_redirect off; <span class="comment"># 关闭后端返回的header修改 </span></span><br><span class="line">    proxy_set_header Host <span class="variable">$host</span>; <span class="comment"># 修改发送到后端的header的host </span></span><br><span class="line">    proxy_set_header X-Real-IP <span class="variable">$remote_addr</span>; <span class="comment"># 设置真实ip </span></span><br><span class="line">    proxy_set_header X-Forwarded-For <span class="variable">$proxy_add_x_forwarded_for</span>;</span><br><span class="line">    proxy_connect_timeout 60; <span class="comment"># 代理服务器和后端真实服务器握手连接超时时间</span></span><br><span class="line">    proxy_read_timeout 600; <span class="comment"># 后端服务器回传数据给Nginx的时间，需要在设置的时间范围内发送完所有数据，否则Nginx将断开连接</span></span><br><span class="line">    proxy_send_timeout 600; <span class="comment"># 代理服务器和后端服务器连接成功后，等待后端服务器响应时间</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># # 负载均衡: https://www.zybuluo.com/phper/note/90310#3%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1</span></span><br><span class="line">    <span class="comment"># # 设定负载均衡后台服务器列表 </span></span><br><span class="line">    <span class="comment"># upstream  backend  &#123; </span></span><br><span class="line">    <span class="comment">#     #ip_hash; # 记录并访问上一次访问过的服务器</span></span><br><span class="line">    <span class="comment">#     server   192.168.0.100:8080 weight=10 max_fails=2 fail_timeout=30s;</span></span><br><span class="line">    <span class="comment">#     server   192.168.0.101:8080 weight=10 max_fails=2 fail_timeout=30s;</span></span><br><span class="line">    <span class="comment"># &#125;</span></span><br><span class="line"></span><br><span class="line">    server &#123;</span><br><span class="line">        listen       1024;</span><br><span class="line">        server_name  localhost;</span><br><span class="line"></span><br><span class="line">        <span class="comment">#charset koi8-r;</span></span><br><span class="line">        charset utf-8;</span><br><span class="line"></span><br><span class="line">        access_log  /logs/nginx/host.access.log  main;</span><br><span class="line"></span><br><span class="line">        <span class="comment"># location匹配: https://segmentfault.com/a/1190000013267839</span></span><br><span class="line">        location / &#123;</span><br><span class="line">            root   html;</span><br><span class="line">            index  index.html index.htm;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">#error_page  404              /404.html;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># redirect server error pages to the static page /50x.html</span></span><br><span class="line">        <span class="comment">#</span></span><br><span class="line">        error_page   500 502 503 504 404 403 /50x.html;</span><br><span class="line">        location = /50x.html &#123;</span><br><span class="line">            root   html;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment"># # proxy the PHP scripts to Apache listening on 127.0.0.1:80</span></span><br><span class="line">        <span class="comment"># location ~ \.php$ &#123;</span></span><br><span class="line">        <span class="comment">#     proxy_pass   http://127.0.0.1;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">#     ### 下面都是次要关注项</span></span><br><span class="line">        <span class="comment">#     proxy_set_header Host $host; # 修改发送到后端的header的host </span></span><br><span class="line">        <span class="comment">#     proxy_set_header X-Real-IP $remote_addr; # 设置真实ip </span></span><br><span class="line">        <span class="comment">#     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 经过的IP列表</span></span><br><span class="line">        <span class="comment">#     proxy_method POST;</span></span><br><span class="line">        <span class="comment">#     # 指定不转发的头部字段</span></span><br><span class="line">        <span class="comment">#     proxy_hide_header Cache-Control;</span></span><br><span class="line">        <span class="comment">#     proxy_hide_header Other-Header;</span></span><br><span class="line">        <span class="comment">#     # 指定转发的头部字段</span></span><br><span class="line">        <span class="comment">#     proxy_pass_header Server-IP;</span></span><br><span class="line">        <span class="comment">#     proxy_pass_header Server-Name;</span></span><br><span class="line">        <span class="comment">#     # 是否转发包体</span></span><br><span class="line">        <span class="comment">#     proxy_pass_request_body on | off;</span></span><br><span class="line">        <span class="comment">#     # 是否转发头部</span></span><br><span class="line">        <span class="comment">#     proxy_pass_request_headers on | off;</span></span><br><span class="line">        <span class="comment">#     # 显形/隐形 URI，上游发生重定向时，Nginx 是否同步更改 uri</span></span><br><span class="line">        <span class="comment">#     proxy_redirect on | off;</span></span><br><span class="line">        <span class="comment"># &#125;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment"># pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000</span></span><br><span class="line">        <span class="comment">#</span></span><br><span class="line">        <span class="comment">#location ~ \.php$ &#123;</span></span><br><span class="line">        <span class="comment">#    root           html;</span></span><br><span class="line">        <span class="comment">#    fastcgi_pass   127.0.0.1:9000;</span></span><br><span class="line">        <span class="comment">#    fastcgi_index  index.php;</span></span><br><span class="line">        <span class="comment">#    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;</span></span><br><span class="line">        <span class="comment">#    include        fastcgi_params;</span></span><br><span class="line">        <span class="comment">#&#125;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># deny access to .htaccess files, if Apache's document root</span></span><br><span class="line">        <span class="comment"># concurs with nginx's one</span></span><br><span class="line">        <span class="comment">#</span></span><br><span class="line">        <span class="comment">#location ~ /\.ht &#123;</span></span><br><span class="line">        <span class="comment">#    deny  all;</span></span><br><span class="line">        <span class="comment">#&#125;</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment"># another virtual host using mix of IP-, name-, and port-based configuration</span></span><br><span class="line">    <span class="comment">#</span></span><br><span class="line">    <span class="comment">#server &#123;</span></span><br><span class="line">    <span class="comment">#    listen       8000;</span></span><br><span class="line">    <span class="comment">#    listen       somename:8080;</span></span><br><span class="line">    <span class="comment">#    server_name  somename  alias  another.alias;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">#    location / &#123;</span></span><br><span class="line">    <span class="comment">#        root   html;</span></span><br><span class="line">    <span class="comment">#        index  index.html index.htm;</span></span><br><span class="line">    <span class="comment">#    &#125;</span></span><br><span class="line">    <span class="comment">#&#125;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment"># HTTPS server</span></span><br><span class="line">    <span class="comment">#</span></span><br><span class="line">    <span class="comment">#server &#123;</span></span><br><span class="line">    <span class="comment">#    listen       443 ssl;</span></span><br><span class="line">    <span class="comment">#    server_name  localhost;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">#    ssl_certificate      cert.pem;</span></span><br><span class="line">    <span class="comment">#    ssl_certificate_key  cert.key;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">#    ssl_session_cache    shared:SSL:1m;</span></span><br><span class="line">    <span class="comment">#    ssl_session_timeout  5m;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">#    ssl_ciphers  HIGH:!aNULL:!MD5;</span></span><br><span class="line">    <span class="comment">#    ssl_prefer_server_ciphers  on;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">#    location / &#123;</span></span><br><span class="line">    <span class="comment">#        root   html;</span></span><br><span class="line">    <span class="comment">#        index  index.html index.htm;</span></span><br><span class="line">    <span class="comment">#    &#125;</span></span><br><span class="line">    <span class="comment">#&#125;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    include servers/*;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>外部 server 配置: /usr/local/etc/nginx/servers/dev.conf</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><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Location 配置demo</span></span><br><span class="line"><span class="comment"># https://segmentfault.com/a/1190000009651161</span></span><br><span class="line"></span><br><span class="line">upstream  rd_header  &#123; </span><br><span class="line">    server rd5-dev.zhaopin.com;</span><br><span class="line">    <span class="comment"># server rd5-pre.zhaopin.com;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">upstream  bole_api  &#123; </span><br><span class="line">    server 192.168.214.110:8089 weight=10 max_fails=2 fail_timeout=30s;</span><br><span class="line">    <span class="comment"># server bole-api-dev.zhaopin.com weight=10 max_fails=2 fail_timeout=30s;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">server &#123;</span><br><span class="line">    listen 80;</span><br><span class="line"></span><br><span class="line">    server_name *.zhaopin.com;</span><br><span class="line">    resolver 127.0.0.1 ipv6=off;</span><br><span class="line"></span><br><span class="line">    access_log  /logs/nginx/<span class="variable">$http_host</span>.access.log  porxy;</span><br><span class="line">    error_log  /logs/nginx/zhaopin.error.log;</span><br><span class="line"></span><br><span class="line">    location ~ /api/rd/rd55/head &#123;</span><br><span class="line">        proxy_pass http://rd_header;</span><br><span class="line"></span><br><span class="line">        proxy_set_header Host  <span class="variable">$host</span>;</span><br><span class="line">        proxy_set_header X-Real-IP <span class="variable">$remote_addr</span>;</span><br><span class="line">        proxy_set_header X-Forwarded-For <span class="variable">$proxy_add_x_forwarded_for</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    location ~ /bolemanage/(.*) &#123;</span><br><span class="line">        proxy_pass http://bole_api/bolemanage/<span class="variable">$1</span>?<span class="variable">$args</span>;</span><br><span class="line"></span><br><span class="line">        proxy_set_header Host  bole-api-dev.zhaopin.com;</span><br><span class="line">        proxy_set_header X-Real-IP <span class="variable">$remote_addr</span>;</span><br><span class="line">        proxy_set_header X-Forwarded-For <span class="variable">$proxy_add_x_forwarded_for</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    location / &#123;</span><br><span class="line">        resolver 127.0.0.1 ipv6=off;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (<span class="variable">$http_host</span> ~* ^(.*-<span class="built_in">local</span>)(\.zhaopin\.com)$) &#123;</span><br><span class="line">            <span class="built_in">set</span> <span class="variable">$porxy_url</span> <span class="variable">$1</span><span class="variable">$2</span>:8000;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment"># proxy_pass http://$porxy_url; </span></span><br><span class="line">        proxy_pass http://127.0.0.1:8000;</span><br><span class="line"></span><br><span class="line">        access_log  /logs/nginx/<span class="variable">$porxy_url</span>.access.log  porxy;</span><br><span class="line">        error_log  /logs/nginx/zhaopin-porxy_url.error.log;</span><br><span class="line"></span><br><span class="line">        proxy_set_header Host <span class="variable">$http_host</span>:8000;</span><br><span class="line">        proxy_set_header X-Real-IP <span class="variable">$remote_addr</span>;</span><br><span class="line">        proxy_set_header X-Forwarded-For <span class="variable">$proxy_add_x_forwarded_for</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    error_page  404 403       https://github.com/404;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># server &#123;</span></span><br><span class="line"><span class="comment">#     listen 80;</span></span><br><span class="line"><span class="comment">#     server_name rd5-dev.zhaopin.com;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#     location /api/rd/rd55/head &#123;</span></span><br><span class="line"><span class="comment">#         proxy_pass http://rd5-pre.zhaopin.com;</span></span><br><span class="line"><span class="comment">#     &#125;</span></span><br><span class="line"><span class="comment"># &#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># server &#123;</span></span><br><span class="line"><span class="comment">#     listen 80;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#     # 泛域名解析</span></span><br><span class="line"><span class="comment">#     server_name *.zhaopin.com;</span></span><br><span class="line"><span class="comment">#     resolver 127.0.0.1 ipv6=off;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#     # 最末级域名作为localhost端口号</span></span><br><span class="line"><span class="comment">#     if ($host ~* ^([^\.]+)\.([^\.]+\.[^\.]+)$) &#123;</span></span><br><span class="line"><span class="comment">#     set $subdomain $1;</span></span><br><span class="line"><span class="comment">#     set $domain $2;</span></span><br><span class="line"><span class="comment">#     set $porxy_url 127.0.0.1:$1;</span></span><br><span class="line"><span class="comment">#     &#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#     access_log  /logs/nginx/$subdomain.zhaopin.access.log  porxy;</span></span><br><span class="line"><span class="comment">#     error_log  /logs/nginx/zhaopin.error.log;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#     location / &#123;</span></span><br><span class="line"><span class="comment">#     proxy_pass http://$porxy_url;</span></span><br><span class="line"><span class="comment">#     &#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#     error_page  404 403       https://github.com/404;</span></span><br><span class="line"><span class="comment"># &#125;</span></span><br></pre></td></tr></table></figure><h1 id="采坑"><a href="#采坑" class="headerlink" title="采坑"></a>采坑</h1><ul><li><p>本机可以访问，但是局域网内其他电脑无法访问</p><blockquote><p>检查防火墙配置</p></blockquote></li><li><p>启动权限错误，不想通过sudo启动</p><p>在使用nginx -t检查nginx配置文件时出现这个错误:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> /usr/<span class="built_in">local</span>/nginx/sbin/nginx -t</span></span><br><span class="line">nginx: [warn] the "user" directive makes sense only if the master process runs with super-user privileges, ignored in /usr/local/nginx/conf/nginx.conf:2</span><br></pre></td></tr></table></figure><p>nginx的所有者是root<br>我们都知道nginx的主进程需要使用root来运行,而子进程可以使用普通用户运行,<br>普通用户如果不使用<code>sudo</code>命令运行nginx时则需要获取<code>SUID</code>权限才能在nginx运行时将身份切换为root,否则就是用自己的身份来执行nginx,而恰巧在这个nginx.conf文件中设置了ssl的证书的放置位置在一些普通用户不能读取的位置或者一些普通用户不能读取的目录,所以出现了权限的错误.<br>解决的方法我总结了两种,一种是使用<code>sudo</code>来运行nginx,另一种是给nginx赋予<code>SUID</code>权限,让普通用户在执行nginx时将身份提升为nginx的所有者也就是root</p><blockquote><p><strong>给nginx赋予<code>SUID</code>权限</strong></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="meta">&gt;</span><span class="bash"> $ sudo chmod u+s /usr/<span class="built_in">local</span>/nginx/sbin/nginx</span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> //或者$ sudo chmod 4755 /usr/<span class="built_in">local</span>/nginx/sbin/nginx</span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> $ ll /usr/<span class="built_in">local</span>/nginx/sbin/nginx</span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> -rwsr-xr-x 1 root root 8641260 Sep  7 14:33 /usr/<span class="built_in">local</span>/nginx/sbin/nginx</span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"></span></span><br></pre></td></tr></table></figure></blockquote><blockquote><p>但是由于<code>SUID</code>权限是linux中比较危险的一种程序,假设要执行的程序的所有者是root,那么任何非sudo用户都可以在运行这个程序的时候身份都变为了root,所以这个程序可以让任何人以root身份执行,有点心里慌慌.<br>还是用<code>sudo</code>执行好一些吧,至少只有在<code>sudoers</code>列表中的用户才能以root权限执行,还必须在命令前敲<code>sudo</code>来提醒一下自己,感觉安全性是提高了不少.</p></blockquote></li></ul><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><ul><li><span class="exturl" data-url="aHR0cDovL3d3dy5uZ2lueC5jbi9kb2Mv" title="http://www.nginx.cn/doc/">Nginx 中文文档<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cDovL2ltd2ViLmlvL3RvcGljLzU2Mzg2OTcyZDEyYjIzMGMyNmUxYTE3ZA==" title="http://imweb.io/topic/56386972d12b230c26e1a17d">Nginx 能为前端开发带来什么？<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cuYXJheXpvdS5jb20vMjAxNi8wOS8yMC8lRTUlODklOEQlRTclQUIlQUYlRTUlQjclQTUlRTclQTglOEIlRTUlQjglODglRTUlQkElOTQlRTglQUYlQTUlRTclOUYlQTUlRTklODElOTMlRTclOUElODRuZ2lueC8=" title="https://www.arayzou.com/2016/09/20/%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%B8%88%E5%BA%94%E8%AF%A5%E7%9F%A5%E9%81%93%E7%9A%84nginx/">前端工程师应该知道的 Nginx<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cuaXRzbnMub3JnL2FydGljbGUvMg==" title="https://www.itsns.org/article/2">前端 Nginx https SSL proxy + 后端 Nginx http 应用的布署教程<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9zZWdtZW50ZmF1bHQuY29tL2EvMTE5MDAwMDAwMjc5NzYwNiNhcnRpY2xlSGVhZGVyMA==" title="https://segmentfault.com/a/1190000002797606#articleHeader0">nginx 配置 location 总结及 rewrite 规则写法<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9zZWdtZW50ZmF1bHQuY29tL2EvMTE5MDAwMDAwMjc5NzYwMSNhcnRpY2xlSGVhZGVyMw==" title="https://segmentfault.com/a/1190000002797601#articleHeader3">nginx 服务器安装及配置文件详解<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cDovL2ZyZWVsb2RhLmJsb2cuNTFjdG8uY29tLzIwMzM1ODEvMTI4ODU1Mw==" title="http://freeloda.blog.51cto.com/2033581/1288553">Nginx 反向代理、负载均衡、页面缓存、URL 重写及读写分离详解<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L05pY2tfcGhwL2FydGljbGUvZGV0YWlscy81Mzg2ODg0NQ==" title="https://blog.csdn.net/Nick_php/article/details/53868845">Mac 通过 Homebrew 安装 nginx 并设置开机启动配置<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly93d3cuY25ibG9ncy5jb20vZGFseTIwMDgvcC81MzY3NzAwLmh0bWw=" title="https://www.cnblogs.com/daly2008/p/5367700.html">mac 下 nginx 加入开机启动<i class="fa fa-external-link"></i></span></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;nginx-是什么？&quot;&gt;&lt;a href=&quot;#nginx-是什么？&quot; class=&quot;headerlink&quot; title=&quot;nginx 是什么？&quot;&gt;&lt;/a&gt;nginx 是什么？&lt;/h1&gt;&lt;p&gt;nginx 是俄罗斯人 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的一个十分轻量级的 HTTP 服务器。它是一个高性能的 HTTP 和反向代理服务器，同时也可以作为 IMAP/POP3/SMTP 的代理服务器。nginx 使用的是 BSD 许可。&lt;/p&gt;
&lt;p&gt;Nginx 以事件驱动的方式编写，所以有非常好的性能，同时也是一个非常高效的反向代理、负载平衡。&lt;/p&gt;
&lt;p&gt;Nginx 因为它的稳定性、丰富的模块库、灵活的配置和低系统资源的消耗而闻名。&lt;/p&gt;
&lt;p&gt;nginx 适合用来做 mongrel clusters 的前端 HTTP 响应。&lt;/p&gt;
    
    </summary>
    
      <category term="工具" scheme="https://xuebin.me/categories/%E5%B7%A5%E5%85%B7/"/>
    
    
      <category term="工具" scheme="https://xuebin.me/tags/%E5%B7%A5%E5%85%B7/"/>
    
      <category term="nginx" scheme="https://xuebin.me/tags/nginx/"/>
    
  </entry>
  
  <entry>
    <title>vscode 插件开发</title>
    <link href="https://xuebin.me/posts/a0508b9c.html"/>
    <id>https://xuebin.me/posts/a0508b9c.html</id>
    <published>2019-03-12T02:16:22.000Z</published>
    <updated>2019-03-12T02:16:22.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><blockquote><p>应用商店地址: <span class="exturl" data-url="aHR0cHM6Ly9tYXJrZXRwbGFjZS52aXN1YWxzdHVkaW8uY29tL2l0ZW1zP2l0ZW1OYW1lPU1yTGVvLnpwbQ==" title="https://marketplace.visualstudio.com/items?itemName=MrLeo.zpm">https://marketplace.visualstudio.com/items?itemName=MrLeo.zpm<i class="fa fa-external-link"></i></span></p></blockquote><p>每个程序猿都有很多自己的代码片段，没到使用的时候都是 Ctrl+C &amp; Ctrl+V 粘过来粘过去，对于一个爱偷懒的程序猿，这样的重复工作能不能减少呢。</p><p>工欲善其事必先利其器。vscode 作为优秀的开发工具，给我的日常开发工作提供了极大的便利。其拓展机制更是如此。</p><p>于是便想着自己来开发这么个东西来管理自己的代码片段，一方面方便后边自己使用，一方面也能学习下 vscode 的插件开发、发布方法，另一方面要是发布后对其他人有所帮助就更好了。</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><ul><li><span class="exturl" data-url="aHR0cHM6Ly9saWlrZWQuZ2l0aHViLmlvL1ZTLUNvZGUtRXh0ZW5zaW9uLURvYy1aSC8jLw==" title="https://liiked.github.io/VS-Code-Extension-Doc-ZH/#/">VS Code 插件开发文档-中文版<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cDovL2Jsb2cuaGFvamkubWUvdnNjb2RlLXBsdWdpbi1vdmVydmlldy5odG1s" title="http://blog.haoji.me/vscode-plugin-overview.html">VSCode 插件开发全攻略<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9tYXJrZXRwbGFjZS52aXN1YWxzdHVkaW8uY29tLw==" title="https://marketplace.visualstudio.com/">插件市场<i class="fa fa-external-link"></i></span> / <span class="exturl" data-url="aHR0cHM6Ly9tYXJrZXRwbGFjZS52aXN1YWxzdHVkaW8uY29tL21hbmFnZQ==" title="https://marketplace.visualstudio.com/manage">插件管理<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9qdWVqaW4uaW0vcG9zdC81YzI5ZTFiN2U1MWQ0NTM1ZjA1YTUzZDQ=" title="https://juejin.im/post/5c29e1b7e51d4535f05a53d4">使用 Webapck 优化 VS Code 插件加载性能<i class="fa fa-external-link"></i></span><a id="more"></a></li></ul><h1 id="vscode-插件开发、发布主要流程"><a href="#vscode-插件开发、发布主要流程" class="headerlink" title="vscode 插件开发、发布主要流程"></a>vscode 插件开发、发布主要流程</h1><ol><li>插件开发前的准备：vscode、nodejs、vscode 插件生产工具、git、微软账号</li><li>插件开发：插件构思、<span class="exturl" data-url="aHR0cHM6Ly9jb2RlLnZpc3VhbHN0dWRpby5jb20vYXBpL2dldC1zdGFydGVkL3lvdXItZmlyc3QtZXh0ZW5zaW9u" title="https://code.visualstudio.com/api/get-started/your-first-extension">官方文档<i class="fa fa-external-link"></i></span>查阅</li><li>插件发布：打包、上传、<span class="exturl" data-url="aHR0cHM6Ly9tYXJrZXRwbGFjZS52aXN1YWxzdHVkaW8uY29tLw==" title="https://marketplace.visualstudio.com/">插件市场<i class="fa fa-external-link"></i></span>操作</li><li>插件维护：更新迭代后打包、上传、<span class="exturl" data-url="aHR0cHM6Ly9tYXJrZXRwbGFjZS52aXN1YWxzdHVkaW8uY29tLw==" title="https://marketplace.visualstudio.com/">插件市场<i class="fa fa-external-link"></i></span>操作</li></ol><h1 id="准备"><a href="#准备" class="headerlink" title="准备"></a>准备</h1><p>vscode、nodejs、git、微软账号，这个的准备无需多说。</p><p>vscode 插件生产工具：<span class="exturl" data-url="aHR0cHM6Ly9jb2RlLnZpc3VhbHN0dWRpby5jb20vYXBpL2dldC1zdGFydGVkL3lvdXItZmlyc3QtZXh0ZW5zaW9u" title="https://code.visualstudio.com/api/get-started/your-first-extension">官方推荐<i class="fa fa-external-link"></i></span>使用 Yeoman 和 VS Code Extension Generator。用如下命令安装：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 插件生成器</span></span><br><span class="line">npm install -g yo generator-code</span><br></pre></td></tr></table></figure><p>至此开发所需的准备已做好。</p><h1 id="开发"><a href="#开发" class="headerlink" title="开发"></a>开发</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 初始化代码</span></span><br><span class="line">yo code</span><br></pre></td></tr></table></figure><p>结果如下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><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">$ yo code</span><br><span class="line"></span><br><span class="line">     _-----_     ╭──────────────────────────╮</span><br><span class="line">    |       |    │   Welcome to the Visual  │</span><br><span class="line">    |--(o)--|    │   Studio Code Extension  │</span><br><span class="line">   `---------´   │        generator!        │</span><br><span class="line">    ( _´U`_ )    ╰──────────────────────────╯</span><br><span class="line">    /___A___\   /</span><br><span class="line">     |  ~  |</span><br><span class="line">   __<span class="string">'.___.'</span>__</span><br><span class="line"> ´   `  |° ´ Y `</span><br><span class="line"></span><br><span class="line">? What <span class="built_in">type</span> of extension <span class="keyword">do</span> you want to create? (Use arrow keys)</span><br><span class="line">&gt; New Extension (TypeScript)</span><br><span class="line">  New Extension (JavaScript)</span><br><span class="line">  New Color Theme</span><br><span class="line">  New Language Support</span><br><span class="line">  New Code Snippets</span><br><span class="line">  New Keymap</span><br><span class="line">  New Extension Pack</span><br><span class="line">(Move up and down to reveal more choices)</span><br></pre></td></tr></table></figure><p>在 os 系统上通过上下键来选择要创建的类型，在 win 上输入 1、2、3 后按回车来选择。</p><p>其余步骤根据提示即可。得到如下结构目录结构：</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></pre></td><td class="code"><pre><span class="line">.</span><br><span class="line">├── .vscode                              // 资源配置文件</span><br><span class="line">├── CHANGELOG.md                         // 更改记录文件，会展示到vscode插件市场</span><br><span class="line">├── README.md                            // 插件介绍文件，会展示到vscode插件市场</span><br><span class="line">├── logo.png                             // 插件图标</span><br><span class="line">├── package.json                         // 资源配置文件</span><br><span class="line">├── snippets                             // 存放所有片段</span><br><span class="line">│   └── javascript.json                  // javascript的代码片段</span><br><span class="line">└── vsc-extension-quickstart.md</span><br></pre></td></tr></table></figure><blockquote><p><strong>ps：</strong>其余项目类型的文档目录可能会有所差别，以生成的文件目录为准。在 snippet 拓展项目下，最重要的就是<code></code>snippets/*.json<code>和</code>package.json`</p></blockquote><p>如果不知道如何编写 snippet 文件，可以使用<span class="exturl" data-url="aHR0cHM6Ly9zbmlwcGV0LWdlbmVyYXRvci5hcHAv" title="https://snippet-generator.app/">snippet-generator<i class="fa fa-external-link"></i></span>生成你需要的代码片段</p><p>如果想知道具体 vscode 支持的代码片段格式，可以阅读：<span class="exturl" data-url="aHR0cHM6Ly9jb2RlLnZpc3VhbHN0dWRpby5jb20vZG9jcy9lZGl0b3IvdXNlcmRlZmluZWRzbmlwcGV0cw==" title="https://code.visualstudio.com/docs/editor/userdefinedsnippets">Creating your own snippets<i class="fa fa-external-link"></i></span></p><p>开发其他 vscode 插件，可以查阅文档<span class="exturl" data-url="aHR0cHM6Ly9jb2RlLnZpc3VhbHN0dWRpby5jb20vYXBpL3JlZmVyZW5jZXMvdnNjb2RlLWFwaQ==" title="https://code.visualstudio.com/api/references/vscode-api">vscode-api<i class="fa fa-external-link"></i></span></p><p>添加不同语言的代码片段，只需要在<code>snippets</code>文件夹中添加对应语言的<code>.json</code>文件，然后向<code>package.json</code>文件的<code>contributes.snippets</code>属性中添加片段文件路径并制定应用的语言 👇</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  "name": "zpm-snippet", // 插件的名称必须用全小写无空格的字母组成。</span><br><span class="line">  "displayName": "zpm-snippet", // 插件市场所显示的插件名称。</span><br><span class="line">  "description": "智联 ZPFE API 项目 VSCode 代码片段", // 简单地描述一下你的插件是做什么的。</span><br><span class="line">  "version": "0.0.10", // 版本号</span><br><span class="line">  "publisher": "MrLeo", // 发行方名称</span><br><span class="line">  "icon": "logo.png", // 应用图标</span><br><span class="line">  // 插件市场所显示的插件关联的github仓库</span><br><span class="line">  "repository": &#123;</span><br><span class="line">    "type": "git",</span><br><span class="line">    "url": "https://github.com/MrLeo/zpm-snippet.git"</span><br><span class="line">  &#125;,</span><br><span class="line">  // 一个至少包含vscode字段的对象，其值必须兼容 VS Code版本。</span><br><span class="line">  // 不可以是*。</span><br><span class="line">  // 例如：^0.10.5 表明最小兼容0.10.5版本的VS Code。</span><br><span class="line">  "engines": &#123;</span><br><span class="line">    "vscode": "^1.31.0"</span><br><span class="line">  &#125;,</span><br><span class="line">  // 你想要使用的插件分类，可选值有：[Programming Languages, Snippets,</span><br><span class="line">  // Linters, Themes, Debuggers, Formatters, Keymaps, SCM Providers,</span><br><span class="line">  // Other, Extension Packs, Language Packs]</span><br><span class="line">  "categories": ["Snippets"],</span><br><span class="line">  // 描述插件发布内容的对象。</span><br><span class="line">  "contributes": &#123;</span><br><span class="line">    "snippets": [</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="attr">"language"</span>: <span class="string">"javascript"</span>,</span><br><span class="line">        <span class="attr">"path"</span>: <span class="string">"./snippets/javascript.json"</span></span><br><span class="line">      &#125;</span><br><span class="line">    ]</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>更多插件清单文件说明：<span class="exturl" data-url="aHR0cHM6Ly9saWlrZWQuZ2l0aHViLmlvL1ZTLUNvZGUtRXh0ZW5zaW9uLURvYy1aSC8jL2V4dGVuc2liaWxpdHktcmVmZXJlbmNlL2V4dGVuc2lvbi1tYW5pZmVzdA==" title="https://liiked.github.io/VS-Code-Extension-Doc-ZH/#/extensibility-reference/extension-manifest">package.json<i class="fa fa-external-link"></i></span></p></blockquote><h1 id="构建"><a href="#构建" class="headerlink" title="构建"></a>构建</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装打包&amp;发布工具</span></span><br><span class="line">npm install -g vsce</span><br><span class="line"></span><br><span class="line"><span class="comment"># 打包插件</span></span><br><span class="line">vsce package</span><br></pre></td></tr></table></figure><p>打包成功后会在根目录下得到：<code>zpm-snippet-0.0.10.vsix</code>文件</p><h1 id="发布"><a href="#发布" class="headerlink" title="发布"></a>发布</h1><h2 id="方式一：vsce-publish-工具发布"><a href="#方式一：vsce-publish-工具发布" class="headerlink" title="方式一：vsce publish 工具发布"></a>方式一：<code>vsce publish</code> 工具发布</h2><blockquote><p>用 vsce 的<code>vsce publish</code>工具来发布，但是需要在官网配置<code>Personal Access Token</code>较为繁琐。</p><p>可参考<span class="exturl" data-url="aHR0cHM6Ly9jb2RlLnZpc3VhbHN0dWRpby5jb20vYXBpL3dvcmtpbmctd2l0aC1leHRlbnNpb25zL3B1Ymxpc2hpbmctZXh0ZW5zaW9u" title="https://code.visualstudio.com/api/working-with-extensions/publishing-extension">官方教程<i class="fa fa-external-link"></i></span> / <span class="exturl" data-url="aHR0cDovL2Jsb2cuaGFvamkubWUvdnNjb2RlLXBsdWdpbi1wdWJsaXNoLmh0bWwjZmEtYnUteWluZy15b25nLXNoaS1jaGFuZw==" title="http://blog.haoji.me/vscode-plugin-publish.html#fa-bu-ying-yong-shi-chang">发布应用市场<i class="fa fa-external-link"></i></span></p></blockquote><p>Visual Studio Code 的应用市场基于微软自己的<code>Azure DevOps</code>，插件的身份验证、托管和管理都是在这里。</p><ul><li>要发布到应用市场首先得有应用市场的<code>publisher</code>账号；</li><li>而要有发布账号首先得有<code>Azure DevOps</code>组织；</li><li>而创建组织之前，首先得创建<code>Azure</code>账号；</li><li>创建<code>Azure</code>账号首先得有<code>Microsoft</code>账号；</li></ul><p>是不是有点晕，梳理一下：</p><ul><li>一个 Microsoft 账号可以创建多个<code>Azure</code>组织；</li><li>一个组织可以创建多个<code>publisher</code>账号；</li><li>同时一个组织可以创建多个<code>PAT</code>（<code>Personal Access Token</code>，个人访问令牌）；</li></ul><h3 id="注册账号"><a href="#注册账号" class="headerlink" title="注册账号"></a>注册账号</h3><p>首先访问 <span class="exturl" data-url="aHR0cHM6Ly9sb2dpbi5saXZlLmNvbS8=" title="https://login.live.com/">https://login.live.com/<i class="fa fa-external-link"></i></span> 登录你的<code>Microsoft</code>账号，没有的先注册一个：</p><p><img src="https://image.xuebin.me/006tKfTcly1g12cbrd34fj30g30de42t.jpg" alt></p><p>然后访问： <span class="exturl" data-url="aHR0cHM6Ly9ha2EubXMvU2lnbnVwQXp1cmVEZXZPcHM=" title="https://aka.ms/SignupAzureDevOps">https://aka.ms/SignupAzureDevOps<i class="fa fa-external-link"></i></span> ，如果你从来没有使用过 Azure，那么会看到如下提示：</p><p><img src="https://image.xuebin.me/006tKfTcly1g12ccc4lz1j30db0963yx.jpg" alt></p><p>点击继续，默认会创建一个以邮箱前缀为名的组织。</p><h3 id="创建令牌"><a href="#创建令牌" class="headerlink" title="创建令牌"></a>创建令牌</h3><p>默认进入组织的主页后，点击右上角的<code>Security</code>：</p><p><img src="https://image.xuebin.me/006tKfTcly1g12cdmzbtoj30ks0gv3zw.jpg" alt></p><p>点击创建新的个人访问令牌，这里特别要注意<code>Organization</code>要选择<code>all accessible organizations</code>，<code>Scopes</code>要选择<code>Full access</code>，否则后面发布会失败。</p><p><img src="https://image.xuebin.me/006tKfTcly1g12ce9wnf9j30hr0dy3zx.jpg" alt></p><p>创建令牌成功后你需要本地记下来，因为网站是不会帮你保存的。</p><h3 id="创建发布账号"><a href="#创建发布账号" class="headerlink" title="创建发布账号"></a>创建发布账号</h3><p>获得个人访问令牌后，使用<code>vsce</code>以下命令创建新的发布者：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vsce create-publisher your-publisher-name</span><br></pre></td></tr></table></figure><p><code>your-publisher-name</code>必须是字母数字下划线，这是全网唯一的账号，然后会依次要求输入昵称、邮箱、令牌：</p><p><img src="https://image.xuebin.me/006tKfTcly1g12cgxdqauj30xc086wfr.jpg" alt></p><p>创建成功后会默认登录这个账号，接下来你可以直接发布了，</p><p>当然，如果你是在其它地方创建的，可以试用<code>vsce login your-publisher-name</code>来登录。</p><p>除了用命令之外，你还可以使用网页版创建发布账号：<span class="exturl" data-url="aHR0cHM6Ly9tYXJrZXRwbGFjZS52aXN1YWxzdHVkaW8uY29tL21hbmFnZQ==" title="https://marketplace.visualstudio.com/manage">https://marketplace.visualstudio.com/manage<i class="fa fa-external-link"></i></span></p><h3 id="发布-1"><a href="#发布-1" class="headerlink" title="发布"></a>发布</h3><p>发布很简单：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vsce publish</span><br></pre></td></tr></table></figure><p><img src="https://image.xuebin.me/006tKfTcly1g12ci26vzcj30uc072myr.jpg" alt="img"></p><p>发布成功后大概需要过几分钟才能在应用市场搜到。过几分钟就可以访问网页版的插件主页：<span class="exturl" data-url="aHR0cHM6Ly9tYXJrZXRwbGFjZS52aXN1YWxzdHVkaW8uY29tL2l0ZW1zP2l0ZW1OYW1lPU1yTGVvLnpwbS1zbmlwcGV0" title="https://marketplace.visualstudio.com/items?itemName=MrLeo.zpm-snippet">https://marketplace.visualstudio.com/items?itemName=MrLeo.zpm-snippet<i class="fa fa-external-link"></i></span></p><h3 id="增量发布"><a href="#增量发布" class="headerlink" title="增量发布"></a>增量发布</h3><p>版本号：<code>major.minor.patch</code></p><p>如果想让发布之后版本号的 patch 自增，例如：<code>1.0.2</code> -&gt; <code>1.0.3</code>，可以这样：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vsce publish patch</span><br></pre></td></tr></table></figure><p>执行这个命令后会自动修改<code>package.json</code>里面的版本号。同理，<code>vsce publish minor</code>也是可以的。</p><h3 id="取消发布"><a href="#取消发布" class="headerlink" title="取消发布"></a>取消发布</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vsce unpublish (publisher name).(extension name)</span><br></pre></td></tr></table></figure><h2 id="方式二：在官网直接上传发布"><a href="#方式二：在官网直接上传发布" class="headerlink" title="方式二：在官网直接上传发布"></a>方式二：在官网直接上传发布</h2><h3 id="在插件市场官网创建发布人"><a href="#在插件市场官网创建发布人" class="headerlink" title="在插件市场官网创建发布人"></a>在<span class="exturl" data-url="aHR0cHM6Ly9tYXJrZXRwbGFjZS52aXN1YWxzdHVkaW8uY29tL21hbmFnZS9wdWJsaXNoZXJzLw==" title="https://marketplace.visualstudio.com/manage/publishers/">插件市场官网<i class="fa fa-external-link"></i></span>创建发布人</h3><p><img src="https://image.xuebin.me/006tKfTcly1g0zv5eor8hj30yg07dwg3.jpg" alt></p><p><img src="https://image.xuebin.me/006tKfTcly1g0zuxvhq1rj30y107zmy9.jpg" alt="image-20190312113750984"></p><h3 id="上传插件"><a href="#上传插件" class="headerlink" title="上传插件"></a>上传插件</h3><p><img src="https://image.xuebin.me/006tKfTcly1g0zv4jpijaj30xs07imxd.jpg" alt><br><img src="https://image.xuebin.me/006tKfTcly1g0zv5qseo2j30wx0gc3zq.jpg" alt></p><p>上传后点击确定即可发布成功。</p><h2 id="发布注意事项"><a href="#发布注意事项" class="headerlink" title="发布注意事项"></a>发布注意事项</h2><ul><li><code>README.md</code>文件默认会显示在插件主页；</li><li><code>README.md</code>中的资源必须全部是<code>HTTPS</code>的，如果是<code>HTTP</code>会发布失败；</li><li><code>CHANGELOG.md</code>会显示在变更选项卡；</li><li>如果代码是放在 git 仓库并且设置了 repository 字段，发布前必须先提交 git，否则会提示<code>Git working directory not clean</code>；</li></ul><p>另外，如前面所说，如果<code>Organization</code>没有选择<code>all accessible organizations</code>，或者<code>Scopes</code>没有选择<code>Full access</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></pre></td><td class="code"><pre><span class="line">Error: Failed Request: Unauthorized(401) - https://marketplace.visualstudio.com/_apis/gallery</span><br><span class="line">Be sure to use a Personal Access Token <span class="built_in">which</span> has access to **all accessible accounts**.</span><br><span class="line">See https://code.visualstudio.com/docs/tools/vscecli<span class="comment">#_common-questions for more information.</span></span><br></pre></td></tr></table></figure><h1 id="检查"><a href="#检查" class="headerlink" title="检查"></a>检查</h1><ul><li><p>在插件市场官网看状态</p><p><img src="https://image.xuebin.me/006tKfTcly1g0zv8cg750j30x107x3yz.jpg" alt></p></li><li><p>在插件市场官网搜索</p><p><img src="https://image.xuebin.me/006tKfTcly1g0zv8eai49j30x70cptaf.jpg" alt></p></li><li><p>在 vscode 插件页搜索</p><p><img src="https://image.xuebin.me/006tKfTcly1g0zv8h3w76j30yg0f5ab4.jpg" alt></p></li></ul><h1 id="维护"><a href="#维护" class="headerlink" title="维护"></a>维护</h1><p>在发现 bug 和新功能开发完成后，需要更新插件，只需要将新生产的.vsix 包上传到官网即可。</p><p><img src="https://image.xuebin.me/006tKfTcly1g0zv9v8qcwj30yb0bmdgi.jpg" alt></p>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;应用商店地址: &lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9tYXJrZXRwbGFjZS52aXN1YWxzdHVkaW8uY29tL2l0ZW1zP2l0ZW1OYW1lPU1yTGVvLnpwbQ==&quot; title=&quot;https://marketplace.visualstudio.com/items?itemName=MrLeo.zpm&quot;&gt;https://marketplace.visualstudio.com/items?itemName=MrLeo.zpm&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;每个程序猿都有很多自己的代码片段，没到使用的时候都是 Ctrl+C &amp;amp; Ctrl+V 粘过来粘过去，对于一个爱偷懒的程序猿，这样的重复工作能不能减少呢。&lt;/p&gt;
&lt;p&gt;工欲善其事必先利其器。vscode 作为优秀的开发工具，给我的日常开发工作提供了极大的便利。其拓展机制更是如此。&lt;/p&gt;
&lt;p&gt;于是便想着自己来开发这么个东西来管理自己的代码片段，一方面方便后边自己使用，一方面也能学习下 vscode 的插件开发、发布方法，另一方面要是发布后对其他人有所帮助就更好了。&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;ul&gt;
&lt;li&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9saWlrZWQuZ2l0aHViLmlvL1ZTLUNvZGUtRXh0ZW5zaW9uLURvYy1aSC8jLw==&quot; title=&quot;https://liiked.github.io/VS-Code-Extension-Doc-ZH/#/&quot;&gt;VS Code 插件开发文档-中文版&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cDovL2Jsb2cuaGFvamkubWUvdnNjb2RlLXBsdWdpbi1vdmVydmlldy5odG1s&quot; title=&quot;http://blog.haoji.me/vscode-plugin-overview.html&quot;&gt;VSCode 插件开发全攻略&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9tYXJrZXRwbGFjZS52aXN1YWxzdHVkaW8uY29tLw==&quot; title=&quot;https://marketplace.visualstudio.com/&quot;&gt;插件市场&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt; / &lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9tYXJrZXRwbGFjZS52aXN1YWxzdHVkaW8uY29tL21hbmFnZQ==&quot; title=&quot;https://marketplace.visualstudio.com/manage&quot;&gt;插件管理&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9qdWVqaW4uaW0vcG9zdC81YzI5ZTFiN2U1MWQ0NTM1ZjA1YTUzZDQ=&quot; title=&quot;https://juejin.im/post/5c29e1b7e51d4535f05a53d4&quot;&gt;使用 Webapck 优化 VS Code 插件加载性能&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;
    
    </summary>
    
      <category term="工具" scheme="https://xuebin.me/categories/%E5%B7%A5%E5%85%B7/"/>
    
    
      <category term="工具" scheme="https://xuebin.me/tags/%E5%B7%A5%E5%85%B7/"/>
    
      <category term="vscode" scheme="https://xuebin.me/tags/vscode/"/>
    
      <category term="snippet" scheme="https://xuebin.me/tags/snippet/"/>
    
      <category term="插件" scheme="https://xuebin.me/tags/%E6%8F%92%E4%BB%B6/"/>
    
  </entry>
  
  <entry>
    <title>实现npm run tag自动为当前git版本生成tag标签</title>
    <link href="https://xuebin.me/posts/dbc584b2.html"/>
    <id>https://xuebin.me/posts/dbc584b2.html</id>
    <published>2019-03-11T01:45:27.000Z</published>
    <updated>2019-03-11T01:45:27.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p>因为公司上线Jenkins构建规定根据固定规则的tag进行筛选构建，每次上线都要手敲一长串的tag甚是麻烦，作为一个爱偷懒的程序猿，能用自动化完成的工作一定不手动。</p><a id="more"></a><p>作为前端，node环境应该是必须的，废话不多说，直接上代码👇</p><blockquote><p>运行脚本会自动检查安装依赖包 <code>npm install --save-dev shelljs inquirer chalk simple-git semver</code></p><p>运行方式：</p><ol><li>直接<code>node tag</code></li><li>package.json 文件的 <code>scripts</code> 属性中添加: <code>&quot;tag&quot;: &quot;node ./tag&quot;</code></li></ol></blockquote><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * @Company: 智联招聘</span></span><br><span class="line"><span class="comment"> * @Author: xuebin.me</span></span><br><span class="line"><span class="comment"> * @LastEditors: Leo</span></span><br><span class="line"><span class="comment"> * @version: 0.0.0</span></span><br><span class="line"><span class="comment"> * @Description: Git自动生成Tag</span></span><br><span class="line"><span class="comment"> * @Date: 2019-03-09 17:06:50</span></span><br><span class="line"><span class="comment"> * @LastEditTime: 2019-03-10 12:18:16</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="comment">/* eslint-disable */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> log = <span class="built_in">console</span>.log</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">'fs'</span>)</span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>)</span><br><span class="line"><span class="keyword">const</span> _exec = <span class="built_in">require</span>(<span class="string">'child_process'</span>).exec</span><br><span class="line"></span><br><span class="line"><span class="built_in">Promise</span>.all([</span><br><span class="line">  checkPackage(<span class="string">'shelljs'</span>),</span><br><span class="line">  checkPackage(<span class="string">'inquirer'</span>),</span><br><span class="line">  checkPackage(<span class="string">'chalk'</span>),</span><br><span class="line">  checkPackage(<span class="string">'simple-git'</span>),</span><br><span class="line">  checkPackage(<span class="string">'semver'</span>),</span><br><span class="line">]).then(<span class="function"><span class="params">()</span> =&gt;</span> app())</span><br><span class="line"></span><br><span class="line"><span class="comment">// #region 检查并自动安装依赖包</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"> * https://sourcegraph.com/github.com/vuejs/vue-cli/-/blob/packages/@vue/cli/lib/util/installDeps.js</span></span><br><span class="line"><span class="comment"> * @param &#123;*&#125; package 依赖包名</span></span><br><span class="line"><span class="comment"> * @returns</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">checkPackage</span>(<span class="params">package</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <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">    fs.exists(path.resolve(<span class="string">`<span class="subst">$&#123;process.cwd()&#125;</span>/node_modules/<span class="subst">$&#123;package&#125;</span>/`</span>), exists =&gt; &#123;</span><br><span class="line">      <span class="keyword">if</span> (!exists) &#123;</span><br><span class="line">        log(<span class="string">'📦  正在安装依赖包: '</span>, package, <span class="string">'...'</span>)</span><br><span class="line">        log(<span class="string">''</span>)</span><br><span class="line">        <span class="keyword">let</span> cwd = <span class="string">`npm install --save-dev <span class="subst">$&#123;package&#125;</span>`</span></span><br><span class="line">        <span class="keyword">const</span> child = _exec(cwd, &#123; <span class="attr">silent</span>: <span class="literal">true</span> &#125;)</span><br><span class="line">        child.stdout.on(<span class="string">'data'</span>, buffer =&gt; process.stdout.write(buffer))</span><br><span class="line">        child.on(<span class="string">'close'</span>, code =&gt; &#123;</span><br><span class="line">          <span class="keyword">if</span> (code !== <span class="number">0</span>) &#123;</span><br><span class="line">            reject(<span class="string">`command failed: <span class="subst">$&#123;cwd&#125;</span>`</span>)</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">          &#125;</span><br><span class="line">          resolve()</span><br><span class="line">        &#125;)</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        resolve()</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// #endregion</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">app</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// #region 引入依赖包</span></span><br><span class="line">  <span class="built_in">require</span>(<span class="string">'shelljs/global'</span>)</span><br><span class="line">  <span class="keyword">const</span> inquirer = <span class="built_in">require</span>(<span class="string">'inquirer'</span>)</span><br><span class="line">  <span class="keyword">const</span> chalk = <span class="built_in">require</span>(<span class="string">'chalk'</span>)</span><br><span class="line">  <span class="keyword">const</span> git = <span class="built_in">require</span>(<span class="string">'simple-git/promise'</span>)(process.cwd())</span><br><span class="line">  <span class="comment">// #endregion</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// #region 获取本地package.json文件配置</span></span><br><span class="line">  <span class="keyword">const</span> packageJsonPath = path.resolve(process.cwd(), <span class="string">'package.json'</span>) <span class="comment">// 获取package文件路径</span></span><br><span class="line">  <span class="keyword">const</span> packageJson = <span class="built_in">require</span>(packageJsonPath) <span class="comment">// 获取当前的package文件配置</span></span><br><span class="line">  <span class="keyword">const</span> envConfig = &#123; <span class="attr">master</span>: <span class="string">'version'</span>, <span class="attr">pre</span>: <span class="string">'version_pre'</span>, <span class="attr">dev</span>: <span class="string">'version_dev'</span> &#125; <span class="comment">// 配置不同环境的version属性名</span></span><br><span class="line">  <span class="comment">// #endregion</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// #region 命令行交互</span></span><br><span class="line">  log(<span class="string">''</span>)</span><br><span class="line">  inquirer</span><br><span class="line">    .prompt([</span><br><span class="line">      &#123;</span><br><span class="line">        name: <span class="string">'baseline'</span>,</span><br><span class="line">        message: <span class="string">`选择Tag基线:`</span>,</span><br><span class="line">        type: <span class="string">'list'</span>,</span><br><span class="line">        <span class="keyword">default</span>: <span class="number">1</span>,</span><br><span class="line">        choices: [</span><br><span class="line">          &#123; <span class="attr">name</span>: <span class="string">'根据package.json文件的version生成并更新文件'</span>, <span class="attr">value</span>: <span class="string">'package'</span> &#125;,</span><br><span class="line">          &#123; <span class="attr">name</span>: <span class="string">'根据最新的Tag生成'</span>, <span class="attr">value</span>: <span class="string">'tag'</span> &#125;,</span><br><span class="line">        ],</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        name: <span class="string">'env'</span>,</span><br><span class="line">        message: <span class="string">`选择环境:`</span>,</span><br><span class="line">        type: <span class="string">'list'</span>,</span><br><span class="line">        <span class="keyword">default</span>: <span class="number">2</span>,</span><br><span class="line">        choices: [<span class="string">'all'</span>, <span class="string">'master'</span>, <span class="string">'pre'</span>, <span class="string">'dev'</span>],</span><br><span class="line">      &#125;,</span><br><span class="line">    ])</span><br><span class="line">    .then(<span class="keyword">async</span> (&#123; baseline, env &#125;) =&gt; &#123;</span><br><span class="line">      <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (baseline === <span class="string">'package'</span>) &#123;</span><br><span class="line">          <span class="keyword">await</span> addTagByPackage(env)</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">          <span class="keyword">await</span> addTagByTags(env)</span><br><span class="line">        &#125;</span><br><span class="line">        git.push()</span><br><span class="line">      &#125; <span class="keyword">catch</span> (err) &#123;&#125;</span><br><span class="line">    &#125;)</span><br><span class="line">  <span class="comment">// #endregion</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// #region 根据Tag列表添加Tag</span></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * 根据Tag列表添加Tag</span></span><br><span class="line"><span class="comment">   *</span></span><br><span class="line"><span class="comment">   * @param &#123;*&#125; env</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">addTagByTags</span>(<span class="params">env</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// const tags = fs.readdirSync('./.git/refs/tags') // 同步版本的readdir</span></span><br><span class="line">    <span class="keyword">await</span> commitAllFiles()</span><br><span class="line">    <span class="keyword">await</span> git.pull(&#123; <span class="string">'--rebase'</span>: <span class="string">'true'</span> &#125;)</span><br><span class="line">    <span class="keyword">const</span> tags = <span class="keyword">await</span> git.tags()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">let</span> addTagSingle = <span class="keyword">async</span> envName =&gt; &#123;</span><br><span class="line">      <span class="keyword">const</span> reg = <span class="keyword">new</span> <span class="built_in">RegExp</span>(<span class="string">`^<span class="subst">$&#123;envName&#125;</span>`</span>)</span><br><span class="line">      <span class="keyword">let</span> envTags = tags.all.filter(<span class="function"><span class="params">tag</span> =&gt;</span> reg.test(tag))</span><br><span class="line">      <span class="keyword">let</span> lastTag = envTags[envTags.length - <span class="number">1</span>] || <span class="string">`<span class="subst">$&#123;envName&#125;</span>-v0.0.0-19000101`</span></span><br><span class="line">      log(chalk<span class="string">`&#123;gray 🏷  仓库最新的Tag: <span class="subst">$&#123;lastTag&#125;</span>&#125;`</span>)</span><br><span class="line">      <span class="keyword">let</span> lastVsersion = lastTag.split(<span class="string">'-'</span>)[<span class="number">1</span>].substring(<span class="number">1</span>)</span><br><span class="line">      <span class="keyword">let</span> version = <span class="keyword">await</span> generateNewTag(envName, lastVsersion)</span><br><span class="line">      log(chalk<span class="string">`&#123;gray 🏷  生成最新的Tag: <span class="subst">$&#123;version.tag&#125;</span>&#125;`</span>)</span><br><span class="line">      <span class="keyword">await</span> createTag([version])</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (env === <span class="string">'all'</span>) &#123;</span><br><span class="line">      <span class="keyword">await</span> <span class="built_in">Promise</span>.all(<span class="built_in">Object</span>.keys(envConfig).map(<span class="function"><span class="params">key</span> =&gt;</span> addTagSingle(key)))</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">await</span> addTagSingle(env)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// #endregion</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// #region 根据package.json添加tag</span></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * 根据package.json添加tag</span></span><br><span class="line"><span class="comment">   * @param &#123;*&#125; env master|pre|dev|all</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">addTagByPackage</span>(<span class="params">env</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="comment">// #region 生成对应环境的最新version和tag</span></span><br><span class="line">      <span class="keyword">let</span> versionsPromise</span><br><span class="line">      <span class="keyword">if</span> (env === <span class="string">'all'</span>) &#123;</span><br><span class="line">        versionsPromise = <span class="built_in">Object</span>.keys(envConfig).map(<span class="function"><span class="params">key</span> =&gt;</span></span><br><span class="line">          generateNewTag(key, packageJson[envConfig[key]] || packageJson.version),</span><br><span class="line">        )</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        versionsPromise = [generateNewTag(env, packageJson[envConfig[env]] || packageJson.version)]</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">const</span> versions = <span class="keyword">await</span> <span class="built_in">Promise</span>.all(versionsPromise)</span><br><span class="line">      <span class="comment">// #endregion</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// #region 更新本地package.json文件，并将更新后的package信息写入本地文件中</span></span><br><span class="line">      versions.forEach(<span class="function">(<span class="params">&#123; version, env &#125;</span>) =&gt;</span> &#123;</span><br><span class="line">        packageJson[envConfig[env]] = version</span><br><span class="line">        log(chalk<span class="string">`&#123;green 📦  package.json 文件添加属性 =&gt; <span class="subst">$&#123;envConfig[env]&#125;</span>: <span class="subst">$&#123;version&#125;</span>&#125;`</span>)</span><br><span class="line">      &#125;) <span class="comment">// 更新package对应环境的version</span></span><br><span class="line">      fs.writeFileSync(packageJsonPath, <span class="built_in">JSON</span>.stringify(packageJson, <span class="literal">null</span>, <span class="string">'  '</span>))</span><br><span class="line">      <span class="comment">// #endregion</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// #region commit package.json 文件的修改</span></span><br><span class="line">      <span class="keyword">const</span> version = versions[<span class="number">0</span>].version</span><br><span class="line">      <span class="keyword">const</span> date = formatTime(<span class="keyword">new</span> <span class="built_in">Date</span>())</span><br><span class="line">      <span class="keyword">const</span> newTagsStr = versions.map(<span class="function"><span class="params">version</span> =&gt;</span> version.tag).join(<span class="string">' / '</span>)</span><br><span class="line">      log(chalk<span class="string">`&#123;gray ➕  暂存package.json文件变更&#125;`</span>)</span><br><span class="line">      <span class="keyword">await</span> git.add(<span class="string">'./package.json'</span>)</span><br><span class="line">      log(chalk<span class="string">`&#123;gray ✔️  提交package.json文件变更&#125;`</span>)</span><br><span class="line">      <span class="keyword">await</span> git.commit(<span class="string">`Relase version <span class="subst">$&#123;version&#125;</span> in <span class="subst">$&#123;date&#125;</span> by <span class="subst">$&#123;newTagsStr&#125;</span>`</span>)</span><br><span class="line">      log(chalk<span class="string">`&#123;green 👌  package.json文件操作完成&#125;`</span>)</span><br><span class="line">      <span class="comment">// #endregion</span></span><br><span class="line"></span><br><span class="line">      <span class="keyword">await</span> commitAllFiles()</span><br><span class="line">      <span class="keyword">await</span> createTag(versions)</span><br><span class="line">    &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">      log(chalk<span class="string">`&#123;red <span class="subst">$&#123;error.message&#125;</span>&#125;`</span>)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// #endregion</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// #region 创建Tag</span></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * 创建Tag</span></span><br><span class="line"><span class="comment">   * @param &#123;*&#125; versions</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">createTag</span>(<span class="params">versions</span>) </span>&#123;</span><br><span class="line">    log(chalk<span class="string">`&#123;green 🔀  更新本地仓库&#125;`</span>)</span><br><span class="line">    <span class="keyword">await</span> git.pull(&#123; <span class="string">'--rebase'</span>: <span class="string">'true'</span> &#125;)</span><br><span class="line"></span><br><span class="line">    versions.forEach(<span class="keyword">async</span> version =&gt; &#123;</span><br><span class="line">      log(chalk<span class="string">`&#123;green 🏷  创建标签 <span class="subst">$&#123;version.tag&#125;</span>&#125;`</span>)</span><br><span class="line">      <span class="keyword">await</span> git.addTag(version.tag)</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// #endregion</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// #region commit 所有未提交的文件</span></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * commit 所有未提交的文件</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">commitAllFiles</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> statusSummary = <span class="keyword">await</span> git.status()</span><br><span class="line">    <span class="keyword">if</span> (statusSummary.files.length) &#123;</span><br><span class="line">      log(chalk<span class="string">`&#123;red 🚨  有未提交的文件变更&#125;`</span>)</span><br><span class="line">      log(chalk<span class="string">`&#123;gray ➕  暂存未提交的文件变更&#125;`</span>)</span><br><span class="line">      <span class="keyword">await</span> git.add(<span class="string">'./*'</span>)</span><br><span class="line">      log(chalk<span class="string">`&#123;gray ✔️  提交未提交的文件变更&#125;`</span>)</span><br><span class="line">      <span class="keyword">await</span> git.commit(<span class="string">'🚀'</span>)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// #endregion</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// #region 生成新Tag</span></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * 生成新Tag</span></span><br><span class="line"><span class="comment">   * @param &#123;*&#125; env master|pre|dev|all</span></span><br><span class="line"><span class="comment">   * @param &#123;*&#125; version</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">generateNewTag</span>(<span class="params">env = <span class="string">'pre'</span>, version = <span class="string">'0.0.0'</span></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <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">      <span class="keyword">const</span> semver = <span class="built_in">require</span>(<span class="string">'semver'</span>)</span><br><span class="line">      <span class="comment">// const major = semver.major(version)</span></span><br><span class="line">      <span class="keyword">const</span> minor = semver.minor(version)</span><br><span class="line">      <span class="keyword">const</span> patch = semver.patch(version)</span><br><span class="line">      <span class="keyword">const</span> date = formatTime(<span class="keyword">new</span> <span class="built_in">Date</span>(), <span class="string">'&#123;y&#125;&#123;m&#125;&#123;d&#125;'</span>)</span><br><span class="line">      <span class="keyword">const</span> config = &#123; env, version, <span class="attr">tag</span>: <span class="string">`<span class="subst">$&#123;env&#125;</span>-v<span class="subst">$&#123;version&#125;</span>-<span class="subst">$&#123;date&#125;</span>`</span> &#125;</span><br><span class="line">      <span class="keyword">if</span> (patch &gt;= <span class="number">99</span>) &#123;</span><br><span class="line">        config.version = semver.inc(version, <span class="string">'minor'</span>)</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (minor &gt;= <span class="number">99</span>) &#123;</span><br><span class="line">        config.version = semver.inc(version, <span class="string">'major'</span>)</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        config.version = semver.inc(version, <span class="string">'patch'</span>)</span><br><span class="line">      &#125;</span><br><span class="line">      config.tag = <span class="string">`<span class="subst">$&#123;env&#125;</span>-v<span class="subst">$&#123;config.version&#125;</span>-<span class="subst">$&#123;date&#125;</span>`</span></span><br><span class="line">      resolve(config)</span><br><span class="line"></span><br><span class="line">      <span class="comment">// const Bump = require('bump-regex') // 为git的version添加自动增长版本号组件</span></span><br><span class="line">      <span class="comment">// Bump(`version:$&#123;version&#125;`, (err, out) =&gt; &#123;</span></span><br><span class="line">      <span class="comment">//   if (out) &#123;</span></span><br><span class="line">      <span class="comment">//     const date = formatTime(new Date(), '&#123;y&#125;&#123;m&#125;&#123;d&#125;')</span></span><br><span class="line">      <span class="comment">//     resolve(&#123;</span></span><br><span class="line">      <span class="comment">//       env,</span></span><br><span class="line">      <span class="comment">//       version: out.new,</span></span><br><span class="line">      <span class="comment">//       tag: `$&#123;env&#125;-v$&#123;out.new&#125;-$&#123;date&#125;`</span></span><br><span class="line">      <span class="comment">//     &#125;)</span></span><br><span class="line">      <span class="comment">//   &#125; else &#123;</span></span><br><span class="line">      <span class="comment">//     reject(err)</span></span><br><span class="line">      <span class="comment">//   &#125;</span></span><br><span class="line">      <span class="comment">// &#125;)</span></span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// #endregion</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// #region 格式化时间</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">   *</span></span><br><span class="line"><span class="comment">   * @param  &#123;time&#125; 时间</span></span><br><span class="line"><span class="comment">   * @param  &#123;cFormat&#125; 格式</span></span><br><span class="line"><span class="comment">   * @return &#123;String&#125; 字符串</span></span><br><span class="line"><span class="comment">   *</span></span><br><span class="line"><span class="comment">   * @example formatTime('2018-1-29', '&#123;y&#125;/&#123;m&#125;/&#123;d&#125; &#123;h&#125;:&#123;i&#125;:&#123;s&#125;') // -&gt; 2018/01/29 00:00:00</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">formatTime</span>(<span class="params">time, cFormat</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">arguments</span>.length === <span class="number">0</span>) <span class="keyword">return</span> <span class="literal">null</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="string">`<span class="subst">$&#123;time&#125;</span>`</span>.length === <span class="number">10</span>) &#123;</span><br><span class="line">      time = +time * <span class="number">1000</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">const</span> format = cFormat || <span class="string">'&#123;y&#125;-&#123;m&#125;-&#123;d&#125; &#123;h&#125;:&#123;i&#125;:&#123;s&#125;'</span></span><br><span class="line">    <span class="keyword">let</span> date</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> time === <span class="string">'object'</span>) &#123;</span><br><span class="line">      date = time</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      date = <span class="keyword">new</span> <span class="built_in">Date</span>(time)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">const</span> formatObj = &#123;</span><br><span class="line">      y: date.getFullYear(),</span><br><span class="line">      m: date.getMonth() + <span class="number">1</span>,</span><br><span class="line">      d: date.getDate(),</span><br><span class="line">      h: date.getHours(),</span><br><span class="line">      i: date.getMinutes(),</span><br><span class="line">      s: date.getSeconds(),</span><br><span class="line">      a: date.getDay(),</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">const</span> time_str = format.replace(<span class="regexp">/&#123;(y|m|d|h|i|s|a)+&#125;/g</span>, (result, key) =&gt; &#123;</span><br><span class="line">      <span class="keyword">let</span> value = formatObj[key]</span><br><span class="line">      <span class="keyword">if</span> (key === <span class="string">'a'</span>) <span class="keyword">return</span> [<span class="string">'一'</span>, <span class="string">'二'</span>, <span class="string">'三'</span>, <span class="string">'四'</span>, <span class="string">'五'</span>, <span class="string">'六'</span>, <span class="string">'日'</span>][value - <span class="number">1</span>]</span><br><span class="line">      <span class="keyword">if</span> (result.length &gt; <span class="number">0</span> &amp;&amp; value &lt; <span class="number">10</span>) &#123;</span><br><span class="line">        value = <span class="string">`0<span class="subst">$&#123;value&#125;</span>`</span></span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">return</span> value || <span class="number">0</span></span><br><span class="line">    &#125;)</span><br><span class="line">    <span class="keyword">return</span> time_str</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// #endregion</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// #region 获取git版本</span></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * 获取git版本</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">getGitVersion</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> gitHEAD = fs.readFileSync(<span class="string">'.git/HEAD'</span>, <span class="string">'utf-8'</span>).trim() <span class="comment">// ref: refs/heads/develop</span></span><br><span class="line">    <span class="keyword">const</span> ref = gitHEAD.split(<span class="string">': '</span>)[<span class="number">1</span>] <span class="comment">// refs/heads/develop</span></span><br><span class="line">    <span class="keyword">const</span> develop = gitHEAD.split(<span class="string">'/'</span>)[<span class="number">2</span>] <span class="comment">// 环境：develop</span></span><br><span class="line">    <span class="keyword">const</span> gitVersion = fs.readFileSync(<span class="string">`.git/<span class="subst">$&#123;ref&#125;</span>`</span>, <span class="string">'utf-8'</span>).trim() <span class="comment">// git版本号，例如：6ceb0ab5059d01fd444cf4e78467cc2dd1184a66</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">`"<span class="subst">$&#123;develop&#125;</span>: <span class="subst">$&#123;gitVersion&#125;</span>"`</span> <span class="comment">// 例如dev环境: "develop: 6ceb0ab5059d01fd444cf4e78467cc2dd1184a66"</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// #endregion</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// #region shelljs直接执行Git脚本更新tag</span></span><br><span class="line">  <span class="comment">// const commitMessage = `"chore(package.json): bump version to $&#123;version&#125;"`</span></span><br><span class="line">  <span class="comment">// const relaseMessage = `Relase version $&#123;version&#125; in $&#123;formatTime(new Date())&#125;`</span></span><br><span class="line">  <span class="comment">// const cmd = `git add package.json</span></span><br><span class="line">  <span class="comment">// &amp;&amp; git commit -m $&#123;commitMessage&#125;</span></span><br><span class="line">  <span class="comment">// &amp;&amp; git tag -a $&#123;tag&#125; -m $&#123;relaseMessage&#125;</span></span><br><span class="line">  <span class="comment">// &amp;&amp; git push origin master</span></span><br><span class="line">  <span class="comment">// &amp;&amp; git push origin --tags`</span></span><br><span class="line">  <span class="comment">// console.log('TCL: cmd', cmd)</span></span><br><span class="line">  <span class="comment">// exec(cmd)</span></span><br><span class="line">  <span class="comment">// #endregion</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;因为公司上线Jenkins构建规定根据固定规则的tag进行筛选构建，每次上线都要手敲一长串的tag甚是麻烦，作为一个爱偷懒的程序猿，能用自动化完成的工作一定不手动。&lt;/p&gt;
    
    </summary>
    
      <category term="前端" scheme="https://xuebin.me/categories/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="node" scheme="https://xuebin.me/categories/%E5%89%8D%E7%AB%AF/node/"/>
    
    
      <category term="dev" scheme="https://xuebin.me/tags/dev/"/>
    
      <category term="前端" scheme="https://xuebin.me/tags/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="开发" scheme="https://xuebin.me/tags/%E5%BC%80%E5%8F%91/"/>
    
      <category term="node" scheme="https://xuebin.me/tags/node/"/>
    
      <category term="git" scheme="https://xuebin.me/tags/git/"/>
    
      <category term="cli" scheme="https://xuebin.me/tags/cli/"/>
    
  </entry>
  
  <entry>
    <title>@vue/cli 3 搭建组件库并发布npm</title>
    <link href="https://xuebin.me/posts/43109cf3.html"/>
    <id>https://xuebin.me/posts/43109cf3.html</id>
    <published>2019-03-08T12:43:33.000Z</published>
    <updated>2019-03-08T12:43:33.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p>市面上目前已有各种各样的 UI 组件库，比如 Element 和 iView，他们的强大毋庸置疑。但是我们面临的情况是需求越来越复杂，当它们不能再满足我们需求的时候，这个时候就有必要开发一套属于自己团队的组件库了。</p><p>所以本文的目的就是让读者能通过此文，小能做一个简单的插件供人使用，大能架构和维护一个组件库不在话下。</p><a id="more"></a><h1 id="技术栈"><a href="#技术栈" class="headerlink" title="技术栈"></a>技术栈</h1><p>如何通过新版脚手架创建项目，这里就不提了，自行看官方文档。</p><ul><li>Vue-cli3: 新版脚手架的库模式，可以让我们很轻松的创建打包一个库</li><li>npm：组件库将存放在 npm</li><li>webpack：修改配置需要一点 webapck 的知识。</li></ul><h1 id="大纲"><a href="#大纲" class="headerlink" title="大纲"></a>大纲</h1><p>想要搭建一个组件库，我们必须先要有一个大概的思路。</p><ol><li>规划目录结构</li><li>配置项目以支持目录结构</li><li>编写组件</li><li>编写示例</li><li>配置使用库模式打包编译</li><li>发布到 npm</li></ol><h1 id="规划目录结构"><a href="#规划目录结构" class="headerlink" title="规划目录结构"></a>规划目录结构</h1><h2 id="创建项目"><a href="#创建项目" class="headerlink" title="创建项目"></a>创建项目</h2><p>在指定目录中使用命令创建一个默认的项目，或者根据自己需要自己选择。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ vue create .</span><br></pre></td></tr></table></figure><h2 id="调整目录"><a href="#调整目录" class="headerlink" title="调整目录"></a>调整目录</h2><p>我们需要一个目录存放组件，一个目录存放示例，按照以下方式对目录进行改造。</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></pre></td><td class="code"><pre><span class="line">.</span><br><span class="line">...</span><br><span class="line">|-- examples      // 原 src 目录，改成 examples 用作示例展示</span><br><span class="line">|-- packages      // 新增 packages 用于编写存放组件</span><br><span class="line">...</span><br><span class="line">.</span><br></pre></td></tr></table></figure><p><img src="https://image.xuebin.me/006tKfTcly1g0yqox7ipkj30bm0dg3yz.jpg" alt></p><h1 id="配置项目以支持新的目录结构"><a href="#配置项目以支持新的目录结构" class="headerlink" title="配置项目以支持新的目录结构"></a>配置项目以支持新的目录结构</h1><p>我们通过上一步的目录改造后，会遇到两个问题。</p><ol><li><code>src</code>目录更名为<code>examples</code>，导致项目无法运行</li><li>新增<code>packages</code>目录，该目录未加入<code>webpack</code>编译</li></ol><p><strong>注：cli3 提供一个可选的 vue.config.js 配置文件。如果这个文件存在则他会被自动加载，所有的对项目和 webpack 的配置，都在这个文件中。</strong></p><h2 id="重新配置入口，修改配置中的-pages-选项"><a href="#重新配置入口，修改配置中的-pages-选项" class="headerlink" title="重新配置入口，修改配置中的 pages 选项"></a>重新配置入口，修改配置中的 <code>pages</code> 选项</h2><p>新版 Vue CLI 支持使用 <code>vue.config.js</code> 中的 <code>pages</code> 选项构建一个多页面的应用。</p><p>这里使用 <code>pages</code> 修改入口到 <code>examples</code></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// 修改 src 目录 为 examples 目录</span></span><br><span class="line">  pages: &#123;</span><br><span class="line">    index: &#123;</span><br><span class="line">      entry: <span class="string">'examples/main.js'</span>,</span><br><span class="line">      template: <span class="string">'public/index.html'</span>,</span><br><span class="line">      filename: <span class="string">'index.html'</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="支持对-packages-目录的处理，修改配置中的-chainWebpack-选项"><a href="#支持对-packages-目录的处理，修改配置中的-chainWebpack-选项" class="headerlink" title="支持对 packages 目录的处理，修改配置中的 chainWebpack 选项"></a>支持对 <code>packages</code> 目录的处理，修改配置中的 <code>chainWebpack</code> 选项</h2><p><code>packages</code> 是我们新增的一个目录，默认是不被 webpack 处理的，所以需要添加配置对该目录的支持。</p><p><code>chainWebpack</code> 是一个函数，会接收一个基于 <code>webpack-chain</code> 的 <code>ChainableConfig</code> 实例。允许对内部的 <code>webpack</code> 配置进行更细粒度的修改。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// 修改 src 为 examples</span></span><br><span class="line">  pages: &#123;</span><br><span class="line">    index: &#123;</span><br><span class="line">      entry: <span class="string">'examples/main.js'</span>,</span><br><span class="line">      template: <span class="string">'public/index.html'</span>,</span><br><span class="line">      filename: <span class="string">'index.html'</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// 扩展 webpack 配置，使 packages 加入编译</span></span><br><span class="line">  chainWebpack: <span class="function"><span class="params">config</span> =&gt;</span> &#123;</span><br><span class="line">    config.module</span><br><span class="line">      .rule(<span class="string">'js'</span>)</span><br><span class="line">      .include.add(<span class="string">'/packages'</span>)</span><br><span class="line">      .end()</span><br><span class="line">      .use(<span class="string">'babel'</span>)</span><br><span class="line">      .loader(<span class="string">'babel-loader'</span>)</span><br><span class="line">      .tap(<span class="function"><span class="params">options</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="comment">// 修改它的选项...</span></span><br><span class="line">        <span class="keyword">return</span> options</span><br><span class="line">      &#125;)</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><span class="exturl" data-url="aHR0cHM6Ly9jbGkudnVlanMub3JnL3poL2d1aWRlL3dlYnBhY2suaHRtbCMlRTklOTMlQkUlRTUlQkMlOEYlRTYlOTMlOEQlRTQlQkQlOUMtJUU5JUFCJTk4JUU3JUJBJUE3" title="https://cli.vuejs.org/zh/guide/webpack.html#%E9%93%BE%E5%BC%8F%E6%93%8D%E4%BD%9C-%E9%AB%98%E7%BA%A7">链式操作<i class="fa fa-external-link"></i></span></p><p><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL25ldXRyaW5vanMvd2VicGFjay1jaGFpbg==" title="https://github.com/neutrinojs/webpack-chain">webpack-chain<i class="fa fa-external-link"></i></span></p><h1 id="编写组件"><a href="#编写组件" class="headerlink" title="编写组件"></a>编写组件</h1><h2 id="创建一个新组件"><a href="#创建一个新组件" class="headerlink" title="创建一个新组件"></a>创建一个新组件</h2><ol><li>在 <code>packages</code> 目录下，所有的单个组件都以文件夹的形式存储，所有这里创建一个目录 <code>color-picker/</code></li><li>在 <code>color-picker/</code> 目录下创建 <code>src/</code> 目录存储组件源码</li><li>在 <code>/color-picker</code> 目录下创建 <code>index.js</code> 文件对外提供对组件的引用。</li></ol><p><img src="https://image.xuebin.me/006tKfTcly1g0ytnidlzgj30fc0k2754.jpg" alt></p><p>修改 <code>/packages/color-picker/index.js</code>文件，对外提供引用。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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"># /packages/color-picker/index.js</span><br><span class="line">// 导入组件，组件必须声明 name</span><br><span class="line">import colorPicker from &apos;./src/color-picker.vue&apos;</span><br><span class="line"></span><br><span class="line">// 为组件提供 install 安装方法，供按需引入</span><br><span class="line">colorPicker.install = function (Vue) &#123;</span><br><span class="line">  Vue.component(colorPicker.name, colorPicker)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// 默认导出组件</span><br><span class="line">export default colorPicker</span><br></pre></td></tr></table></figure><h2 id="整合所有的组件，对外导出，即一个完整的组件库"><a href="#整合所有的组件，对外导出，即一个完整的组件库" class="headerlink" title="整合所有的组件，对外导出，即一个完整的组件库"></a>整合所有的组件，对外导出，即一个完整的组件库</h2><p>修改 <code>/packages/index.js</code> 文件，对整个组件库进行导出。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 导入颜色选择器组件</span></span><br><span class="line"><span class="keyword">import</span> colorPicker <span class="keyword">from</span> <span class="string">'./color-picker'</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 存储组件列表</span></span><br><span class="line"><span class="keyword">const</span> components = [colorPicker]</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义 install 方法，接收 Vue 作为参数。如果使用 use 注册插件，则所有的组件都将被注册</span></span><br><span class="line"><span class="keyword">const</span> install = <span class="function"><span class="keyword">function</span>(<span class="params">Vue</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 判断是否安装</span></span><br><span class="line">  <span class="keyword">if</span> (install.installed) <span class="keyword">return</span></span><br><span class="line">  <span class="comment">// 遍历注册全局组件</span></span><br><span class="line">  components.map(<span class="function"><span class="params">component</span> =&gt;</span> Vue.component(component.name, component))</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 判断是否是直接引入文件</span></span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">typeof</span> <span class="built_in">window</span> !== <span class="string">'undefined'</span> &amp;&amp; <span class="built_in">window</span>.Vue) &#123;</span><br><span class="line">  install(<span class="built_in">window</span>.Vue)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">  <span class="comment">// 导出的对象必须具有 install，才能被 Vue.use() 方法安装</span></span><br><span class="line">  install,</span><br><span class="line">  <span class="comment">// 以下是具体的组件列表</span></span><br><span class="line">  colorPicker,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="编写示例"><a href="#编写示例" class="headerlink" title="编写示例"></a>编写示例</h1><h2 id="在示例中导入组件库"><a href="#在示例中导入组件库" class="headerlink" title="在示例中导入组件库"></a>在示例中导入组件库</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> Vue <span class="keyword">from</span> <span class="string">'vue'</span></span><br><span class="line"><span class="keyword">import</span> App <span class="keyword">from</span> <span class="string">'./App.vue'</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 导入组件库</span></span><br><span class="line"><span class="keyword">import</span> ColorPicker <span class="keyword">from</span> <span class="string">'./../packages/index'</span></span><br><span class="line"><span class="comment">// 注册组件库</span></span><br><span class="line">Vue.use(ColorPicker)</span><br><span class="line"></span><br><span class="line">Vue.config.productionTip = <span class="literal">false</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">new</span> Vue(&#123;</span><br><span class="line">  render: <span class="function"><span class="params">h</span> =&gt;</span> h(App),</span><br><span class="line">&#125;).$mount(<span class="string">'#app'</span>)</span><br></pre></td></tr></table></figure><h2 id="在示例中使用组件库中的组件"><a href="#在示例中使用组件库中的组件" class="headerlink" title="在示例中使用组件库中的组件"></a>在示例中使用组件库中的组件</h2><p>在上一步用使用 <code>Vue.use()</code> 全局注册后，即可在任意页面直接使用了，而不需另外引入。当然也可以按需引入。</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></pre></td><td class="code"><pre><span class="line">&lt;template&gt;</span><br><span class="line">  &lt;colorPicker v-model=&quot;color&quot; v-on:change=&quot;headleChangeColor&quot;&gt;&lt;/colorPicker&gt;</span><br><span class="line">&lt;/template&gt;</span><br><span class="line"></span><br><span class="line">&lt;script&gt;</span><br><span class="line">export default &#123;</span><br><span class="line">  data() &#123;</span><br><span class="line">    return &#123;</span><br><span class="line">      color: &apos;#ff0000&apos;,</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  methods: &#123;</span><br><span class="line">    headleChangeColor() &#123;</span><br><span class="line">      console.log(&apos;颜色改变&apos;)</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;</span><br><span class="line">&lt;/script&gt;</span><br></pre></td></tr></table></figure><h1 id="发布到-npm，方便直接在项目中引用"><a href="#发布到-npm，方便直接在项目中引用" class="headerlink" title="发布到 npm，方便直接在项目中引用"></a>发布到 npm，方便直接在项目中引用</h1><p>到此为止我们一个完整的组件库已经开发完成了，接下来就是发布到 <code>npm</code> 以供后期使用。</p><h2 id="package-json-中新增一条编译为库的命令"><a href="#package-json-中新增一条编译为库的命令" class="headerlink" title="package.json 中新增一条编译为库的命令"></a><code>package.json</code> 中新增一条编译为库的命令</h2><p>在库模式中，Vue 是外置的，这意味着即使在代码中引入了 Vue，打包后的文件也是不包含 Vue 的。</p><p><span class="exturl" data-url="aHR0cHM6Ly9jbGkudnVlanMub3JnL3poL2d1aWRlL2J1aWxkLXRhcmdldHMuaHRtbCMlRTUlQkElOTM=" title="https://cli.vuejs.org/zh/guide/build-targets.html#%E5%BA%93">Vue Cli3 构建目标：库<i class="fa fa-external-link"></i></span></p><p><strong>以下我们在 scripts 中新增一条命令 npm run lib</strong></p><ul><li><code>--target</code>: 构建目标，默认为应用模式。这里修改为 <code>lib</code> 启用库模式。</li><li><code>--dest</code> : 输出目录，默认 <code>dist</code>。这里我们改成 <code>lib</code></li><li><code>[entry]</code>: 最后一个参数为入口文件，默认为 <code>src/App.vue</code>。这里我们指定编译 <code>packages/</code> 组件库目录。</li></ul><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">"scripts": &#123;</span><br><span class="line">// ...</span><br><span class="line">"lib": "vue-cli-service build --target lib --name vcolorpicker --dest lib packages/index.js"</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>执行编译库命令</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm run lib</span><br></pre></td></tr></table></figure><p><img src="https://image.xuebin.me/006tKfTcly1g0ytwsz7byj30tg086wja.jpg" alt></p><h2 id="配置-package-json-文件中发布到-npm-的字段"><a href="#配置-package-json-文件中发布到-npm-的字段" class="headerlink" title="配置 package.json 文件中发布到 npm 的字段"></a>配置 <code>package.json</code> 文件中发布到 npm 的字段</h2><ul><li><code>name</code>: 包名，该名字是唯一的。可在 npm 官网搜索名字，如果存在则需换个名字。</li><li><code>version</code>: 版本号，每次发布至 npm 需要修改版本号，不能和历史版本号相同。</li><li><code>description</code>: 描述。</li><li><code>main</code>: 入口文件，该字段需指向我们最终编译后的包文件。</li><li><code>keyword</code>：关键字，以空格分离希望用户最终搜索的词。</li><li><code>author</code>：作者</li><li><code>private</code>：是否私有，需要修改为 false 才能发布到 npm</li><li><code>license</code>： 开源协议</li></ul><p>以下为参考设置</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"name"</span>: <span class="string">"vcolorpicker"</span>,</span><br><span class="line">  <span class="attr">"version"</span>: <span class="string">"0.1.5"</span>,</span><br><span class="line">  <span class="attr">"description"</span>: <span class="string">"基于 Vue 的颜色选择器"</span>,</span><br><span class="line">  <span class="attr">"main"</span>: <span class="string">"lib/vcolorpicker.umd.min.js"</span>,</span><br><span class="line">  <span class="attr">"keyword"</span>: <span class="string">"vcolorpicker colorpicker color-picker"</span>,</span><br><span class="line">  <span class="attr">"private"</span>: <span class="literal">false</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="添加-npmignore-文件，设置忽略发布文件"><a href="#添加-npmignore-文件，设置忽略发布文件" class="headerlink" title="添加 .npmignore 文件，设置忽略发布文件"></a>添加 <code>.npmignore</code> 文件，设置忽略发布文件</h2><p>我们发布到 npm 中，只有编译后的 <code>lib</code> 目录、package.json、README.md 才是需要被发布的。所以我们需要设置忽略目录和文件。</p><p>和 <code>.gitignore</code> 的语法一样，具体需要提交什么文件，看各自的实际情况。</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></pre></td><td class="code"><pre><span class="line"># 忽略目录</span><br><span class="line">examples/</span><br><span class="line">packages/</span><br><span class="line">public/</span><br><span class="line"></span><br><span class="line"># 忽略指定文件</span><br><span class="line">vue.config.js</span><br><span class="line">babel.config.js</span><br><span class="line">*.map</span><br></pre></td></tr></table></figure><h2 id="登录到登录到-npm"><a href="#登录到登录到-npm" class="headerlink" title="登录到登录到 npm"></a>登录到登录到 npm</h2><p>首先需要到 npm 上注册一个账号，注册过程略。</p><p>如果配置了淘宝镜像，先设置回 npm 镜像：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm config <span class="built_in">set</span> registry http://registry.npmjs.org</span><br></pre></td></tr></table></figure><p>然后在终端执行登录命令，输入用户名、密码、邮箱即可登录。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm login</span><br></pre></td></tr></table></figure><p><img src="https://image.xuebin.me/006tKfTcly1g0yu1xprimj30k403w76s.jpg" alt></p><h2 id="发布到-npm"><a href="#发布到-npm" class="headerlink" title="发布到 npm"></a>发布到 npm</h2><p>执行发布命令，发布组件到 npm</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm publish</span><br></pre></td></tr></table></figure><p><img src="https://image.xuebin.me/006tKfTcly1g0yu3kehc6j30rg0igtmp.jpg" alt></p><h2 id="发布成功"><a href="#发布成功" class="headerlink" title="发布成功"></a>发布成功</h2><p>发布成功后稍等几分钟，即可在 npm 官网搜索到。以下是刚提交的 <code>vcolorpicker</code></p><p><img src="https://image.xuebin.me/006tKfTcly1g0yu7h3oyfj315i0u0414.jpg" alt></p><h2 id="使用新发布的组件库"><a href="#使用新发布的组件库" class="headerlink" title="使用新发布的组件库"></a>使用新发布的组件库</h2><p>安装</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm install vcolorpicker -S</span><br></pre></td></tr></table></figure><p>使用</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">// 在 main.js 引入并注册 import vcolorpicker from &apos;vcolorpicker&apos; Vue.use(vcolorpicker) //</span><br><span class="line">在组件中使用</span><br><span class="line">&lt;template&gt;</span><br><span class="line">  &lt;colorPicker v-model=&quot;color&quot; /&gt;</span><br><span class="line">&lt;/template&gt;</span><br><span class="line">&lt;script&gt;</span><br><span class="line">export default &#123;</span><br><span class="line">  data() &#123;</span><br><span class="line">    return &#123;</span><br><span class="line">      color: &apos;#ff0000&apos;,</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;</span><br><span class="line">&lt;/script&gt;</span><br></pre></td></tr></table></figure><blockquote><p><span class="exturl" data-url="aHR0cHM6Ly9qdWVqaW4uaW0vcG9zdC81YmJhYjlkZTUxODgyNTVjOGMwY2IwZTM=" title="https://juejin.im/post/5bbab9de5188255c8c0cb0e3">详解：Vue cli3 库模式搭建组件库并发布到 npm<i class="fa fa-external-link"></i></span></p></blockquote><h1 id="切换-npm-版本"><a href="#切换-npm-版本" class="headerlink" title="切换 npm 版本"></a>切换 npm 版本</h1><p>如果不小心把<code>1.0.0-alpha.1</code>直接<code>npm publish</code>了，我们该怎么办？</p><h2 id="查看当前的-tag-和对应的-version。"><a href="#查看当前的-tag-和对应的-version。" class="headerlink" title="查看当前的 tag 和对应的 version。"></a>查看当前的 tag 和对应的 version。</h2><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 dist-tag ls</span><br></pre></td></tr></table></figure><p>通过这个命令，我们可以得到这样的结果：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">latest: 1.0.0</span><br><span class="line">next: 1.0.0-alpha.0</span><br></pre></td></tr></table></figure><p>如果我们不小心发布了一个<code>1.0.0-alpha.1</code>到<code>latest</code>，那么我们会得到：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">latest: 1.0.0-alpha.1</span><br><span class="line">next: 1.0.0-alpha.0</span><br></pre></td></tr></table></figure><p>这个时候我们只要切一下 tag 就可以挽回局面了。</p><h2 id="查看-my-package-发布过的所有版本号"><a href="#查看-my-package-发布过的所有版本号" class="headerlink" title="查看 my-package 发布过的所有版本号"></a>查看 my-package 发布过的所有版本号</h2><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 view my-package versions</span><br></pre></td></tr></table></figure><p>通过这个命令可以得到一个数组：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"><span class="string">'0.1.0'</span>,</span><br><span class="line">  <span class="string">'1.0.0'</span>,</span><br><span class="line">  <span class="string">'1.0.0-alpha.0'</span>,</span><br><span class="line">  <span class="string">'1.0.0-alpha.1'</span></span><br><span class="line">]</span><br></pre></td></tr></table></figure><h2 id="给-my-package-设置-tag，对应到版本-version"><a href="#给-my-package-设置-tag，对应到版本-version" class="headerlink" title="给 my-package 设置 tag，对应到版本 version"></a>给 my-package 设置 tag，对应到版本 version</h2><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 dist-tag add my-package@version tag</span><br></pre></td></tr></table></figure><p>如：</p><ol><li><p>把原来的 1.0.0 设置成最新的正式版</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm dist-tag add my-package@1.0.0 latest</span><br></pre></td></tr></table></figure></li><li><p>把 1.0.0-alpha.1 更新到最新的测试版</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm dist-tag add my-package@1.0.0-alpha.1 next</span><br></pre></td></tr></table></figure></li></ol><p>老板再也不用担心我搞出线上故障了。</p><h1 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h1><blockquote class="blockquote-center"><ul><li><span class="exturl" data-url="aHR0cHM6Ly9zZWdtZW50ZmF1bHQuY29tL2EvMTE5MDAwMDAxNjg5MDQwNA==" title="https://segmentfault.com/a/1190000016890404">基于@vue/cli 3 搭建属于自己的组件库<i class="fa fa-external-link"></i></span></li><li><span class="exturl" data-url="aHR0cHM6Ly9qdWVqaW4uaW0vcG9zdC81ZDY3MjM4ZDZmYjlhMDZiMTgyOWNkY2M=" title="https://juejin.im/post/5d67238d6fb9a06b1829cdcc">Vue CLI3搭建组件库并用npm发布实战操作<i class="fa fa-external-link"></i></span></li></ul></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;市面上目前已有各种各样的 UI 组件库，比如 Element 和 iView，他们的强大毋庸置疑。但是我们面临的情况是需求越来越复杂，当它们不能再满足我们需求的时候，这个时候就有必要开发一套属于自己团队的组件库了。&lt;/p&gt;
&lt;p&gt;所以本文的目的就是让读者能通过此文，小能做一个简单的插件供人使用，大能架构和维护一个组件库不在话下。&lt;/p&gt;
    
    </summary>
    
      <category term="前端" scheme="https://xuebin.me/categories/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="vue" scheme="https://xuebin.me/categories/%E5%89%8D%E7%AB%AF/vue/"/>
    
    
      <category term="dev" scheme="https://xuebin.me/tags/dev/"/>
    
      <category term="前端" scheme="https://xuebin.me/tags/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="开发" scheme="https://xuebin.me/tags/%E5%BC%80%E5%8F%91/"/>
    
      <category term="npm" scheme="https://xuebin.me/tags/npm/"/>
    
      <category term="vue" scheme="https://xuebin.me/tags/vue/"/>
    
      <category term="cli" scheme="https://xuebin.me/tags/cli/"/>
    
      <category term="组件库" scheme="https://xuebin.me/tags/%E7%BB%84%E4%BB%B6%E5%BA%93/"/>
    
  </entry>
  
  <entry>
    <title>flutter学习</title>
    <link href="https://xuebin.me/posts/595a1d75.html"/>
    <id>https://xuebin.me/posts/595a1d75.html</id>
    <published>2019-02-11T05:11:48.000Z</published>
    <updated>2019-02-11T05:11:48.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><h1 id="相关链接"><a href="#相关链接" class="headerlink" title="相关链接"></a>相关链接</h1><ul><li><p>Flutter: <span class="exturl" data-url="aHR0cHM6Ly9mbHV0dGVyLmlvLw==" title="https://flutter.io/">英文<i class="fa fa-external-link"></i></span> / <span class="exturl" data-url="aHR0cHM6Ly9mbHV0dGVyY2hpbmEuY2x1Yi8=" title="https://flutterchina.club/">中文<i class="fa fa-external-link"></i></span></p></li><li><p>Dart: <span class="exturl" data-url="aHR0cHM6Ly93d3cuZGFydGxhbmcub3JnLw==" title="https://www.dartlang.org/">英文<i class="fa fa-external-link"></i></span> / <span class="exturl" data-url="aHR0cDovL3d3dy5kYXJ0ZG9jLmNu" title="http://www.dartdoc.cn">中文<i class="fa fa-external-link"></i></span></p></li><li><p><span class="exturl" data-url="aHR0cHM6Ly9kb2NzLmZsdXR0ZXIua2ltL3dpZGdldHMvd2lkZ2V0cy1saWJyYXJ5Lmh0bWw=" title="https://docs.flutter.kim/widgets/widgets-library.html">flutter-widgets 的官方库地址<i class="fa fa-external-link"></i></span></p></li><li><p><span class="exturl" data-url="aHR0cDovL2RvYy5mbHV0dGVyLWRldi5jbi93aWRnZXRzLw==" title="http://doc.flutter-dev.cn/widgets/">flutter-widgets 的官方目录集<i class="fa fa-external-link"></i></span></p></li><li><p><span class="exturl" data-url="aHR0cHM6Ly9ib29rLmZsdXR0ZXJjaGluYS5jbHVi" title="https://book.flutterchina.club">Flutter 实战<i class="fa fa-external-link"></i></span></p></li><li><p><span class="exturl" data-url="aHR0cHM6Ly9qdWVqaW4uaW0vcG9zdC81YzQxYWY0NjZmYjlhMDRhMGUyZDdkNTE=" title="https://juejin.im/post/5c41af466fb9a04a0e2d7d51">用前端 最舒服的躺姿 “搞定” Flutter<i class="fa fa-external-link"></i></span> / <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2FsaWJhYmEvZmx1dHRlci1nbw==" title="https://github.com/alibaba/flutter-go">flutter-go<i class="fa fa-external-link"></i></span></p></li><li><p><span class="exturl" data-url="aHR0cHM6Ly96aHVhbmxhbi56aGlodS5jb20vcC81NTMyOTYzMQ==" title="https://zhuanlan.zhihu.com/p/55329631">为前端工程师准备的 Flutter 入门指南<i class="fa fa-external-link"></i></span></p></li><li><p>…</p></li><li><p><span class="exturl" data-url="aHR0cHM6Ly9qdWVqaW4uaW0vdGFnL0ZsdXR0ZXI=" title="https://juejin.im/tag/Flutter">掘金 - Flutter<i class="fa fa-external-link"></i></span></p></li></ul><a id="more"></a><h1 id="Widgets"><a href="#Widgets" class="headerlink" title="Widgets"></a>Widgets</h1><h2 id="StatelessWidget"><a href="#StatelessWidget" class="headerlink" title="StatelessWidget"></a>StatelessWidget</h2><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyApp</span> <span class="keyword">extends</span> <span class="title">StatelessWidget</span> </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> MaterialApp(...);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="StatefulWidget"><a href="#StatefulWidget" class="headerlink" title="StatefulWidget"></a>StatefulWidget</h2><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyHomePage</span> <span class="keyword">extends</span> <span class="title">StatefulWidget</span> </span>&#123;</span><br><span class="line">  _MyHomePageState createState() =&gt; _MyHomePageState();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">_MyHomePageState</span> <span class="keyword">extends</span> <span class="title">State</span>&lt;<span class="title">MyHomePage</span>&gt; </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> Scaffold(...)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="flutter-widgets-的官方目录集"><a href="#flutter-widgets-的官方目录集" class="headerlink" title="flutter-widgets 的官方目录集"></a><span class="exturl" data-url="aHR0cDovL2RvYy5mbHV0dGVyLWRldi5jbi93aWRnZXRzLw==" title="http://doc.flutter-dev.cn/widgets/">flutter-widgets 的官方目录集<i class="fa fa-external-link"></i></span></h2><p><img src="https://image.xuebin.me/006tNc79ly1g02fza8b2ij30ni3m5gyb.jpg" alt="widgets目录"></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;ul&gt;
&lt;li&gt;&lt;p&gt;Flutter: &lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9mbHV0dGVyLmlvLw==&quot; title=&quot;https://flutter.io/&quot;&gt;英文&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt; / &lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9mbHV0dGVyY2hpbmEuY2x1Yi8=&quot; title=&quot;https://flutterchina.club/&quot;&gt;中文&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dart: &lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly93d3cuZGFydGxhbmcub3JnLw==&quot; title=&quot;https://www.dartlang.org/&quot;&gt;英文&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt; / &lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cDovL3d3dy5kYXJ0ZG9jLmNu&quot; title=&quot;http://www.dartdoc.cn&quot;&gt;中文&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9kb2NzLmZsdXR0ZXIua2ltL3dpZGdldHMvd2lkZ2V0cy1saWJyYXJ5Lmh0bWw=&quot; title=&quot;https://docs.flutter.kim/widgets/widgets-library.html&quot;&gt;flutter-widgets 的官方库地址&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cDovL2RvYy5mbHV0dGVyLWRldi5jbi93aWRnZXRzLw==&quot; title=&quot;http://doc.flutter-dev.cn/widgets/&quot;&gt;flutter-widgets 的官方目录集&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9ib29rLmZsdXR0ZXJjaGluYS5jbHVi&quot; title=&quot;https://book.flutterchina.club&quot;&gt;Flutter 实战&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9qdWVqaW4uaW0vcG9zdC81YzQxYWY0NjZmYjlhMDRhMGUyZDdkNTE=&quot; title=&quot;https://juejin.im/post/5c41af466fb9a04a0e2d7d51&quot;&gt;用前端 最舒服的躺姿 “搞定” Flutter&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt; / &lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9naXRodWIuY29tL2FsaWJhYmEvZmx1dHRlci1nbw==&quot; title=&quot;https://github.com/alibaba/flutter-go&quot;&gt;flutter-go&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly96aHVhbmxhbi56aGlodS5jb20vcC81NTMyOTYzMQ==&quot; title=&quot;https://zhuanlan.zhihu.com/p/55329631&quot;&gt;为前端工程师准备的 Flutter 入门指南&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;…&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;span class=&quot;exturl&quot; data-url=&quot;aHR0cHM6Ly9qdWVqaW4uaW0vdGFnL0ZsdXR0ZXI=&quot; title=&quot;https://juejin.im/tag/Flutter&quot;&gt;掘金 - Flutter&lt;i class=&quot;fa fa-external-link&quot;&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
    
    </summary>
    
      <category term="flutter" scheme="https://xuebin.me/categories/flutter/"/>
    
    
      <category term="前端" scheme="https://xuebin.me/tags/%E5%89%8D%E7%AB%AF/"/>
    
      <category term="开发" scheme="https://xuebin.me/tags/%E5%BC%80%E5%8F%91/"/>
    
      <category term="flutter" scheme="https://xuebin.me/tags/flutter/"/>
    
      <category term="移动端" scheme="https://xuebin.me/tags/%E7%A7%BB%E5%8A%A8%E7%AB%AF/"/>
    
  </entry>
  
</feed>
