唐志远
2024-02-23T12:42:33.496Z
https://fe32.top/
唐志远
Hexo
CentOS7.6 安装宝塔
https://fe32.top/articles/linux001/
2024-02-23T10:17:39.276Z
2024-02-23T12:42:33.496Z
面向小白,简单介绍 CentOS7.6 如何安装宝塔。
解决 Vue3 + Element Plus 树形表格全选多选以及子节点勾选的问题
https://fe32.top/articles/vue20006/
2023-10-23T07:02:24.000Z
2023-10-23T10:44:06.000Z
解决 Vue3 + Element Plus 树形表格全选多选以及子节点勾选的问题。
前端断点调试是如何实现的?
https://fe32.top/articles/fe100003/
2023-10-23T01:01:11.000Z
2023-10-23T06:59:10.000Z
本文概述了浏览器断点调试的基本原理,也介绍了 Vscode Web 代码断点调试能力,详细介绍了各模块中各 CDP 消息通信逻辑。
几个非常实用的 Chrome Devtools 技巧
https://fe32.top/articles/skill001/
2023-10-17T07:53:32.000Z
2023-10-23T04:29:54.000Z
分享几个实用的 Chrome Devtools 技巧。
卷王之王:快来看看前端出的新框架!
https://fe32.top/articles/fe100002/
2023-10-17T04:57:45.000Z
2024-02-23T10:55:34.852Z
Nue 是一个完整的前端开发工具集。Vite,Next.js和Astro等生态系统的替代品。
前端如何实现埋点上报?
https://fe32.top/articles/fe100001/
2023-09-15T12:55:32.000Z
2023-10-13T08:29:12.000Z
只有了解用户,才能服务好用户,那么要如何去了解用户呢? 最直接有效的方式就是埋点上报。
前端如何实现自动检测更新?
https://fe32.top/articles/vue20005/
2023-08-16T14:11:53.000Z
2023-08-16T14:44:54.000Z
本文介绍用【轮询】的方式来对比每次打包后的指纹,分析文件是否存在变动,如果有变动则提示用户更新。
如何在 CSS 中设计出漂亮的阴影?
https://fe32.top/articles/css10005/
2023-08-16T12:44:35.000Z
2023-08-16T14:42:06.000Z
在本教程中,我们将学习如何将典型的箱形阴影转换为漂亮、逼真的阴影。
推荐几个你可能会用到的 CSS 动画库
https://fe32.top/articles/css10004/
2023-08-16T12:29:35.000Z
2023-08-16T14:58:22.000Z
几个不错的 css 动画库 ~
如何将路径字符串数组(string[])转成树结构(treeNode[])?
https://fe32.top/articles/vue20004/
2023-07-23T16:56:12.000Z
2023-08-04T17:04:24.000Z
vue + element-plus 将路径字符串数组转换成目标树结构,并用el-tree成功渲染。
分享ikun小鸡仔表情包 300+
https://fe32.top/articles/ikun0001/
2023-07-16T15:35:17.000Z
2024-03-13T07:41:12.799Z
只因你太美,啊只因你太美!!!
vue3 如何将页面生成 pdf 导出
https://fe32.top/articles/vue20003/
2023-07-15T17:10:14.000Z
2023-09-25T07:44:16.000Z
html2Canvas + jspdf 将页面生产 pdf 导出。
亲测有效!Typora 安装激活教程
https://fe32.top/articles/typra001/
2023-07-15T16:53:26.000Z
2023-07-15T17:06:44.000Z
分享 Typora 安装激活教程,写文章的速度这不得起飞!!!
el-date-picker 宽度溢出浏览器问题
https://fe32.top/articles/ele266ui/
2023-07-15T16:47:19.000Z
2023-07-16T09:29:22.000Z
解决 vue3 + el-date-picker 宽度溢出浏览器问题。
Element Plus el-table 自定义合并行和列
https://fe32.top/articles/eleui266/
2023-07-07T14:12:50.000Z
2023-07-07T14:49:26.000Z
Element Plus el-table 将行数据中某个属性值相同的项合并到一起。
JavaScript 将对象数组按字母顺序排序
https://fe32.top/articles/jsnb9548/
2023-07-07T13:50:56.000Z
2023-07-07T15:36:20.000Z
javaScript 将对象数组,按字母顺序排序。
被发现了,这些吊炸天的摸鱼网站!
https://fe32.top/articles/joy10001/
2023-06-18T16:40:07.000Z
2023-06-18T16:45:48.000Z
推荐几个好玩的网页游戏网站!
解决 Vue 中使用 Echarts 出现 There is a chart instance already initialized on the dom 的警告问题
https://fe32.top/articles/vue20002/
2023-06-18T16:20:32.000Z
2023-06-18T16:45:34.000Z
解决 Vue 中使用 Echarts 出现 There is a chart instance already initialized on the dom 的警告问题。
如何新窗口打开导航链接?
https://fe32.top/articles/hexo1619/
2023-06-04T13:53:07.000Z
2023-06-05T15:23:36.000Z
简单介绍 Hexo +Butterfly 项目如何在新窗口中打开导航链接。
一文读懂TS的(.d.ts)文件
https://fe32.top/articles/ts30001/
2023-04-30T14:28:06.000Z
2023-05-17T10:01:12.000Z
随着前端技术的不断发展,TypeScript(简称:TS)已经在逐步取代JavaScript(简称:JS),尤其在以Vue3使用TS重构后,TS更是成为前端框架编写的主力语言。
Property 'xxx' does not exist on type 'AxiosResponse<any, any>' 的解决办法
https://fe32.top/articles/vue20001/
2023-04-30T12:34:18.000Z
2023-05-17T10:01:12.000Z
定义接口返回数据类型,解决 Property 'xxxx' does not exist on type 'AxiosResponse<any, any>'。
Css中有哪些可继承的属性?
https://fe32.top/articles/css10003/
2023-04-30T08:49:50.000Z
2023-05-17T10:01:12.000Z
简述 Css 中有哪些可继承属性和不可继承属性。
关于博客更新的二三事
https://fe32.top/articles/blog9528/
2023-04-14T16:45:14.000Z
2024-02-23T15:35:21.416Z
简述博客相比上一版本(v3.7.1)做了哪些优化
虚拟列表,我真的会了!
https://fe32.top/articles/xn2023lb/
2023-02-14T13:24:50.000Z
2024-02-23T12:41:15.238Z
<div class="tzy-post">
<h2 id="虚拟列表的使用场景"><a href="#虚拟列表的使用场景" class="headerlink" title="虚拟列表的使用场景"></a>虚拟列表的使用场景</h2><p>如果我想要在网页中放大量的列表项,纯渲染的话,对于浏览器性能将会是个极大的挑战,会造成滚动卡顿,整体体验非常不好,主要有以下问题:</p>
<ul>
<li>页面等待时间极长,用户体验差</li>
<li>CPU 计算能力不够,滑动会卡顿</li>
<li>GPU 渲染能力不够,页面会跳屏</li>
<li>RAM 内存容量不够,浏览器崩溃</li>
</ul>
<h3 id="传统做法"><a href="#传统做法" class="headerlink" title="传统做法"></a>传统做法</h3><p>对于长列表渲染,传统的方法是使用懒加载的方式,下拉到底部获取新的内容加载进来,其实就相当于是在垂直方向上的分页叠加功能,<strong>但随着加载数据越来越多,浏览器的回流和重绘的开销将会越来越大</strong>,整个滑动也会造成卡顿,这个时候我们就可以考虑使用虚拟列表来解决问题。</p>
<h3 id="虚拟列表"><a href="#虚拟列表" class="headerlink" title="虚拟列表"></a>虚拟列表</h3><p>其核心思想就是在处理用户滚动时,只改变列表在可视区域的渲染部分,具体步骤为:</p>
<p>先计算可见区域起始数据的索引值<code>startIndex</code>和当前可见区域结束数据的索引值<code>endIndex</code>,假如元素的高度是固定的,那么<code>startIndex</code>的算法很简单,即<code>startIndex = Math.floor(scrollTop/itemHeight),endIndex = startIndex + (clientHeight/itemHeight) - 1</code>,再根据<code>startIndex</code>和<code>endIndex</code>取相应范围的数据,渲染到可视区域,然后再计算<code>startOffset</code>(上滚动空白区域)和<code>endOffset</code>(下滚动空白区域),这两个偏移量的作用就是来撑开容器元素的内容,从而起到缓冲的作用,使得滚动条保持平滑滚动,并使滚动条处于一个正确的位置</p>
<p>上述的操作可以总结成五步:</p>
<ul>
<li>不把长列表数据一次性全部直接渲染在页面上</li>
<li>截取长列表一部分数据用来填充可视区域</li>
<li>长列表数据不可视部分使用空白占位填充(下图中的startOffset和endOffset区域)</li>
<li>监听滚动事件根据滚动位置动态改变可视列表</li>
<li>监听滚动事件根据滚动位置动态改变空白填充</li>
</ul>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2023/02/04/63de2ce894ab0.jpg"/></div></div>
<h2 id="定高虚拟列表实现步骤"><a href="#定高虚拟列表实现步骤" class="headerlink" title="定高虚拟列表实现步骤"></a>定高虚拟列表实现步骤</h2><blockquote>
<p>掘金使用的是传统懒加载的方式加载的哈,用的并不是虚拟列表,这里只是想表达一下什么是定高的列表!</p>
</blockquote>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2023/02/04/63de2cf6e083e.jpg"/></div></div>
<p>实现的效果应该是:<strong>不论怎么滚动,我们改变的只是滚动条的高度和可视区的元素内容,并没有增加任何多余的元素,下面来看看要怎么实现吧!</strong></p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">// 虚拟列表DOM结构</span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">className</span>=<span class="string">'container'</span>></span></span><br><span class="line"> // 监听滚动事件的盒子,该高度继承了父元素的高度</span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">className</span>=<span class="string">'scroll-box'</span> <span class="attr">ref</span>=<span class="string">{containerRef}</span> <span class="attr">onScroll</span>=<span class="string">{boxScroll}</span>></span></span><br><span class="line"> // 该盒子的高度一定会超过父元素,要不实现不了滚动的效果,而且还要动态的改变它的padding值用于控制滚动条的状态</span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">{topBlankFill.current}</span>></span></span><br><span class="line"> {</span><br><span class="line"> showList.map(item => <span class="tag"><<span class="name">div</span> <span class="attr">className</span>=<span class="string">'item'</span> <span class="attr">key</span>=<span class="string">{item.commentId</span> || (<span class="attr">Math.random</span>() + <span class="attr">item.comments</span>)}></span>{item.content}<span class="tag"></<span class="name">div</span>></span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure>
<h3 id="计算容器最大容积数量"><a href="#计算容器最大容积数量" class="headerlink" title="计算容器最大容积数量"></a>计算容器最大容积数量</h3><p>简单来说,就是我们必须要知道在可视区域内最多能够容纳多少个列表项,这是我们在截取内容数据渲染到页面之前关键的步骤之一</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"> <span class="comment">// 滚动容器高度改变后执行的函数</span></span><br><span class="line"><span class="keyword">const</span> changeHeight = <span class="title function_">useCallback</span>(<span class="title function_">throttle</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="comment">// 容器高度,通过操作dom元素获取高度是因为它不一定是个定值</span></span><br><span class="line"> curContainerHeight.<span class="property">current</span> = containerRef.<span class="property">current</span>.<span class="property">offsetHeight</span></span><br><span class="line"> <span class="comment">// 列表最大数量,考虑到列表中顶部和底部可能都会出现没有展现完的item</span></span><br><span class="line"> curViewNum.<span class="property">current</span> = <span class="title class_">Math</span>.<span class="title function_">ceil</span>(curContainerHeight.<span class="property">current</span> / itemHeight) + <span class="number">1</span></span><br><span class="line">}, <span class="number">500</span>), [])</span><br><span class="line"></span><br><span class="line"><span class="title function_">useEffect</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="comment">// 组件第一次挂载需要初始化容器的高度以及最大容纳值</span></span><br><span class="line"> <span class="title function_">changeHeight</span>()</span><br><span class="line"> <span class="comment">// 因为我们的可视窗口和浏览器大小有关系,所以我们需要监听浏览器大小的变化</span></span><br><span class="line"> <span class="comment">// 当浏览器大小改变之后需要重新执行changeHeight函数计算当前可视窗口对应的最大容纳量是多少</span></span><br><span class="line"> <span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">'resize'</span>, changeHeight)</span><br><span class="line"> <span class="keyword">return</span> <span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">window</span>.<span class="title function_">removeEventListener</span>(<span class="string">'resize'</span>, changeHeight)</span><br><span class="line"> }</span><br><span class="line">}, [changeHeight])</span><br></pre></td></tr></table></figure>
<h3 id="监听滚动事件动态截取数据-amp-amp-设置上下滚动缓冲消除快速滚动白屏"><a href="#监听滚动事件动态截取数据-amp-amp-设置上下滚动缓冲消除快速滚动白屏" class="headerlink" title="监听滚动事件动态截取数据&&设置上下滚动缓冲消除快速滚动白屏"></a>监听滚动事件动态截取数据&&设置上下滚动缓冲消除快速滚动白屏</h3><p>这是虚拟列表的核心之处,不将所有我们请求到的元素渲染出来,而是只渲染我们能够看到的元素,大大减少了容器内的dom节点数量。</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><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">scrollHandle</span> = (<span class="params"></span>) => {</span><br><span class="line"> <span class="comment">// 注意这个对应的是可视区第一个元素的索引值,而不是第多少个元素</span></span><br><span class="line"> <span class="keyword">let</span> startIndex = <span class="title class_">Math</span>.<span class="title function_">floor</span>(containerRef.<span class="property">current</span>.<span class="property">scrollTop</span> / itemHeight) <span class="comment">// itemHeight是列表每一项的高度</span></span><br><span class="line"> <span class="comment">// 优化:如果是用户滚动触发的,而且两次startIndex的值都一样,那么就没有必要执行下面的逻辑</span></span><br><span class="line"> <span class="keyword">if</span> (!isNeedLoad && lastStartIndex.<span class="property">current</span> === startIndex) <span class="keyword">return</span></span><br><span class="line"> isNeedLoad.<span class="property">current</span> = <span class="literal">false</span></span><br><span class="line"> lastStartIndex.<span class="property">current</span> = startIndex</span><br><span class="line"> <span class="keyword">const</span> containerMaxSize = curViewNum.<span class="property">current</span></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 解决滑动过快出现的白屏问题:注意endIndex要在startIndex人为改变之前就计算好</span></span><br><span class="line"><span class="comment"> * 因为我们实际上需要三板的数据用于兼容低性能的设备,用做上下滚动的缓冲区域,避免滑动的时候出现白屏</span></span><br><span class="line"><span class="comment"> * 现在的startIndex是可视区的第一个元素索引,再加上2倍可视区元素量,刚好在下方就会多出一板来当做缓冲区</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="comment">// 此处的endIndex是为了在可视区域的下方多出一板数据</span></span><br><span class="line"> <span class="keyword">let</span> endIndex = startIndex + <span class="number">2</span> * containerMaxSize - <span class="number">1</span></span><br><span class="line"> <span class="comment">// 接近滚动到屏幕底部的时候,就可以请求发送数据了,这个时候触底的并不是可视区的最后一个元素,而是多出那一版的最后一个元素触底了</span></span><br><span class="line"> <span class="keyword">const</span> currLen = dataListRef.<span class="property">current</span>.<span class="property">length</span></span><br><span class="line"> <span class="keyword">if</span> (endIndex > currLen - <span class="number">1</span>) {</span><br><span class="line"> <span class="comment">// 更新请求参数,发送请求获取新的数据(但是要保证当前不在请求过程中,否则就会重复请求相同的数据)</span></span><br><span class="line"> !isRequestRef.<span class="property">current</span> && <span class="title function_">setOptions</span>(<span class="function"><span class="params">state</span> =></span> ({ <span class="attr">offset</span>: state.<span class="property">offset</span> + <span class="number">1</span> }))</span><br><span class="line"> <span class="comment">// 如果已经滚动到了底部,那么就设置endIndex为最后一个元素索引即可</span></span><br><span class="line"> endIndex = currLen - <span class="number">1</span></span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 此处的endIndex是为了在可视区域的上方多出一板数据</span></span><br><span class="line"> <span class="comment">// 这里人为的调整startIndex的值,目的就是为了能够在可视区域上方多出一板来当做缓冲区</span></span><br><span class="line"> <span class="keyword">if</span> (startIndex <= containerMaxSize) { <span class="comment">// containerMaxSize是我们之前计算出来的容器容纳量</span></span><br><span class="line"> startIndex = <span class="number">0</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> startIndex = startIndex - containerMaxSize</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 使用slice方法截取数据,但是要记住第二个参数对应的索引元素不会被删除,最多只能删除到它的前一个,所以我们这里的endIndex需要加一</span></span><br><span class="line"> <span class="title function_">setShowList</span>(dataListRef.<span class="property">current</span>.<span class="title function_">slice</span>(startIndex, endIndex + <span class="number">1</span>))</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="动态设置上下空白占位"><a href="#动态设置上下空白占位" class="headerlink" title="动态设置上下空白占位"></a>动态设置上下空白占位</h3><p>这是虚拟列表的灵魂所在,本质上我们数据量是很少的,一般来说只有几条到十几条数据,如果不对列表做一些附加的操作,连生成滚动条都有点困难,更别说让用户自由操控滚动条滚动了。</p>
<p>我们必须要用某种方法将内容区域撑起来,这样才会出现比较合适的滚动条。我这里采取的方法就是设置<code>paddingTop</code>和<code>paddingBottom</code>的值来动态的撑开内容区域。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 以下代码要放在更新列表数据之前,也是在滚动事件boxScroll当中</span></span><br><span class="line"><span class="comment">// 改变空白填充区域的样式,否则就会出现可视区域的元素与滚动条不匹配的情况,实现不了平滑滚动的效果</span></span><br><span class="line">topBlankFill.<span class="property">current</span> = {</span><br><span class="line"> <span class="comment">// 起始索引就是缓冲区第一个元素的索引,索引为多少就代表前面有多少个元素</span></span><br><span class="line"> <span class="attr">paddingTop</span>: <span class="string">`<span class="subst">${startIndex * itemHeight}</span>px`</span>,</span><br><span class="line"> <span class="comment">// endIndex是缓冲区的最后一个元素,可能不在可视区内;用dataListRef数组最后一个元素的索引与endIndex相减就可以得到还没有渲染元素的数目</span></span><br><span class="line"> <span class="attr">paddingBottom</span>: <span class="string">`<span class="subst">${(dataListRef.current.length - <span class="number">1</span> - endIndex) * itemHeight}</span>px`</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="下拉置底自动请求和加载数据"><a href="#下拉置底自动请求和加载数据" class="headerlink" title="下拉置底自动请求和加载数据"></a>下拉置底自动请求和加载数据</h3><p>在真实的开发场景中,我们不会一次性请求1w、10w条数据过来,这样请求时间那么长,用户早就把页面关掉了,还优化个屁啊哈哈!</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 组件刚挂载以及下拉触底的时候请求更多数据</span></span><br><span class="line"><span class="title function_">useEffect</span>(<span class="function">() =></span> {</span><br><span class="line"> (<span class="keyword">async</span> () => {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="comment">// 表明当前正处于请求过程中</span></span><br><span class="line"> isRequestRef.<span class="property">current</span> = <span class="literal">true</span></span><br><span class="line"> <span class="keyword">const</span> { offset } = options</span><br><span class="line"> <span class="keyword">let</span> limit = <span class="number">20</span></span><br><span class="line"> <span class="keyword">if</span> (offset === <span class="number">1</span>) limit = <span class="number">40</span></span><br><span class="line"> <span class="keyword">const</span> { <span class="attr">data</span>: { comments, more } } = <span class="keyword">await</span> <span class="title function_">axios</span>({</span><br><span class="line"> <span class="attr">url</span>: <span class="string">`http://localhost:3000/comment/music?id=<span class="subst">${<span class="number">186015</span> - offset}</span>&limit=<span class="subst">${limit}</span>&offset=1`</span></span><br><span class="line"> })</span><br><span class="line"> isNeedLoad.<span class="property">current</span> = more</span><br><span class="line"> <span class="comment">// 将新请求到的数据添加到存储列表数据的变量当中</span></span><br><span class="line"> dataListRef.<span class="property">current</span> = [...dataListRef.<span class="property">current</span>, ...comments]</span><br><span class="line"> <span class="comment">// 必选要在boxScroll之前将isRequestRef设为false,因为boxScroll函数内部会用到这个变量</span></span><br><span class="line"> isRequestRef.<span class="property">current</span> = <span class="literal">false</span></span><br><span class="line"> <span class="comment">// 请求完最新数据的时候需要重新触发一下boxScroll函数,因为容器内的数据、空白填充区域可能需要变化</span></span><br><span class="line"> <span class="title function_">boxScroll</span>()</span><br><span class="line"> } <span class="keyword">catch</span> (err) {</span><br><span class="line"> isRequestRef.<span class="property">current</span> = <span class="literal">false</span></span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(err);</span><br><span class="line"> }</span><br><span class="line"> })()</span><br><span class="line"> <span class="comment">// 在boxScroll函数里面,一旦发生了触底操作就会去改变optiosn的值</span></span><br><span class="line">}, [options])</span><br></pre></td></tr></table></figure>
<h4 id="滚动事件请求动画帧进行节流优化"><a href="#滚动事件请求动画帧进行节流优化" class="headerlink" title="滚动事件请求动画帧进行节流优化"></a>滚动事件请求动画帧进行节流优化</h4><p>虚拟列表很依赖于滚动事件,考虑到用户可能会滑动很快,我们在用节流优化的时候事件必须要设置的够短,否则还是会出现白屏现象。</p>
<p>这里我没有用传统的节流函数,而是用到了请求动画帧帮助我们进行节流,这里就不做具体介绍了。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 利用请求动画帧做了一个节流优化</span></span><br><span class="line"><span class="keyword">let</span> then = <span class="title function_">useRef</span>(<span class="number">0</span>)</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">boxScroll</span> = (<span class="params"></span>) => {</span><br><span class="line"> <span class="keyword">const</span> now = <span class="title class_">Date</span>.<span class="title function_">now</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"> * 因为间隔时间过长的话,太久没有触发滚动更新事件,下滑就会到padding-bottom的空白区域</span></span><br><span class="line"><span class="comment"> * 电脑屏幕的刷新频率一般是60HZ,渲染的间隔时间为16.6ms,我们的时间间隔最好小于两次渲染间隔16.6*2=33.2ms,一般情况下30ms左右,</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">if</span> (now - then.<span class="property">current</span> > <span class="number">30</span>) {</span><br><span class="line"> then.<span class="property">current</span> = now</span><br><span class="line"> <span class="comment">// 重复调用scrollHandle函数,让浏览器在下一次重绘之前执行函数,可以确保不会出现丢帧现象</span></span><br><span class="line"> <span class="variable language_">window</span>.<span class="title function_">requestAnimationFrame</span>(scrollHandle)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>当然,填充空白区域、模拟滚动条还有其它的办法,比如根据总数据量让一个盒子撑开父盒子用于生成滚动条,根据<code>startIndex</code>计算出可视区域距离顶部的距离并调节内容区域元素的<code>transform</code>属性,即<code>startOffset = scrollTop \- (scrollTop % this.itemSize)</code>,让内容区域一直暴露在可视区域内</p>
<p>目前为止,我们已经实现了固定高度的列表项用虚拟列表来展示的功能!接下里我们将会介绍关于不定高(其高度由内容进行撑开)的列表项如何用虚拟列表进行优化</p>
<h2 id="不定高虚拟列表实现步骤"><a href="#不定高虚拟列表实现步骤" class="headerlink" title="不定高虚拟列表实现步骤"></a>不定高虚拟列表实现步骤</h2><blockquote>
<p>微博是一个很典型的不定高虚拟列表,大家感兴趣的话可以去看一下哦!</p>
</blockquote>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2023/02/04/63de2cffacfd5.jpg"/></div></div>
<p>在之前的实现中,列表项的高度是固定的,因为高度固定,所以可以很轻易的就能获取列表项的整体高度以及滚动时的显示数据与对应的偏移量。而实际应用的时候,当列表中包含文本、图片之类的可变内容,会导致列表项的高度并不相同。</p>
<p>我们在列表渲染之前,确实没有办法知道每一项的高度,但是又必须要渲染出来,那怎么办呢?</p>
<p>这里有一个解决方法,就是<strong>先给没有渲染出来的列表项设置一个预估高度,等到这些数据渲染成真实dom元素了之后,再获取到他们的真实高度去更新原来设置的预估高度</strong>,下面我们来看看跟定高列表有什么不同,具体要怎么实现吧!</p>
<h3 id="请求到新数据对数据进行初始化(设置预估高度)"><a href="#请求到新数据对数据进行初始化(设置预估高度)" class="headerlink" title="请求到新数据对数据进行初始化(设置预估高度)"></a>请求到新数据对数据进行初始化(设置预估高度)</h3><p>预估高度的设置其实是有技巧的,列表项预估高度设置的越大,展现出来的数据就会越少,所以当预估高度比实际高度大很多的时候,很容易出现可视区域数据量太少而引起的可视区域出现部分空白。为了避免这种情况,我们的预估高度应该设置为列表项产生的最小值,这样尽管可能会多渲染出几条数据,但能保证首次呈现给用户的画面中没有空白</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 请求更多的数据</span></span><br><span class="line"><span class="title function_">useEffect</span>(<span class="function">() =></span> {</span><br><span class="line"> (<span class="keyword">async</span> () => {</span><br><span class="line"> <span class="comment">// 只有当前不在请求状态的时候才可以发送新的请求</span></span><br><span class="line"> <span class="keyword">if</span> (!isRequestRef.<span class="property">current</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'发送请求了'</span>);</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> isRequestRef.<span class="property">current</span> = <span class="literal">true</span></span><br><span class="line"> <span class="keyword">const</span> { offset } = options</span><br><span class="line"> <span class="keyword">let</span> limit = <span class="number">20</span></span><br><span class="line"> <span class="keyword">if</span> (offset === <span class="number">1</span>) limit = <span class="number">40</span></span><br><span class="line"> <span class="keyword">const</span> { <span class="attr">data</span>: { comments, more } } = <span class="keyword">await</span> <span class="title function_">axios</span>({</span><br><span class="line"> <span class="attr">url</span>: <span class="string">`http://localhost:3000/comment/music?id=<span class="subst">${<span class="number">186015</span> - offset}</span>&limit=<span class="subst">${limit}</span>&offset=1`</span></span><br><span class="line"> })</span><br><span class="line"> isNeedLoad.<span class="property">current</span> = more</span><br><span class="line"> <span class="comment">// 获取缓存中最后一个数据的索引值,如果没有,则返回-1</span></span><br><span class="line"> <span class="keyword">const</span> lastIndex = dataListRef.<span class="property">current</span>.<span class="property">length</span> ? dataListRef.<span class="property">current</span>[dataListRef.<span class="property">current</span>.<span class="property">length</span> - <span class="number">1</span>].<span class="property">index</span> : -<span class="number">1</span></span><br><span class="line"> <span class="comment">// 先将请求到的数据添加到缓存数组中去</span></span><br><span class="line"> dataListRef.<span class="property">current</span> = [...dataListRef.<span class="property">current</span>, ...comments]</span><br><span class="line"> <span class="keyword">const</span> dataList = dataListRef.<span class="property">current</span></span><br><span class="line"> <span class="comment">// 将刚刚请求到的新数据做一下处理,为他们添加上对应的索引值、预估高度、以及元素首尾距离容器顶部的距离</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = lastIndex + <span class="number">1</span>, len = dataListRef.<span class="property">current</span>.<span class="property">length</span>; i < len; i++) {</span><br><span class="line"> dataList[i].<span class="property">index</span> = i</span><br><span class="line"> <span class="comment">// 预估高度是列表项对应的最小高度</span></span><br><span class="line"> dataList[i].<span class="property">height</span> = <span class="number">63</span></span><br><span class="line"> <span class="comment">// 每一个列表项头部距离容器顶部的距离等于上一个元素尾部距离容器顶部的距离</span></span><br><span class="line"> dataList[i].<span class="property">top</span> = dataList[i - <span class="number">1</span>]?.<span class="property">bottom</span> || <span class="number">0</span></span><br><span class="line"> <span class="comment">// 每一个列表项尾部距离容器顶部的距离等于上一个元素头部距离容器顶部的距离加上自身列表项的高度</span></span><br><span class="line"> dataList[i].<span class="property">bottom</span> = dataList[i].<span class="property">top</span> + dataList[i].<span class="property">height</span></span><br><span class="line"> }</span><br><span class="line"> isRequestRef.<span class="property">current</span> = <span class="literal">false</span></span><br><span class="line"> <span class="title function_">boxScroll</span>()</span><br><span class="line"> } <span class="keyword">catch</span> (err) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(err);</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> isRequestRef.<span class="property">current</span> = <span class="literal">false</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> })()</span><br><span class="line"> <span class="comment">// eslint-disable-next-line</span></span><br><span class="line">}, [options])</span><br></pre></td></tr></table></figure>
<h3 id="每次列表更新之后将列表项真实高度更新缓存中的预估高度"><a href="#每次列表更新之后将列表项真实高度更新缓存中的预估高度" class="headerlink" title="每次列表更新之后将列表项真实高度更新缓存中的预估高度"></a>每次列表更新之后将列表项真实高度更新缓存中的预估高度</h3><p>在<code>React</code>函数式组件中,<code>useEffect</code>只要不传第二个参数,就可以实现类组件<code>componentDidUpdate</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 每次组件重新渲染即用户滚动更改了数据之后需要将列表中我们还不知道的列表项高度更新到我们的缓存数据中去,以便下一次更新的时候能够正常渲染</span></span><br><span class="line"><span class="title function_">useEffect</span>(<span class="function">() =></span> { </span><br><span class="line"> <span class="keyword">const</span> doms = containerRef.<span class="property">current</span>.<span class="property">children</span>[<span class="number">0</span>].<span class="property">children</span></span><br><span class="line"> <span class="keyword">const</span> len = doms.<span class="property">length</span></span><br><span class="line"> <span class="comment">// 因为一开始我们没有请求数据,所以即使组件渲染完了,但是没有列表项,此时不需要执行后续操作</span></span><br><span class="line"> <span class="keyword">if</span> (len) {</span><br><span class="line"> <span class="comment">// 遍历所有的列表结点,根据结点的真实高度去更改缓存中的高度</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < len; i++) {</span><br><span class="line"> <span class="keyword">const</span> realHeight = doms[i].<span class="property">offsetHeight</span></span><br><span class="line"> <span class="keyword">const</span> originHeight = showList[i].<span class="property">height</span></span><br><span class="line"> <span class="keyword">const</span> dValue = realHeight - originHeight</span><br><span class="line"> <span class="comment">// 如果列表项的真实高度就是缓存中的高度,则不需要进行更新</span></span><br><span class="line"> <span class="keyword">if</span> (dValue) {</span><br><span class="line"> <span class="keyword">const</span> index = showList[i].<span class="property">index</span></span><br><span class="line"> <span class="keyword">const</span> allData = dataListRef.<span class="property">current</span></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 如果列表项的真实高度不是缓存中的高度,那么不仅要更新缓存中这一项的bottom和height属性</span></span><br><span class="line"><span class="comment"> * 在该列表项后续的所有列表项的top、bottom都会受到它的影响,所以我们又需要一层for循环进行更改缓存中后续的值</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> allData[index].<span class="property">bottom</span> += dValue</span><br><span class="line"> allData[index].<span class="property">height</span> = realHeight</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 注意:这里更改的一定要是缓存数组中对应位置后续的所有值,如果只改变的是showList值的话</span></span><br><span class="line"><span class="comment"> * 会造成dataList间断性的bottom和下一个top不连续,因为startIndex、endIndex以及空白填充区域都是依据top和bottom值来进行计算的</span></span><br><span class="line"><span class="comment"> * 所以会导致最后计算的结果出错,滑动得来的startIndex变化幅度大且滚动条不稳定,出现明显抖动问题</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> j = index + <span class="number">1</span>, len = allData.<span class="property">length</span>; j < len; j++) {</span><br><span class="line"> allData[j].<span class="property">top</span> = allData[j - <span class="number">1</span>].<span class="property">bottom</span></span><br><span class="line"> allData[j].<span class="property">bottom</span> += dValue</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// eslint-disable-next-line</span></span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<h3 id="得到可视区域的起始和结束元素索引-amp-amp-设置上下滚动缓冲区域消除快速滚动白屏"><a href="#得到可视区域的起始和结束元素索引-amp-amp-设置上下滚动缓冲区域消除快速滚动白屏" class="headerlink" title="得到可视区域的起始和结束元素索引&&设置上下滚动缓冲区域消除快速滚动白屏"></a>得到可视区域的起始和结束元素索引&&设置上下滚动缓冲区域消除快速滚动白屏</h3><p>列表项的<code>bottom</code>属性代表的就是该元素尾部到容器顶部的距离,不难发现,可视区的第一个元素的<code>bottom</code>是第一个大于滚动高度的;可视区最后一个元素的<code>bottom</code>是第一个大于(滚动高度+可视高度)的。我们可以利用这条规则遍历缓存数组找到对应的<code>startIndex</code>和<code>endIndex</code></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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 得到要渲染数据的起始索引和结束索引</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">getIndex</span> = (<span class="params"></span>) => {</span><br><span class="line"> <span class="comment">// 设置缓冲区域的数据量</span></span><br><span class="line"> <span class="keyword">const</span> aboveCount = <span class="number">5</span></span><br><span class="line"> <span class="keyword">const</span> belowCount = <span class="number">5</span></span><br><span class="line"> <span class="comment">// 结果数组,里面包含了起始索引和结束索引</span></span><br><span class="line"> <span class="keyword">const</span> resObj = {</span><br><span class="line"> <span class="attr">startIndex</span>: <span class="number">0</span>,</span><br><span class="line"> <span class="attr">endIndex</span>: <span class="number">0</span>,</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">const</span> scrollTop = containerRef.<span class="property">current</span>.<span class="property">scrollTop</span></span><br><span class="line"> <span class="keyword">const</span> dataList = dataListRef.<span class="property">current</span></span><br><span class="line"> <span class="keyword">const</span> len = dataList.<span class="property">length</span></span><br><span class="line"> <span class="comment">// 设置上层缓冲区,如果索引值大于缓冲区域,那么就需要减小startIndex的值用于设置顶层缓冲区</span></span><br><span class="line"> <span class="keyword">const</span> startIndex = <span class="title function_">binarySearch</span>(scrollTop)</span><br><span class="line"> <span class="keyword">if</span> (startIndex <= aboveCount) {</span><br><span class="line"> resObj.<span class="property">startIndex</span> = <span class="number">0</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> resObj.<span class="property">startIndex</span> = startIndex - aboveCount</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 缓冲数据中第一个bottom大于滚动高度加上可视区域高度的元素就是可视区域最后一个元素</span></span><br><span class="line"><span class="comment"> * 如果没有找到的话就说明当前滚动的幅度过大,缓存中没有数据的bottom大于我们的目标值,所以搜索不到对应的索引,我们只能拿缓存数据中的最后一个元素补充上</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">const</span> endIndex = <span class="title function_">binarySearch</span>(scrollTop + curContainerHeight.<span class="property">current</span>) || len - <span class="number">1</span></span><br><span class="line"> <span class="comment">// 增大endIndex的索引值用于为滚动区域下方设置一段缓冲区,避免快速滚动所导致的白屏问题</span></span><br><span class="line"> resObj.<span class="property">endIndex</span> = endIndex + belowCount</span><br><span class="line"> <span class="keyword">return</span> resObj</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 由于我们的缓存数据,本身就是有顺序的,所以获取开始索引的方法可以考虑通过二分查找的方式来降低检索次数:</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">binarySearch</span> = (<span class="params">value</span>) => {</span><br><span class="line"> <span class="keyword">const</span> list = dataListRef.<span class="property">current</span></span><br><span class="line"> <span class="keyword">let</span> start = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">let</span> end = list.<span class="property">length</span> - <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">let</span> tempIndex = <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">while</span> (start <= end) {</span><br><span class="line"> <span class="keyword">let</span> midIndex = <span class="built_in">parseInt</span>((start + end) / <span class="number">2</span>);</span><br><span class="line"> <span class="keyword">let</span> midValue = list[midIndex].<span class="property">bottom</span>;</span><br><span class="line"> <span class="keyword">if</span> (midValue === value) {</span><br><span class="line"> <span class="comment">// 说明当前滚动区域加上可视区域刚好是一个结点的边界,那么我们可以以其下一个结点作为末尾元素</span></span><br><span class="line"> <span class="keyword">return</span> midIndex + <span class="number">1</span>;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (midValue < value) {</span><br><span class="line"> <span class="comment">// 由于当前值与目标值还有一定的差距,所以我们需要增加start值以让下次中点的落点更靠后</span></span><br><span class="line"> start = midIndex + <span class="number">1</span>;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (midValue > value) {</span><br><span class="line"> <span class="comment">// 因为我们的目的并不是找到第一个满足条件的值,而是要找到满足条件的最小索引值</span></span><br><span class="line"> <span class="keyword">if</span> (tempIndex === <span class="literal">null</span> || tempIndex > midIndex) {</span><br><span class="line"> tempIndex = midIndex;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 由于我们要继续找更小的索引,所以需要让end-1以缩小范围,让下次中点的落点更靠前</span></span><br><span class="line"> end--</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> tempIndex;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="监听滚动事件动态截取数据-amp-amp-动态设置上下空白占位"><a href="#监听滚动事件动态截取数据-amp-amp-动态设置上下空白占位" class="headerlink" title="监听滚动事件动态截取数据&&动态设置上下空白占位"></a>监听滚动事件动态截取数据&&动态设置上下空白占位</h3><p>动态截取数据的操作和定高的虚拟列表几乎一样,区别比较大的地方就在<code>padding</code>值的计算方式上。在定高的列表中,我们可以根据起始索引值和结尾索引值直接计算出空白填充区域的高度。</p>
<p>其实在不定高的列表中,计算方式更加简单,因为<code>startIndex</code>对应元素的<code>top</code>值就是我们需要填充的上空白区域,下空白区域也可以根据整个列表的高度(最后一个元素的bottom值)和<code>endIndex</code>对应元素的<code>bottom</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">scrollHandle</span> = (<span class="params"></span>) => {</span><br><span class="line"> <span class="comment">// 获取当前要渲染元素的起始索引和结束索引值</span></span><br><span class="line"> <span class="keyword">let</span> { startIndex, endIndex } = <span class="title function_">getIndex</span>()</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 如果是用户滚动触发的,而且两次startIndex的值都一样,那么就没有必要执行下面的逻辑,</span></span><br><span class="line"><span class="comment"> * 除非是用户重新请求了之后需要默认执行一次该函数,这是一种特殊情况,就是startIndex没变,但需要执行后续的操作</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">if</span> (!isNeedLoad && lastStartIndex.<span class="property">current</span> === startIndex) <span class="keyword">return</span></span><br><span class="line"> <span class="comment">// 渲染完一次之后就需要初始化isNeedLoad</span></span><br><span class="line"> isNeedLoad.<span class="property">current</span> = <span class="literal">false</span></span><br><span class="line"> <span class="comment">// 用于实时监控lastStartIndex的值</span></span><br><span class="line"> lastStartIndex.<span class="property">current</span> = startIndex</span><br><span class="line"> <span class="comment">// 下层缓冲区域最后的元素接触到屏幕底部的时候,就可以请求发送数据了</span></span><br><span class="line"> <span class="keyword">const</span> currLen = dataListRef.<span class="property">current</span>.<span class="property">length</span></span><br><span class="line"> <span class="keyword">if</span> (endIndex >= currLen - <span class="number">1</span>) {</span><br><span class="line"> <span class="comment">// 当前不在请求状态下时才可以改变请求参数发送获取更多数据的请求</span></span><br><span class="line"> !isRequestRef.<span class="property">current</span> && <span class="title function_">setOptions</span>(<span class="function"><span class="params">state</span> =></span> ({ <span class="attr">offset</span>: state.<span class="property">offset</span> + <span class="number">1</span> }))</span><br><span class="line"> <span class="comment">// 注意endIndex不可以大于缓存中最后一个元素的索引值</span></span><br><span class="line"> endIndex = currLen - <span class="number">1</span></span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 空白填充区域的样式</span></span><br><span class="line"> topBlankFill.<span class="property">current</span> = {</span><br><span class="line"> <span class="comment">// 改变空白填充区域的样式,起始元素的top值就代表起始元素距顶部的距离,可以用来充当paddingTop值</span></span><br><span class="line"> <span class="attr">paddingTop</span>: <span class="string">`<span class="subst">${dataListRef.current[startIndex].top}</span>px`</span>,</span><br><span class="line"> <span class="comment">// 缓存中最后一个元素的bottom值与endIndex对应元素的bottom值的差值可以用来充当paddingBottom的值</span></span><br><span class="line"> <span class="attr">paddingBottom</span>: <span class="string">`<span class="subst">${dataListRef.current[dataListRef.current.length - <span class="number">1</span>].bottom - dataListRef.current[endIndex].bottom}</span>px`</span></span><br><span class="line"> }</span><br><span class="line"> <span class="title function_">setShowList</span>(dataListRef.<span class="property">current</span>.<span class="title function_">slice</span>(startIndex, endIndex + <span class="number">1</span>))</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="问题思考"><a href="#问题思考" class="headerlink" title="问题思考"></a>问题思考</h3><p>我们虽然实现了根据列表项动态高度下的虚拟列表,但如果列表项中包含图片,并且列表高度由图片撑开。在这种场景下,由于图片会发送网络请求,列表项可能已经渲染到页面中了,但是图片还没有加载出来,此时无法保证我们在获取列表项真实高度时图片是否已经加载完成,获取到的高度有无包含图片高度,从而造成计算不准确的情况。</p>
<p>但是这种任意由图片来撑开盒子大小的场景很少见,因为这样会显得整个列表很不规则。大多数展示图片的列表场景,其实都是提前确定要展示图片的尺寸的,比如微博,1张图片的尺寸是多少,2x2,3x3的尺寸是多少都是提前设计好的,只要我们给img标签加了固定高度,这样就算图片还没有加载出来,但是我们也能够准确的知道列表项的高度是多少。</p>
<p>如果你真的遇到了这种列表项会由图片任意撑开的场景,可以给图片绑定<code>onload</code>事件,等到它加载完之后再重新计算一下列表的高度,然后把它更新到缓存数据中,这是一种方法。其次,还可以使用ResizeObserver来监听列表项内容区域的高度改变,从而实时获取每一列表项的高度,只不过MDN有说道这只是在实验中的一个功能,所以暂时可能没有办法兼容所有的浏览器!</p>
<p>如果大家有其它更好的方法,可以在评论区交流哦!</p>
</div>
<blockquote>
<p>原文链接 : <a href="https://juejin.cn/post/7085941958228574215">Running53 -
uni-app 如何实现消息推送功能?
https://fe32.top/articles/uni10089/
2023-01-12T06:47:47.000Z
2023-05-17T10:01:12.000Z
uni-app 实现消息推送功能,包括安卓、IOS、在线、离线都能收到推送的消息。
Hexo + Butterfly 侧边栏公众号
https://fe32.top/articles/hexo1618/
2023-01-05T12:50:31.000Z
2023-05-17T10:01:12.000Z
有几个小伙伴想要同款侧边栏公众号效果,这里简单说下侧边栏公众号如何实现。
uni-app 提交 app应用 到 Google Play 提示 API 级别过低的解决办法
https://fe32.top/articles/uni10088/
2023-01-05T08:05:31.000Z
2024-03-18T09:56:30.982Z
uniapp 提交 aab 版本到 Google Play 提示 API 级别过低的解决办法
时光飞逝,博客两周年啦
https://fe32.top/articles/blog9527/
2022-12-11T15:45:14.000Z
2023-07-15T17:10:06.000Z
随便聊聊这两年半的博客练习生~
包管理工具的演进
https://fe32.top/articles/pack1997/
2022-12-10T08:14:02.000Z
2023-05-17T10:01:12.000Z
本文主要是围绕 npm( Node.js 直接内置)、yarn (corepack 内置) 以及 pnpm (corepack 内置)来阐述包管理工具在迭代演进中提出的一些创新性特性以及其遇到困难是如何解决问题的。
Hexo + Butterfly 自定义页脚
https://fe32.top/articles/hexo1617/
2022-12-09T08:02:44.000Z
2023-06-05T13:57:38.000Z
<!-- <div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://imglf4.lf127.net/img/84c6c9e28c79ce23/eEVDYmMvcTdRS0g0SGtrR2Q4cUcyWXZzRm1kd0hGZXVaZkVTQkwyNjNrOD0.jpg?imageView&thumbnail=2560y1440&type=jpg&quality=96&stripmeta=0&type=jpg"/></div></div> -->
<h2 id="推荐阅读"><a href="#推荐阅读" class="headerlink" title="推荐阅读"></a>推荐阅读</h2><ul>
<li><a href="https://fe32.top/articles/hexo1601/">基于 Hexo 从零开始搭建个人博客(一): 环境准备</a></li>
<li><a href="https://fe32.top/articles/hexo1602/">基于 Hexo 从零开始搭建个人博客(二): 项目初识</a></li>
<li><a href="https://fe32.top/articles/hexo1603/">基于 Hexo 从零开始搭建个人博客(三): 主题安装</a></li>
<li><a href="https://fe32.top/articles/hexo1604/">基于 Hexo 从零开始搭建个人博客(四): 基础配置</a></li>
<li><a href="https://fe32.top/articles/hexo1605/">基于 Hexo 从零开始搭建个人博客(五): 详细配置</a></li>
<li><a href="https://fe32.top/articles/hexo1606/">基于 Hexo 从零开始搭建个人博客(六): 主题美化</a></li>
<li><a href="https://fe32.top/articles/hexo1607/">基于 Hexo 键入搜索功能</a></li>
<li><a href="https://fe32.top/articles/hexo1609/">基于 Hexo 键入分享功能</a></li>
<li><a href="https://fe32.top/articles/hexo1610/">基于 Hexo 键入在线聊天功能</a></li>
<li><a href="https://fe32.top/articles/hexo1611/">基于 Hexo 键入评论功能</a></li>
<li><a href="https://fe32.top/articles/hexo1608/">Hexo + Butterfly 自定义右键菜单</a></li>
<li><a href="https://fe32.top/articles/hexo1612/">Hexo + Butterfly 一些常见问题</a></li>
<li><a href="https://fe32.top/articles/hexo1613/">请收下这只可爱的猫咪吧</a></li>
<li><a href="https://fe32.top/articles/hexo1614/">关于Vercel被墙导致获取Twikoo评论失败的解决方案</a></li>
<li><a href="https://fe32.top/articles/hexo1615/">飞只因太美,给你的首页装上吧!</a></li>
<li><a href="https://fe32.top/articles/hexo1618/">Hexo + Butterfly 侧边栏公众号</a></li>
</ul>
<h2 id="效果预览"><a href="#效果预览" class="headerlink" title="效果预览"></a>效果预览</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/12/11/6395f7970a3bb.jpg" alt=" 3.7.1 版本效果"/></div><span class="image-caption"> 3.7.1 版本效果</span></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/12/13/63986fa379730.png" alt=" 4.5.1 版本效果"/></div><span class="image-caption"> 4.5.1 版本效果</span></div>
<blockquote>
<p>这里用 4.5.1 版本跑了【自定义页脚】的功能,主题是新拉的,可能未过多美化,只是为了验证下该功能有没有问题。如果你在使用此功能时遇见了 BUG ,请检查<code>footer.pug</code>的格式以及<code>custom.css</code>是否正确引入。</p>
</blockquote>
<!--
> 注意:我的博客根目录路径为 【G:/hexo-blog/blog-demo】,下文所说的根目录都是此路径,将用`[BlogRoot]`代替。如果不清楚根目录路径,请回到教程 [基于 Hexo 从零开始搭建个人博客(二)](https://fe32.top/articles/hexo1602/),查看你执行`hexo init xxx`这条命令时所选择的路径,例如我选择的路径是【G:/hexo-blog】,我的博客根目录即为【G:/hexo-blog/xxx】。<br>
修改站点配置文件`_config.yml`,路径为【BlogRoot/_config.yml】。<br>
修改主题配置文件`_config.butterfly.yml`,路径为【BlogRoot/_config.butterfly.yml】。 -->
<h2 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h2><ol>
<li>在<code>BlogRoot/themes/butterfly/layout/includes/footer.pug</code>中添加如下代码: <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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></pre></td><td class="code"><pre><span class="line">#footer-wrap</span><br><span class="line"> #ft</span><br><span class="line"> .ft-item-1</span><br><span class="line"> .t-top</span><br><span class="line"> .t-t-l</span><br><span class="line"> p.ft-t.t-l-t 说点什么</span><br><span class="line"> .bg-ad</span><br><span class="line"> div</span><br><span class="line"> | (这里的内容随你写,但是不要过长影响整体美观度,具体可根据实现效果修改)随便说点什么说点什么、随便说点什么说点什么随便说点什么说点什么随便说点什么说点什么随便说点什么说。</span><br><span class="line"> .btn-xz-box</span><br><span class="line"> a.btn-xz(href='https://fe32.top/') 点击跳转到哪儿</span><br><span class="line"> .t-t-r</span><br><span class="line"> p.ft-t.t-l-t 修仙导航</span><br><span class="line"> ul.ft-links</span><br><span class="line"> li</span><br><span class="line"> a(href='https://fe32.top/articles/hexo1600/') 建站指南</span><br><span class="line"> a(href='https://fe32.top/nav.html') 网址导航</span><br><span class="line"> li</span><br><span class="line"> a(href='https://fe32.top/sponsorWall/') 来杯咖啡</span><br><span class="line"> a(href='https://fe32.top/comments/') 留点什么</span><br><span class="line"> li</span><br><span class="line"> a(href='https://fe32.top/about/') 关于博主</span><br><span class="line"> a(href='https://fe32.top/archives/') 文章归档</span><br><span class="line"> li</span><br><span class="line"> a(href='https://fe32.top/categories/') 文章分类</span><br><span class="line"> a(href='https://fe32.top/tags/') 文章标签</span><br><span class="line"> li</span><br><span class="line"> a(href='https://fe32.top/gallery/') 我的相册</span><br><span class="line"> a(href='https://fe32.top/bangumis/') 我的追番</span><br><span class="line"> li</span><br><span class="line"> a(href='https://fe32.top/specialEffects/') 一些特效</span><br><span class="line"> a(href='https://fe32.top/wallpaper/') 一些壁纸</span><br><span class="line"> .ft-item-2</span><br><span class="line"> p.ft-t 推荐友链</span><br><span class="line"> .ft-img-group</span><br><span class="line"> .img-group-item</span><br><span class="line"> a(href='https://fe32.top/')</span><br><span class="line"> img(src='https://bu.dusays.com/2022/05/02/626f92e193879.jpg' alt='')</span><br><span class="line"> .img-group-item</span><br><span class="line"> a(href='https://fe32.top/')</span><br><span class="line"> img(src='https://bu.dusays.com/2022/05/02/626f92e193879.jpg' alt='')</span><br><span class="line"> .img-group-item</span><br><span class="line"> a(href='https://fe32.top/')</span><br><span class="line"> img(src='https://bu.dusays.com/2022/05/02/626f92e193879.jpg' alt='')</span><br><span class="line"> .img-group-item</span><br><span class="line"> a(href='https://fe32.top/')</span><br><span class="line"> img(src='https://bu.dusays.com/2022/05/02/626f92e193879.jpg' alt='')</span><br><span class="line"> .img-group-item</span><br><span class="line"> a(href='https://fe32.top/')</span><br><span class="line"> img(src='https://bu.dusays.com/2022/05/02/626f92e193879.jpg' alt='')</span><br><span class="line"> .img-group-item</span><br><span class="line"> a(href='https://fe32.top/')</span><br><span class="line"> img(src='https://bu.dusays.com/2022/05/02/626f92e193879.jpg' alt='')</span><br><span class="line"> .img-group-item</span><br><span class="line"> a(href='https://fe32.top/')</span><br><span class="line"> img(src='https://bu.dusays.com/2022/05/02/626f92e193879.jpg' alt='')</span><br><span class="line"> .img-group-item</span><br><span class="line"> a(href='https://fe32.top/')</span><br><span class="line"> img(src='https://bu.dusays.com/2022/05/02/626f92e193879.jpg' alt='')</span><br><span class="line"> if theme.footer.owner.enable</span><br><span class="line"> - var now = new Date()</span><br><span class="line"> - var nowYear = now.getFullYear()</span><br><span class="line"> if theme.footer.owner.since && theme.footer.owner.since != nowYear</span><br><span class="line"> .copyright!= `&copy;${theme.footer.owner.since} - ${nowYear + ' '} <i id="heartbeat" class="fa fas fa-heartbeat"></i> ${config.author}`</span><br><span class="line"> else</span><br><span class="line"> .copyright!= `&copy;${nowYear + ' '} <i id="heartbeat" class="fa fas fa-heartbeat"></i> ${config.author}`</span><br><span class="line"> if theme.footer.copyright</span><br><span class="line"> .framework-info</span><br><span class="line"> span= _p('footer.framework') + ' '</span><br><span class="line"> a(href='https://hexo.io')= 'Hexo'</span><br><span class="line"> span.footer-separator |</span><br><span class="line"> span= _p('footer.theme') + ' '</span><br><span class="line"> a(href='https://github.com/jerryc127/hexo-theme-butterfly')= 'Butterfly'</span><br><span class="line"> if theme.footer.custom_text</span><br><span class="line"> .footer_custom_text!=`${theme.footer.custom_text}`</span><br></pre></td></tr></table></figure>
<blockquote>
<p>你需要添加的代码为<code>#ft</code>整个 div,注意在缩进上与主题的几个<code>if</code>对齐。这里你可以根据自己的需求修改以上内容等,例如【说点什么】、自定义底部导航链接等,【推荐友链】的图片尺寸建议 1:1。</p>
</blockquote>
</li>
<li>将以下代码复制到自定义的<code>custom.css</code>中,不会自定义css的请阅读 <a href="https://fe32.top/articles/hexo1612/">Hexo + Butterfly 一些常见问题</a> 一文中关于【关于自定义的 js 和 css 文件】部分。<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 自定义底部 start */</span></span><br><span class="line"><span class="selector-id">#ft</span> {</span><br><span class="line"> <span class="attribute">max-width</span>: <span class="number">1200px</span>;</span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">0</span> auto <span class="number">12px</span>;</span><br><span class="line"> <span class="attribute">display</span>: flex;</span><br><span class="line"> <span class="attribute">color</span>: <span class="built_in">rgb</span>(<span class="number">255</span> <span class="number">255</span> <span class="number">255</span> / <span class="number">80%</span>) <span class="meta">!important</span>;</span><br><span class="line"> <span class="attribute">text-align</span>: left;</span><br><span class="line"> <span class="attribute">flex-wrap</span>: wrap;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.ft-item-1</span>,</span><br><span class="line"><span class="selector-class">.ft-item-2</span> {</span><br><span class="line"> <span class="attribute">display</span>: flex;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">100%</span>;</span><br><span class="line"> <span class="attribute">padding</span>: <span class="number">10px</span> <span class="number">14px</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.ft-item-1</span> {</span><br><span class="line"> <span class="attribute">flex-direction</span>: column;</span><br><span class="line"> <span class="attribute">flex</span>: <span class="number">2</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.ft-item-2</span> {</span><br><span class="line"> <span class="attribute">flex</span>: <span class="number">1</span>;</span><br><span class="line"> <span class="attribute">flex-direction</span>: column;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.t-top</span> {</span><br><span class="line"> <span class="attribute">display</span>: flex;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.t-top</span> <span class="selector-class">.t-t-l</span> {</span><br><span class="line"> <span class="attribute">display</span>: flex;</span><br><span class="line"> <span class="attribute">flex-direction</span>: column;</span><br><span class="line"> <span class="attribute">flex</span>: <span class="number">1.4</span>;</span><br><span class="line"> <span class="attribute">margin-right</span>: <span class="number">10px</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.t-top</span> <span class="selector-class">.t-t-l</span> <span class="selector-class">.bg-ad</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">85%</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">10px</span>;</span><br><span class="line"> <span class="attribute">padding</span>: <span class="number">0</span> <span class="number">10px</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.btn-xz-box</span> {</span><br><span class="line"> <span class="attribute">margin-top</span>: <span class="number">10px</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.btn-xz</span> {</span><br><span class="line"> <span class="attribute">display</span>: block;</span><br><span class="line"> <span class="attribute">background-color</span>: <span class="built_in">var</span>(--btn-bg);</span><br><span class="line"> <span class="attribute">color</span>: <span class="built_in">var</span>(--btn-color);</span><br><span class="line"> <span class="attribute">text-align</span>: center;</span><br><span class="line"> <span class="attribute">line-height</span>: <span class="number">2.4</span>;</span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">8px</span> <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">cursor</span>: pointer <span class="meta">!important</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.btn-xz</span><span class="selector-pseudo">:hover</span> {</span><br><span class="line"> <span class="attribute">text-decoration</span>: none <span class="meta">!important</span>;</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.btn-xz-box</span><span class="selector-pseudo">:hover</span> <span class="selector-class">.btn-xz</span> {</span><br><span class="line"> <span class="attribute">background-color</span>: <span class="number">#6f42c1</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.t-top</span> <span class="selector-class">.t-t-r</span> {</span><br><span class="line"> <span class="attribute">display</span>: flex;</span><br><span class="line"> <span class="attribute">flex-direction</span>: column;</span><br><span class="line"> <span class="attribute">flex</span>: <span class="number">1</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.ft-links</span> {</span><br><span class="line"> <span class="attribute">padding</span>: <span class="number">0</span> <span class="number">14px</span>;</span><br><span class="line"> <span class="attribute">list-style</span>: none;</span><br><span class="line"> <span class="attribute">margin-top</span>: <span class="number">0</span> <span class="meta">!important</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.ft-links</span> <span class="selector-tag">li</span> <span class="selector-tag">a</span> {</span><br><span class="line"> <span class="attribute">display</span>: inline-block <span class="meta">!important</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">50%</span>;</span><br><span class="line"> <span class="attribute">cursor</span>: pointer <span class="meta">!important</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.ft-links</span> <span class="selector-tag">li</span> <span class="selector-tag">a</span><span class="selector-pseudo">:hover</span> {</span><br><span class="line"> <span class="attribute">text-decoration</span>: none <span class="meta">!important</span>;</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#6f42c1</span> <span class="meta">!important</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.ft-item-2</span> <span class="selector-class">.ft-img-group</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.ft-t</span> {</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">0.8rem</span>;</span><br><span class="line"> <span class="attribute">margin-bottom</span>: <span class="number">20px</span>;</span><br><span class="line"> <span class="attribute">line-height</span>: <span class="number">1</span>;</span><br><span class="line"> <span class="attribute">font-weight</span>: <span class="number">600</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.t-l-t</span> {</span><br><span class="line"> <span class="attribute">padding-left</span>: <span class="number">14px</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.ft-item-2</span> <span class="selector-class">.ft-img-group</span> <span class="selector-class">.img-group-item</span> {</span><br><span class="line"> <span class="attribute">display</span>: inline-block;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">18.4%</span>;</span><br><span class="line"> <span class="attribute">margin-right</span>: <span class="number">14px</span>;</span><br><span class="line"> <span class="attribute">margin-bottom</span>: <span class="number">6px</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.ft-item-2</span> <span class="selector-class">.ft-img-group</span> <span class="selector-class">.img-group-item</span> <span class="selector-tag">a</span> {</span><br><span class="line"> <span class="attribute">display</span>: inline-block;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">100%</span>;</span><br><span class="line"> <span class="attribute">cursor</span>: pointer <span class="meta">!important</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.ft-item-2</span> <span class="selector-class">.ft-img-group</span> <span class="selector-class">.img-group-item</span> <span class="selector-tag">a</span> <span class="selector-tag">img</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line"> <span class="attribute">max-height</span>: <span class="number">80px</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">@media</span> screen <span class="keyword">and</span> (<span class="attribute">max-width</span>: <span class="number">768px</span>) {</span><br><span class="line"></span><br><span class="line"> <span class="selector-class">.ft-item-1</span> {</span><br><span class="line"> <span class="attribute">flex-basis</span>: <span class="number">100%</span> <span class="meta">!important</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="selector-class">.ft-item-2</span> {</span><br><span class="line"> <span class="attribute">flex-basis</span>: <span class="number">100%</span> <span class="meta">!important</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="selector-class">.t-top</span> <span class="selector-class">.t-t-l</span> <span class="selector-class">.bg-ad</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">@media</span> screen <span class="keyword">and</span> (<span class="attribute">max-width</span>: <span class="number">576px</span>) {</span><br><span class="line"> <span class="selector-class">.t-top</span> {</span><br><span class="line"> <span class="attribute">flex-wrap</span>: wrap;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="selector-class">.t-top</span> <span class="selector-class">.t-t-l</span> {</span><br><span class="line"> <span class="attribute">flex-basis</span>: <span class="number">100%</span> <span class="meta">!important</span>;</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="selector-class">.t-top</span> <span class="selector-class">.t-t-r</span> {</span><br><span class="line"> <span class="attribute">margin-top</span>: <span class="number">16px</span>;</span><br><span class="line"> <span class="attribute">flex-basis</span>: <span class="number">100%</span> <span class="meta">!important</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">/* 自定义底部 End */</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>css 中的<code>#6f42c1</code>是我的主题色,这里记得换成你的主题色。</p>
</blockquote>
</li>
<li>到这里你已经成功了
深入理解函数式编程(下)
https://fe32.top/articles/jsnb9547/
2022-12-06T07:28:26.000Z
2023-05-17T10:01:10.000Z
<div class="tzy-post">
<h2 id="前文回顾"><a href="#前文回顾" class="headerlink" title="前文回顾"></a>前文回顾</h2><p>在上篇中,我们分析了函数式编程的起源和基本特性,并通过每一个特性的示例来演示这种特性的实际效果。首先,函数式编程起源于数理逻辑,起源于<strong>λ演算</strong>,这是一种演算法,它定义一些基础的数据结构,然后通过归约和代换来实现更复杂的数据结构,而函数本身也是它的一种数据。其次,我们探讨了很多函数式编程的特性,比如:</p>
<ul>
<li>First Class</li>
<li>纯函数</li>
<li>引用透明</li>
<li>表达式</li>
<li>高阶函数</li>
<li>柯里化</li>
<li>函数组合</li>
<li>point-free</li>
<li>…</li>
</ul>
<p>但我们也指出了一个实际问题:不能处理副作用的程序是毫无意义的。我们的计算机程序随时都在产生副作用。我们程序里面有大量的网络请求、多媒体输入输出、内部状态、全局状态等,甚至在提倡“碳中和”的今天,电脑的发热量也是一个不容小觑的副作用。那么我们应该如何处理这些问题呢?</p>
</div>
<h2 id="本文简介"><a href="#本文简介" class="headerlink" title="本文简介"></a>本文简介</h2><p>本文通过深入函数式编程的副作用处理及实际应用场景,提供一个学习和使用函数式编程的视角给读者。一方面,这种副作用管理方式是一种高级的抽象形式,不易理解;另一方面,我们在学习和使用函数式编程的过程中,几乎都会遇到类似的副作用问题需要解决,能否解决这个问题也决定了一门函数式编程语言最终是否能走上成功。</p>
<p>本文主要分为三个部分:</p>
<ul>
<li>副作用处理方式</li>
<li>函数式编程的应用</li>
<li>函数式编程的优缺点比较</li>
</ul>
<h2 id="副作用处理:单子-Monad,一种不可避免的抽象"><a href="#副作用处理:单子-Monad,一种不可避免的抽象" class="headerlink" title="副作用处理:单子 Monad,一种不可避免的抽象"></a>副作用处理:单子 Monad,一种不可避免的抽象</h2><p>上面说的,都是最基础的 JavaScript 概念+函数式编程概念。但我们还留了一个“坑”。</p>
<p><strong>如何去处理 IO 操作?</strong></p>
<div class="tzy-post">
<p>我们的代码经常在和副作用打交道,如果要满足纯函数的要求,几乎连一个需求都完成不了。不用急,我们来看一下<strong>React Hooks</strong>。<strong>React Hooks</strong>的设计是很巧妙的,以<code>useEffect</code>为例:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385cfb887318.png"/></div></div>
<p>在函数组件中,<code>useState</code>用来产生状态,在使用<code>useEffect</code>的时候,我们需要挂载这个<code>state</code>到第二个参数,而第一个参数给到的运行函数在<code>state</code>变更的时候被调用,被调用时得到最新的<code>state</code>。</p>
<p>这里面有一个状态转换:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385cfbaef537.png"/></div></div>
<p><strong>React Hooks</strong>给我们的启发是,副作用都被放到一个状态节点里面去被动触发,行程一个单向的数据流动。而实际上,函数式编程语言确实也是这么做的,把副作用包裹到一个特殊的函数里面。</p>
<p>如果一个函数既包含了我们的值,又封装了值的统一操作,使得我们可以在它限定的范围内进行任意运算,那么,我们称这种函数类型为<strong>Monad</strong>。<strong>Monad</strong>是一种高级别的思维抽象。</p>
<h3 id="什么是Monad?"><a href="#什么是Monad?" class="headerlink" title="什么是Monad?"></a>什么是Monad?</h3><p>先思考一个问题,下面两个定义有什么区别?</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385cfed55040.png"/></div></div>
<p><code>num1</code>是数字类型,而<code>num2</code>是对象类型,这是一个直观的区别。</p>
<p>不过,不仅仅如此。利用类型,我们可以做更多的事。因为作为数字的<code>num1</code>是支持加减乘除运算的,而<code>num2</code>却不行,必须要把它视为一个对象<code>{val: 2}</code>,并通过属性访问符<code>num2.val</code>才能进行计算<code>num2.val + 2</code>。但我们知道,函数式编程是不能改变状态的,现在为了计算<code>num2.val</code>被改变了,这不是我们期望的,并且我们使用属性操作符去读数据,更像是在操作对象,而不是操作函数,这与我们的初衷有所背离。</p>
<p>现在我们把<code>num2</code>当作一个独立的数据,并假设存在一个方法<code>fmap</code>可以操作这个数据,可能是这样的。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385cff682642.png"/></div></div>
<p>得到的还是对象,但操作通过一个纯函数<code>addOne</code>去实现了。</p>
<p>上面这个例子里面的<code>Num</code>,实际上就是一个最简单的<strong>Monad</strong>,而<code>fmap</code>是属于<code>Functor(函子)</code>的概念。我们说函数就是从一个数据到另一个数据的映射,这里的fmap就是一个映射函数,在范畴论里面叫做<strong>态射</strong>(后面讲解)。</p>
<p>由于有一个包裹的过程,很多人会把<strong>Monad</strong>看作是一个盒子类型。但<strong>Monad</strong>不仅是一个盒子的概念,它还需要满足一些特定的运算规律(后面涉及)。</p>
<p>但是我们直接使用数字的加减乘除不行吗?为什么一定要<strong>Monad</strong>类型?</p>
<p>首先,<code>fmap</code>的目的是把数据从一个类型映射到另一个类型,而<code>JavaScript</code>里面的<code>map</code>函数实际上就是这个功能。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385cfff14fdd.png"/></div></div>
<p>我们可以认为<code>Array</code>就是一个<strong>Monad</strong>实现,<code>map</code>把<code>Array< T ></code>类型映射到<code>Array< K ></code>类型,操作仍然在数组范畴,数组的值被映射为新的值。 如果用 TypeScript 来表示,会不会更清晰一点?</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385d00b3d4dc.png"/></div></div>
<p>看起来<strong>Monad</strong>只是一个实现了<code>fmap</code>的对象(<code>Functor</code>类型,<code>mappable</code>接口)而已。但<strong>Monad</strong>类型不仅是一个<code>Functor</code>,它还有很多其他的工具函数,比如:</p>
<ul>
<li>bind函数</li>
<li>flatMap函数</li>
<li>liftM函数</li>
</ul>
<p>这些概念在学习<strong>Haskell</strong>时可以遇到,本文不作过多提及。这些额外的函数可以帮助我们操作被封装起来的值。</p>
<h3 id="范畴、群、幺半群"><a href="#范畴、群、幺半群" class="headerlink" title="范畴、群、幺半群"></a>范畴、群、幺半群</h3><p>范畴论是一种研究抽象数学形式的科学,它把我们的数学世界抽象为两个概念:</p>
<ul>
<li>对象</li>
<li>态射</li>
</ul>
<p>为什么说这是一种<strong>形式</strong>上的抽象呢?因为很多数学的概念都可以被这种形式所描述,比如集合,对集合范畴来说,一个集合就是一个范畴对象,从集合A到集合B的映射就是集合的态射,再细化一点,整数集合到整数集合的加减乘操作构成了整数集合的态射(除法会产生整数集合无法表示的数字,因此这里排除了除法)。又比如,三角形可以被代数表示,也可以用几何表示、向量表示,从代数表示到几何表示的运算就可以视为三角形范畴的一种态射。</p>
<p>总之,<strong>对象描述了一个范畴中的元素,而态射描述了针对这些元素的操作</strong>。范畴论不仅可以应用到数学科学里面,在其他科学里面也有一些应用,实际上,范畴论就是我们描述客观世界的一种方式(抽象形式)。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385d012bc38e.png"/></div></div>
<p>相对应的,<strong>函子就是描述一个范畴对象和另一个范畴对象间关系的态射</strong>,具体到编程语言中,函子是一个帮助我们映射一个范畴元素(比如<strong>Monad</strong>)到另一个范畴元素的函数。</p>
<p><strong>群论(Group)</strong>研究的是<strong>群</strong>这种代数结构,怎么去理解群呢?比如一个三角形有三个顶点<strong>A/B/C</strong>,那么我们可以表示一个三角形为<strong>ABC</strong>或者<strong>ACB</strong>,三角形还是这个三角形,但是从<strong>ABC</strong>到<strong>ACB</strong>一定是经过了某种变换。这就像范畴论,三角形的表示是范畴对象,而一个三角形的表示变换到另一个形式,就是范畴的态射。而我们说这些三角形表示方式的集合为一个<strong>群</strong>。<strong>群论主要是研究变换关系</strong>,群又可以分为很多种类,也有很多规律特性,这不在本文研究范围之内,读者可以自行学习相关内容。</p>
<p>科学解释一个<strong>Monad</strong>为自函子范畴上的幺半群。如果没有学习群论和范畴论的话,我们是很难理解这个解释的。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385d01e80c1c.png"/></div></div>
<p>简单来说先固定一个正方形abcd,它和它的几何变换方式(旋转/逆时针旋转/对称/中心对称等)形成的其他正方形一起构成一个群。从这个角度来说,群研究的事物是同一类,只是性质稍有不一样(态射后)。</p>
<p>另外一个理解群的概念就是<strong>自然数(构成一个群)和加法(群的二元运算,且满足结合律,半群)</strong>。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385d0366e1d6.png"/></div></div>
<p>到此,我们可以理解<strong>Monad</strong>为:</p>
<ul>
<li>满足自函子运算(从A范畴态射到A范畴,fmap是在自己空间做映射)。</li>
<li>满足含幺半群的结合律。</li>
</ul>
<p>很多函数式编程里面都会实现一个<code>Identity</code>函数,实际就是一个幺元素。比如 JavaScript 中对 Just 满足二元结合律可以这么操作:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385d041267f1.png"/></div></div>
<h3 id="Monad范畴:定律、折叠和链"><a href="#Monad范畴:定律、折叠和链" class="headerlink" title="Monad范畴:定律、折叠和链"></a>Monad范畴:定律、折叠和链</h3><p>我们要在一个更大的空间上讨论这个范畴对象(Monad)。就像Number封装了数字类型,<strong>Monad</strong>也封装了一些类型。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385d05494b4d.png"/></div></div>
<p><strong>Monad</strong>需要满足一些定律:</p>
<ul>
<li>结合律:比如a · b · c = a · (b · c)。</li>
<li>幺元:比如a · e = e · a = a。</li>
</ul>
<p>一旦定义了<strong>Monad</strong>为一类对象,<strong>fmap</strong>为针对这种对象的操作,那么定律我们可以很容易证明:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385d0574244c.png"/></div></div>
<p>我们可以通过<code>Monad Just</code>上挂载的操作来对数据进行计算,这些运算是限定在了<code>Just</code>上的,也就是说你只能得到<code>Just(..)</code>类型。要获取原始数据,可以基于这个定义一个<code>fold</code>方法。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385d059b1b97.png"/></div></div>
<p><strong>fold</strong>(折叠,对应能力我们称为foldable)的意义在于你可以将数据从一个特定范畴映射到你的常用范畴,比如面向对象语言的 toString 方法,就是把数据从对象域转换到字符串域。</p>
<blockquote>
<p>JavaScript 中的 Array.prototype.reduce 其实就是一个 fold 函数,它把数据从 Array 范畴映射到其他范畴。</p>
</blockquote>
<p>一旦数据类型被我们锁定在了 Monad 空间(范畴),那我们就可以在这个范畴内连续调用 fmap(或者其他这个空间的函数)来进行值操作,这样我们就可以<strong>链式处理</strong>我们的数据。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385d05bb5e05.png"/></div></div>
<h3 id="Maybe和Either"><a href="#Maybe和Either" class="headerlink" title="Maybe和Either"></a>Maybe和Either</h3><p>有了<strong>Just</strong>的概念,我们再来学习一些新的<strong>Monad</strong>概念。比如<strong>Nothing</strong>。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385d05e382d4.png"/></div></div>
<p><strong>Nothing</strong>表示在<strong>Monad</strong>范畴上没有的值。和<strong>Just</strong>一起正好描述了所有的数据情况,合称为<strong>Maybe</strong>,我们的<strong>Maybe Monad</strong>要么是<strong>Just</strong>,要么是<strong>Nothing</strong>。这有什么意义呢?</p>
<p>其实这就是模拟了其他范畴内的“有”和“无”的概念,方便我们模拟其他编程范式的空值操作。比如:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385d060abbed.png"/></div></div>
<p>这种情况下我们需要去判断<strong>x</strong>和<strong>y</strong>是否为空。在<strong>Monad</strong>空间中,这种情况就很好表示:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385d0632e89f.png"/></div></div>
<p>我们在<strong>Monad</strong>空间中消除了烦人的<code>!== null</code>判断,甚至消除了三元运算符。一切都只有函数。实际使用中一个 <strong>Maybe</strong>要么是<strong>Just</strong>要么是<strong>Nothing</strong>。因此,这里用**Maybe(..)**构造可能让我们难以理解。</p>
<p>如果非要理解的话,可以理解<strong>Maybe</strong>为<strong>Nothing</strong>和<strong>Just</strong>的抽象类,<strong>Just</strong>和<strong>Nothing</strong>构成这个抽象类的两个实现。实际在函数式编程语言实现中,<strong>Maybe</strong>确实只是一个类型(称为代数类型),具体的一个值有具体类型<strong>Just</strong>或<strong>Nothing</strong>,就像数字可以分为有理数和无理数一样。</p>
<p>除了这种值存在与否的判断,我们的程序还有一些分支结构的方式,因此我们来看一下在<strong>Monad</strong>空间中,分支情况怎么去模拟?</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385d0653dcca.png"/></div></div>
<p>假设我们有一个代数类型<strong>Either</strong>,<strong>Left</strong>和<strong>Right</strong>分别表示当数据为错误和数据为正确情况下的逻辑。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385d06845509.png"/></div></div>
<p>这样,我们就可以使用“函数”来替代分支了。这里的<strong>Either</strong>实现比较粗糙,因为<strong>Either</strong>类型应该只在<strong>Monad</strong>空间。这里加入了布尔常量的判断,目的是好理解一些。其他的编程语言特性,在函数式编程中也能找到对应的影子,比如循环结构,我们往往使用函数递归来实现。</p>
<h3 id="IO的处理方式"><a href="#IO的处理方式" class="headerlink" title="IO的处理方式"></a>IO的处理方式</h3><p>终于到<strong>IO</strong>了,如果不能处理好<strong>IO</strong>,我们的程序是不健全的。到目前为止,我们的<strong>Monad</strong>都是针对数据的。这句话对也不对,因为函数也是一种数据(函数是第一公民)。我们先让<strong>Monad Just</strong>能存储函数。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385d06b888cb.png"/></div></div>
<p>你可以想象为<strong>Just</strong>增加了一个抽象类实现,这个抽象类为:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385d0cf760e5.png"/></div></div>
<p>这个抽象类我们称为“应用函子”,它可以保存一个函数作为内部值,并且使用<code>apply</code>方法可以把这个函数作用到另一个<code>Monad</code>上。到这里,我们完全可以把Monad之间的各种操作(接口,比如<code>fmap</code>和<code>apply</code>)视为契约,也就是数学上的态射。</p>
<p>现在,如果我们有一个单子叫<code>IO</code>,并且它有如下表现:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385d0d2673c9.png"/></div></div>
<p>我们把这种类型的Monad称为IO,我们在IO中处理打印(副作用)。你可以把之前我们学习到的类型合并一下,得到一个示例:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385d0d4ad896.png"/></div></div>
<p>通常一个程序会有一个主入口函数<code>main</code>,这个main函数返回值类型是一个<code>IO</code>,我们的副作用现在全在<code>IO</code>这个范畴下运行,而其他操作,都可以保持纯净(类型运算)。</p>
<p>IO类型让我们可以在<code>Monad</code>空间处理那些烦人的副作用,这个<code>Monad</code>类型和<code>Promise</code>(限定副作用到<code>Promise</code>域处理,可链式调用,可用<code>then</code>折叠和映射)很像。</p>
<h2 id="函数式编程的应用"><a href="#函数式编程的应用" class="headerlink" title="函数式编程的应用"></a>函数式编程的应用</h2><p>除了上面我们提到的一些示例,函数式编程可以应用到更广的业务代码开发中,用来替代我们的一些基础业务代码。这里举几个例子。</p>
<h3 id="设计一个请求模块"><a href="#设计一个请求模块" class="headerlink" title="设计一个请求模块"></a>设计一个请求模块</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385d0d720cd5.png"/></div></div>
<p>用这种方式构建的模块,组合和复用性很强,你也可以利用<code>lodash</code>的其他库对<code>req</code>做一个其他改造。我们调用业务代码的时候只管传递<code>params</code>,分支校验和错误检查就教给<code>validate.js</code>里面的高阶函数就好了。</p>
<h3 id="设计一个输入框"><a href="#设计一个输入框" class="headerlink" title="设计一个输入框"></a>设计一个输入框</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385d0d9bbd98.png"/></div></div>
<p>这个例子也是来源于前端常见的场景。我们使用函数式编程的思想,把多个看似不相关的函数进行组合,得到了业务需要的<code>subscribe</code>函数,但同时,上面的任意一个函数都可以被用于其他功能组合。比如<code>callback</code>函数可以直接给<code>dom</code>回调,<code>listenInput</code>可以用于任意一个<code>dom</code>。</p>
<p>这种通过高阶组件不停组合得到最终结果的方式,我们可以认为就是函数式的。(尽管它没有像上一个例子一样引入<strong>IO/Monad</strong>等概念)</p>
<h3 id="超长文本省略:Ramdajs为例"><a href="#超长文本省略:Ramdajs为例" class="headerlink" title="超长文本省略:Ramdajs为例"></a>超长文本省略:Ramdajs为例</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385d0dcc134a.png"/></div></div>
<p>这个也是常见的前端场景,当文本长度大于X时,显示省略号,这个实现使用 Ramdajs 。这个过程中你就像是在搭积木,很容易就把业务给“搭建”完成了。</p>
<h2 id="函数式编程库、语言"><a href="#函数式编程库、语言" class="headerlink" title="函数式编程库、语言"></a>函数式编程库、语言</h2><p>函数式编程的库可以学习:</p>
<ul>
<li>Ramda.js:函数式编程库</li>
<li>lodash.js:函数工具</li>
<li>immutable.js:数据不可变</li>
<li>rx.js:响应式编程</li>
<li>partial.lenses:函数工具</li>
<li>monio.js:函数式编程工具库/IO库</li>
<li>…</li>
</ul>
<p>你可以结合起来使用。下面是<strong>Ramda.js</strong>示例:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/29/6385d0dece84c.png"/></div></div>
<p>而纯函数式语言,有很多:</p>
<ul>
<li>Lisp 代表软件 emacs…</li>
<li>Haskell 代表软件 pandoc…</li>
<li>Ocaml …</li>
<li>…</li>
</ul>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>函数式编程并不是什么“黑科技”,它已经存在的时间甚至比面向对象编程更久远。希望本文能帮助大家理解什么是函数式编程。</p>
<p>现在我们来回顾先览,实际上,函数式编程也是程序实现方式的一种,它和面向对象是殊途同归的。在函数式语言中,我们要构建一个个小的基础函数,并通过一些通用的流程把他们粘合起来。举个例子,面向对象里面的继承,我在函数式编程中可以使用组合compose或者高阶函数hoc来实现。</p>
<p>尽管在实现上是等价的,但和面向对象的编程范式对比,函数式编程有很多优点值得大家去尝试。</p>
<h3 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h3><p>除了上面提到的风格和特性之外,函数式编程相对其他编程范式,有很多优点:</p>
<ul>
<li>函数纯净 程序有更少的状态量,编码心智负担更小。随着状态量的增加,某些编程范式构建的软件库代码复杂度可能呈几何增长,而函数式编程的状态量都收敛了,对软件复杂度带来的影响更小。</li>
<li>引用透明性 可以让你在不影响其他功能的前提下,升级某一个特定功能(一个对象的引用需要改动的话,可能牵一发而动全身)。</li>
<li>高度可组合 函数之间复用方便(需要关注的状态量更少),函数的功能升级改造也更容易(高阶组件)。</li>
<li>副作用隔离 所有的状态量被收敛到一个盒子(函数)里面处理,关注点更加集中。</li>
<li>代码简洁/流程更清晰 通常函数式编程风格的程序,代码量比其他编程风格的少很多,这得益于函数的高度可组合性以及大量的完善的基础函数,简洁性也使得代码更容易维护。</li>
<li>语义化 一个个小的函数分别完成一种小的功能,当你需要组合上层能力的时候,基本可以按照函数语义来进行快速组合。</li>
<li>惰性计算 被组合的函数只会生成一个更高阶的函数,最后调用时数据才会在函数之间流动。</li>
<li>跨语言统一性 不同的语言,似乎都遵从类似的函数式编程范式,比如Java 8的lambda表达式,Rust的collection、匿- 名函数;而面向对象的实现,不同语言可能千差万别,函数式编程的统一性让你可以舒服地跨语言开发。</li>
<li>关键领域应用 因为函数式编程状态少、代码简洁等特点,使得它在交互复杂、安全性要求高的领域有重要的应用,像Lisp和Haskell就是因上一波人工智能热而火起来的,后来也在一些特殊的领域(银行、水利、航空航天等)得到了较大规模的应用。</li>
<li>…</li>
</ul>
<h3 id="不足"><a href="#不足" class="headerlink" title="不足"></a>不足</h3><p>当然,函数式编程也存在一些不足之处:</p>
<ul>
<li>陡峭的学习曲线 面向对象和命令式编程范式都是贴近我们的日常习惯的方式,而函数式编程要更抽象一些,要想更好地管理副作用,你可能需要学习很多新的概念(响应式、Monad等),这些概念入门很难,而且是一个长期积累的过程。</li>
<li>可能的调用栈溢出问题 惰性计算在一些电脑或特种程序架构上可能有函数调用栈错误(超长调用链、超长递归),另外许多函数式编程语言需要编译器支持尾递归优化(优化为循环迭代)以得到更好的性能。</li>
<li>额外的抽象负担 当程序有大量可变状态、副作用时,用函数式编程可能造成额外的抽象负担,项目开发周期可能会延长,这时可能用其他抽象方式更好(比如OOP)。</li>
<li>数据不变性的问题 为了数据不变,运行时可能会构建生成大量的数据副本,造成时间和空间消耗更大,拖慢性能;同时数据不可变性可能会造成构建一些基础数据结构的时候语法不简洁,性能也更差(比如LinkedList、HashMap等数据结构)。</li>
<li>语义化的问题 往往为了开发一个功能,去造许多的基础函数,大量业务组件想要语义化的命名,也会带给开发人员很多负担;并且功能抽象能力因人而异,公共函数往往不够公用或者过度设计。</li>
<li>生态问题 函数式编程在工业生产领域因其抽象性和性能带来的问题,被许多开发者拒之门外,一些特定功能的解决方案也更小众(相比其他编程范式),生态也一直比较小,这成为一些新的开发人员学习和使用函数式编程的又一个巨大障碍。</li>
<li>…</li>
</ul>
<p>日常业务开发中,往往我们需要取长补短,在适合的领域用适合的方法/范式。大家只要要记住,软件开发并没有“银弹”。</p>
<h2 id="FAQ"><a href="#FAQ" class="headerlink" title="FAQ"></a>FAQ</h2><p><strong>Q:你觉得Promise是不是一种Monad IO模型?</strong></p>
<p>A:我认为是的。纯函数是没有异步概念的,Promise用了一种很棒的方式把异步和IO转化为了.then函数。你仍然可以在.then函数中写纯粹的函数,也可以在.then函数中调用其他的Promise,这就和IO
深入理解函数式编程(上)
https://fe32.top/articles/jsnb9546/
2022-12-04T11:17:06.000Z
2023-05-17T10:01:12.000Z
<div class="tzy-post">
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本文分为上下两篇,上篇讲述<strong>函数式编程的基础概念和特性</strong>,下篇讲述<strong>函数式编程的进阶概念、应用及优缺点</strong>。函数式编程既不是简单的堆砌函数,也不是语言范式的终极之道。我们将深入浅出地讨论它的特性,以期在日常工作中能在对应场景中进行灵活应用。</p>
<h2 id="代码组合和复用"><a href="#代码组合和复用" class="headerlink" title="代码组合和复用"></a>代码组合和复用</h2><p>在前端代码中,我们现有一些可行的模块复用方式,比如:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/63847e23e120f.png"/></div></div>
<p>除了上面提到的组件和功能级别的代码复用,我们也可以在软件架构层面上,通过选择一些合理的架构设计来减少重复开发的工作量,比如说很多公司在中后台场景中大量使用的<strong>低代码平台</strong>。</p>
<p>可以说,在大部分软件项目中,我们都要去探索<strong>代码组合和复用</strong>。</p>
<p>函数式编程,曾经有过一段黄金时代,后来又因面向对象范式的崛起而逐步变为小众范式。但是,函数式编程目前又开始在不同的语言中流行起来了,像Java 8、JS、Rust等语言都有对函数式编程的支持。</p>
<p>今天我们就来探讨JavaScript的<strong>函数</strong>,并进一步探讨<strong>JavaScript中的函数式编程</strong>(关于函数式编程风格软件的<strong>组织、组合和复用</strong>)。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/63847e2688387.png"/></div></div>
<h2 id="什么是函数式编程?"><a href="#什么是函数式编程?" class="headerlink" title="什么是函数式编程?"></a>什么是函数式编程?</h2><h3 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h3><p>函数式编程是一种风格范式,没有一个标准的教条式定义。我们来看一下维基百科的定义:</p>
<blockquote>
<p>函数式编程是一种编程范式,它将电脑运算视为函数运算,并且避免使用程序状态以及易变对象。其中,<strong>λ演算</strong>是该语言最重要的基础。而且λ演算的函数可以接受函数作为输入的参数和输出的返回值。</p>
</blockquote>
<p>我们可以直接读出以下信息:</p>
<ol>
<li>避免状态变更</li>
<li>函数作为输入输出</li>
<li>和<strong>λ演算</strong>有关</li>
</ol>
<p>那什么是<strong>λ演算</strong>呢?</p>
<h3 id="函数式编程起源:λ演算"><a href="#函数式编程起源:λ演算" class="headerlink" title="函数式编程起源:λ演算"></a>函数式编程起源:λ演算</h3><p><strong>λ演算(读作lambda演算)</strong>由数学家<strong>阿隆佐·邱奇</strong>在20世纪30年代首次发表,它从<strong>数理逻辑</strong>(Mathematical logic)中发展而来,使用变量绑定(binding)和代换规则(substitution)来研究函数如何抽象化定义(define)、函数如何被应用(apply)以及递归(recursion)的形式系统。</p>
<blockquote>
<p><strong>λ演算</strong>和图灵机等价(图灵完备,作为一种研究语言又很方便)。</p>
</blockquote>
<p>通常用这个定义形式来表示一个<strong>λ演算</strong>。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/63849ee359221.png"/></div></div>
<p>所以λ演算式就三个要点:</p>
<ol>
<li>绑定关系。变量任意性,x、y和z都行,它仅仅是具体数据的代称。</li>
<li>递归定义。λ项递归定义,M可以是一个λ项。</li>
<li>替换归约。λ项可应用,空格分隔表示对M应用N,N可以是一个λ项。</li>
</ol>
<p>比如这样的演算式:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/63849ee6247b3.png"/></div></div>
<p>通过变<strong>量代换(substitution)</strong>和<strong>归约(reduction)</strong>,我们可以像化简方程一样处理我们的演算。</p>
<p><strong>λ演算</strong>有很多方式进行,数学家们也总结了许多和它相关的规律和定律(可查看维基百科)。</p>
<p>举个例子,小时候我们学习整数就是学会几个数字,然后用加法/减法来推演其他数字。在<strong>函数</strong>式编程中,我们可以用函数来定义自然数,有很多定义方式,这里我们讲一种实现方式:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/63849ee791b92.png"/></div></div>
<p>上面的演算式表示有一个函数<code>f</code>和一个参数<code>x</code>。令<code>0</code>为<code>x</code>,<code>1</code>为<code>f x</code>,<code>2</code>为<code>f f x</code>,以此类推</p>
<p>什么意思呢?这是不是很像我们数学中的幂:<strong>a^x(a的x次幂表示a对自身乘x次)</strong>。相应的,我们理解上面的演算式就是<strong>数字n就是f对x作用的次数</strong>。有了这个数字的定义之后,我们就可以在这个基础上定义运算。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/63849eebb3c27.png"/></div></div>
<p>其中<code>SUCC</code>表示后继函数(<code>+1操作</code>),<code>PLUS</code>表示加法。现在我们来推导这个正确性。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/63849f6067df6.png"/></div></div>
<p>这样,进行<strong>λ演算</strong>就像是方程的代换和化简,在一个已知前提(公理,比如0/1,加法)下,进行规则推演。</p>
<h4 id="演算:变量的含义"><a href="#演算:变量的含义" class="headerlink" title="演算:变量的含义"></a>演算:变量的含义</h4><p>在<strong>λ演算</strong>中我们的表达式只有一个参数,那它怎么实现两个数字的二元操作呢?比如加法 a + b,需要两个参数。</p>
<p><strong>这时,我们要把函数本身也视为值</strong>,可以通过把一个变量绑定到上下文,然后返回一个新的函数,来实现数据(或者说是状态)的保存和传递,被绑定的变量可以在需要实际使用的时候从上下文中引用到。</p>
<p>比如下面这个简单的演算式:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384a3545a994.png"/></div></div>
<p>第一次函数调用传入<code>m=5</code>,返回一个新函数,这个新函数接收一个参数<code>n</code>,并返回<code>m + n</code>的结果。像这种情况产生的上下文,就是<code>Closure</code>(闭包,函数式编程常用的状态保存和引用手段),我们称变量<code>m</code>是被<strong>绑定(binding)</strong>到了第二个函数的上下文。</p>
<p>除了绑定的变量,<strong>λ演算</strong>也支持自由的变量,比如下面这个<code>y</code>:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384a35863451.png"/></div></div>
<p>这里的<code>y</code>是一个没有绑定到参数位置的变量,被称为一个<strong>自由变量</strong>。</p>
<p><strong>绑定变量</strong>和<strong>自由变量</strong>是函数的两种状态来源,一个可以被代换,另一个不能。实际程序中,通常把绑定变量实现为局部变量或者参数,自由变量实现为全局变量或者环境变量。</p>
<h4 id="演算:代换和归约"><a href="#演算:代换和归约" class="headerlink" title="演算:代换和归约"></a>演算:代换和归约</h4><p>演算分为<strong>Alpha</strong>代换和<strong>Beta</strong>归约。 前面章节我们实际上已经涉及这两个概念,下面来介绍一下它们。</p>
<p><strong>Alpha代换</strong>指的是变量的名称是不重要的,你可以写<code>λm.λn.m + n</code>,也可以写<code>λx.λy.x + y</code>,在演算过程中它们表示同一个函数。也就是说我们只<strong>关心计算的形式</strong>,而不关心细节用什么变量去实现。这方便我们不改变运算结果的前提下去修改变量命名,以方便在函数比较复杂的情况下进行化简操作。实际上,连整个lambda演算式的名字也是不重要的,我们只需要这种形式的计算,而不在乎这个形式的命名。</p>
<p><strong>Beta归约</strong>指的是如果你有一个<strong>函数应用(函数调用)</strong>,那么你可以对这个函数体中与标识符对应的部分做<strong>代换(substitution)</strong>,方式为使用参数(可能是另一个演算式)去替换标识符。听起来有点绕口,但是它实际上就是<strong>函数调用的参数替换</strong>。比如:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384a433652ea.png"/></div></div>
<p>可以使用<code>1</code>替换<code>m</code>,<code>3</code>替换<code>n</code>,那么整个表达式可以化简为<code>4</code>。这也是函数式编程里面的引用透明性的由来。需要注意的是,这里的<code>1</code>和<code>3</code>表示表达式运算值,可以替换为其他表达式。比如把<code>1</code>替换为<code>(λm.λn.m + n 1 3)</code>,这里就需要做两次归约来得到下面的最终结果:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384a474a3eb3.png"/></div></div>
<h3 id="JavaScript中的λ表达式:箭头函数"><a href="#JavaScript中的λ表达式:箭头函数" class="headerlink" title="JavaScript中的λ表达式:箭头函数"></a>JavaScript中的λ表达式:箭头函数</h3><p>ECMAScript 2015规范引入了箭头函数,它没有<code>this</code>,没有<code>arguments</code>。只能作为一个<strong>表达式(expression)</strong>而不能作为一个<strong>声明式(statement)</strong>,表达式产生一个箭头函数引用,该箭头函数引用仍然有<code>name</code>和<code>length</code>属性,分别表示箭头函数的名字、形参(parameters)长度。一个箭头函数就是一个单纯的运算式,箭头函数我们也可以称为<strong>lambda函数</strong>,它在书写形式上就像是一个<strong>λ演算式</strong>。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384a4cf91714.png"/></div></div>
<p>可以利用箭头函数做一些简单的运算,下例比较了四种箭头函数的使用方式:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384a4eaf19cb.png"/></div></div>
<p>这是直接针对数字(基本数据类型)的情况,如果是针对函数做运算(<strong>引用数据类型</strong>),事情就变得有趣起来了。我们看一下下面的示例:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384a51a40066.png"/></div></div>
<p><code>fn_x</code>类型,表明我们可以利用函数内的函数,当函数被当作数据传递的时候,就可以对函数进行应用(apply),生成更高阶的操作。 并且<code>x => y => x(y)</code>可以有两种理解,一种是<code>x => y</code>函数传入<code>X => x(y)</code>,另一种是<code>x</code>传入 <code>y => x(y)</code>。</p>
<p><code>add_x</code>类型表明,一个运算式可以有很多不同的路径来实现。</p>
<blockquote>
<p>上面的add_1/add_2/add_3我们用到了JavaScript的立即运算表达式<strong>IIFE</strong>。</p>
</blockquote>
<p><strong>λ演算</strong>是一种抽象的数学表达方式,我们不关心真实的运算情况,我们只关心这种<strong>运算形式</strong>。因此上一节的演算可以用JavaScript来模拟。下面我们来实现<strong>λ演算的JavaScript表示</strong>。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384a6ec5866f.png"/></div></div>
<p>我们把<strong>λ演算中</strong>的<code>f</code>和<code>x</code>分别取为<code>countTime</code>和<code>x</code>,代入运算就得到了我们的自然数。</p>
<p>这也说明了不管你使用<strong>符号系统</strong>还是<strong>JavaScript语言</strong>,你想要表达的<strong>自然数</strong>是等价的。这也侧面说明<strong>λ演算</strong>是一种<strong>形式上的抽象(和具体语言表述无关的抽象表达)</strong>。</p>
<h3 id="函数式编程基础:函数的元、柯里化和Point-Free"><a href="#函数式编程基础:函数的元、柯里化和Point-Free" class="headerlink" title="函数式编程基础:函数的元、柯里化和Point-Free"></a>函数式编程基础:函数的元、柯里化和Point-Free</h3><p>回到 JavaScript 本身,我们要探究<strong>函数</strong>本身能不能带给我们更多的东西?我们在 JavaScript 中有很多创建函数的方式:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384a76db19cc.png"/></div></div>
<p>虽然函数有这么多定义,但 function 关键字声明的函数带有<code>arguments</code>和<code>this</code>关键字,这让他们看起来更像是对象方法(method),而不是函数(function)。</p>
<p>况且 function 定义的函数大多数还能被构造(比如 new Array)。</p>
<p>接下来我们将只研究箭头函数,因为它更像是数学意义上的函数(仅执行计算过程)。</p>
<ul>
<li>没有<code>arguments</code>和<code>this</code></li>
<li>不可以被构造<code>new</code></li>
</ul>
<h4 id="函数的元:完全调用和不完全调用"><a href="#函数的元:完全调用和不完全调用" class="headerlink" title="函数的元:完全调用和不完全调用"></a>函数的元:完全调用和不完全调用</h4><p>不论使用何种方式去构造一个函数,这个函数都有两个固定的信息可以获取: </p>
<ul>
<li><code>name</code>表示当前标识符指向的函数的名字。</li>
<li><code>length</code>表示当前标识符指向的函数定义时的参数列表长度。</li>
</ul>
<p>在数学上,我们定义<code>f(x) = x</code>是一个一元函数,而<code>f(x, y) = x + y</code>是一个二元函数。在JavaScript中我们可以使用函数定义时的<code>length</code>来定义它的元。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384a81cb8c0d.png"/></div></div>
<p>定义<strong>函数的元</strong>的意义在于,我们可以对函数进行归类,并且可以明确一个函数需要的确切参数个数。函数的元在编译期(类型检查、重载)和运行时(异常处理、动态生成代码)都有重要作用。</p>
<p>如果我给你一个<strong>二元函数</strong>,你就知道需要传递两个参数。比如+就可以看成是一个二元函数,它的左边接受一个参数,右边接受一个参数,返回它们的和(或字符串连接)。</p>
<p>在一些其他语言中,+确实也是由抽象类来定义实现的,比如 Rust 语言的<code>trait Add</code>。</p>
<p>但我们上面看到的<strong>λ演算</strong>,每个函数都只有一个元。为什么呢?</p>
<p>只有一个元的函数方便我们进行代数运算。λ演算的参数列表使用<strong>λx.λy.λz</strong>的格式进行分割,返回值一般都是函数,如果一个二元函数,调用时只使用了一个参数,则返回一个「不完全调用函数」。这里用三个例子解释“不完全调用”。</p>
<p>第一个,不完全调用,代换参数后产生了一个<strong>不完全调用函数 λz.3 + z</strong>。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384a86381b03.png"/></div></div>
<p>第二个,Haskell代码,调用一个函数<code>add</code>(类型为<code>a -> a -> a</code>),得到另一个函数<code>add 1</code>(类型为<code>a -> a</code>)。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384a89cda8b2.png"/></div></div>
<p>第三个,上一个例子的 JavaScript 版本。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384a8c865714.png"/></div></div>
<p>“不完全调用”在 JavaScript 中也成立。实际上它就是 JavaScript 中闭包(Closure,上面我们已经提到过)产生的原因,一个函数还没有被销毁(调用没有完全结束),你可以在子环境内使用父环境的变量。</p>
<p>注意,上面我们使用到的是一元函数,如果使用三元函数来表示addThree,那么函数一次性就调用完毕了,不会产生不完全调用。</p>
<p>函数的元还有一个著名的例子(面试题):</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384a8df21e6d.png"/></div></div>
<p>造成上述结果的原因就是,<code>Number</code>是一元的,接受<code>map</code>第一个参数就转换得到返回值;而<code>parseInt</code>是二元的,第二个参数拿到进制为<code>1</code>(<code>map</code>函数为三元函数,第二个参数返回元素索引),无法输出正确的转换,只能返回<code>NaN</code>。这个例子里面涉及到了一元、二元、三元函数,多一个元,函数体就多一个状态。如果世界上只有一元函数就好了!我们可以全通过一元函数和不完全调用来生成新的函数处理新的问题。</p>
<p>认识到函数是有元的,这是函数式编程的重要一步,多一个元就多一种复杂度。</p>
<h4 id="柯里化函数:函数元降维技术"><a href="#柯里化函数:函数元降维技术" class="headerlink" title="柯里化函数:函数元降维技术"></a>柯里化函数:函数元降维技术</h4><p><strong>柯里化(curry)函数</strong>是一种把函数的元降维的技术,这个名词是为了纪念我们上文提到的数学家<strong>阿隆佐·邱奇</strong>。</p>
<p>首先,函数的几种写法是等价的(最终计算结果一致)。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384a938848d0.png"/></div></div>
<p>这里列一个简单的把普通函数变为柯里化函数的方式(柯里化函数Curry)。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384a959bb07a.png"/></div></div>
<p>柯里化函数帮助我们把一个多元函数变成一个不完全调用,利用Closure的魔法,把函数调用变成延迟的偏函数(不完全调用函数)调用。这在函数<strong>组合、复用</strong>等场景非常有用。比如:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384a97db9246.png"/></div></div>
<p>虽然你可以用其他闭包方式来实现函数的延迟调用,但 Curry 函数绝对是其中形式最美的几种方式之一。</p>
<h4 id="Point-Free|无参风格:函数的高阶组合"><a href="#Point-Free|无参风格:函数的高阶组合" class="headerlink" title="Point-Free|无参风格:函数的高阶组合"></a>Point-Free|无参风格:函数的高阶组合</h4><p>函数式编程中有一种<strong>Point-Free</strong>风格,中文语境大概可以把<strong>point</strong>认为是参数点,对应<strong>λ演算</strong>中的函数应用(Function Apply),或者 JavaScript 中的函数调用(Function Call),所以可以理解<strong>Point-Free就指的是无参调用</strong>。</p>
<p>来看一个日常的例子,把二进制数据转换为八进制数据:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384a9f98721d.png"/></div></div>
<p>这段代码运行起来没有问题,但我们为了处理这个转换,需要了解<code>parseInt(x, 2)</code>和<code>toString(8)</code>这两个函数(为什么有魔法数字2和魔法数字8),并且要关心数据(函数类型<code>a -> b</code>)在每个节点的形态(关心数据的流向)。有没有一种方式,可以让我们只关心入参和出参,不关心数据流动过程呢?</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384aa4144361.png"/></div></div>
<p>上面的方法假设我们已经有了一些基础函数<code>toBinary</code>(语义化,消除魔法数字2)和<code>toStringOx</code>(语义化,消除魔法数字8),并且有一种方式(<code>pipe</code>)把基础函数组合(<code>Composition</code>)起来。如果用<strong>Typescript</strong>表示我们的数据流动就是:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384aa7a9bb59.png"/></div></div>
<p>用 Haskell 表示更简洁,后面都用 Haskell 类型表示方式,作为我们的符号语言。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384aa932c79b.png"/></div></div>
<p>值得注意的是,这里的<code>x-> [int] ->y</code>我们不用关心,因为<code>pipe(..)</code>函数帮我们处理了它们。pipe 就像一个盒子。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384aabd3e651.png"/></div></div>
<p><code>BOX</code>内容不需要我们理解。而为了达成这个目的,我们需要做这些事:</p>
<ul>
<li>utils 一些特定的工具函数。</li>
<li>curry 柯里化并使得函数可以被复用。</li>
<li>composition 一个组合函数,像胶水一样粘合所有的操作。</li>
<li>name 给每个函数定义一个见名知意的名字。</li>
</ul>
<p>综上,<strong>Point-Free</strong>风格是粘合一些基础函数,最终让我们的数据操作不再关心中间态的方式。这是函数式编程,或者说函数式编程语言中我们会一直遇到的风格,表明我们的基础函数是值得信赖的,我们仅关心数据转换这种形式,而不是过程。JavaScript 中有很多实现这种基础函数工具的库,最出名的是 Lodash 。</p>
<p>可以说函数式编程范式就是在不停地组合函数。</p>
<h3 id="函数式编程特性"><a href="#函数式编程特性" class="headerlink" title="函数式编程特性"></a>函数式编程特性</h3><p>说了这么久,都是在讲函数,那么究竟什么是函数式编程呢?在网上你可以看到很多定义,但大都离不开这些特性。</p>
<ul>
<li>First Class 函数:函数可以被应用,也可以被当作数据。</li>
<li>Pure 纯函数,无副作用:任意时刻以相同参数调用函数任意次数得到的结果都一样。</li>
<li>Referential Transparency 引用透明:可以被表达式替代。</li>
<li>Expression 基于表达式:表达式可以被计算,促进数据流动,状态声明就像是一个暂停,好像数据到这里就会停滞了一下。</li>
<li>Immutable 不可变性:参数不可被修改、变量不可被修改—宁可牺牲性能,也要产生新的数据(Rust内存模型例外)。</li>
<li>High Order Function 大量使用高阶函数:变量存储、闭包应用、函数高度可组合。</li>
<li>Curry 柯里化:对函数进行降维,方便进行组合。</li>
<li>Composition 函数组合:将多个单函数进行组合,像流水线一样工作。</li>
</ul>
<p>另外还有一些特性,有的会提到,有的一笔带过,但实际也是一个特性(以Haskell为例)。</p>
<ul>
<li>Type Inference 类型推导:如果无法确定数据的类型,那函数怎么去组合?(常见,但非必需)</li>
<li>Lazy Evaluation 惰性求值:函数天然就是一个执行环境,惰性求值是很自然的选择。</li>
<li>Side Effect IO:一种类型,用于处理副作用。一个不能执行打印文字、修改文件等操作的程序,是没有意义的,总要有位置处理副作用。(边缘)</li>
</ul>
<p>数学上,我们定义函数为<strong>集合A</strong>到<strong>集合B</strong>的映射。在函数式编程中,我们也是这么认为的。函数就是把数据从某种形态映射到另一种形态。注意理解“映射”,后面我们还会讲到。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384ab2abb9c7.png"/></div></div>
<h4 id="First-Class:函数也是一种数据"><a href="#First-Class:函数也是一种数据" class="headerlink" title="First Class:函数也是一种数据"></a>First Class:函数也是一种数据</h4><p>函数本身也是数据的一种,可以是参数,也可以是返回值。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384ab4ede612.png"/></div></div>
<p>通过这种方式,我们可以让函数作为一种可以保存状态的值进行流动,也可以充分利用不完全调用函数来进行函数组合。把函数作为数据,实际上就让我们有能力获取函数内部的状态,这样也产生了闭包。但函数式编程不强调状态,大部分情况下,我们的“状态”就是一个函数的元(我们从元获取外部状态)。</p>
<h4 id="纯函数:无状态的世界"><a href="#纯函数:无状态的世界" class="headerlink" title="纯函数:无状态的世界"></a>纯函数:无状态的世界</h4><p>通常我们定义输入输出(IO)是不纯的,因为 IO 操作不仅操作了数据,还操作了这个数据范畴外部的世界,比如打印、播放声音、修改变量状态、网络请求等。这些操作并不是说对程序造成了破坏,相反,一个完整的程序一定是需要它们的,不然我们的所有计算都将毫无意义。</p>
<p>但纯函数是可预测的,引用透明的,我们希望代码中更多地出现纯函数式的代码,这样的代码可以被预测,可以被表达式替换,而更多地把 IO 操作放到一个统一的位置做处理。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384ab71c3579.png"/></div></div>
<p>这个<code>add</code>函数是不纯的,但我们把副作用都放到它里面了。任意使用这个<code>add</code>函数的位置,都将变成不纯的(就像是<code>async/await</code>的传递性一样)。需要说明的是抛开实际应用来谈论函数的纯粹性是毫无意义的,我们的程序需要和终端、网络等设备进行交互,不然一个计算的运行结果将毫无意义。但对于函数的元来说,这种纯粹性就很有意义,比如:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384ab947b333.png"/></div></div>
<p>当函数的元像上面那样是一个引用值,如果一个函数的调用不去控制自己的纯粹性,对别人来说,可能会造成毁灭性打击。因此我们需要对引用值特别小心。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384abad9962d.png"/></div></div>
<p>上面这种解构赋值的方式仅解决了第一层的引用值,很多其他情况下,我们要处理一个引用树、或者返回一个引用树,这需要更深层次的解引用操作。请小心对待你的引用。</p>
<p>函数的纯粹性要求我们时刻提醒自己降低状态数量,把变化留在函数外部。</p>
<h4 id="引用透明:代换"><a href="#引用透明:代换" class="headerlink" title="引用透明:代换"></a>引用透明:代换</h4><p>通过表达式替代(也就是λ演算中讲的归约),可以得到最终数据形态。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384abe27fa21.png"/></div></div>
<p>也就是说,调用一个函数的位置,我们可以使用函数的调用结果来替代此函数调用,产生的结果不变。</p>
<p>一个引用透明的函数调用链永远是可以被合并式代换的。</p>
<h4 id="不可变性:把简单留给自己"><a href="#不可变性:把简单留给自己" class="headerlink" title="不可变性:把简单留给自己"></a>不可变性:把简单留给自己</h4><p>一个函数不应该去改变原有的引用值,避免对运算的其他部分造成影响。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384ac0c5a89b.png"/></div></div>
<p>一个充满变化的世界是混沌的,在函数式编程世界,我们需要强调参数和值的不可变性,甚至在很多时候我们可以为了不改变原来的引用值,牺牲性能以产生新的对象来进行运算。牺牲一部分性能来保证我们程序的每个部分都是可预测的,任意一个对象从创建到消失,它的值应该是固定的。</p>
<p>一个元如果是引用值,请使用一个副本(克隆、复制、替代等方式)来得到状态变更。</p>
<h4 id="高阶:函数抽象和组合"><a href="#高阶:函数抽象和组合" class="headerlink" title="高阶:函数抽象和组合"></a>高阶:函数抽象和组合</h4><p>JS 中用的最多的就是 Array 相关的高阶函数。实际上 Array 是一种 Monad(后面讲解)。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384ac3bb03d8.png"/></div></div>
<p>通过高阶函数传递和修改变量:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384ac5d68209.png"/></div></div>
<p>高阶函数实际上为我们提供了注入环境变量(或者说绑定环境变量)提供了更多可能。React 的高阶组件就从这个思想上借用而来。一个高阶函数就是使用或者产生另一个函数的函数。高阶函数是函数组合(Composition)的一种方式。</p>
<p>高阶函数让我们可以更好地组合业务。常见的高阶函数有:</p>
<ul>
<li>map</li>
<li>compose</li>
<li>fold</li>
<li>pipe</li>
<li>curry</li>
<li>…</li>
</ul>
<h4 id="惰性计算:降低运行时开销"><a href="#惰性计算:降低运行时开销" class="headerlink" title="惰性计算:降低运行时开销"></a>惰性计算:降低运行时开销</h4><p>惰性计算的含义就是在真正调用到的时候才执行,中间步骤不真实执行程序。这样可以让我们在运行时创建很多基础函数,但并不影响实际业务运行速度,唯有业务代码真实调用时才产生开销。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384aca401e7f.png"/></div></div>
<p><code>map(addOne)</code>并不会真实执行<code>+1</code>,只有真实调用<code>exec</code>才执行。当然这个只是一个简单的例子,强大的惰性计算在函数式编程语言中还有很多其他例子。比如:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384acb0302d6.png"/></div></div>
<p>无穷”只是一个字面定义,我们知道计算机是无法定义无穷的数据的,因此数据在take的时候才真实产生。</p>
<p>惰性计算让我们可以无限使用函数组合,在写这些函数组合的过程中并不产生调用。但这种形式,可能会有一个严重的问题,那就是产生一个非常长的调用栈,而虚拟机或者解释器的函数调用栈一般都是有上限的,比如2000个,超过这个限制,函数调用就会栈溢出。虽然函数调用栈过长会引起这个严重的问题,但这个问题其实不是函数式语言设计的逻辑问题,因为调用栈溢出的问题在任何设计不良的程序中都有可能出现,惰性计算只是利用了函数调用栈的特性,而不是一种缺陷。</p>
<p>记住,任何时候我们都可以重构代码,通过良好的设计来解决栈溢出的问题。</p>
<h4 id="类型推导"><a href="#类型推导" class="headerlink" title="类型推导"></a>类型推导</h4><p>当前的JS有TypeScript的加持,也可以算是有类型推导了。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384acddc4fdf.png"/></div></div>
<p>没有类型推导的函数式编程,在使用的时候会很不方便,所有的工具函数都要查表查示例,开发中效率会比较低下,也容易造成错误。</p>
<p>但并不是说一门函数式语言必须要类型推导,这不是强制的。像<strong>Lisp</strong>就没有强制类型推导,JavaScript 也没有强制的类型推导,这不影响他们的成功。只是说,有了类型推导,我们的编译器可以在编译器期间提前捕获错误,甚至在编译之前,写代码的时候就可以发现错误。类型推导是一些语言强调的优秀特性,它确实可以帮助我们提前发现更多的代码问题。像 Rust、Haskell 等。</p>
<h4 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h4><p>你现在也可以总结一些其他的风格或者定义。比如强调函数的组合、强调<strong>Point-Free</strong>的风格等等。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/28/6384aedc01f2f.png"/></div></div>
<h3 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h3><p>函数式有很多基础的特性,熟练地使用这些特性,并加以巧妙地组合,就形成了我们的“函数式编程范式”。这些基础特性让我们对待一个 function,更多地看作函数,而不是一个方法。在许多场景下,使用这种范式进行编程,就像是在做数学推导(或者说是类型推导),它让我们像学习数学一样,把一个个复杂的问题简单化,再进行累加/累积,从而得到结果。</p>
<p>同时,函数式编程还有一块大的领域需要进入,即副作用处理。不处理副作用的程序是毫无意义的(仅在内存中进行计算),下篇我们将深入函数式编程的应用,为我们的工程应用发掘出更多的特性。</p>
</div>
<blockquote>
<p>原文链接:<a
uni-app 整包更新与热更新方案(安卓和IOS)
https://fe32.top/articles/uni10087/
2022-11-27T08:36:23.000Z
2023-05-17T10:01:12.000Z
提供一种uni-app整包更新与热更新的方案,包括APP和后台客户端版本管理。
如何白嫖一个视频云存储?
https://fe32.top/articles/hexo1616/
2022-11-12T08:02:44.000Z
2024-02-23T15:36:41.988Z
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><details class="folding-tag" yellow><summary> 效果预览 </summary>
<div class='content'>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/735570394b43e.jpg"/></div></div>
</div>
</details>
<p>实际视频效果请移步: <a href="https://old.fe32.top/comments/">留言板</a> 、 <a href="https://old.fe32.top/link/">友链</a></p>
<h2 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h2><ol>
<li>登录 <a href="https://cp.kuaishou.com/">快手创作者服务平台</a></li>
<li>上传目标视频<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/14/63723f09c07d6.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/14/63723f7fcf25b.png"/></div></div></li>
<li>填好相关信息(记得公开视频),发布视频。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/14/63723f91c1678.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/14/63723f9e4e252.png"/></div></div></li>
<li>只要不涉及违规内容,一般几分钟内就过审了。我是 21:48 左右发布的,21:50 就通过了。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/14/63723fe451de2.png"/></div></div></li>
<li>点击、播放视频,然后 f12 打开控制台,找到 video 标签的 src 属性的值。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/14/637241501ea95.png"/></div></div>
<blockquote>
<p>链接类似长成这样:<br><a href="https://v213x8lm4u6mmiog26eezs9fet01ayhzb.mobgslb.tbcache.com/v2.kwaicdn.com/upic/2022/11/13/21/BMjAyMjExMTMyMTQxNThfMjk1MTM4ODg1MV84ODY2ODQ2MzE3NV8wXzM=_b_B5980ab28f909cff50f35f4b4b1c8c298.mp4?pkey=AAXiQ8JSCuGGABOk_mqk_wvH4ySvg8HAT78WZCQ1G-jb6U0uQK6D6Tym1DM_CCMXoXC4y8TTZbZj1cwgFhaOYWlrIACZbnoxgc7kxykzJRwvw1wFX0ovoDQFxw2JarNXirM&tag=1-1668349109-unknown-0-wkh6ikmtw8-af102da173c49a79&clientCacheKey=3xbzqru9scddmwi_b.mp4&di=7788c6d6&bp=10004&tt=b&ss=vp&ali_redirect_ex_hot=66666800&ali_redirect_ex_beacon=1">https://v213x8lm4u6mmiog26eezs9fet01ayhzb.mobgslb.tbcache.com/v2.kwaicdn.com/upic/2022/11/13/21/BMjAyMjExMTMyMTQxNThfMjk1MTM4ODg1MV84ODY2ODQ2MzE3NV8wXzM=_b_B5980ab28f909cff50f35f4b4b1c8c298.mp4?pkey=AAXiQ8JSCuGGABOk_mqk_wvH4ySvg8HAT78WZCQ1G-jb6U0uQK6D6Tym1DM_CCMXoXC4y8TTZbZj1cwgFhaOYWlrIACZbnoxgc7kxykzJRwvw1wFX0ovoDQFxw2JarNXirM&tag=1-1668349109-unknown-0-wkh6ikmtw8-af102da173c49a79&clientCacheKey=3xbzqru9scddmwi_b.mp4&di=7788c6d6&bp=10004&tt=b&ss=vp&ali_redirect_ex_hot=66666800&ali_redirect_ex_beacon=1</a><br>【.mp4】后面拼的参数可以去掉。最终变成:<br><a href="https://v213x8lm4u6mmiog26eezs9fet01ayhzb.mobgslb.tbcache.com/v2.kwaicdn.com/upic/2022/11/13/21/BMjAyMjExMTMyMTQxNThfMjk1MTM4ODg1MV84ODY2ODQ2MzE3NV8wXzM=_b_B5980ab28f909cff50f35f4b4b1c8c298.mp4">https://v213x8lm4u6mmiog26eezs9fet01ayhzb.mobgslb.tbcache.com/v2.kwaicdn.com/upic/2022/11/13/21/BMjAyMjExMTMyMTQxNThfMjk1MTM4ODg1MV84ODY2ODQ2MzE3NV8wXzM=_b_B5980ab28f909cff50f35f4b4b1c8c298.mp4</a></p>
</blockquote>
</li>
<li>此链接过一段时间(大概24小时),就会被拒绝访问。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/14/6372437d3ab72.png"/></div></div></li>
<li>最后一步,也是最重要的一步。你只需将 【<a href="https://v213x8lm4u6mmiog26eezs9fet01ayhzb.mobgslb.tbcache.com/v2.kwaicdn.com/upic/2022/11/13/21/BMjAyMjExMTMyMTQxNThfMjk1MTM4ODg1MV84ODY2ODQ2MzE3NV8wXzM=_b_B5980ab28f909cff50f35f4b4b1c8c298.mp4">https://v213x8lm4u6mmiog26eezs9fet01ayhzb.mobgslb.tbcache.com/v2.kwaicdn.com/upic/2022/11/13/21/BMjAyMjExMTMyMTQxNThfMjk1MTM4ODg1MV84ODY2ODQ2MzE3NV8wXzM=_b_B5980ab28f909cff50f35f4b4b1c8c298.mp4</a>】<code>upic</code>之前的链接 【<a href="https://v213x8lm4u6mmiog26eezs9fet01ayhzb.mobgslb.tbcache.com/v2.kwaicdn.com/">https://v213x8lm4u6mmiog26eezs9fet01ayhzb.mobgslb.tbcache.com/v2.kwaicdn.com/</a>】替换成 【<a href="https://txmov2.a.yximgs.com/">https://txmov2.a.yximgs.com/</a>】即可。</li>
<li>最后让我们来看下视频效果:<div class="video"><video controls preload><source src='https://txmov2.a.yximgs.com/upic/2022/11/13/21/BMjAyMjExMTMyMTQxNThfMjk1MTM4ODg1MV84ODY2ODQ2MzE3NV8wXzM=_b_B5980ab28f909cff50f35f4b4b1c8c298.mp4' type='video/mp4'>Your browser does not support the video
飞只因太美,给你的首页装上吧!
https://fe32.top/articles/hexo1615/
2022-11-06T04:45:08.000Z
2024-02-23T15:35:21.199Z
一架会飞的大灰机~
一文吃透 CSS Flex 布局
https://fe32.top/articles/cssnb001/
2022-10-30T14:53:19.000Z
2023-05-17T10:01:12.000Z
Flexbox 布局也叫 Flex 布局。它的主要思想是使父元素能够调整子元素的宽度、高度、排列方式,从而更好的适应可用的布局空间。
优雅的使用Webstack打造个人网址导航
https://fe32.top/articles/we0524bs/
2022-10-30T04:01:01.000Z
2024-03-26T07:54:24.358Z
Webstack一个开源的公益项目,你可以拿来制作自己的网址导航,多种主题版本供你选择,个人选择的是Hexo。
关于Vercel被墙导致获取Twikoo评论失败的解决方案
https://fe32.top/articles/hexo1614/
2022-10-23T14:07:18.000Z
2023-05-17T10:01:12.000Z
由于Vercel官方域名的Dns污染问题,导致在不挂梯子的情况下无法获取Twikoo评论数据。这里提供一种解决方案,需要的请自提!
浅谈性能优化之图片压缩、加载和格式选择
https://fe32.top/articles/we0523bs/
2022-10-23T08:38:14.000Z
2023-05-17T10:01:12.000Z
<p>在认识图片优化前,我们先了解下 【二进制位数】与【色彩呈现】的关系。</p>
<h2 id="二进制位数与色彩"><a href="#二进制位数与色彩" class="headerlink" title="二进制位数与色彩"></a>二进制位数与色彩</h2><p>在计算机中,一般用二进制数来表示像素。在不同的图片格式中,像素与二进制位数之间对应的关系是不同的。一个像素对应的二进制位数越多,它能表示的颜色种类就丰富,成像效果也就越精致,图片所需的存储空间相应也会越大。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/10/23/6355019ae9db4.png"/></div></div>
<p>目前市场上优化图片资源的方式有很多,如压缩图片、选择正确格式、 CDN 加速、懒加载等。</p>
<h2 id="压缩图片"><a href="#压缩图片" class="headerlink" title="压缩图片"></a>压缩图片</h2><p>压缩图片相信是大家第一时间想到的方案。像我们比较熟悉的 <a href="https://tinypng.com/">tinpng</a>,他的原理是通过有”选择性”地减少图像所要存储的颜色数量,来减少图片所要存储的内存。</p>
<blockquote>
<p>“ When you upload a PNG (Portable Network Graphics) file, similar colors in your image are combined. This technique is called “quantization”. By reducing the number of colors, 24-bit PNG files can be converted to much smaller 8-bit indexed color images. ”</p>
</blockquote>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/10/23/6355019d177bd.png"/></div></div>
<p>我们来看下样例:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/10/23/6355087a5e6d5.png"/></div></div>
<p>细节展示:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/10/23/635501a1f3d53.png"/></div></div>
<h2 id="图片格式"><a href="#图片格式" class="headerlink" title="图片格式"></a>图片格式</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/10/23/6355131a9d264.png" alt="思维导图"/></div><span class="image-caption">思维导图</span></div>
<h3 id="JPEG-x2F-JPG"><a href="#JPEG-x2F-JPG" class="headerlink" title="JPEG / JPG"></a>JPEG / JPG</h3><p>JPEG 是最常用的图像文件格式。</p>
<h4 id="优势"><a href="#优势" class="headerlink" title="优势"></a>优势</h4><ul>
<li>支持极高的压缩率,可使文件传输、下载、预览速度大大加快。</li>
<li>利用可变的压缩比可以控制文件大小。</li>
<li>能够轻松地处理 1600 万种颜色,可以很好地再现全彩色的图像。</li>
</ul>
<h4 id="缺陷"><a href="#缺陷" class="headerlink" title="缺陷"></a>缺陷</h4><p>JPG 的有损压缩在 <strong>轮播图</strong> 和 <strong>背景图</strong> 的展示上确实很难看出破绽,但当它处理矢量图形和 Logo 等线条感较强、颜色对比强烈的图像时,人为压缩导致的 <strong>图片模糊</strong> 会相当明显。因此不适宜用该格式来显示 <strong>高清晰度</strong> 和 <strong>线条感较强</strong> 的图像。</p>
<p>除此之外, JPG 并不支持对有透明度要求的图像进行显示,如果需要显示 <strong>透明图片</strong> 还是需要另寻它路。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/10/23/635501a4c52bb.png"/></div></div>
<h4 id="业务场景"><a href="#业务场景" class="headerlink" title="业务场景"></a>业务场景</h4><p>JPG 适用于呈现色彩丰富的图片,在我们日常开发中,JPG 图片经常作为大的 <strong>背景图</strong>、<strong>轮播图</strong>或 <strong>预览图</strong> 出现。打开某电商网站首页,即可看到大图片的处理几乎都是使用了 JPG。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/10/23/635501a6b0034.png"/></div></div>
<h3 id="PNG-8-与-PNG-24"><a href="#PNG-8-与-PNG-24" class="headerlink" title="PNG-8 与 PNG-24"></a>PNG-8 与 PNG-24</h3><p>png 是一种采用无损压缩算法的位图格式。</p>
<h4 id="优势-1"><a href="#优势-1" class="headerlink" title="优势"></a>优势</h4><ul>
<li>无损压缩</li>
<li>完全支持 alpha 透明度。</li>
<li>可以重复保存且不降低图像质量。</li>
</ul>
<h4 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h4><p>体积太大</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/10/23/6355096610cf0.png"/></div></div>
<h4 id="业务场景-1"><a href="#业务场景-1" class="headerlink" title="业务场景"></a>业务场景</h4><p>理论上来说,当你追求最佳的显示效果(详情展示图、图片有放大需求、摄影作品等),并且不在意存储大小或所需带宽时,可以使用 <a href="https://baike.baidu.com/item/PNG/174154?fr=aladdin">PNG-24</a>。但实践当中,为了避免文件体积过大的问题,我们一般不用 PNG 去处理较复杂的图像。当我们遇到适合 PNG 的场景时,也会优先选择更为小巧的 PNG-8 。</p>
<p>亦或者需要处理有透明度或线条明显的图片时,也会采用 PNG 。如网站主 logo:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/10/23/6355096a53689.png"/></div></div>
<h3 id="SVG"><a href="#SVG" class="headerlink" title="SVG"></a>SVG</h3><p>严格来说应该是一种开放标准的矢量图形语言。</p>
<h4 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h4><ul>
<li>可缩放,可支持无限放大</li>
<li>可编程</li>
</ul>
<h4 id="缺点-1"><a href="#缺点-1" class="headerlink" title="缺点"></a>缺点</h4><ul>
<li>不是所有的浏览器都支持 SVG,IE8 和早期版本都需要一个插件。</li>
<li>复杂的图片会降低渲染速度(只支持小图)。</li>
</ul>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/10/23/6355096e70295.png"/></div></div>
<h4 id="业务场景-2"><a href="#业务场景-2" class="headerlink" title="业务场景"></a>业务场景</h4><p>SVG 是文本文件,我们既可以像写代码一样定义 SVG ,把它写在 HTML 里、成为 DOM 的一部分。用的比较多的就是 <a href="https://www.iconfont.cn/">iconfont</a>。我们可以通过设置模块的<code>fill</code>属性轻松适配图标的换肤功能,并通过<code>font-size</code>调节其大小。</p>
<h3 id="Base64"><a href="#Base64" class="headerlink" title="Base64"></a>Base64</h3><p>一种基于 64 个可打印字符来表示二进制数据的方法。</p>
<h4 id="优点-1"><a href="#优点-1" class="headerlink" title="优点"></a>优点</h4><ul>
<li>减少网络请求</li>
<li>对于动态实时生成的图片无需将图片存储在服务器占用服务器资源</li>
</ul>
<h4 id="缺点-2"><a href="#缺点-2" class="headerlink" title="缺点"></a>缺点</h4><ul>
<li>只适于小图。</li>
<li>若需要频繁替换的图片需要整个代码替换,可维护性低。</li>
</ul>
<h4 id="业务场景-3"><a href="#业务场景-3" class="headerlink" title="业务场景"></a>业务场景</h4><p>Base64 和雪碧图一样,是作为小图标解决方案而存在的。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/10/23/63550972c22e4.png"/></div></div>
<blockquote>
<p>“ Base64 是一种用于传输 8Bit 字节码的编码方式,通过对图片进行 Base64 编码,我们可以直接将编码结果写入 HTML 或者写入 CSS ,从而减少 HTTP 请求的次数。”</p>
</blockquote>
<p>在 Elements 中搜索 “base64” 关键字,你会发现 Base64 也有很多使用的地方。而且它对应的图片占用内存较小。</p>
<p>既然 Base64 这么棒,我们把所有图片都用Base64 好了嘛。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/10/23/6355097710675.png"/></div></div>
<p>Base64 编码后,图片大小会膨胀为原文件的 4/3(<a href="https://blog.csdn.net/wo541075754/article/details/81734770">Base64 编码原理</a>)。如果我们把大图也编码到 HTML 或 CSS 文件中,后者的体积会明显增加,即便我们减少了 HTTP 请求,也无法弥补这庞大的体积带来的性能开销。也就是说我们牺牲的 <strong>渲染性能</strong> 大于 <strong>资源请求性能</strong>,这样做不太值得。</p>
<p>我们可以看到,大多数用 Base64 编码的图片都是小图。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/10/23/6355097a2861a.png"/></div></div>
<h3 id="WebP"><a href="#WebP" class="headerlink" title="WebP"></a>WebP</h3><p>一种同时提供了有损压缩与无损压缩(可逆压缩)的图片文件格式。</p>
<h4 id="优点-2"><a href="#优点-2" class="headerlink" title="优点"></a>优点</h4><ul>
<li>支持有损无损</li>
<li>占用体积小</li>
<li>可支持透明</li>
</ul>
<h4 id="缺点-3"><a href="#缺点-3" class="headerlink" title="缺点"></a>缺点</h4><ul>
<li>兼容性不好</li>
</ul>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/10/23/63551310ab344.png"/></div></div>
<h4 id="业务场景-4"><a href="#业务场景-4" class="headerlink" title="业务场景"></a>业务场景</h4><p>同 JPEG/JPG 。因为目前兼容性不好,一般搭配 JPEG/JPG 一起使用。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/10/23/635513172207c.png"/></div></div>
<h2 id="OSS-搭配-CDN"><a href="#OSS-搭配-CDN" class="headerlink" title="OSS 搭配 CDN"></a>OSS 搭配 CDN</h2><p>我们原始的方式是将图片等资源一起放入项目中打包上线。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/10/23/6355131d0ce12.png"/></div></div>
<p>这样做的缺点在于打包出来的包大不说,用户请求资源的速度也会受到限制。比如我们的服务器在华南,华北的用户请求就会稍慢。当遇到并发量大的情况时,从部署服务器请求接口与资源这无外乎给我们的服务器提供了多余的压力。当我们临时想替换一张图片时,也需要重新打包并发布上线,非常麻烦。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/10/23/6355131fcb0e2.png"/></div></div>
<p>当我们将图片进行 OSS 放置并 CDN 加速后,这个问题就得到了很好的解决。不同地区的用户可以访问就近服务器,重复的请求也会产生缓存,避免 OSS 流量的浪费。大家也可以参考这篇文章:<a href="https://www.cnblogs.com/jsfh/p/14076992.html">OSS 和 CDN 的区别</a></p>
<h2 id="图片的懒加载"><a href="#图片的懒加载" class="headerlink" title="图片的懒加载"></a>图片的懒加载</h2><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><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></pre></td><td class="code"><pre><span class="line"><span class="variable language_">window</span>.<span class="property">onload</span> = <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="comment">// 获取图片列表,即 img 标签列表</span></span><br><span class="line"> <span class="keyword">var</span> imgs = <span class="variable language_">document</span>.<span class="title function_">querySelectorAll</span>(<span class="string">'img'</span>);</span><br><span class="line"> <span class="comment">// 获取到浏览器顶部的距离</span></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">getTop</span>(<span class="params">e</span>) {</span><br><span class="line"> <span class="keyword">return</span> e.<span class="property">offsetTop</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 懒加载实现</span></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">lazyload</span>(<span class="params">imgs</span>) {</span><br><span class="line"> <span class="comment">// 可视区域高度</span></span><br><span class="line"> <span class="keyword">var</span> h = <span class="variable language_">window</span>.<span class="property">innerHeight</span>;</span><br><span class="line"> <span class="comment">// 滚动区域高度</span></span><br><span class="line"> <span class="keyword">var</span> s = <span class="variable language_">document</span>.<span class="property">documentElement</span>.<span class="property">scrollTop</span> || <span class="variable language_">document</span>.<span class="property">body</span>.<span class="property">scrollTop</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < imgs.<span class="property">length</span>; i++) {</span><br><span class="line"> <span class="comment">//图片距离顶部的距离大于可视区域和滚动区域之和时懒加载</span></span><br><span class="line"> <span class="keyword">if</span> ((h + s) > <span class="title function_">getTop</span>(imgs[i])) {</span><br><span class="line"> <span class="comment">// 真实情况是页面开始有2秒空白,所以使用 setTimeout 定时 2s</span></span><br><span class="line"> (<span class="keyword">function</span> (<span class="params">i</span>) {</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="comment">// 不加立即执行函数i会等于9</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="keyword">var</span> temp = <span class="keyword">new</span> <span class="title class_">Image</span>();</span><br><span class="line"> temp.<span class="property">src</span> = imgs[i].<span class="title function_">getAttribute</span>(<span class="string">'data-src'</span>);<span class="comment">//只会请求一次</span></span><br><span class="line"> <span class="comment">// onload 判断图片加载完毕,真是图片加载完毕,再赋值给 dom 节点</span></span><br><span class="line"> temp.<span class="property">onload</span> = <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="comment">// 获取自定义属性 data-src,用真图片替换假图片</span></span><br><span class="line"> imgs[i].<span class="property">src</span> = imgs[i].<span class="title function_">getAttribute</span>(<span class="string">'data-src'</span>)</span><br><span class="line"> }</span><br><span class="line"> }, <span class="number">2000</span>)</span><br><span class="line"> })(i)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="title function_">lazyload</span>(imgs);</span><br><span class="line"> <span class="comment">// 滚屏函数</span></span><br><span class="line"> <span class="variable language_">window</span>.<span class="property">onscroll</span> = <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="title function_">lazyload</span>(imgs);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<blockquote>
<p>参考链接:<br><a href="https://mp.weixin.qq.com/s/IQ1Sm_pqzCOgcM59tsUlXQ">性能优化——图片压缩、加载和格式选择</a><br><a href="https://developer.aliyun.com/article/770616?utm_content=g_1000173381">使用阿里云 CDN 加速 OSS
推荐几款好看又好用的开源博客
https://fe32.top/articles/blsh9527/
2022-10-14T03:13:37.000Z
2024-02-24T07:21:50.917Z
分享几款好看又好用的纯静态博客开源博客,无需后端,易上手(有计算机基础),克隆项目,安装依赖,打包部署即可看到效果。
V8 是如何执行 JavaScript 代码的?
https://fe32.top/articles/jsnb9545/
2022-09-25T13:23:31.000Z
2023-05-17T10:01:12.000Z
<h2 id="JS-代码执行过程"><a href="#JS-代码执行过程" class="headerlink" title="JS 代码执行过程"></a>JS 代码执行过程</h2><p>在了解 V8 的执行 JavaScript 代码的机制之前,我们先来看看<strong>编译型</strong>和<strong>解释型</strong>语言的区别。</p>
<h3 id="编译型语言和解释型语言"><a href="#编译型语言和解释型语言" class="headerlink" title="编译型语言和解释型语言"></a>编译型语言和解释型语言</h3><p>机器是不能直接理解代码的。所以,在执行程序之前,需要将代码翻译成机器能读懂的机器语言。按语言的执行流程,可以把计算机语言划分为编译型语言和解释型语言: </p>
<ul>
<li><strong>编译型语言</strong>: 在代码运行前编译器直接将对应的代码转换成机器码,运行时不需要再重新翻译,直接可以使用编译后的结果</li>
<li><strong>解释型语言</strong>: 需要将代码转换成机器码,和编译型语言的区别在于运行时需要转换。解释型语言的执行速度要慢于编译型语言,因为解释型语言每次执行都需要把源码转换一次才能执行。</li>
</ul>
<p>Java 和 C++ 等语言都是编译型语言,而 JavaScript 是解释性语言,它整体的执行速度会略慢于编译型的语言。V8 是众多浏览器的 JS 引擎中性能表现最好的一个,并且它是 Chrome 的内核,Node.js 也是基于 V8 引擎研发的。</p>
<p>编译型语言和解释器语言代码执行的具体流程如下: </p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/09/25/6330667876afb.png"/></div></div>
<p>两者的执行流程如下: </p>
<ul>
<li>在编译型语言的编译过程中,编译器首先会依次对源代码进行词法分析、语法分析,生成抽象语法树(AST),然后优化代码,最后再生成处理器能够理解的机器码。如果编译成功,将会生成一个可执行的文件。但如果编译过程发生了语法或者其他的错误,那么编译器就会抛出异常,最后的二进制文件也不会生成成功。</li>
<li>在解释型语言的解释过程中,同样解释器也会对源代码进行词法分析、语法分析,并生成抽象语法树(AST),不过它会再基于抽象语法树生成字节码,最后再根据字节码来执行程序、输出结果。</li>
</ul>
<h3 id="V8-执行代码过程"><a href="#V8-执行代码过程" class="headerlink" title="V8 执行代码过程"></a>V8 执行代码过程</h3><p>V8 在执行过程用到了 <strong>解释器</strong> 和 <strong>编译器</strong> 。 其执行过程如下: </p>
<ol>
<li>Parse 阶段: V8 引擎将 JS 代码转换成 AST(抽象语法树)</li>
<li>Ignition 阶段: 解释器将 AST 转换为字节码,解析执行字节码也会为下一个阶段优化编译提供需要的信息</li>
<li>TurboFan 阶段: 编译器利用上个阶段收集的信息,将字节码优化为可以执行的机器码</li>
<li>Orinoco 阶段: 垃圾回收阶段,将程序中不再使用的内存空间进行回收</li>
</ol>
<p>这里前三个步骤是 JavaScript 的执行过程,最后一步是垃圾回收的过程。下面就先来看看V8 执行 JavaScript 的过程。</p>
<h4 id="生成抽象语法树"><a href="#生成抽象语法树" class="headerlink" title="生成抽象语法树"></a>生成抽象语法树</h4><p>这个过程就是将源代码转换为抽象语法树(AST),并生成执行上下文,执行上下文就是代码在执行过程中的环境信息。</p>
<p>将 JS 代码解析成 AST主要分为两个阶段: </p>
<ol>
<li>词法分析: 这个阶段会将源代码拆成最小的、不可再分的词法单元,称为 token。比如代码 var a = 1; 通常会被分解成 var 、a、=、1、; 这五个词法单元。代码中的空格在 JavaScript 中是直接忽略的,简单来说就是将 JavaScript 代码解析成一个个令牌(Token)。</li>
<li>语法分析: 这个过程是将上一步生成的 token 数据,根据语法规则转为 AST。如果源码符合语法规则,这一步就会顺利完成。如果源码存在语法错误,这一步就会终止,并抛出一个语法错误,简单来说就是将令牌组装成一棵抽象的语法树(AST)。</li>
</ol>
<p>通过词法分析会对代码逐个字符进行解析,生成类似下面结构的令牌(Token),这些令牌类型各不相同,有关键字、标识符、符号、数字等。代码 var a = 1; 会转化为下面这样的令牌: </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="title class_">Keyword</span>(<span class="keyword">var</span>)</span><br><span class="line"><span class="title class_">Identifier</span>(name)</span><br><span class="line"><span class="title class_">Punctuator</span>(=)</span><br><span class="line"><span class="title class_">Number</span>(<span class="number">1</span>)</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 第一段代码</span></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">1</span>;</span><br><span class="line"><span class="comment">// 第二段代码</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">sum</span> (a,b) {</span><br><span class="line"> <span class="keyword">return</span> a + b;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>将这两段代码分别转换成 AST 抽象语法树之后返回的 JSON 如下: </p>
<p>第一段代码,编译后的结果: </p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="string">"Program"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"end"</span><span class="punctuation">:</span> <span class="number">10</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"body"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="string">"VariableDeclaration"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"end"</span><span class="punctuation">:</span> <span class="number">10</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"declarations"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="string">"VariableDeclarator"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start"</span><span class="punctuation">:</span> <span class="number">4</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"end"</span><span class="punctuation">:</span> <span class="number">9</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="string">"Identifier"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start"</span><span class="punctuation">:</span> <span class="number">4</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"end"</span><span class="punctuation">:</span> <span class="number">5</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"a"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"init"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="string">"Literal"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start"</span><span class="punctuation">:</span> <span class="number">8</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"end"</span><span class="punctuation">:</span> <span class="number">9</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"value"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"raw"</span><span class="punctuation">:</span> <span class="string">"1"</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"kind"</span><span class="punctuation">:</span> <span class="string">"var"</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"sourceType"</span><span class="punctuation">:</span> <span class="string">"module"</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure>
<p>它的结构大致如下: </p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/09/25/6330667b36b44.png"/></div></div>
<p>第二段代码,编译出来的结果: </p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="string">"Program"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"end"</span><span class="punctuation">:</span> <span class="number">38</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"body"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="string">"FunctionDeclaration"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"end"</span><span class="punctuation">:</span> <span class="number">38</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="string">"Identifier"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start"</span><span class="punctuation">:</span> <span class="number">9</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"end"</span><span class="punctuation">:</span> <span class="number">12</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"sum"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"expression"</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"generator"</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"async"</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"params"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="string">"Identifier"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start"</span><span class="punctuation">:</span> <span class="number">14</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"end"</span><span class="punctuation">:</span> <span class="number">15</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"a"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="string">"Identifier"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start"</span><span class="punctuation">:</span> <span class="number">16</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"end"</span><span class="punctuation">:</span> <span class="number">17</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"b"</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"body"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="string">"BlockStatement"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start"</span><span class="punctuation">:</span> <span class="number">19</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"end"</span><span class="punctuation">:</span> <span class="number">38</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"body"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="string">"ReturnStatement"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start"</span><span class="punctuation">:</span> <span class="number">23</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"end"</span><span class="punctuation">:</span> <span class="number">36</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"argument"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="string">"BinaryExpression"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start"</span><span class="punctuation">:</span> <span class="number">30</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"end"</span><span class="punctuation">:</span> <span class="number">35</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"left"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="string">"Identifier"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start"</span><span class="punctuation">:</span> <span class="number">30</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"end"</span><span class="punctuation">:</span> <span class="number">31</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"a"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"operator"</span><span class="punctuation">:</span> <span class="string">"+"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"right"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="string">"Identifier"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start"</span><span class="punctuation">:</span> <span class="number">34</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"end"</span><span class="punctuation">:</span> <span class="number">35</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"b"</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">]</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"sourceType"</span><span class="punctuation">:</span> <span class="string">"module"</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure>
<p>它的结构大致如下: </p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/09/25/6330667e5988f.png"/></div></div>
<p>可以看到,AST 只是源代码语法结构的一种抽象的表示形式,计算机也不会去直接去识别 JS 代码,转换成抽象语法树也只是识别这一过程中的第一步。AST 的结构和代码的结构非常相似,其实也可以把 AST 看成代码的结构化的表示,编译器或者解释器后续的工作都需要依赖于 AST。</p>
<p><strong>AST的应用场景:</strong> </p>
<p>AST 是一种很重要的数据结构,很多地方用到了AST。比如在 Babel 中,Babel 是一个代码转码器,可以将 ES6 代码转为 ES5 代码。Babel 的工作原理就是先将 ES6 源码转换为 AST,然后再将 ES6 语法的 AST 转换为 ES5 语法的 AST,最后利用 ES5 的 AST 生成 JavaScript 源代码。</p>
<p>除了 Babel 之外,ESLint 也使用到了 AST。ESLint 是一个用来检查 JavaScript 编写规范的插件,其检测流程也是需要将源码转换为 AST,然后再利用 AST 来检查代码规范化的问题。</p>
<p>除了上述应用场景,AST 的应用场景还有很多: </p>
<ul>
<li>JS 反编译,语法解析</li>
<li>代码高亮</li>
<li>关键字匹配</li>
<li>代码压缩。</li>
</ul>
<h4 id="生成字节码"><a href="#生成字节码" class="headerlink" title="生成字节码"></a>生成字节码</h4><p>有了 抽象语法树 AST 和执行上下文后,就轮到解释器就登场了,它会根据 AST 生成字节码,并解释执行字节码。</p>
<p>在 V8 的早期版本中,是通过 AST 直接转换成机器码的。将 AST 直接转换为机器码会存在一些问题: </p>
<ul>
<li>直接转换会带来内存占用过大的问题,因为将抽象语法树全部生成了机器码,而机器码相比字节码占用的内存多了很多; </li>
<li>某些 JavaScript 使用场景使用解释器更为合适,解析成字节码,有些代码没必要生成机器码,进而尽可能减少了占用内存过大的问题。</li>
</ul>
<p>为了解决内存占用问题,就在 V8 引擎中引入了字节码。那什么是字节码呢?为什么引入字节码就能解决内存占用问题呢?</p>
<span class='p blue'>字节码就是介于 AST 和机器码之间的一种代码。</span>需要将其转换成机器码后才能执行,字节码是对机器码的一个抽象描述,相对于机器码而言,它的代码量更小,从而可以减少内存消耗。解释器除了可以快速生成没有优化的字节码外,还可以执行部分字节码。
<h4 id="生成机器码"><a href="#生成机器码" class="headerlink" title="生成机器码"></a>生成机器码</h4><p>生成字节码之后,就进入执行阶段了,实际上,<span class='p blue'>这一步就是将字节码生成机器码。</span></p>
<p>一般情况下,如果字节码是第一次执行,那么解释器就会逐条解释执行。在执行字节码过程中,如果发现有热代码(重复执行的代码,运行次数超过某个阈值就被标记为热代码),那么后台的编译器就会把该段热点的字节码编译为高效的机器码,然后当再次执行这段被优化的代码时,只需要执行编译后的机器码即可,这样提升了代码的执行效率。</p>
<p>字节码配合解释器和编译器的技术就是 即时编译(JIT)。在 V8 中就是指解释器在解释执行字节码的同时,收集代码信息,当它发现某一部分代码变热了之后,编译器便闪亮登场,把热点的字节码转换为机器码,并把转换后的机器码保存起来,以备下次使用。</p>
<p>因为 V8 引擎是多线程的,编译器的编译线程和生成字节码不会在同一个线程上,这样可以和解释器相互配合着使用,不受另一方的影响。下面是JIT技术的工作机制: </p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/09/25/6330668170580.png"/></div></div>
<p>解释器在得到 AST 之后,会按需进行解释和执行。也就是说如果某个函数没有被调用,则不会去解释执行它。在这个过程中解释器会将一些重复可优化的操作收集起来生成分析数据,然后将生成的字节码和分析数据传给编译器,编译器会依据分析数据来生成高度优化的机器码。</p>
<p>优化后的机器码的作用和缓存很类似,当解释器再次遇到相同的内容时,就可以直接执行优化后的机器码。当然优化后的代码有时可能会无法运行(比如函数参数类型改变),那么会再次反优化为字节码交给解释器。</p>
<p>整个过程如下图所示: </p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/09/25/6330668595a0e.png"/></div></div>
<h4 id="执行过程优化"><a href="#执行过程优化" class="headerlink" title="执行过程优化"></a>执行过程优化</h4><p>如果 JavaScript 代码在执行前都要完全经过解析才能执行,那可能会面临以下问题: </p>
<ul>
<li>代码执行时间变长: 一次性解析所有代码会增加代码的运行时间</li>
<li>消耗更多内存: 解析完的 AST 以及根据 AST 编译后的字节码都会存放在内存中,会占用更多内存空间</li>
<li>占用磁盘空间: 编译后的代码会缓存在磁盘上,占用磁盘空间</li>
</ul>
<p>所以,V8 引擎使用了<span class='p blue'>延迟解析</span>: 在解析过程中,对于不是立即执行的函数,只进行预解析; 只有当函数调用时,才对函数进行全量解析。</p>
<p>进行预解析时,只验证函数语法是否有效、解析函数声明、确定函数作用域,不生成 AST,而实现预解析的,就是 Pre-Parser 解析器。</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="keyword">function</span> <span class="title function_">sum</span>(<span class="params">a, b</span>) {</span><br><span class="line"> <span class="keyword">return</span> a + b;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">const</span> a = <span class="number">666</span>;</span><br><span class="line"><span class="keyword">const</span> c = <span class="number">996</span>;</span><br><span class="line"><span class="title function_">sum</span>(<span class="number">1</span>, <span class="number">1</span>);</span><br></pre></td></tr></table></figure>
<p>V8 解析器是从上往下解析代码的,当解析器遇到函数声明 sum 时,发现它不是立即执行,所以会用 Pre-Parser 解析器对其预解析,过程中只会解析函数声明,不会解析函数内部代码,不会为函数内部代码生成 AST。</p>
<p>之后解释器会把 AST 编译为字节码并执行,解释器会按照自上而下的顺序执行代码,先执行 const a = 666; 和 const c = 996; ,然后执行函数调用 sum(1, 1) ,这时 Parser 解析器才会继续解析函数内的代码、生成 AST,再交给解释器编译执行。</p>
<h2 id="垃圾回收"><a href="#垃圾回收" class="headerlink" title="垃圾回收"></a>垃圾回收</h2><h3 id="JS-内存管理机制"><a href="#JS-内存管理机制" class="headerlink" title="JS 内存管理机制"></a>JS 内存管理机制</h3><p>计算机程序语言都运行在对应的代码引擎上,使用内存过程可以分为以下三个步骤: </p>
<ol>
<li>分配所需要的系统内存空间</li>
<li>使用分配到的内存进行读或写等操作</li>
<li>不需要使用内存时,将其空间释放或者归还</li>
</ol>
<p>在 JavaScript 中,当创建变量时,系统会自动给对象分配对应的内存,来看下面的例子: </p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">123</span>; <span class="comment">// 给数值变量分配栈内存</span></span><br><span class="line"><span class="keyword">var</span> etf = <span class="string">"ARK"</span>; <span class="comment">// 给字符串分配栈内存</span></span><br><span class="line"><span class="comment">// 给对象及其包含的值分配堆内存</span></span><br><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'tom'</span>,</span><br><span class="line"> <span class="attr">age</span>: <span class="number">13</span></span><br><span class="line">}; </span><br><span class="line"><span class="comment">// 给数组及其包含的值分配内存</span></span><br><span class="line"><span class="keyword">var</span> a = [<span class="number">1</span>, <span class="literal">null</span>, <span class="string">"str"</span>]; </span><br><span class="line"><span class="comment">// 给函数分配内存</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">sum</span>(<span class="params">a, b</span>){</span><br><span class="line"> <span class="keyword">return</span> a + b;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>JavaScript 中的数据分为两类: </p>
<ul>
<li>基本类型: 这些类型在内存中会占据固定的内存空间,它们的值都保存在栈空间中,直接可以通过值来访问这些</li>
<li>引用类型: 由于引用类型值大小不固定,栈内存中存放地址指向堆内存中的对象,是通过引用来访问的</li>
</ul>
<p>栈内存中的基本类型,可以通过操作系统直接处理; 而堆内存中的引用类型,正是由于可以经常变化,大小不固定,因此需要 JavaScript 的引擎通过垃圾回收机制来处理。所谓的垃圾回收是指: <span class='p blue'>JavaScript代码运行时,需要分配内存空间来储存变量和值。当变量不在参与运行时,就需要系统收回被占用的内存空间。</span>Javascript 具有自动垃圾回收机制,会定期对那些不再使用的变量、对象所占用的内存进行释放,原理就是找到不再使用的变量,然后释放掉其占用的内存。</p>
<p>JavaScript中存在两种变量: 局部变量和全局变量。全局变量的生命周期会持续要页面卸载; 而局部变量声明在函数中,它的生命周期从函数执行开始,直到函数执行结束,在这个过程中,局部变量会在堆或栈中存储它们的值,当函数执行结束后,这些局部变量不再被使用,它们所占有的空间就会被释放。不过,当局部变量被外部函数使用时,其中一种情况就是闭包,在函数执行结束后,函数外部的变量依然指向函数内部的局部变量,此时局部变量依然在被使用,所以不会回收。</p>
<h3 id="V8-垃圾回收过程"><a href="#V8-垃圾回收过程" class="headerlink" title="V8 垃圾回收过程"></a>V8 垃圾回收过程</h3><p>先来看看 Chrome 浏览器的垃圾回收过程: </p>
<p><strong>① 通过 GC Root 标记空间中活动对象和⾮活动对象</strong> </p>
<p>⽬前 V8 采⽤的 <span class='p blue'>可访问性</span> 算法来判断堆中的对象是否是活动对象。这个算法是将⼀些 GC Root 作为初始存活的对象的集合,从 GC Roots 对象出发,遍历 GC Root 中所有对象: </p>
<ul>
<li>通过 GC Root 遍历到的对象是<span class='p blue'>可访问的</span>,必须保证这些对象应该在内存中保留,可访问的对象称为<span class='p blue'>活动对象</span></li>
<li>通过 GC Roots 没有遍历到的对象是<span class='p blue'>不可访问的</span>,这些不可访问的对象就可能被回收,不可访问的对象称为<span class='p blue'>⾮活动对象</span></li>
</ul>
<p><strong>② 回收⾮活动对象所占据的内存</strong></p>
<p>在所有的标记完成之后,统⼀清理内存中所有被标记为可回收的对象。</p>
<p><strong>③ 内存整理</strong></p>
<p>⼀般来说,频繁回收对象后,内存中就会存在⼤量不连续空间,这些不连续的内存空间称为内存碎⽚。当内存中出现了⼤量的内存碎⽚之后,如果需要分配较⼤的连续内存时,就有可能出现内存不⾜的情况,所以最后⼀步需要整理这些内存碎⽚。这步其实是可选的,因为有的垃圾回收器不会产⽣内存碎⽚。<br>以上就是⼤致的垃圾回收流程。⽬前 V8 使用了两个垃圾回收器: <span class='p blue'>主垃圾回收器和副垃圾回收器</span>。下面就来看看 V8 是如何实现垃圾回收的。<br>在 V8 中,<span class='p blue'>会把堆分为新生代和老生代两个区域,新生代中存放的是生存时间短的对象,老生代中存放生存时间久的对象:</span></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/09/25/633066878cf39.png"/></div></div>
<p>新⽣代通常只⽀持 1~8M 的容量,⽽⽼⽣代⽀持的容量就⼤很多。对于这两块区域,V8分别使⽤两个不同的垃圾回收器,以便更⾼效地实施垃圾回收: </p>
<ul>
<li>副垃圾回收器: 负责新⽣代的垃圾回收</li>
<li>主垃圾回收器: 负责⽼⽣代的垃圾回</li>
</ul>
<blockquote>
<p>副垃圾回收器(新生代)</p>
</blockquote>
<p>副垃圾回收器主要负责新⽣代的垃圾回收。大多数的对象最开始都会被分配在新生代,该存储空间相对较小,分为两个空间: from 空间(对象区)和 to 空间(空闲区)。</p>
<p>新加⼊的对象都会存放到对象区域,当对象区域快被写满时,就需要执⾏⼀次垃圾清理操作: 首先要对对象区域中的垃圾做标记,标记完成之后,就进入垃圾清理阶段。副垃圾回收器会把这些存活的对象复制到空闲区域中,同时它还会把这些对象有序地排列起来。这个复制过程就相当于完成了内存整理操作,复制后空闲区域就没有内存碎片了: </p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/09/25/6330668a7d0eb.png"/></div></div>
<p>完成复制后,对象区域与空闲区域进行角色翻转,也就是原来的对象区域变成空闲区域,原来的空闲区域变成了对象区域,这种算法称之为 Scavenge 算法,这样就完成了垃圾对象的回收操作。同时,这种角色翻转的操作还能让新生代中的这两块区域无限重复使用下去: </p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/09/25/6330668c678ac.png"/></div></div>
<p>不过,副垃圾回收器每次执⾏清理操作时,都需要将存活的对象从对象区域复制到空闲区域,复制操作需要时间成本,如果新⽣区空间设置得太⼤了,那么每次清理的时间就会过久,所以<span class='p blue'>为了执⾏效率,⼀般新⽣区的空间会被设置得⽐较⼩</span>。也正是因为新⽣区的空间不⼤,所以很容易被存活的对象装满整个区域,副垃圾回收器⼀旦监控对象装满了,便执⾏垃圾回收。同时,副垃圾回收器还会采⽤对象晋升策略,也就是移动那些经过两次垃圾回收依然还存活的对象到⽼⽣代中。</p>
<blockquote>
<p>主垃圾回收器(老生代)</p>
</blockquote>
<p>主垃圾回收器主要负责⽼⽣代中的垃圾回收。除了新⽣代中晋升的对象,⼀些⼤的对象会直接被分配到⽼⽣代⾥。因此,⽼⽣代中的对象有两个特点: </p>
<ul>
<li>对象占⽤空间⼤</li>
<li>对象存活时间⻓</li>
</ul>
<p>由于⽼⽣代的对象⽐较⼤,若要在⽼⽣代中使⽤ Scavenge 算法进⾏垃圾回收,复制这些⼤的对象将会花费较多时间,从⽽导致回收执⾏效率不⾼,同时还会浪费空间。所以,主垃圾回收器采⽤标记清除的算法进⾏垃圾回收。</p>
<p>这种方式分为标记和清除两个阶段: </p>
<ul>
<li>标记阶段: 从一组根元素开始,递归遍历这组根元素,在这个遍历过程中,能到达的元素称为活动对象,没有到达的元素就可以判断为垃圾数据</li>
<li>清除阶段: 主垃圾回收器会直接将标记为垃圾的数据清理掉</li>
</ul>
<p>这两个阶段如图所示: </p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/09/25/63306694906a2.png"/></div></div>
<p>对垃圾数据进⾏标记,然后清除,这就是标记清除算法,不过对⼀块内存多次执⾏标记清除算法后,会产⽣⼤量不连续的内存碎⽚。⽽碎⽚过多会导致⼤对象⽆法分配到⾜够的连续内存,于是⼜引⼊了另外⼀种算法——标记整理。</p>
<p>这个算法的标记过程仍然与标记清除算法⾥的是⼀样的,先标记可回收对象,但后续步骤不是直接对可回收对象进⾏清理,⽽是让所有存活的对象都向⼀端移动,然后直接清理掉这⼀端之外的内存: </p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/09/25/633066971a699.png"/></div></div>
<blockquote>
<p>全停顿 </p>
</blockquote>
<p>我们知道,JavaScript 是单行线语言,运行在主线程上。一旦执行垃圾回收算法,都需要将正在执行的 JavaScript 脚本暂停下来,待垃圾回收完毕后再恢复脚本执行。这种行为叫做<span class='p blue'>全停顿</span>。</p>
<p>主垃圾回收器执行一次完整的垃圾回收流程如下图所示: </p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/09/25/63306699a9c8b.png"/></div></div>
<p>在 V8 新生代的垃圾回收中,因其空间较小,且存活对象较少,所以全停顿的影响不大。但老生代中,如果在执行垃圾回收的过程中,占用主线程时间过久,主线程是不能做其他事情的,需要等待执行完垃圾回收操作才能做其他事情,这将就可能会造成页面的卡顿现象。</p>
<p>为了降低老生代的垃圾回收而造成的卡顿,V8 将标记过程分为一个个的子标记过程,同时让垃圾回收标记和 JavaScript 应用逻辑交替进行,直到标记阶段完成,这个算法称为<span class='p blue'>增量标记算法</span>。如下图所示: </p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/09/25/633066a04f948.png"/></div></div>
<p>使用增量标记算法可以把一个完整的垃圾回收任务拆分为很多小的任务,这些小的任务执行时间比较短,可以穿插在其他的 JavaScript 任务中间执行,这样当执行代码时,就不会让用户因为垃圾回收任务而感受到页面的卡顿了。</p>
<h3 id="减少垃圾回收"><a href="#减少垃圾回收" class="headerlink" title="减少垃圾回收"></a>减少垃圾回收</h3><p>虽然浏览器可以进行垃圾自动回收,但是当代码比较复杂时,垃圾回收所带来的代价较大,所以应该尽量减少垃圾回收: </p>
<ul>
<li>对数组进行优化: 在清空一个数组时,最简单的方法就是给其赋值为[ ],但是与此同时会创建一个新的空对象,可以将数组的长度设置为0,以此来达到清空数组的目的</li>
<li>对object进行优化: 对象尽量复用,对于不再使用的对象,就将其设置为null,尽快被回收</li>
<li>对函数进行优化: 在循环中的函数表达式,如果可以复用,尽量放在函数的外面</li>
</ul>
<div class="note no-icon flat"><p>原文链接:<a href="https://mp.weixin.qq.com/s/OobHbsVbBEM8kiIIts8P_w">前端充电宝-V8 是如何执行 JavaScript
请收下这只可爱的猫咪吧
https://fe32.top/articles/hexo1613/
2022-09-18T13:16:59.000Z
2023-05-17T10:01:12.000Z
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>对于宽屏设备上,用猫咪滚动条替换原生滚动条,可不可爱,你说了算!</p>
<h2 id="推荐阅读"><a href="#推荐阅读" class="headerlink" title="推荐阅读"></a>推荐阅读</h2><ul>
<li><a href="https://fe32.top/articles/hexo1601/">基于 Hexo 从零开始搭建个人博客(一)</a></li>
<li><a href="https://fe32.top/articles/hexo1602/">基于 Hexo 从零开始搭建个人博客(二)</a></li>
<li><a href="https://fe32.top/articles/hexo1603/">基于 Hexo 从零开始搭建个人博客(三)</a></li>
<li><a href="https://fe32.top/articles/hexo1604/">基于 Hexo 从零开始搭建个人博客(四)</a></li>
<li><a href="https://fe32.top/articles/hexo1605/">基于 Hexo 从零开始搭建个人博客(五)</a></li>
<li><a href="https://fe32.top/articles/hexo1606/">基于 Hexo 从零开始搭建个人博客(六)</a></li>
<li><a href="https://fe32.top/articles/hexo1607/">基于 Hexo 键入搜索功能</a></li>
<li><a href="https://fe32.top/articles/hexo1609/">基于 Hexo 键入分享功能</a></li>
<li><a href="https://fe32.top/articles/hexo1610/">基于 Hexo 键入在线聊天功能</a></li>
<li><a href="https://fe32.top/articles/hexo1608/">Hexo + Butterfly 自定义右键菜单</a></li>
<li><a href="https://fe32.top/articles/hexo1612/">Hexo + Butterfly 一些常见问题</a></li>
<li><a href="https://fe32.top/articles/hexo1614/">关于Vercel被墙导致获取Twikoo评论失败的解决方案</a></li>
<li><a href="https://fe32.top/articles/hexo1615/">飞只因太美,给你的首页装上吧!</a></li>
<li><a href="https://fe32.top/articles/hexo1617/">Hexo + Butterfly 自定义页脚</a></li>
<li><a href="https://fe32.top/articles/hexo1618/">Hexo + Butterfly 侧边栏公众号</a></li>
</ul>
<h2 id="效果"><a href="#效果" class="headerlink" title="效果"></a>效果</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/09/18/6327204e6e66f.jpg"/></div></div>
<h2 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h2><blockquote>
<p>没什么要求,但必须引入 Jquery。</p>
</blockquote>
<ol>
<li>制作一个盛放内容的盒子,在<code>BlogRoot/themes/butterfly/layout/includes/head.pug</code>最后一行加入如下代码:<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">#myscoll</span><br></pre></td></tr></table></figure>
其实随便放在哪里都行,像我放在了<code>BlogRoot/themes/butterfly/layout/includes/right-menu/index.pug</code>的末尾(如果用了自定义右键功能的话,可以放在这里)。</li>
<li>在<code>BlogRoot/themes/butterfly/source/js</code>文件夹下新建一个<code>cat.js</code>,将以下代码复制到文件中。<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="variable language_">document</span>.<span class="property">body</span>.<span class="property">clientWidth</span> > <span class="number">992</span>) {</span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">getBasicInfo</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="comment">/* 窗口高度 */</span></span><br><span class="line"> <span class="keyword">var</span> <span class="title class_">ViewH</span> = $(<span class="variable language_">window</span>).<span class="title function_">height</span>();</span><br><span class="line"> <span class="comment">/* document高度 */</span></span><br><span class="line"> <span class="keyword">var</span> <span class="title class_">DocH</span> = $(<span class="string">"body"</span>)[<span class="number">0</span>].<span class="property">scrollHeight</span>;</span><br><span class="line"> <span class="comment">/* 滚动的高度 */</span></span><br><span class="line"> <span class="keyword">var</span> <span class="title class_">ScrollTop</span> = $(<span class="variable language_">window</span>).<span class="title function_">scrollTop</span>();</span><br><span class="line"> <span class="comment">/* 可滚动的高度 */</span></span><br><span class="line"> <span class="keyword">var</span> <span class="variable constant_">S_V</span> = <span class="title class_">DocH</span> - <span class="title class_">ViewH</span>;</span><br><span class="line"> <span class="keyword">var</span> <span class="title class_">Band</span>_H = <span class="title class_">ScrollTop</span> / (<span class="title class_">DocH</span> - <span class="title class_">ViewH</span>) * <span class="number">100</span>;</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> <span class="title class_">ViewH</span>: <span class="title class_">ViewH</span>,</span><br><span class="line"> <span class="title class_">DocH</span>: <span class="title class_">DocH</span>,</span><br><span class="line"> <span class="title class_">ScrollTop</span>: <span class="title class_">ScrollTop</span>,</span><br><span class="line"> <span class="title class_">Band</span><span class="attr">_H</span>: <span class="title class_">Band</span>_H,</span><br><span class="line"> <span class="attr">S_V</span>: <span class="variable constant_">S_V</span></span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">show</span>(<span class="params">basicInfo</span>) {</span><br><span class="line"> <span class="keyword">if</span> (basicInfo.<span class="property">ScrollTop</span> > <span class="number">0.001</span>) {</span><br><span class="line"> $(<span class="string">".neko"</span>).<span class="title function_">css</span>(<span class="string">'display'</span>, <span class="string">'block'</span>);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $(<span class="string">".neko"</span>).<span class="title function_">css</span>(<span class="string">'display'</span>, <span class="string">'none'</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> (<span class="keyword">function</span> (<span class="params">$</span>) {</span><br><span class="line"> $.fn.<span class="property">nekoScroll</span> = <span class="keyword">function</span> (<span class="params">option</span>) {</span><br><span class="line"> <span class="keyword">var</span> defaultSetting = {</span><br><span class="line"> <span class="attr">top</span>: <span class="string">'0'</span>,</span><br><span class="line"> <span class="attr">scroWidth</span>: <span class="number">6</span> + <span class="string">'px'</span>,</span><br><span class="line"> <span class="attr">z_index</span>: <span class="number">9999</span>,</span><br><span class="line"> <span class="attr">zoom</span>: <span class="number">0.9</span>,</span><br><span class="line"> <span class="attr">borderRadius</span>: <span class="number">5</span> + <span class="string">'px'</span>,</span><br><span class="line"> <span class="attr">right</span>: <span class="number">60</span> + <span class="string">'px'</span>,</span><br><span class="line"> <span class="attr">nekoImg</span>: <span class="string">"https://bu.dusays.com/2022/07/20/62d812db74be9.png"</span>,</span><br><span class="line"> <span class="attr">hoverMsg</span>: <span class="string">"喵喵喵~"</span>,</span><br><span class="line"> <span class="attr">color</span>: <span class="string">"#6f42c1"</span>,</span><br><span class="line"> <span class="attr">during</span>: <span class="number">500</span>,</span><br><span class="line"> <span class="attr">blog_body</span>: <span class="string">"body"</span>,</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">var</span> setting = $.<span class="title function_">extend</span>(defaultSetting, option);</span><br><span class="line"> <span class="keyword">var</span> getThis = <span class="variable language_">this</span>.<span class="title function_">prop</span>(<span class="string">"className"</span>) !== <span class="string">""</span> ? <span class="string">"."</span> + <span class="variable language_">this</span>.<span class="title function_">prop</span>(<span class="string">"className"</span>) : <span class="variable language_">this</span>.<span class="title function_">prop</span>(<span class="string">"id"</span>) !== <span class="string">""</span> ? <span class="string">"#"</span> +</span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">prop</span>(<span class="string">"id"</span>) : <span class="variable language_">this</span>.<span class="title function_">prop</span>(<span class="string">"nodeName"</span>);</span><br><span class="line"> <span class="keyword">if</span> ($(<span class="string">".neko"</span>).<span class="property">length</span> == <span class="number">0</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">after</span>(<span class="string">"<div class=\"neko\" id="</span> + setting.<span class="property">nekoname</span> + <span class="string">" data-msg=\""</span> + setting.<span class="property">hoverMsg</span> + <span class="string">"\"></div>"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">let</span> basicInfo = <span class="title function_">getBasicInfo</span>();</span><br><span class="line"> $(getThis)</span><br><span class="line"> .<span class="title function_">css</span>({</span><br><span class="line"> <span class="string">'position'</span>: <span class="string">'fixed'</span>,</span><br><span class="line"> <span class="string">'width'</span>: setting.<span class="property">scroWidth</span>,</span><br><span class="line"> <span class="string">'top'</span>: setting.<span class="property">top</span>,</span><br><span class="line"> <span class="string">'height'</span>: basicInfo.<span class="property">Band_H</span> * setting.<span class="property">zoom</span> * basicInfo.<span class="property">ViewH</span> * <span class="number">0.01</span> + <span class="string">'px'</span>,</span><br><span class="line"> <span class="string">'z-index'</span>: setting.<span class="property">z_index</span>,</span><br><span class="line"> <span class="string">'background-color'</span>: setting.<span class="property">bgcolor</span>,</span><br><span class="line"> <span class="string">"border-radius"</span>: setting.<span class="property">borderRadius</span>,</span><br><span class="line"> <span class="string">'right'</span>: setting.<span class="property">right</span>,</span><br><span class="line"> <span class="string">'background-image'</span>: <span class="string">'url('</span> + setting.<span class="property">scImg</span> + <span class="string">')'</span>,</span><br><span class="line"> <span class="string">'background-image'</span>: <span class="string">'-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.1) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.1) 50%, rgba(255, 255, 255, 0.1) 75%, transparent 75%, transparent)'</span>, <span class="string">'border-radius'</span>: <span class="string">'2em'</span>,</span><br><span class="line"> <span class="string">'background-size'</span>: <span class="string">'contain'</span></span><br><span class="line"> });</span><br><span class="line"> $(<span class="string">"#"</span> + setting.<span class="property">nekoname</span>)</span><br><span class="line"> .<span class="title function_">css</span>({</span><br><span class="line"> <span class="string">'position'</span>: <span class="string">'fixed'</span>,</span><br><span class="line"> <span class="string">'top'</span>: basicInfo.<span class="property">Band_H</span> * setting.<span class="property">zoom</span> * basicInfo.<span class="property">ViewH</span> * <span class="number">0.01</span> - <span class="number">50</span> + <span class="string">'px'</span>,</span><br><span class="line"> <span class="string">'z-index'</span>: setting.<span class="property">z_index</span> * <span class="number">10</span>,</span><br><span class="line"> <span class="string">'right'</span>: setting.<span class="property">right</span>,</span><br><span class="line"> <span class="string">'background-image'</span>: <span class="string">'url('</span> + setting.<span class="property">nekoImg</span> + <span class="string">')'</span>,</span><br><span class="line"> });</span><br><span class="line"> <span class="title function_">show</span>(<span class="title function_">getBasicInfo</span>());</span><br><span class="line"> $(<span class="variable language_">window</span>)</span><br><span class="line"> .<span class="title function_">scroll</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">let</span> basicInfo = <span class="title function_">getBasicInfo</span>();</span><br><span class="line"> <span class="title function_">show</span>(basicInfo);</span><br><span class="line"> $(getThis)</span><br><span class="line"> .<span class="title function_">css</span>({</span><br><span class="line"> <span class="string">'position'</span>: <span class="string">'fixed'</span>,</span><br><span class="line"> <span class="string">'width'</span>: setting.<span class="property">scroWidth</span>,</span><br><span class="line"> <span class="string">'top'</span>: setting.<span class="property">top</span>,</span><br><span class="line"> <span class="string">'height'</span>: basicInfo.<span class="property">Band_H</span> * setting.<span class="property">zoom</span> * basicInfo.<span class="property">ViewH</span> * <span class="number">0.01</span> + <span class="string">'px'</span>,</span><br><span class="line"> <span class="string">'z-index'</span>: setting.<span class="property">z_index</span>,</span><br><span class="line"> <span class="string">'background-color'</span>: setting.<span class="property">bgcolor</span>,</span><br><span class="line"> <span class="string">"border-radius"</span>: setting.<span class="property">borderRadius</span>,</span><br><span class="line"> <span class="string">'right'</span>: setting.<span class="property">right</span>,</span><br><span class="line"> <span class="string">'background-image'</span>: <span class="string">'url('</span> + setting.<span class="property">scImg</span> + <span class="string">')'</span>,</span><br><span class="line"> <span class="string">'background-image'</span>: <span class="string">'-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.1) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.1) 50%, rgba(255, 255, 255, 0.1) 75%, transparent 75%, transparent)'</span>, <span class="string">'border-radius'</span>: <span class="string">'2em'</span>,</span><br><span class="line"> <span class="string">'background-size'</span>: <span class="string">'contain'</span></span><br><span class="line"> });</span><br><span class="line"> $(<span class="string">"#"</span> + setting.<span class="property">nekoname</span>)</span><br><span class="line"> .<span class="title function_">css</span>({</span><br><span class="line"> <span class="string">'position'</span>: <span class="string">'fixed'</span>,</span><br><span class="line"> <span class="string">'top'</span>: basicInfo.<span class="property">Band_H</span> * setting.<span class="property">zoom</span> * basicInfo.<span class="property">ViewH</span> * <span class="number">0.01</span> - <span class="number">50</span> + <span class="string">'px'</span>,</span><br><span class="line"> <span class="string">'z-index'</span>: setting.<span class="property">z_index</span> * <span class="number">10</span>,</span><br><span class="line"> <span class="string">'right'</span>: setting.<span class="property">right</span>,</span><br><span class="line"> <span class="string">'background-image'</span>: <span class="string">'url('</span> + setting.<span class="property">nekoImg</span> + <span class="string">')'</span>,</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">if</span> (basicInfo.<span class="property">ScrollTop</span> == basicInfo.<span class="property">S_V</span>) {</span><br><span class="line"> $(<span class="string">"#"</span> + setting.<span class="property">nekoname</span>)</span><br><span class="line"> .<span class="title function_">addClass</span>(<span class="string">"showMsg"</span>)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $(<span class="string">"#"</span> + setting.<span class="property">nekoname</span>)</span><br><span class="line"> .<span class="title function_">removeClass</span>(<span class="string">"showMsg"</span>);</span><br><span class="line"> $(<span class="string">"#"</span> + setting.<span class="property">nekoname</span>)</span><br><span class="line"> .<span class="title function_">attr</span>(<span class="string">"data-msg"</span>, setting.<span class="property">hoverMsg</span>);</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">click</span>(<span class="keyword">function</span> (<span class="params">e</span>) {</span><br><span class="line"> btf.<span class="title function_">scrollToDest</span>(<span class="number">0</span>, <span class="number">500</span>)</span><br><span class="line"> });</span><br><span class="line"> $(<span class="string">"#"</span> + setting.<span class="property">nekoname</span>)</span><br><span class="line"> .<span class="title function_">click</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> btf.<span class="title function_">scrollToDest</span>(<span class="number">0</span>, <span class="number">500</span>)</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>;</span><br><span class="line"> }</span><br><span class="line"> })(jQuery);</span><br><span class="line"></span><br><span class="line"> $(<span class="variable language_">document</span>).<span class="title function_">ready</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="comment">//部分自定义</span></span><br><span class="line"> $(<span class="string">"#myscoll"</span>).<span class="title function_">nekoScroll</span>({</span><br><span class="line"> <span class="attr">bgcolor</span>: <span class="string">'rgb(0 0 0 / .5)'</span>, <span class="comment">//背景颜色,没有绳子背景图片时有效</span></span><br><span class="line"> <span class="attr">borderRadius</span>: <span class="string">'2em'</span>,</span><br><span class="line"> <span class="attr">zoom</span>: <span class="number">0.9</span></span><br><span class="line"> }</span><br><span class="line"> );</span><br><span class="line"> <span class="comment">//自定义(去掉以下注释,并注释掉其他的查看效果)</span></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> $("#myscoll").nekoScroll({</span></span><br><span class="line"><span class="comment"> nekoname:'neko1', //nekoname,相当于id</span></span><br><span class="line"><span class="comment"> nekoImg:'img/猫咪.png', //neko的背景图片</span></span><br><span class="line"><span class="comment"> scImg:"img/绳1.png", //绳子的背景图片</span></span><br><span class="line"><span class="comment"> bgcolor:'#1e90ff', //背景颜色,没有绳子背景图片时有效</span></span><br><span class="line"><span class="comment"> zoom:0.9, //绳子长度的缩放值</span></span><br><span class="line"><span class="comment"> hoverMsg:'你好~喵', //鼠标浮动到neko上方的对话框信息</span></span><br><span class="line"><span class="comment"> right:'100px', //距离页面右边的距离</span></span><br><span class="line"><span class="comment"> fontFamily:'楷体', //对话框字体</span></span><br><span class="line"><span class="comment"> fontSize:'14px', //对话框字体的大小</span></span><br><span class="line"><span class="comment"> color:'#1e90ff', //对话框字体颜色</span></span><br><span class="line"><span class="comment"> scroWidth:'8px', //绳子的宽度</span></span><br><span class="line"><span class="comment"> z_index:100, //不用解释了吧</span></span><br><span class="line"><span class="comment"> during:1200, //从顶部到底部滑动的时长</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><br><span class="line">}</span><br></pre></td></tr></table></figure></li>
<li>在<code>BlogRoot/themes/butterfly/source/css</code>文件夹下新建一个<code>cat.css</code>,将以下代码复制到文件中。当然你也可以选择不新建 css 文件,复制代码到<code>custom.css</code>也行,总之有地方引入就行。<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="selector-tag">body</span>::-webkit-scrollbar {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.neko</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">64px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">64px</span>;</span><br><span class="line"> <span class="attribute">background-image</span>: <span class="built_in">url</span>(<span class="string">"https://bu.dusays.com/2022/07/20/62d812db74be9.png"</span>);</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">right</span>: <span class="number">32px</span>;</span><br><span class="line"> <span class="attribute">background-repeat</span>: no-repeat;</span><br><span class="line"> <span class="attribute">background-size</span>: contain;</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">translateX</span>(<span class="number">50%</span>);</span><br><span class="line"> <span class="attribute">cursor</span>: pointer;</span><br><span class="line"> <span class="attribute">font-family</span>: tzy;</span><br><span class="line"> <span class="attribute">font-weight</span>: <span class="number">600</span>;</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">16px</span>;</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#6f42c1</span>;</span><br><span class="line"> <span class="attribute">display</span>: none;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.neko</span><span class="selector-pseudo">::after</span> {</span><br><span class="line"> <span class="attribute">display</span>: none;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">100px</span>;</span><br><span class="line"> <span class="attribute">background-image</span>: <span class="built_in">url</span>(<span class="string">"https://bu.dusays.com/2022/07/20/62d812d95e6f5.png"</span>);</span><br><span class="line"> <span class="attribute">background-size</span>: contain;</span><br><span class="line"> <span class="attribute">z-index</span>: <span class="number">9999</span>;</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">right</span>: <span class="number">50%</span>;</span><br><span class="line"> <span class="attribute">text-align</span>: center;</span><br><span class="line"> <span class="attribute">line-height</span>: <span class="number">100px</span>;</span><br><span class="line"> <span class="attribute">top</span>: -<span class="number">115%</span>;</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.neko</span><span class="selector-class">.showMsg</span><span class="selector-pseudo">::after</span> {</span><br><span class="line"> <span class="attribute">content</span>: <span class="built_in">attr</span>(data-msg);</span><br><span class="line"> <span class="attribute">display</span>: block;</span><br><span class="line"> <span class="attribute">overflow</span>: hidden;</span><br><span class="line"> <span class="attribute">text-overflow</span>: ellipsis;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.neko</span><span class="selector-pseudo">:hover</span><span class="selector-pseudo">::after</span> {</span><br><span class="line"> <span class="attribute">content</span>: <span class="built_in">attr</span>(data-msg);</span><br><span class="line"> <span class="attribute">display</span>: block;</span><br><span class="line"> <span class="attribute">overflow</span>: hidden;</span><br><span class="line"> <span class="attribute">text-overflow</span>: ellipsis;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.neko</span><span class="selector-class">.fontColor</span><span class="selector-pseudo">::after</span> {</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#333</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @description: 滚动条样式 跟猫二选一</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">@media</span> screen <span class="keyword">and</span> (<span class="attribute">max-width</span>:<span class="number">992px</span>) {</span><br><span class="line"> ::-webkit-scrollbar {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">8px</span> <span class="meta">!important</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">8px</span> <span class="meta">!important</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> ::-webkit-scrollbar-track {</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">2em</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> ::-webkit-scrollbar-thumb {</span><br><span class="line"> <span class="attribute">background-color</span>: <span class="built_in">rgb</span>(<span class="number">255</span> <span class="number">255</span> <span class="number">255</span> / .<span class="number">3</span>);</span><br><span class="line"> <span class="attribute">background-image</span>: <span class="built_in">-webkit-linear-gradient</span>(<span class="number">45deg</span>, <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.1</span>) <span class="number">25%</span>, transparent <span class="number">25%</span>, transparent <span class="number">50%</span>, <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.1</span>) <span class="number">50%</span>, <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.1</span>) <span class="number">75%</span>, transparent <span class="number">75%</span>, transparent);</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">2em</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> ::-webkit-scrollbar-corner {</span><br><span class="line"> <span class="attribute">background-color</span>: transparent</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li>
<li>在主题配置文件<code>_config.butterfly.yml</code>中引入<code>cat.js</code>和<code>cat.css</code>。<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">inject:</span></span><br><span class="line"> <span class="attr">head:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string"><link</span> <span class="string">rel="stylesheet"</span> <span class="string">href="/css/cat.css"></span></span><br><span class="line"> <span class="attr">bottom:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string"><script</span> <span class="string">defer</span> <span class="string">src="https://npm.elemecdn.com/jquery@latest/dist/jquery.min.js"></script></span></span><br><span class="line"> <span class="bullet">-</span> <span class="string"><script</span> <span class="string">defer</span> <span class="string">data-pjax</span> <span class="string">src="/js/cat.js"></script></span></span><br></pre></td></tr></table></figure></li>
<li>最后重新编译运行即可看见效果。</li>
</ol>
<h2 id="拓展"><a href="#拓展" class="headerlink"
Vue3 后台管理系统模板推荐
https://fe32.top/articles/xi3mpxmd/
2022-08-28T13:55:28.000Z
2023-12-01T09:59:20.000Z
Vue3 在今年2月份已成为新的默认版本,本文收集了一些 Vue3 的后台管理系统模板,分享给在座的仌(打工人乃人上人👣)。
推荐几个不错的 CSS 工具,持续收录!
https://fe32.top/articles/css10002/
2022-08-17T15:32:26.000Z
2023-05-17T10:01:12.000Z
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/18/62fd1d0654ec2.jpg"/></div></div>
<h2 id="更新记录"><a href="#更新记录" class="headerlink" title="更新记录"></a>更新记录</h2><div class="timeline purple"><div class='timeline-item headline'><div class='timeline-item-title'><div class='item-circle'><p>2022-12-14 </p>
</div></div></div></div>
<h2 id="Neumorphism"><a href="#Neumorphism" class="headerlink" title="Neumorphism"></a>Neumorphism</h2><p><a href="https://neumorphism.io/">Neumorphism</a> 是一个很棒的工具,可以根据你的喜好选择颜色、编辑大小、半径、距离等为你的设计生成 UI CSS 代码。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/18/62fd1d12e6179.png"/></div></div>
<h2 id="Animista"><a href="#Animista" class="headerlink" title="Animista"></a>Animista</h2><p><a href="https://animista.net/">Animista</a> 是CSS动画的中最佳的工具之一。你只需选择你喜欢的动画类型,并设置一些参数,然后将生成的 CSS 代码用到你的项目中。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/18/62fd1d1ed778f.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/18/62fd1ef998d9d.png"/></div></div>
<h2 id="PurgeCSS"><a href="#PurgeCSS" class="headerlink" title="PurgeCSS"></a>PurgeCSS</h2><p><a href="https://purgecss.com/">PurgeCSS</a> 可以删除 CSS 中未使用到的代码,减小 CSS 文件的体积,并提高性能。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/18/62fd1d3140584.png"/></div></div>
<h2 id="Shadow-Brumm"><a href="#Shadow-Brumm" class="headerlink" title="Shadow Brumm"></a>Shadow Brumm</h2><p><a href="https://shadows.brumm.af/">Shadow Brumm</a> 是一款快速创建阴影的工具。使用此工具,你只需定义一些阴影选项,就可以创建酷炫且平滑的阴影,并为你生成效果代码。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/18/62fd1e1314441.png"/></div></div>
<h2 id="CSS-Gradient"><a href="#CSS-Gradient" class="headerlink" title="CSS Gradient"></a>CSS Gradient</h2><p><a href="https://cssgradient.io/">CSS Gradient</a> 是一款值得推荐的工具。你可以选择不同类型的颜色和选项来创建渐变背景,它自动为你的渐变背景生成 CSS 代码。这是我日常开发中经常用到的工具之一。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/18/62fd201ac8b0c.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/18/62fd2030b5bb6.png"/></div></div>
<h2 id="CSS-Grid-Generator"><a href="#CSS-Grid-Generator" class="headerlink" title="CSS Grid Generator"></a>CSS Grid Generator</h2><p><a href="https://cssgrid-generator.netlify.app/">CSS Grid Generator</a> 可以创建响应式网格布局,你只需设置列、行和单位。可以同步看到网页的效果,还可以获取该效果的 CSS 代码 甚至 HTML 代码。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/18/62fd20dade8fe.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/18/62fd20eb615d6.png"/></div></div>
<h2 id="Get-Waves"><a href="#Get-Waves" class="headerlink" title="Get Waves"></a>Get Waves</h2><p><a href="https://getwaves.io/">Get Waves</a> 可以为你的项目创建 SVG 波形。你只需调好参数,该工具会同步生成 wave 效果,当达到目标效果时,你只需要下载生成的 CSS 代码运用到你的项目中即可。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/18/62fd21309fe35.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/18/62fd2142197d6.png"/></div></div>
<h2 id="CSS-Stripes-Generator"><a href="#CSS-Stripes-Generator" class="headerlink" title="CSS Stripes Generator"></a>CSS Stripes Generator</h2><p><a href="https://stripesgenerator.com/">CSS Stripes Generator</a> 不用Flash,不用图像,仅使用 CSS 生成条纹背景。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/10/23/6355274d4555a.jpg"/></div></div>
<h2 id="Blob-Maker"><a href="#Blob-Maker" class="headerlink" title="Blob Maker"></a>Blob Maker</h2><p><a href="https://www.blobmaker.app/">Blob Maker</a> 此工具可帮助你创建不同的不寻常形状,并允许你复制和下载 SVG 文件。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/10/23/6355274e74830.jpg"/></div></div>
<h2 id="CSS-Accordion-Slider-Generator"><a href="#CSS-Accordion-Slider-Generator" class="headerlink" title="CSS Accordion Slider Generator"></a>CSS Accordion Slider Generator</h2><p><a href="https://accordionslider.com/">CSS Accordion Slider Generator</a> 此工具可帮助你创建完全响应的、仅 CSS(无 javascript)的手风琴滑块。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/10/23/6355274dbc1a1.jpg"/></div></div>
<h2 id="Glassmorphism-CSS-Generator-Hype4-Academy"><a href="#Glassmorphism-CSS-Generator-Hype4-Academy" class="headerlink" title="Glassmorphism CSS Generator | Hype4 Academy"></a>Glassmorphism CSS Generator | Hype4 Academy</h2><p><a href="https://hype4.academy/tools/glassmorphism-generator">Glassmorphism CSS Generator | Hype4 Academy</a> 是一种设计风格,由 Hype4.Academy 的 Michal Malewicz 创造,用于连接和组合UI中 “磨砂玻璃” 效果。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/10/23/6355274f45c75.jpg"/></div></div>
<h2 id="CSS-Button-Generator"><a href="#CSS-Button-Generator" class="headerlink" title="CSS Button Generator"></a>CSS Button Generator</h2><p><a href="https://cssbuttongenerator.com/">CSS Button Generator</a> 是一个免费的在线工具,可以在没有代码的情况下创建CSS按钮。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/12/14/6399bb8aa8954.jpg"/></div></div>
<h2 id="Fancy-Border-Radius-Generator"><a href="#Fancy-Border-Radius-Generator" class="headerlink" title="Fancy Border Radius Generator"></a>Fancy Border Radius Generator</h2><p><a href="https://9elements.github.io/fancy-border-radius/">Fancy Border Radius Generator</a> 以 CSS3边界半径 构建形状。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/12/14/6399bb97342cf.jpg"/></div></div>
<h2 id="LOADING-IO"><a href="#LOADING-IO" class="headerlink" title="LOADING.IO"></a>LOADING.IO</h2><p><a href="https://loading.io/">LOADING.IO</a> 使用SVG/CSS/GIF/PNG构建Ajax加载图标、动画文本等!</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/12/14/6399bbb6971a0.webp"/></div></div>
<h2 id="Can-I-use-?"><a href="#Can-I-use-?" class="headerlink" title="Can I use _ ?"></a>Can I use _ ?</h2><p><a href="https://caniuse.com/">an I use _</a> 提供了最新的浏览器支持表,以支持桌面和移动 web 浏览器上的前端 web 技术。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/12/14/6399bbc0e14a5.png"/></div></div>
<!-- ## You-need-to-know-css
[我可以用这个css吗?](https://caniuse.com/) is a visual generator to build organic looking shapes with the help of CSS3 border-radius property.
实现元素居中的 10 个CSS方法
https://fe32.top/articles/css10001/
2022-08-16T14:36:59.000Z
2023-05-17T10:01:12.000Z
<h2 id="absolute-margin"><a href="#absolute-margin" class="headerlink" title="absolute + (-margin)"></a>absolute + (-margin)</h2><p>如果元素的宽度和高度已知,我们可以使用至少 3 种方法来使元素居中。例如,在下图中,小猫的宽度和高度分别为 “500px” 和 “366px” 。我们应该如何居中?</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/16/62fbb718d9d73.png"/></div></div>
<p>用 “absolute + (-margin)” 很容易完成, 代码如下:</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"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"container"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">img</span> <span class="attr">class</span>=<span class="string">"cat"</span> <span class="attr">src</span>=<span class="string">"https://images.unsplash.com/photo-1533743983669-94fa5c4338ec?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1584&q=80"</span> <span class="attr">alt</span>=<span class="string">""</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></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><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="selector-class">.container</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">800px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">600px</span>;</span><br><span class="line"> <span class="attribute">border</span>: solid <span class="number">1px</span> <span class="number">#e3c1a9</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">30px</span>;</span><br><span class="line"> <span class="comment">/* Key css */</span></span><br><span class="line"> <span class="attribute">position</span>: relative;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.cat</span>{</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">500px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">366px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">50%</span>;</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="comment">/* Key css */</span></span><br><span class="line"> <span class="attribute">left</span>: <span class="number">50%</span>;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">50%</span>;</span><br><span class="line"> <span class="comment">/* half the width */</span></span><br><span class="line"> <span class="attribute">margin-left</span>: -<span class="number">250px</span>;</span><br><span class="line"> <span class="comment">/* half the height */</span></span><br><span class="line"> <span class="attribute">margin-top</span>: -<span class="number">183px</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcodepen.io%2Fqianlong%2Fembed%2Fpreview%2FyLKXLxM%3Fdefault-tabs%3Dcss%252Cresult%26height%3D600%26host%3Dhttps%253A%252F%252Fcodepen.io%26slug-hash%3DyLKXLxM&display_name=CodePen&url=https%3A%2F%2Fcodepen.io%2Fqianlong%2Fpen%2FyLKXLxM&image=https%3A%2F%2Fshots.codepen.io%2Fqianlong%2Fpen%2FyLKXLxM-512.jpg%3Fversion%3D1658449813&key=a19fcc184b9711e1b4764040d3dc5c07&type=text%2Fhtml&schema=codepen" allowfullscreen="" frameborder="0" height="600" width="100%" title="yLKXLxM" class="fj aq as ag cf" scrolling="auto"></iframe>
<blockquote>
<p>这种方法简单易懂,兼容性好;缺点是我们需要知道子元素的宽高。</p>
</blockquote>
<h2 id="absolute-margin-auto"><a href="#absolute-margin-auto" class="headerlink" title="absolute + margin auto"></a>absolute + margin auto</h2><p>我们还可以通过将所有方向的距离设置为 0 ,并将边距设置为自动来使小猫居中。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">800px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">600px</span>;</span><br><span class="line"> <span class="attribute">border</span>: solid <span class="number">1px</span> <span class="number">#e3c1a9</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">30px</span>;</span><br><span class="line"> <span class="attribute">position</span>: relative;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.cat</span>{</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">500px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">366px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">50%</span>;</span><br><span class="line"> <span class="comment">/* Key css */</span></span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">right</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">bottom</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">margin</span>: auto;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcodepen.io%2Fqianlong%2Fembed%2Fpreview%2FRwMgweO%3Fdefault-tabs%3Dcss%252Cresult%26height%3D600%26host%3Dhttps%253A%252F%252Fcodepen.io%26slug-hash%3DRwMgweO&display_name=CodePen&url=https%3A%2F%2Fcodepen.io%2Fqianlong%2Fpen%2FRwMgweO&image=https%3A%2F%2Fshots.codepen.io%2Fqianlong%2Fpen%2FRwMgweO-512.jpg%3Fversion%3D1658450114&key=a19fcc184b9711e1b4764040d3dc5c07&type=text%2Fhtml&schema=codepen" allowfullscreen="" frameborder="0" height="600" width="100%" title="RwMgweO" class="fj aq as ag cf" scrolling="auto"></iframe>
<blockquote>
<p>和第一种方法一样,兼容性也挺好,缺点是需要知道子元素的宽高。</p>
</blockquote>
<h2 id="absolute-calc"><a href="#absolute-calc" class="headerlink" title="absolute + calc"></a>absolute + calc</h2><p>CSS3 带来了 calc 计算属性,它允许我们通过它来居中一个元素,代码如下:</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">800px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">600px</span>;</span><br><span class="line"> <span class="attribute">border</span>: solid <span class="number">1px</span> <span class="number">#e3c1a9</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">30px</span>;</span><br><span class="line"> <span class="attribute">position</span>: relative;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.cat</span>{</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">500px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">366px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">50%</span>;</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="comment">/* Key css */</span></span><br><span class="line"> <span class="attribute">top</span>: <span class="built_in">calc</span>(<span class="number">50%</span> - <span class="number">183px</span>);</span><br><span class="line"> <span class="attribute">left</span>: <span class="built_in">calc</span>(<span class="number">50%</span> - <span class="number">250px</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcodepen.io%2Fqianlong%2Fembed%2Fpreview%2FzYWzYyR%3Fdefault-tabs%3Dcss%252Cresult%26height%3D600%26host%3Dhttps%253A%252F%252Fcodepen.io%26slug-hash%3DzYWzYyR&display_name=CodePen&url=https%3A%2F%2Fcodepen.io%2Fqianlong%2Fpen%2FzYWzYyR&image=https%3A%2F%2Fshots.codepen.io%2Fqianlong%2Fpen%2FzYWzYyR-512.jpg%3Fversion%3D1658450486&key=a19fcc184b9711e1b4764040d3dc5c07&type=text%2Fhtml&schema=codepen" allowfullscreen="" frameborder="0" height="600" width="100%" title="zYWzYyR" class="fj aq as ag cf" scrolling="auto"></iframe>
<blockquote>
<p>该方法的兼容性取决于calc的兼容性,缺点是需要知道子元素的宽高。</p>
</blockquote>
<p>上面介绍的三种方法必须提前知道元素的宽高,但元素的宽高不确定怎么办?于是就有了<code>flex</code>。</p>
<h2 id="flex"><a href="#flex" class="headerlink" title="flex"></a>flex</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/16/62fbba7fa95a2.gif"/></div></div>
<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"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"container"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">contenteditable</span>=<span class="string">"true"</span> <span class="attr">class</span>=<span class="string">"content"</span>></span>hello medium<span class="tag"></<span class="name">span</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></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><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">400px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">200px</span>;</span><br><span class="line"> <span class="attribute">border</span>: solid <span class="number">1px</span> <span class="number">#e3c1a9</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">30px</span>;</span><br><span class="line"> <span class="comment">/* Key css */</span></span><br><span class="line"> <span class="attribute">display</span>: flex;</span><br><span class="line"> <span class="attribute">align-items</span>: center;</span><br><span class="line"> <span class="attribute">justify-content</span>: center;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.content</span>{</span><br><span class="line"> <span class="attribute">padding</span>: <span class="number">20px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">10px</span>;</span><br><span class="line"> <span class="attribute">background-color</span>: <span class="number">#e3c1a9</span>;</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#ffffff</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcodepen.io%2Fqianlong%2Fembed%2Fpreview%2FabYyzvG%3Fdefault-tabs%3Dcss%252Cresult%26height%3D600%26host%3Dhttps%253A%252F%252Fcodepen.io%26slug-hash%3DabYyzvG&display_name=CodePen&url=https%3A%2F%2Fcodepen.io%2Fqianlong%2Fpen%2FabYyzvG&image=https%3A%2F%2Fshots.codepen.io%2Fqianlong%2Fpen%2FabYyzvG-512.jpg%3Fversion%3D1658676029&key=a19fcc184b9711e1b4764040d3dc5c07&type=text%2Fhtml&schema=codepen" allowfullscreen="" frameborder="0" height="600" width="100%" title="ayyzvG" class="fj aq as ag cf" scrolling="auto"></iframe>
<p>可以用很少的代码来居中一个元素,是我最喜欢的使用方式。</p>
<h2 id="grid"><a href="#grid" class="headerlink" title="grid"></a>grid</h2><p>像<code>flex</code>一样,<code>grid</code>也可以非常方便地用于使元素居中。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">400px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">200px</span>;</span><br><span class="line"> <span class="attribute">border</span>: solid <span class="number">1px</span> <span class="number">#e3c1a9</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">30px</span>;</span><br><span class="line"> <span class="comment">/* Key css */</span></span><br><span class="line"> <span class="attribute">display</span>: grid;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.content</span>{</span><br><span class="line"> <span class="comment">/* Key css */</span></span><br><span class="line"> <span class="attribute">align-self</span>: center;</span><br><span class="line"> justify-self: center;</span><br><span class="line"> <span class="attribute">padding</span>: <span class="number">20px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">10px</span>;</span><br><span class="line"> <span class="attribute">background-color</span>: <span class="number">#e3c1a9</span>;</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#ffffff</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcodepen.io%2Fqianlong%2Fembed%2Fpreview%2FdymzPMa%3Fdefault-tabs%3Dcss%252Cresult%26height%3D600%26host%3Dhttps%253A%252F%252Fcodepen.io%26slug-hash%3DdymzPMa&display_name=CodePen&url=https%3A%2F%2Fcodepen.io%2Fqianlong%2Fpen%2FdymzPMa&image=https%3A%2F%2Fshots.codepen.io%2Fqianlong%2Fpen%2FdymzPMa-512.jpg%3Fversion%3D1658676375&key=a19fcc184b9711e1b4764040d3dc5c07&type=text%2Fhtml&schema=codepen" allowfullscreen="" frameborder="0" height="600" width="100%" title="dymzPMa" class="fj aq as ag cf" scrolling="auto"></iframe>
<h2 id="absolute-transform"><a href="#absolute-transform" class="headerlink" title="absolute + transform"></a>absolute + transform</h2><p>使用变换,我们还可以在事先不知道元素的宽度和高度的情况下使元素居中。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">400px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">200px</span>;</span><br><span class="line"> <span class="attribute">border</span>: solid <span class="number">1px</span> <span class="number">#e3c1a9</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">30px</span>;</span><br><span class="line"> <span class="comment">/* Key css */</span></span><br><span class="line"> <span class="attribute">position</span>: relative;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.content</span>{</span><br><span class="line"> <span class="comment">/* Key css */</span></span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">50%</span>;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">50%</span>;</span><br><span class="line"> <span class="comment">/* Key css */</span></span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">translate</span>(-<span class="number">50%</span>, -<span class="number">50%</span>);</span><br><span class="line"> <span class="attribute">padding</span>: <span class="number">20px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">10px</span>;</span><br><span class="line"> <span class="attribute">background-color</span>: <span class="number">#e3c1a9</span>;</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#ffffff</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcodepen.io%2Fqianlong%2Fembed%2Fpreview%2FKKovwgW%3Fdefault-tabs%3Dcss%252Cresult%26height%3D600%26host%3Dhttps%253A%252F%252Fcodepen.io%26slug-hash%3DKKovwgW&display_name=CodePen&url=https%3A%2F%2Fcodepen.io%2Fqianlong%2Fpen%2FKKovwgW&image=https%3A%2F%2Fshots.codepen.io%2Fqianlong%2Fpen%2FKKovwgW-512.jpg%3Fversion%3D1658676728&key=a19fcc184b9711e1b4764040d3dc5c07&type=text%2Fhtml&schema=codepen" allowfullscreen="" frameborder="0" height="600" width="100%" title="KKovwgW" class="fj aq as ag cf" scrolling="auto"></iframe>
<p>以上6种方式比较容易理解,在我们的工作中也经常用到,接下来的 4 种方法似乎使用频率较低,但也值得学习。</p>
<h2 id="text-align-line-height-vertical-align"><a href="#text-align-line-height-vertical-align" class="headerlink" title="text-align + line-height + vertical-align"></a>text-align + line-height + vertical-align</h2><p>首先,我们可以将 span 的 “display” 属性设置为 “inline-block”。然后通过设置容器的<code>text-align</code>属性为<code>center</code>,span 元素可以水平居中。结合 <code>line-height</code>和其他属性使其垂直居中。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">400px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">200px</span>;</span><br><span class="line"> <span class="attribute">border</span>: solid <span class="number">1px</span> <span class="number">#e3c1a9</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">30px</span>;</span><br><span class="line"> <span class="comment">/* Key css */</span></span><br><span class="line"> <span class="attribute">text-align</span>: center;</span><br><span class="line"> <span class="attribute">line-height</span>: <span class="number">200px</span>;</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">0px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.content</span>{</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">16px</span>;</span><br><span class="line"> <span class="comment">/* Key css */</span></span><br><span class="line"> <span class="attribute">display</span>: inline-block;</span><br><span class="line"> <span class="attribute">vertical-align</span>: middle;</span><br><span class="line"> <span class="attribute">line-height</span>: initial;</span><br><span class="line"> <span class="attribute">text-align</span>: left;</span><br><span class="line"> <span class="attribute">padding</span>: <span class="number">20px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">10px</span>;</span><br><span class="line"> <span class="attribute">background-color</span>: <span class="number">#e3c1a9</span>;</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#ffffff</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcodepen.io%2Fqianlong%2Fembed%2Fpreview%2FdymzPWL%3Fdefault-tabs%3Dcss%252Cresult%26height%3D600%26host%3Dhttps%253A%252F%252Fcodepen.io%26slug-hash%3DdymzPWL&display_name=CodePen&url=https%3A%2F%2Fcodepen.io%2Fqianlong%2Fpen%2FdymzPWL&image=https%3A%2F%2Fshots.codepen.io%2Fqianlong%2Fpen%2FdymzPWL-512.jpg%3Fversion%3D1658677631&key=a19fcc184b9711e1b4764040d3dc5c07&type=text%2Fhtml&schema=codepen" allowfullscreen="" frameborder="0" height="600" width="100%" title="dymzPWL" class="fj aq as ag cf" scrolling="auto"></iframe>
<h2 id="css-table"><a href="#css-table" class="headerlink" title="css-table"></a>css-table</h2><p>CSS新的table属性让我们可以将普通元素变成表格元素的真实效果,通过这个特性,一个元素也可以居中。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">400px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">200px</span>;</span><br><span class="line"> <span class="attribute">border</span>: solid <span class="number">1px</span> <span class="number">#e3c1a9</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">30px</span>;</span><br><span class="line"> <span class="comment">/* Key css */</span></span><br><span class="line"> <span class="attribute">display</span>: table-cell;</span><br><span class="line"> <span class="attribute">text-align</span>: center;</span><br><span class="line"> <span class="attribute">vertical-align</span>: middle;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.content</span> {</span><br><span class="line"> <span class="comment">/* Key css */</span></span><br><span class="line"> <span class="attribute">display</span>: inline-block;</span><br><span class="line"> <span class="attribute">padding</span>: <span class="number">20px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">10px</span>;</span><br><span class="line"> <span class="attribute">background-color</span>: <span class="number">#e3c1a9</span>;</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#ffffff</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcodepen.io%2Fqianlong%2Fembed%2Fpreview%2FdymzPJE%3Fdefault-tabs%3Dcss%252Cresult%26height%3D600%26host%3Dhttps%253A%252F%252Fcodepen.io%26slug-hash%3DdymzPJE&display_name=CodePen&url=https%3A%2F%2Fcodepen.io%2Fqianlong%2Fpen%2FdymzPJE&image=https%3A%2F%2Fshots.codepen.io%2Fqianlong%2Fpen%2FdymzPJE-512.jpg%3Fversion%3D1658678536&key=a19fcc184b9711e1b4764040d3dc5c07&type=text%2Fhtml&schema=codepen" allowfullscreen="" frameborder="0" height="600" width="100%" title="dymzPJE" class="fj aq as ag cf" scrolling="auto"></iframe>
<h2 id="writing-mode"><a href="#writing-mode" class="headerlink" title="writing-mode"></a>writing-mode</h2><p>过去,我习惯使用<code>writing-mode</code>将内容的布局方向更改为垂直。</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></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"container"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"content-wrap"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">contenteditable</span>=<span class="string">"true"</span> <span class="attr">class</span>=<span class="string">"content"</span>></span>hello medium<span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></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><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">400px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">200px</span>;</span><br><span class="line"> <span class="attribute">border</span>: solid <span class="number">1px</span> <span class="number">#e3c1a9</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">30px</span>;</span><br><span class="line"> <span class="comment">/* Key css */</span></span><br><span class="line"> <span class="attribute">writing-mode</span>: vertical-lr;</span><br><span class="line"> <span class="attribute">text-align</span>: center;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.content-wrap</span>{</span><br><span class="line"> <span class="comment">/* Key css */</span></span><br><span class="line"> <span class="attribute">writing-mode</span>: horizontal-tb;</span><br><span class="line"> <span class="attribute">display</span>: inline-block;</span><br><span class="line"> <span class="attribute">text-align</span>: center;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.content</span> {</span><br><span class="line"> <span class="comment">/* Key css */</span></span><br><span class="line"> <span class="attribute">display</span>: inline-block;</span><br><span class="line"> <span class="attribute">margin</span>: auto;</span><br><span class="line"> <span class="attribute">text-align</span>: left;</span><br><span class="line"> <span class="attribute">padding</span>: <span class="number">20px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">10px</span>;</span><br><span class="line"> <span class="attribute">background-color</span>: <span class="number">#e3c1a9</span>;</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#ffffff</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcodepen.io%2Fqianlong%2Fembed%2Fpreview%2FvYRJErY%3Fdefault-tabs%3Dcss%252Cresult%26height%3D600%26host%3Dhttps%253A%252F%252Fcodepen.io%26slug-hash%3DvYRJErY&display_name=CodePen&url=https%3A%2F%2Fcodepen.io%2Fqianlong%2Fpen%2FvYRJErY&image=https%3A%2F%2Fshots.codepen.io%2Fqianlong%2Fpen%2FvYRJErY-512.jpg%3Fversion%3D1658679041&key=a19fcc184b9711e1b4764040d3dc5c07&type=text%2Fhtml&schema=codepen" allowfullscreen="" frameborder="0" height="600" width="100%" title="虚拟机" class="fj aq as ag cf" scrolling="auto"></iframe>
<h2 id="table(不推荐)"><a href="#table(不推荐)" class="headerlink" title="table(不推荐)"></a>table(不推荐)</h2><p>提到它只是作为学习的一个例子,我不建议你在工作中使用它,因为它(在我看来有点)很糟糕。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">table</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">tbody</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">tr</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">td</span> <span class="attr">class</span>=<span class="string">"container"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">contenteditable</span>=<span class="string">"true"</span> <span class="attr">class</span>=<span class="string">"content"</span>></span>hello medium<span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">td</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">tr</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">tbody</span>></span></span><br><span class="line"><span class="tag"></<span class="name">table</span>></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><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">400px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">200px</span>;</span><br><span class="line"> <span class="attribute">border</span>: solid <span class="number">1px</span> <span class="number">#e3c1a9</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">30px</span>;</span><br><span class="line"> <span class="comment">/* Key css */</span></span><br><span class="line"> <span class="attribute">text-align</span>: center;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.content</span> {</span><br><span class="line"> <span class="comment">/* Key css */</span></span><br><span class="line"> <span class="attribute">display</span>: inline-block;</span><br><span class="line"> <span class="attribute">padding</span>: <span class="number">20px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">10px</span>;</span><br><span class="line"> <span class="attribute">background-color</span>: <span class="number">#e3c1a9</span>;</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#ffffff</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcodepen.io%2Fqianlong%2Fembed%2Fpreview%2FyLKoyqv%3Fdefault-tabs%3Dcss%252Cresult%26height%3D600%26host%3Dhttps%253A%252F%252Fcodepen.io%26slug-hash%3DyLKoyqv&display_name=CodePen&url=https%3A%2F%2Fcodepen.io%2Fqianlong%2Fpen%2FyLKoyqv&image=https%3A%2F%2Fshots.codepen.io%2Fqianlong%2Fpen%2FyLKoyqv-512.jpg%3Fversion%3D1658679386&key=a19fcc184b9711e1b4764040d3dc5c07&type=text%2Fhtml&schema=codepen" allowfullscreen="" frameborder="0" height="600" width="100%" title="yLKoyqv" class="fj aq as ag cf" scrolling="auto"></iframe>
<div class="note purple no-icon flat"><p>原文链接:<a href="https://javascript.plainenglish.io/10-css-tricks-you-should-know-for-centering-elements-61092d35b659">10 CSS Tricks You Should Know for Centering
深入前端架构的前世今生
https://fe32.top/articles/fe0814mn/
2022-08-14T10:27:49.000Z
2023-05-17T10:01:12.000Z
<h2 id="前端架构的前世今生"><a href="#前端架构的前世今生" class="headerlink" title="前端架构的前世今生"></a>前端架构的前世今生</h2><h3 id="架构是如何产生的?"><a href="#架构是如何产生的?" class="headerlink" title="架构是如何产生的?"></a>架构是如何产生的?</h3><p>刚开始时,前端是没有架构的。为什么会这么说呢?</p>
<p>因为 js 在刚开始诞生时,它的目的是为了让用户在 <strong>页面上的交互</strong> 会更加地流畅,仅仅只是作为一个工具来使用。通常情况下,那个时候的前端代码会内嵌到 <strong>后端的应用</strong> 中来使用。</p>
<p>随着时间的推移和前端的发展,在一个网页当中,js 的代码会越来越多,交互也变得越来越复杂。于是有了一些后端的架构,比如说:后端<code>MVC</code>框架。</p>
<h3 id="MVC架构"><a href="#MVC架构" class="headerlink" title="MVC架构"></a>MVC架构</h3><p>所谓 MVC 架构,即将前端的渲染体系,从后端的服务体系中拆解出去。后端 MVC 架构具有以下特点:</p>
<ul>
<li>将视图层、数据层、控制层做分离,后端的服务主要在数据层和控制层</li>
<li>缺点:重度依赖开发环境,代码混淆严重,复杂度比较高</li>
</ul>
<h3 id="前后端分离架构"><a href="#前后端分离架构" class="headerlink" title="前后端分离架构"></a>前后端分离架构</h3><p>所谓前后端分离架构,即将前端代码从后端环境中提炼出来( ajax 促进了 <strong>前后端分离架构</strong> 的发展),也就是我们所说的 <strong>多页面架构</strong> 。下面我们用一张图来了解前后端分离架构的特点:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/15/62f91c0da4bcd.png" alt="前后端分离架构"/></div><span class="image-caption">前后端分离架构</span></div>
<p>即使出现了前后端分离架构,但它还是存在一定的缺点。比如:前端缺乏 <strong>独立部署能力</strong> ,整体流程依赖后端环境。</p>
<p>针对存在的这些问题,后来也就有了 Nodejs 。</p>
<h3 id="Nodejs"><a href="#Nodejs" class="headerlink" title="Nodejs"></a>Nodejs</h3><p>Nodejs 的广泛适应促进了前端技术的飞速发展,因为 Node 的出现,各种 <strong>打包</strong> 、 <strong>构建工具</strong> 应运而生。同时,也诞生了 <strong>多元化</strong> 的前端开发方式,使得前端开发可以 <strong>脱离整体后端环境</strong> 。</p>
<p>Nodejs 的出现,使得前端产生了新的架构体系,即 <strong>单页面架构</strong> 。</p>
<h3 id="单页面架构"><a href="#单页面架构" class="headerlink" title="单页面架构"></a>单页面架构</h3><p>(1) 现有单页面架构</p>
<p>市面上流行的单页面架构有以下几种类型:</p>
<ul>
<li>打包:gulp、rollup、webpack和vite……</li>
<li>框架:vue/react/angular/……</li>
<li>ui库:antd/iview/elementui/mintui……</li>
</ul>
<p>(2) 单页面架构的优势</p>
<ul>
<li>切换页面无刷新浏览器,用户体验号</li>
<li>组件化开发方式,极大提升了代码复用率</li>
</ul>
<p>(3) 单页面架构的劣势</p>
<p>单页面架构有一定的优势,但同时也存在一定的不足。具体如下:</p>
<ul>
<li>单页面架构所有的内容都是由 js 来渲染的,因此会不利于 SEO ,也不利于跨端搜索,因为它只有一个简单的 html 文件。</li>
<li>且由于所有的渲染都是由 js 来控制的,因此首次渲染会出现较长时间的白屏。但比较好的一点是,这两个存在的问题都可以利用其他技术手段来解决。</li>
</ul>
<p>说到这里,细心的同学可能会发现一个问题,所有的前端内容都还只停留在 <strong>浏览器阶段</strong> ,只是跟用户在做一些简单的交互。那有没有可能有一种情况,我们的 js 也可以写一些服务端的逻辑呢?比如说:连接数据库、做一些增删改查的操作或者一些运维的工作。基于这样的背景,大前端时代到来了。</p>
<h3 id="大前端时代"><a href="#大前端时代" class="headerlink" title="大前端时代"></a>大前端时代</h3><p>大前端时代的到来,为开发者们带来了一些服务端的框架。比如:</p>
<ul>
<li>后端框架:express、koa</li>
<li>包管理工具:npm、yarn</li>
<li>node版本管理:nvm</li>
</ul>
<p>综上,大前端时代给我们提供了一定的便利,但同时也带来了一定的弊端。比如:</p>
<ul>
<li>所有的代码基本都可以通过应用拆分来实现细颗粒度,但是呢,这样细颗粒度的拆分也会导致一些维护上的困难,即过于灵活的实现也导致了前端应用拆分过多,维护困难。</li>
<li>往往一个功能或需求会跨两三个项目来进行开发,这样会增加很大的开发负担。</li>
</ul>
<p>基于以上弊端的存在,现如今衍生出许多新型的架构,比如微前端等。</p>
<h3 id="微前端架构"><a href="#微前端架构" class="headerlink" title="微前端架构"></a>微前端架构</h3><p>微前端等新型架构有以下特点:</p>
<ul>
<li>跟技术栈无关,可以使用<code>vue</code>、<code>react</code>、<code>angular</code>、<code>html</code>原生等技术栈;</li>
<li>主框架不限制所接入应用的技术栈,微应用具备完全自主权;</li>
<li>可以进行独立开发和独立部署。</li>
</ul>
<p>微前端架构也是一样的。它具有以下优点:</p>
<ul>
<li>增量升级 - 不论是上线、发布还是开发,都可以使用增量的方式来升级;</li>
<li>微前端是一种非常好的实施渐进式重构的手段和策略;</li>
<li>所谓渐进式重构,即不断地对既有代码进行抽象、分离和组合。有时候在我们的大型应用里,我们需要把一部分页面通过重构的方式来进行重写或优化,以让代码后期可复用、无副作用和逻辑更单一,而这所使用的方式就是渐进式重构;</li>
<li>微应用仓库独立,前后端可独立开发,主框架自动完成同步更新;</li>
<li>微前端的每一个子应用,都是可以独立运行的;</li>
<li>每个微应用之间的状态是隔离的,运行时状态不共享。</li>
</ul>
<p>微前端架构劣势如下:</p>
<ul>
<li>接入难度高 - 微前端的整体理念跟我们以往的开发方式还略有不同,初步理解时会稍有困难,因此会有较长一段时间的接受程度。</li>
<li>应用场景 - 在移动端较少、而在 PC 端 和 后台管理端 应用较多。</li>
</ul>
<h2 id="软件设计原则与分层"><a href="#软件设计原则与分层" class="headerlink" title="软件设计原则与分层"></a>软件设计原则与分层</h2><h3 id="软件设计原则"><a href="#软件设计原则" class="headerlink" title="软件设计原则"></a>软件设计原则</h3><p>(1)单一职责原则</p>
<p>概念: 永远不应该有多于一个原因来改变某个类。</p>
<p>理解: 对于一个类而言,应该仅有一个引起它变化的原因。</p>
<p>应用: 如果一个类拥有了两种职责,那就可以将这个类分为两个类。</p>
<p>相信看上面的概念可能很多小伙伴会觉得很枯燥,接下来我们用一个例子来进行阐述。具体如下:</p>
<ul>
<li>比如说我们现在在做 <strong>登录页面</strong> ,这个页面会有 <strong>两个验证功能</strong> 。第一个是验证用户的用户名,第二个是验证用户的密码。那假设说我们把这两个功能放在同一个方法里,这不仅仅会不利于查找错误的原因,而且在一个方法里面,我们会写很多的逻辑判断来验证我们当前所验证的字段是用户名还是密码。</li>
<li>这样做,不仅仅会加重我们开发的负担,而且在我们后期的开发过程中,维护起来也是比较困难的。因此在这个时候,我们就可以将验证用户名和验证密码这两个功能,拆分成两个方法去进行实现。</li>
</ul>
<p>(2)开放封闭原则</p>
<p>概念: 软件实体扩展应该是开放的,但对于修改应该是封闭的。</p>
<p>理解: 在我们整体的开发过程中,封装的类或者方法,应该是便于扩展而不便于修改的。也就是说:“可以去扩展某个类或者某个方法,但是尽量不要去对它进行修改”。</p>
<p>应用:当需求有改动,尽量用继承或组合的方式来扩展类的功能,而不是直接修改类的代码。</p>
<p>同样我们用一个例子来阐述开放封闭原则,具体如下:</p>
<ul>
<li>在上面验证用户名和验证密码功能的基础上,我们继续添加一个核对图片验证码的功能。在输入了用户名和密码之后,需要让用户再输入验证码,只有验证码输入正确后,才可以进行正常登录。</li>
<li>那这个时候可能有的小伙伴会想去修改验证密码这个函数的功能,那么我们不仅仅要付出大量的开发时间,还有多出很多的测试内容要去测试。同时,上线了之后,我们也不能保证是否会对验证密码这个功能造成影响。所以这样做,后期的维护成本是相当大的。</li>
<li>因此,我们应该先去封装一个新的函数,来验证图片验证码的功能。这样处理,即使是线上环境出了问题,我们也可以更好地追溯到出现问题的源头。</li>
</ul>
<p>(3)里氏替换原则</p>
<p>概念: 父类在使用的过程中,一定是能够被子类替换的。</p>
<p>理解:</p>
<ul>
<li>那么也就是说,我们在使用父类方法的时候,如果说将父类的引用化为子类的引用,那么对于整体的程序设计来说,是没有任何影响的。里氏替换可能在我们适应面向对象的时候,会不断的注意到。而对于现在大部分在使用函数式编程时,对里氏替换原则的关注度,可能就没有那么高了。</li>
<li>对于架构设计而言,里氏替换原则是一个比较重要的原则,我们需要对一些整体的程序做一些可替换的操作。</li>
</ul>
<p>(4)最少知识原则</p>
<p>概念: 只与你最直接的对象交流(即与你直接关联的对象)。</p>
<p>理解: 低耦合,高内聚。</p>
<p>应用: 做系统设计时,尽量减少依赖关系。</p>
<p>(5)接口隔离原则</p>
<p>概念: 一个类与另一个类之间的依赖性,应该依赖于尽可能小的接口。</p>
<p>理解: 不要对外暴露没有实际意义的接口,用户不应该依赖它不需要的接口。</p>
<p>应用: 当需要对外暴露接口时,如果是非必要对外提供,尽量删除。</p>
<p>(6)依赖导致原则</p>
<p>概念:高层模块不应该依赖于低层模块,他们应该依赖于抽象。抽象不应该依赖于细节,而细节应该依赖于抽象。</p>
<p>理解:应该面向接口编程,不应该面向实现类编程。</p>
<p>注意点:并不是说,所有的类都要有一个对应的接口,而是说,如果有接口,那就尽量使用接口来编程。</p>
<p>(7) 总结</p>
<p>将以上六大原则的英文首字母拼在一起就是<code>SOLID</code>(稳定的),所以也称之为<code>SOLID</code>原则。</p>
<p>只有满足了这六大原则之后,才能设计出稳定的软件架构!</p>
<h3 id="补充设计原则"><a href="#补充设计原则" class="headerlink" title="补充设计原则"></a>补充设计原则</h3><h4 id="组合-x2F-聚合复用原则"><a href="#组合-x2F-聚合复用原则" class="headerlink" title="组合/聚合复用原则"></a>组合/聚合复用原则</h4><ul>
<li>当要扩展类的功能时,优先考虑使用组合,而不是继承。</li>
<li>该原则在 23 种经典设计模式中频繁使用。</li>
<li>如:代理模式、装饰模式、适配器模式等等。</li>
</ul>
<h4 id="无依赖原则"><a href="#无依赖原则" class="headerlink" title="无依赖原则"></a>无依赖原则</h4><ul>
<li>当<code>A</code>模块依赖于<code>B</code>模块,<code>B</code>模块依赖于<code>C</code>模块,<code>C</code>模块依赖于<code>A</code>模块,此时将出现循环依赖。</li>
<li>在设计中避免该问题,可通过引入 “中介者模式” 解决。</li>
</ul>
<h4 id="共同封装原则"><a href="#共同封装原则" class="headerlink" title="共同封装原则"></a>共同封装原则</h4><ul>
<li>应该将易变的类放在同一个包里,将变化隔离出来。</li>
<li>该原则是 “开放-封闭原则” 的延伸。</li>
</ul>
<h4 id="共同重用原则"><a href="#共同重用原则" class="headerlink" title="共同重用原则"></a>共同重用原则</h4><ul>
<li>如果我们重用了某个包中的一个类,那么这个包又还需要依赖于它的一个父类。</li>
<li>这样就相当于重用了包中的所有类,因此我们要尽可能减少包的大小。</li>
</ul>
<h4 id="好莱坞原则"><a href="#好莱坞原则" class="headerlink" title="好莱坞原则"></a>好莱坞原则</h4><ul>
<li>秉承<code>Don't call me, I‘ll call you.</code>的原则;</li>
<li>控制反转(或称为“依赖注入”);</li>
<li>不需要主动创建对象,而是由容器帮我们来创建并管理这些对象。</li>
</ul>
<h4 id="其他设计原则"><a href="#其他设计原则" class="headerlink" title="其他设计原则"></a>其他设计原则</h4><ul>
<li>不要重复你自己 —— 不要让重复的代码到处都是,要让它们足够的重用,所以要尽可能地封装。</li>
<li>保持它简单与傻瓜 —— 保持系统界面简洁,功能实用,操作方便。</li>
<li>高内聚与低耦合 —— 模块内部需要做到内聚度高,模块之间需要做到耦合度低。</li>
<li>关注点分离 —— ①将一个复杂的问题分离为多个简单的问题,然后逐个解决。②难点:如何进行分离。</li>
<li>你不需要它 —— ①不要一开始就把问题设计的非常复杂,不要陷入“过渡设计”的深渊;②让系统足够简单,而又不失去扩展性。</li>
</ul>
<h2 id="软件分层设计"><a href="#软件分层设计" class="headerlink" title="软件分层设计"></a>软件分层设计</h2><p>软件的设计分层可以分为4个部分,分别是: <strong>系统级架构</strong> 、<strong>应用级架构</strong> 、<strong>代码级架构和</strong> 和 <strong>模块级别架构</strong> 。具体如下图所示:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/15/62f91c113335a.png" alt="软件设计分层"/></div><span class="image-caption">软件设计分层</span></div>
<h3 id="系统级架构"><a href="#系统级架构" class="headerlink" title="系统级架构"></a>系统级架构</h3><ul>
<li>应用在整个系统内,如与后台服务如何通信,与第三方系统如何集成。</li>
<li>设计前端首要条件:了解前端系统与其他系统之间的关系。其中关系包括:业务关系和协同机制。</li>
<li>设计后端首要条件:只需要规定与后台数据传递的机制。</li>
<li>包括:api 设计规则,访问授权的一个开放标准(OAuth)跳转 token 的验证,数据传递 cookie 等。</li>
<li>前端与后端的关系考虑的主要因素是:前后端分离的架构设计。</li>
<li>前后端分离架构其实是如何实施技术决策,包括用户鉴权、API接口管理和设计、API文档管理、Mock的使用、BFF(服务于前端的后端,nodejs),以及是否需要服务端渲染等。</li>
</ul>
<h3 id="微前端"><a href="#微前端" class="headerlink" title="微前端"></a>微前端</h3><ul>
<li>在一个系统内微前端是应用间的架构方案。</li>
<li>在多个应用之间,微前端则是一种系统间等架构方案。</li>
<li>微前端是将多个前端应用以某种形式结合在一起进行应用。</li>
<li>旨在解决单体应用在一个相对长的时间跨度下,由于参与的人员、团队的增多、变迁,从一个普通应用演变成一个巨石应用(FrontEnd Monolith)后,随之而来的是应用不可维护的问题。</li>
<li>单实例:即同一时刻,只有一个子应用被展示,子应用具备一个完整的应用生命周期。</li>
<li>多实例:通常基于 url 的变化来做子应用的切换,值得注意的是,同一时刻可展示多个子应用。</li>
<li>通常使用 Web Components 方案来做子应用封装,子应用更像是一个业务组件而不是应用。</li>
</ul>
<h3 id="应用级架构"><a href="#应用级架构" class="headerlink" title="应用级架构"></a>应用级架构</h3><ul>
<li>应用级架构可以看作是系统级别架构的细化。</li>
<li>单个应用与其他外部应用的关系,微服务架构下多个应用的协作、数据交换等。</li>
<li>应用级架构举例:脚手架、模式库和设计系统</li>
</ul>
<h3 id="模块级架构"><a href="#模块级架构" class="headerlink" title="模块级架构"></a>模块级架构</h3><p>这部分内容是我们开始业务编码之前进行设计,我们称之为迭代。</p>
<h3 id="代码级架构"><a href="#代码级架构" class="headerlink" title="代码级架构"></a>代码级架构</h3><p>主要谈论:规范与原则。</p>
<blockquote>
<p>上面我们谈到软件的 4 种设计分层,同时对于这 4 种设计分层来说,它的难度是 <strong>逐级递减</strong> 的。<br>在开发中,要注意 <strong>可维护性</strong> 和 <strong>可扩展性</strong>。<br>简单的代码可维护性高;越是写得抽象的代码越难维护。</p>
</blockquote>
<h2 id="如何保证架构的质量"><a href="#如何保证架构的质量" class="headerlink" title="如何保证架构的质量"></a>如何保证架构的质量</h2><h3 id="系统的稳定性和健壮性"><a href="#系统的稳定性和健壮性" class="headerlink" title="系统的稳定性和健壮性"></a>系统的稳定性和健壮性</h3><h4 id="稳定性"><a href="#稳定性" class="headerlink" title="稳定性"></a>稳定性</h4><p>定义:当一个实际的系统处于一个平衡的状态时,如果受到外来作用的影响时,系统经过一个过渡过程仍然能够回到原来的平衡状态,我们称这个系统就是稳定的,否则称系统不稳定。</p>
<p>特点:</p>
<ul>
<li>架构设计的基石</li>
<li>可以更好地实现自我修复</li>
</ul>
<h4 id="健壮性"><a href="#健壮性" class="headerlink" title="健壮性"></a>健壮性</h4><p>定义:计算机软件在输入错误、磁盘故障、网络过载或有意攻击情况下,能否不死机、不崩溃,就是该软件健壮性的具体表现。</p>
<p>解释:一个系统容错能力强,运行不易被干扰,安全性好。</p>
<p>度量标准:</p>
<ul>
<li>一个软件可以从错误的输入中推断出正确合理的输入;</li>
<li>一个软件可以正确的运行在不同环境下;</li>
<li>一个软件能够检测到自己的内部设计或者编码错误,并得到正确的结果。</li>
</ul>
<h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><ul>
<li>健壮性和稳定性是特定软件自身的要求;</li>
<li>健壮性和稳定性是软件处理的一部分;</li>
<li>软件架构的健壮性和稳定性是该软件规划时所确定的目标;</li>
<li>若软件的实现未能达到原定的目标,则该软件的健壮性和稳定性不够或不好。</li>
</ul>
<h3 id="架构质量的衡量"><a href="#架构质量的衡量" class="headerlink" title="架构质量的衡量"></a>架构质量的衡量</h3><p>对于日常开发过程中的软件架构来说,总会有好的架构和不好的架构设计。那对于架构质量来说,有以下几点衡量指标:</p>
<ul>
<li>理解难度</li>
<li>崩溃率和错误率的指标</li>
<li>接入依赖的成本</li>
<li>开发效率</li>
<li>错误上报和信息收集等功能</li>
</ul>
<h2 id="架构尹始"><a href="#架构尹始" class="headerlink" title="架构尹始"></a>架构尹始</h2><h3 id="架构前期准备"><a href="#架构前期准备" class="headerlink" title="架构前期准备"></a>架构前期准备</h3><p>都说正确的选择是良好架构的开端,也就是说,如果前期我们把所有需要准备的内容准备好,那么在做架构设计时,也将会让架构设计更加的得心应手。下面我们就来谈论几点架构前期需要准备的内容。</p>
<h4 id="架构师分类"><a href="#架构师分类" class="headerlink" title="架构师分类"></a>架构师分类</h4><p>系统架构师👇:</p>
<ul>
<li>从系统的维度出发,负责整体系统的架构设计;</li>
<li>主要是基础服务和各系统间协调,着眼全局;</li>
<li>比如关注负载、可靠性、伸缩、扩展、整体项目切分、缓存应用等方面的基础架构设计。</li>
</ul>
<p>应用架构师👇:</p>
<ul>
<li>从应用程序的维度出发,负责某个应用的技术架构,主要偏业务系统。</li>
<li>关注和理解业务,梳理模型、设计模式、接口和数据交互等方面。</li>
</ul>
<p>业务架构师👇:</p>
<ul>
<li>从业务流程的维度出发,对某一个行业、业务的领域做相应的分析,获取领域模型,最终获得系统的模型。</li>
<li>也可以称为是领域专家、行业专家、产品咨询师和资深顾问。</li>
</ul>
<h4 id="技术前期贮备"><a href="#技术前期贮备" class="headerlink" title="技术前期贮备"></a>技术前期贮备</h4><ul>
<li>技术选型: 社区氛围、发展规模、未来发展趋势、与当前团队的契合度、执行成本、维护和迁移成本、执行效率等内容的调研和报告。想</li>
<li>充分调研每一项技术可能带来的利与弊。</li>
<li>最大程度上预测架构设计中的缺陷,以防重大问题的发生。</li>
</ul>
<h4 id="技术优化"><a href="#技术优化" class="headerlink" title="技术优化"></a>技术优化</h4><p>在架构发展过程中,可能会存在一些有悖于当前架构设计的实现,造成了架构发展阻塞,所以需要进行架构优化,使得架构设计的适应性更高。</p>
<h4 id="架构优化"><a href="#架构优化" class="headerlink" title="架构优化"></a>架构优化</h4><ul>
<li>架构不是一蹴而就的,即不是一成不变的。在业务发展的过程中,架构也在不断地演进。</li>
<li>因此要对架构设计进行实时调优,使架构优化成为常态化。</li>
<li>通过不断的调整架构实现,改进初始架构中设计的不足来补足短板。</li>
</ul>
<h3 id="技术填补"><a href="#技术填补" class="headerlink" title="技术填补"></a>技术填补</h3><h4 id="编码方案问题"><a href="#编码方案问题" class="headerlink" title="编码方案问题"></a>编码方案问题</h4><p>在开发中,有时候会因为开发时间紧急的原因,让我们写的代码有些粗糙。如果每次都不让自己去追求完美,而是一味的妥协现有不好用的编码方案。久而久之,很容易使得整个软件架构零散成一地,像是东拼西凑的,没有任何支撑点可言。常见问题的存在形式有以下几大方面:</p>
<ul>
<li>开发过程中因为时间紧迫导致的实现不合理 —— 比如:查找10000以内的质数,利用循环或筛选法等方式来进行实现。</li>
<li>暂时没有想到更好的实现方式而妥协的版本 —— 刚开始使用 if……else 实现,慢慢地演变成责任链的模式。</li>
<li>架构设计前期没有考虑到的细节 —— ①交互细节→ props 传递参数(交互冗余,流程较长);②使用全局状态管理实现参数传递。</li>
<li>不合理的交互设计,导致技术实现复杂。</li>
<li>旧功能文档缺失,无正确拓展、修改和兼容旧功能,导致上线后问题剧增。</li>
</ul>
<h4 id="技术填补会引发的后果"><a href="#技术填补会引发的后果" class="headerlink" title="技术填补会引发的后果"></a>技术填补会引发的后果</h4><p>在上面中我们举例了各种软件编码过程中修修补补的方案,那这样不断的技术填补后,会导致什么样的后果么?具体表现有:</p>
<ul>
<li>修复变重构 —— 小的技术债务不做偿还,最后会演变成一场大规模的重构工作,导致产出不高;</li>
<li>影响开发速度 —— 技术债务的存在导致整体开发需要减容的点过多,影响开发效率。极大影响上线速度,导致整体项目迭代缓慢,失去核心竞争力;</li>
<li>开发死循环 —— 容易陷入维护旧功能→开发新功能→兼容旧功能→维护旧功能→开发新功能……这样的恶性循环。</li>
</ul>
<h4 id="技术填补解决方案"><a href="#技术填补解决方案" class="headerlink" title="技术填补解决方案"></a>技术填补解决方案</h4><p>上面说到了技术填补会引发的后果,那后果产生了,自然也就需要有对应的解决方案来弥补。</p>
<p>具体解决方案有以下几种:</p>
<ul>
<li>优秀的架构设计是基础 —— ①必须能够有效处理当前需求中可预见的情况,而对于未知的、可能出现的特殊情况,一般很小的改动就能解决问题;②根据当前的业务,进行合理的项目拆分,尽量地进行代码解耦,减少高耦合度的情况发生;③必须有日志模块,操作日志,错误日志,业务日志等等。<br>良好的技术培训和传帮带能力 —— ①让每一位开发者可以从更深一层次理解自己所需要实现的功能;②从最开始的代码规范、到熟悉业务、最后再到编写文档。(传帮带)</li>
<li>充分的技术方案可避免一部分技术债务的产生 —— 技术方案是充分理解需求之后所能产出的对需求理想的实现方式,必要性不言而喻。</li>
<li>不同工程师之间可以相互review —— CodeReview 是非常重要的,同时也是对自身的一个提高。</li>
<li>提升对修复技术债务重要性的认知 —— 工程师如果能预见一个债务可能导致的问题,自然愿意花时间去处理。</li>
<li>善于发现和定期处理一些技术债务 —— 勇于发现系统中的技术债务,让自己为系统负责。</li>
</ul>
<p>总结来说就是:</p>
<p>等产品上线后,开发就没有那么紧张了,这个时候就可以找个时间来处理下遗留下来的技术债务,在解决技术债务中不断去突破自己的上限,更好地提高自己的技术能力和软件架构设计的能力。</p>
<h3 id="奔溃预防"><a href="#奔溃预防" class="headerlink" title="奔溃预防"></a>奔溃预防</h3><p>架构奔溃是严重的架构设计事故,也是我们需要预防的关键所在。在软件奔溃的时候,首先要分析原因:①系统奔溃的产生;②日志记录,如:操作日志,错误日志,业务日志等。</p>
<p>分析完成原因之后,接下来要讲的是,如何预防架构奔溃。主要有以下几点:</p>
<ul>
<li>用户行为抓取→争取在最新时间获取到用户操作链条</li>
<li>解决存量问题→技术债务</li>
<li>遏制新增→减少新增问题的概率</li>
<li>对脏数据进行兜底和校验</li>
<li>单元测试</li>
<li>奔溃预警</li>
<li>自动化测试</li>
<li>更广的灰度触达</li>
<li>性能优化体系</li>
<li>……</li>
</ul>
<h3 id="系统重构"><a href="#系统重构" class="headerlink" title="系统重构"></a>系统重构</h3><p>架构并不是永恒不变的,架构也是具有生命周期的,也会经历初生、发展、巅峰、衰弱、消亡的过程。那下面就来了解软件重构的相关内容。</p>
<h4 id="基础概念"><a href="#基础概念" class="headerlink" title="基础概念"></a>基础概念</h4><p>什么是重构:</p>
<ul>
<li>对软件内部结构的一种调整。</li>
<li>目的是在不改变可观察行为的前提下,提高其可理解性,降低其修改成本。</li>
</ul>
<p>实现方式:</p>
<ul>
<li><p>使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。<br>重构理念:</p>
</li>
<li><p>运用大量微小且保持软件行为的步骤,一步步达成大规模的修改。</p>
</li>
</ul>
<h4 id="早期和晚期的系统"><a href="#早期和晚期的系统" class="headerlink" title="早期和晚期的系统"></a>早期和晚期的系统</h4><p>早期系统特点:</p>
<ul>
<li>开发速度快</li>
<li>代码复杂度低</li>
<li>代码规范都保持完好</li>
<li>严格注重开发规范,不会允许危及架构设计的代码产生</li>
<li>以上因素导致添加功能难度低,成本低</li>
</ul>
<p>晚期系统特点:</p>
<ul>
<li>具备所有早期系统的劣势</li>
<li>代码复杂度高</li>
<li>代码规范不完善</li>
<li>很多需求或功能会出现为了愉悦架构设计的情况</li>
<li>添加新功能兼顾较多,涉及较多模块,牵一发而动全身</li>
</ul>
<h4 id="什么时候需要重构"><a href="#什么时候需要重构" class="headerlink" title="什么时候需要重构"></a>什么时候需要重构</h4><ul>
<li>当发现现有系统体系已经不能满足当前迭代速度的时候,就需要进行重构工作。</li>
<li>对【坏味道】的代码通过一些重构手段进行微重构。</li>
</ul>
<h4 id="重构流程"><a href="#重构流程" class="headerlink" title="重构流程"></a>重构流程</h4><p>重构一般具有以下流程:</p>
<ul>
<li>确定问题点,确定重构功能和范围;</li>
<li>根据旧架构中已有的设计,梳理重构后软件应有的逻辑走向;</li>
<li>确保重构后的软件具有一定的稳定性和性能上的优化;</li>
<li>解决此前在需求过程中存在的冲突问题。</li>
</ul>
<div class="note info flat"><p>原文链接:<a
Hexo + Butterfly 一些常见问题
https://fe32.top/articles/hexo1612/
2022-08-07T14:54:07.000Z
2024-03-13T07:48:57.953Z
Hexo + Butterfly 博客搭建中遇到的一些常见问题!
推荐 10 套个人觉得还不错的网页模板
https://fe32.top/articles/hweb0804/
2022-08-04T13:02:47.000Z
2023-05-17T10:01:12.000Z
<blockquote>
<p>这里推荐 10 套 从风格,配色,响应式等几方面个人觉得还不错的网页模板,假以时日用来做企业站,还是某种品牌的官网也好,觉得可以借鉴某种效果或者风格的朋友可以看看。</p>
</blockquote>
<h2 id="Demo-01"><a href="#Demo-01" class="headerlink" title="Demo-01"></a>Demo-01</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebe968df42c.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebe96b0868a.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebe96ce9155.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebe96f4df67.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebe976b7f9d.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebe976e0f0e.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebe9779c676.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebe978c7165.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebe97b6115b.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebe97ee0e96.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebe9807c7bd.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebe9839486e.jpg"/></div></div>
<h2 id="Demo-02(推荐)"><a href="#Demo-02(推荐)" class="headerlink" title="Demo-02(推荐)"></a>Demo-02(推荐)</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebea7a7ae1e.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebea7a3610e.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebea7cb0025.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebea7e56159.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebea801e35c.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebea833702b.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebea84ae4f4.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebea861fbbf.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebea8b4196d.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebea8f69ac7.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebea91bb88f.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebea957a9be.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebea97e4d60.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebea996aa48.png"/></div></div>
<h2 id="Demo-03"><a href="#Demo-03" class="headerlink" title="Demo-03"></a>Demo-03</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebeb72e69b0.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebeb764b334.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebeb7758225.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebeb7892291.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebeb9489fda.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebeb98c66fa.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebeb9a10700.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebeb9fbd1ca.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebeba4f225d.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebeba74aa38.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebeba897146.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ebebad8b508.png"/></div></div>
<h2 id="Demo-04(推荐)"><a href="#Demo-04(推荐)" class="headerlink" title="Demo-04(推荐)"></a>Demo-04(推荐)</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed378d5caf8.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3799cdf1d.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed379b7cca2.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed379da3bb5.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed37a05e69d.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed37a257ce8.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed37aa32f31.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed37ae05bbf.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed37b5550d7.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed37b715240.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed37bdf3cd1.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed37bf057c3.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed37c25aa71.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed37c540de9.jpg"/></div></div>
<h2 id="Demo-05"><a href="#Demo-05" class="headerlink" title="Demo-05"></a>Demo-05</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed389c40644.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed389f70b33.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed38a32b1cc.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed38a633a6d.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed38a6e40f4.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed38a933e28.jpg"/></div></div>
<h2 id="Demo-06"><a href="#Demo-06" class="headerlink" title="Demo-06"></a>Demo-06</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3993e1bc0.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed399502a14.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3997814f9.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed399cb7d73.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed399eae922.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed39a11fc11.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed39a40fdf8.jpg"/></div></div>
<h2 id="Demo-07(推荐)"><a href="#Demo-07(推荐)" class="headerlink" title="Demo-07(推荐)"></a>Demo-07(推荐)</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3ba1be602.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3ba5d3157.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3ba8b7cbc.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3baa744b9.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3bafd9085.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3bb5009ae.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3bb7de601.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3bbb1fd61.jpg"/></div></div>
<h2 id="Demo-08"><a href="#Demo-08" class="headerlink" title="Demo-08"></a>Demo-08</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3cb201c8a.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3cb53a257.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3cb882da0.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3cbbe6cb1.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3ce071a68.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3ce59a122.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3ce527864.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3ce9f40c5.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3cf165889.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3cf1512d7.jpg"/></div></div>
<h2 id="Demo-09(推荐)"><a href="#Demo-09(推荐)" class="headerlink" title="Demo-09(推荐)"></a>Demo-09(推荐)</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3d4459d86.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3d549aae1.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3d5bba018.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3d5d871f7.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3d5ec964e.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3d60e4e7c.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3d65b4bcd.jpg"/></div></div>
<h2 id="Demo-10(推荐)"><a href="#Demo-10(推荐)" class="headerlink" title="Demo-10(推荐)"></a>Demo-10(推荐)</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3e254a7e3.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3e250644d.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3e2788777.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3e2d0c66b.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3e2c85ce6.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3e2f7ed7e.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3e344ea76.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3e36a6af8.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3e3924847.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/05/62ed3e38af1f5.jpg"/></div></div>
<h2 id="仓库地址"><a href="#仓库地址" class="headerlink" title="仓库地址"></a>仓库地址</h2><a class="ghcard" rel="external nofollow noopener noreferrer" href="https://github.com/tzy13755126023/ten-html-temp"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://github-readme-stats.vercel.app/api/pin/?username=tzy13755126023&repo=ten-html-temp&theme=onedark&show_owner=true"/></a>
<!-- <div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/01/09/3a2053f3f8799.jpg" style="width:500px;"/></div></div>
<p>正在火力书写中…
20 多个好用的 Vue 组件库
https://fe32.top/articles/vu0724se/
2022-07-24T14:45:25.000Z
2023-05-17T10:01:12.000Z
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/27/62e1453e80459.png"/></div></div>
<p>在本文中,将分享一些常见的 vue.js 组件。</p>
<h2 id="Tables-x2F-Data-Grids"><a href="#Tables-x2F-Data-Grids" class="headerlink" title="Tables / Data Grids"></a>Tables / Data Grids</h2><h3 id="Vue-Tables-2"><a href="#Vue-Tables-2" class="headerlink" title="Vue Tables-2"></a>Vue Tables-2</h3><p>地址:<a href="https://github.com/matfish2/vue-tables-2">https://github.com/matfish2/vue-tables-2</a></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/27/62e145477d311.png"/></div></div>
<p>Vue Tables 2 旨在为开发者提供一个功能齐全的工具集,以便用 Vue 创建漂亮而实用的数据表格。数百个商业软件应用正在使用它。此外,Vue Tables 2 正在不断成长、改进,同时也在获得新的功能。</p>
<p>特点如下:</p>
<ul>
<li>可选行及粘性头部</li>
<li>虚拟分页</li>
<li>下载客户组件数据的 CSV</li>
<li>有数据层支持的多级分组</li>
<li>Tailwind 主题</li>
</ul>
<h3 id="Handsontable"><a href="#Handsontable" class="headerlink" title="Handsontable"></a>Handsontable</h3><p>地址:<a href="https://github.com/handsontable/handsontable/tree/master/wrappers/vue">https://github.com/handsontable/handsontable/tree/master/wrappers/vue</a></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/27/62e1454aadda6.png"/></div></div>
<p>Handsontable 是一款页面端的表格交互插件,可以通过它加载显示表格内容,能够支持合并项、统计、行列拖动等。</p>
<p>支持对加载后的表格页面的处理:添加/删除行/列,合并单元格等操作。</p>
<p>此外,它还适用于 React、Angular 和 Vue。Handsontable 是一个 JavaScript 组件,它将数据网格功能与电子表格的用户体验相结合。此外,它还提供数据绑定、数据验证、过滤、排序和 CRUD 操作。</p>
<p>特点如下:</p>
<ul>
<li>多列排序</li>
<li>非连续选择</li>
<li>过滤数据和验证数据</li>
<li>导出文件</li>
<li>有条件的格式化</li>
<li>合并单元格</li>
<li>隐藏行/列</li>
<li>上下文菜单和注释</li>
</ul>
<h3 id="Ag-Grid-Vue"><a href="#Ag-Grid-Vue" class="headerlink" title="Ag Grid Vue"></a>Ag Grid Vue</h3><p>地址:<a href="https://github.com/ag-grid/ag-grid">https://github.com/ag-grid/ag-grid</a></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/27/62e1454e2d6d5.png"/></div></div>
<p>Ag-Grid 是一个基于 Vue.js 的数据表格组件。其中,“ag” 表示 “agnostic”。内部 ag-Grid 引擎是在 TypeScript 中实现的,零依赖关系。</p>
<p>ag-Grid 通过包装器组件支持 Vue,你可以在应用程序中,就像其他任何 Vue 组件一样使用 ag-Grid。它支持通过属性绑定传递配置,通过事件绑定来处理事件。你甚至可以使用 Vue 组件来自定义网格 UI 和单元格内容/行为。</p>
<h3 id="Vue-Easytable"><a href="#Vue-Easytable" class="headerlink" title="Vue Easytable"></a>Vue Easytable</h3><p>地址:<a href="https://github.com/huangshuwei/vue-easytable">https://github.com/huangshuwei/vue-easytable</a></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/27/62e1461d86dac.png"/></div></div>
<p>vue-easytable 是我遇到过的最强大的 Vue 表格组件之一。</p>
<p>表格组件具有许多内置功能,比如说,单元格省略号、固定/灵活的列大小调整、自定义过滤等等。它有几个特点:</p>
<ul>
<li>国际化</li>
<li>主题定制</li>
<li>内置主题</li>
<li>虚拟滚动</li>
<li>列固定</li>
<li>表头固定</li>
<li>表头分组</li>
</ul>
<h3 id="Vue-Good-Table"><a href="#Vue-Good-Table" class="headerlink" title="Vue Good Table"></a>Vue Good Table</h3><p>地址:<a href="https://github.com/xaksis/vue-good-table">https://github.com/xaksis/vue-good-table</a></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/27/62e1462095a19.png"/></div></div>
<p>Vue-Good-Table 是一个基于 Vue.js 的数据表组件,简单、干净,具有排序、列过滤、分页等更多基本功能。它有几个特性:</p>
<ul>
<li>表搜索和排序</li>
<li>列过滤和分页</li>
<li>复选框表格</li>
<li>行分组</li>
<li>行样式</li>
<li>行多选</li>
</ul>
<h2 id="Notification"><a href="#Notification" class="headerlink" title="Notification"></a>Notification</h2><h3 id="Vue-Toastification"><a href="#Vue-Toastification" class="headerlink" title="Vue Toastification"></a>Vue Toastification</h3><p>地址:<a href="https://github.com/Maronato/vue-toastification">https://github.com/Maronato/vue-toastification</a></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/27/62e14622f27b6.png"/></div></div>
<p>它提供轻巧、简单和漂亮的吐司提示。它有内置的 Nuxt 支持。而且,它还支持新的 Composition API 和 Vue 3。我们还可以使用 JSX 来开发自定义组件,提供更加灵活的功能。另外,通用注册允许它在任何应用程序内使用,甚至是 React。它有几点特性:</p>
<ul>
<li>完全用 Typescript 编写,支持所有类型</li>
<li>支持 RTL</li>
<li>定制一切</li>
<li>滑动关闭</li>
<li>使用 onClose、onClick 和 onMounted 钩子创建自定义体验</li>
<li>以编程方式删除和更新吐司</li>
</ul>
<h3 id="Vue-Toasted"><a href="#Vue-Toasted" class="headerlink" title="Vue Toasted"></a>Vue Toasted</h3><p>地址:<a href="https://github.com/shakee93/vue-toasted">https://github.com/shakee93/vue-toasted</a></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/27/62e1462478234.png"/></div></div>
<p>Vue Toasted 是 Vue 最好的 toast (提示)插件之一。它被Vue,Laravel,NuxtJS 等许多组织所信任,它响应性强,兼容性好,使用方便,吸引人,有丰富的功能、图标、动作等。</p>
<h3 id="Vue-Notifications"><a href="#Vue-Notifications" class="headerlink" title="Vue Notifications"></a>Vue Notifications</h3><p>地址:<a href="https://github.com/se-panfilov/vue-notifications">https://github.com/se-panfilov/vue-notifications</a></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/27/62e14629da79e.png"/></div></div>
<p>Vue Notifications 是一个与库无关的通知组件,非阻塞。</p>
<p>VueNotiments 将您的应用程序与通知 UI 库连接起来。支持 miniToastr、VueToasted、VueEasyToast、toastr、iziToast、Noty、swal。</p>
<h3 id="Vue-Awesome-Notifications"><a href="#Vue-Awesome-Notifications" class="headerlink" title="Vue Awesome Notifications"></a>Vue Awesome Notifications</h3><p>地址:<a href="https://f3oall.github.io/awesome-notifications/">https://f3oall.github.io/awesome-notifications/</a></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/27/62e1462e4cf76.png"/></div></div>
<p>Awesome Notifications 是一个轻量级的,完全可自定义的 JavaScrip Vue Awesome Notifications,它是 Awesome Notifications 库的 Vue.js 版本。</p>
<h2 id="Loader"><a href="#Loader" class="headerlink" title="Loader"></a>Loader</h2><h3 id="Vue-Wait"><a href="#Vue-Wait" class="headerlink" title="Vue Wait"></a>Vue Wait</h3><p>地址:<a href="https://github.com/f/vue-wait">https://github.com/f/vue-wait</a></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/27/62e1462e83087.png"/></div></div>
<p>Vue Wait 这是一个用于 VUE、Vuex 和 Nuxt 应用的复杂装载器和进度管理组件。</p>
<p>Vue Wait 帮助管理页面上的多个加载状态,状态之间没有任何冲突。基于一个非常简单的想法:通过管理具有多个加载状态的数组(或者 Vuex 存储),让内置加载程序组件侦听注册的加载程序,并立即进入加载状态。</p>
<h3 id="Vue-Content-Loader"><a href="#Vue-Content-Loader" class="headerlink" title="Vue Content Loader"></a>Vue Content Loader</h3><p>地址:<a href="https://github.com/egoist/vue-content-loader">https://github.com/egoist/vue-content-loader</a></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/27/62e146ea0019d.png"/></div></div>
<p>Vue Content Loader 是一个基于 Vue.js 的 SVG 占位符加载,可自定义的 SVG 组件,用于创建占位符加载,例如 Facebook 加载卡。Vue Content Loader 是 react-content-loader 的 Vue 实现。</p>
<h3 id="Epic-Spinners"><a href="#Epic-Spinners" class="headerlink" title="Epic Spinners"></a>Epic Spinners</h3><p>地址:<a href="https://epic-spinners.epicmax.co/">https://epic-spinners.epicmax.co/</a></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/27/62e146ebd5733.png"/></div></div>
<p>EpicSpinners 是一组易于使用的纯 css 打造的网页 Loading 效果,并且同时整合了 Vue 组件可以方便的在 Vue 项目中进行使用,由于是纯 css 打造,你可以在任意网页项目中自行整合并使用!</p>
<h3 id="Vue-Radial-Progress"><a href="#Vue-Radial-Progress" class="headerlink" title="Vue Radial Progress"></a>Vue Radial Progress</h3><p>地址:<a href="https://github.com/wyzant-dev/vue-radial-progress">https://github.com/wyzant-dev/vue-radial-progress</a></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/27/62e1476deb2c2.png"/></div></div>
<p>Vue Radial Progress 这是一个径向进度条效果的加载器组件,使用 svg 和 javascript 绘制带有渐变径向进度条效果的加载器,可以用作加载、进度提示。</p>
<h2 id="ICONS"><a href="#ICONS" class="headerlink" title="ICONS"></a>ICONS</h2><h3 id="Vue-Feather-Icons"><a href="#Vue-Feather-Icons" class="headerlink" title="Vue Feather Icons"></a>Vue Feather Icons</h3><p>地址:<a href="https://github.com/egoist/vue-feather-icons">https://github.com/egoist/vue-feather-icons</a></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/27/62e1479c50e36.png"/></div></div>
<p>Feather 是一套面向设计师和开发者的开源图标库,是一个简单漂亮的开源图标库。每个图标都设计在一个24×24的网格上,强调简单,一致性和易读性。很多大厂出品的前端框架都内置了这款可以免费商用的图标。它的特点如下:</p>
<ul>
<li>每一枚图标都是按照统一标准进行设计,具有完美像素对齐;</li>
<li>设计风格一致,完胜那些拼凑混搭的图标库;</li>
<li>覆盖多种开发场景的支持,对开发者非常友好。</li>
</ul>
<h3 id="Vue-Awesome"><a href="#Vue-Awesome" class="headerlink" title="Vue Awesome"></a>Vue Awesome</h3><p>地址:<a href="https://github.com/Justineo/vue-awesome">https://github.com/Justineo/vue-awesome</a></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/27/62e1479ec5062.png"/></div></div>
<p>Font Awesome是一套流行的图标字体库,我们在实际开发的过程中会经常遇到需要使用图标的场景,对于一些常用的图标,不用设计师,我们可以直接在Font Awesome中找到并且使用。个人感觉Font Awesome的图标还是很齐全的,绝大多数的图标它都包含了,而且支持各种框架。</p>
<h2 id="Charts"><a href="#Charts" class="headerlink" title="Charts"></a>Charts</h2><h3 id="Vue-Apexcharts"><a href="#Vue-Apexcharts" class="headerlink" title="Vue Apexcharts"></a>Vue Apexcharts</h3><p>地址:<a href="https://github.com/apexcharts/vue-apexcharts">https://github.com/apexcharts/vue-apexcharts</a></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/27/62e147d0d81e5.png"/></div></div>
<p>Apexcharts 是一个现代的 JavaScript 图表库/可通过简单的 API 构建交互式图表和可视化。Vue Apexcharts 是 ApexCharts 的 Vue.js 组件。</p>
<h3 id="Vue-Echarts"><a href="#Vue-Echarts" class="headerlink" title="Vue Echarts"></a>Vue Echarts</h3><p>地址:<a href="https://github.com/Justineo/vue-echarts">https://github.com/Justineo/vue-echarts</a></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/27/62e147d277d5d.png"/></div></div>
<p>vue-echarts 是基于 echarts 封装实现的一个组件库,直接按照正常的组件引用方式,安装引用即可,具体的安装和引用读者可以直接阅读 vue-echarts 技术文档。</p>
<h3 id="Vue-Chartjs"><a href="#Vue-Chartjs" class="headerlink" title="Vue Chartjs"></a>Vue Chartjs</h3><p>地址:<a href="https://github.com/apertureless/vue-chartjs">https://github.com/apertureless/vue-chartjs</a></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/27/62e147fcc7e6e.png"/></div></div>
<p>vue-chartjs 是一个 Vue 对于 Chart.js 的封装,让用户可以在Vue中轻松使用Chart.js,很简单的创建可复用的图表组件,非常适合需要简单的图表并尽可能快地运行的人。vue-chartjs抽象了基本逻辑,同时也暴露了Chart.js对象,让用户获得最大的灵活性。</p>
<h3 id="V-Charts"><a href="#V-Charts" class="headerlink" title="V-Charts"></a>V-Charts</h3><p>地址:<a href="https://github.com/ElemeFE/v-charts">https://github.com/ElemeFE/v-charts</a></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/27/62e147ff76e55.png"/></div></div>
<p>V-Charts 是基于 Vue2.0 和 Echarts 封装的图标组件,只需要统一提供一种对前后端都友好的数据格式设置简单的配置项,就可以生成常见的图表。</p>
<h2 id="时间"><a href="#时间" class="headerlink" title="时间"></a>时间</h2><h3 id="Vue-Timer-Hook"><a href="#Vue-Timer-Hook" class="headerlink" title="Vue Timer Hook"></a>Vue Timer Hook</h3><p>地址:<a href="https://github.com/riderx/vue-timer-hook">https://github.com/riderx/vue-timer-hook</a></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/27/62e14802ce107.png"/></div></div>
<p>Vue3 计时器模块的灵感来自 react-timer-hook。此外,它是一个自定义的钩子,用来处理 vue 3 组件中的定时器、秒表和时间逻辑/状态。</p>
<h3 id="Vue-Horizontal-Timeline"><a href="#Vue-Horizontal-Timeline" class="headerlink" title="Vue Horizontal Timeline"></a>Vue Horizontal Timeline</h3><p>地址:<a href="https://github.com/guastallaigor/vue-horizontal-timeline">https://github.com/guastallaigor/vue-horizontal-timeline</a></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/27/62e14809a0929.png"/></div></div>
<p>Vue Horizontal Timeline 是一个用 Vue.js 制作的简单的水平时间线组件。</p>
<div class="note blue no-icon flat"><p>原文链接:<a href="https://medium.com/js-dojo/20-useful-vue-js-components-2022-3bf9fbe5b556">20+ Useful Vue JS Components
基于 Hexo 键入评论功能
https://fe32.top/articles/hexo1611/
2022-07-24T14:24:28.000Z
2024-02-23T15:35:21.203Z
<blockquote>
<p>注意:本站的评论系统已从 <a href="https://valine.js.org/">Valine</a> 更换成 <a href="https://twikoo.js.org/">Twikoo</a>。</p>
</blockquote>
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><!-- > 本站基于`Hexo`搭建,用的 🦋 [hexo-theme-butterfly](https://github.com/jerryc127/hexo-theme-butterfly.git) 主题 [v3.7.1](https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1),请注意最新的🦋 [hexo-theme-butterfly](https://github.com/jerryc127/hexo-theme-butterfly.git) 版本已经更新到 [v4.8.1](https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.8.1) 。<br>
如果你是 [v3.7.1](https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1) 之外的版本,可能有些地方会有出入,请留意。 -->
<blockquote>
<p>本站基于<code>Hexo</code>搭建,用的 🦋 <a href="https://github.com/jerryc127/hexo-theme-butterfly.git">hexo-theme-butterfly</a> 主题,已经升级到 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.7.0">v4.7.0</a> 。 请注意最新的🦋 <a href="https://github.com/jerryc127/hexo-theme-butterfly.git">hexo-theme-butterfly</a> 版本已经更新到 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.8.1">v4.8.1</a> 。<br><br>如果你是 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1">v3.7.1</a> 的版本,请移步 <a href="https://old.fe32.top/" target="_blank">v3.7.1</a> 站点进行浏览。</p>
</blockquote>
<blockquote>
<p>注意:我的博客根目录路径为 【G:/hexo-blog/blog-demo】,下文所说的根目录都是此路径,将用<code>[BlogRoot]</code>代替。如果不清楚根目录路径,请回到教程 <a href="https://fe32.top/articles/hexo1602/">基于 Hexo 从零开始搭建个人博客(二)</a>,查看你执行<code>hexo init xxx</code>这条命令时所选择的路径,例如我选择的路径是【G:/hexo-blog】,我的博客根目录即为【G:/hexo-blog/xxx】。<br><br>修改站点配置文件<code>_config.yml</code>,路径为【BlogRoot/_config.yml】。<br><br>修改主题配置文件<code>_config.butterfly.yml</code>,路径为【BlogRoot/_config.butterfly.yml】。</p>
</blockquote>
<blockquote>
<p>若你的主题版本大于 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.0.0">v4.0.0</a>,应该留意到,与 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1">v3.7.1</a> 版本相比,各评论的配置项已经变的更为精简,具体请参考你所使用主题版本的配置项,只需要将目标信息,填入评论的配置项就行,这里主要推荐使用 Twikoo。</p>
</blockquote>
<h2 id="推荐阅读"><a href="#推荐阅读" class="headerlink" title="推荐阅读"></a>推荐阅读</h2><ul>
<li><a href="https://fe32.top/articles/hexo1601/">基于 Hexo 从零开始搭建个人博客(一)</a></li>
<li><a href="https://fe32.top/articles/hexo1602/">基于 Hexo 从零开始搭建个人博客(二)</a></li>
<li><a href="https://fe32.top/articles/hexo1603/">基于 Hexo 从零开始搭建个人博客(三)</a></li>
<li><a href="https://fe32.top/articles/hexo1604/">基于 Hexo 从零开始搭建个人博客(四)</a></li>
<li><a href="https://fe32.top/articles/hexo1605/">基于 Hexo 从零开始搭建个人博客(五)</a></li>
<li><a href="https://fe32.top/articles/hexo1606/">基于 Hexo 从零开始搭建个人博客(六)</a></li>
<li><a href="https://fe32.top/articles/hexo1607/">基于 Hexo 键入搜索功能</a></li>
<li><a href="https://fe32.top/articles/hexo1609/">基于 Hexo 键入分享功能</a></li>
<li><a href="https://fe32.top/articles/hexo1610/">基于 Hexo 键入在线聊天功能</a></li>
<li><a href="https://fe32.top/articles/hexo1608/">Hexo + Butterfly 自定义右键菜单</a></li>
<li><a href="https://fe32.top/articles/hexo1612/">Hexo + Butterfly 一些常见问题</a></li>
<li><a href="https://fe32.top/articles/hexo1613/">请收下这只可爱的猫咪吧</a></li>
<li><a href="https://fe32.top/articles/hexo1614/">关于Vercel被墙导致获取Twikoo评论失败的解决方案</a></li>
<li><a href="https://fe32.top/articles/hexo1615/">飞只因太美,给你的首页装上吧!</a></li>
<li><a href="https://fe32.top/articles/hexo1617/">Hexo + Butterfly 自定义页脚</a></li>
<li><a href="https://fe32.top/articles/hexo1618/">Hexo + Butterfly 侧边栏公众号</a></li>
</ul>
<h2 id="通用配置(一定要配置)"><a href="#通用配置(一定要配置)" class="headerlink" title="通用配置(一定要配置)"></a>通用配置(一定要配置)</h2><p>从3.0.0开始,开启评论需要在<code>comments-use</code>中填写你需要的评论,这里参照你主题版本的格式写。</p>
<p>支持双评论显示,只需要配置两个评论(第一个为默认显示)</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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="attr">comments:</span></span><br><span class="line"> <span class="comment"># Up to two comments system, the first will be shown as default</span></span><br><span class="line"> <span class="comment"># Choose: Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo</span></span><br><span class="line"> <span class="attr">use:</span></span><br><span class="line"> <span class="comment"># - Disqus</span></span><br><span class="line"> <span class="comment"># - Valine</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">Twikoo</span> <span class="comment"># 这里按照你主题版本的格式写</span></span><br><span class="line"> <span class="attr">text:</span> <span class="literal">true</span> <span class="comment"># Display the comment name next to the button</span></span><br><span class="line"> <span class="comment"># lazyload: The comment system will be load when comment element enters the browser's viewport.</span></span><br><span class="line"> <span class="comment"># If you set it to true, the comment count will be invalid</span></span><br><span class="line"> <span class="attr">lazyload:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">count:</span> <span class="literal">true</span> <span class="comment"># Display comment count in post's top_img</span></span><br><span class="line"> <span class="attr">card_post_count:</span> <span class="literal">true</span> <span class="comment"># Display comment count in Home Page</span></span><br></pre></td></tr></table></figure>
<table>
<thead>
<tr>
<th align="left">参数</th>
<th align="left">解释</th>
</tr>
</thead>
<tbody><tr>
<td align="left">use</td>
<td align="left">使用的评论(请注意,最多支持两个,如果不需要请留空) <br>注意:双评论不能是 Disqus 和 Disqusjs 一起,由于其共用同一个 ID,会出错</td>
</tr>
<tr>
<td align="left">text</td>
<td align="left">是否显示评论服务商的名字</td>
</tr>
<tr>
<td align="left">lazyload</td>
<td align="left">是否为评论开启lazyload,开启后,只有滚动到评论位置时才会加载评论所需要的资源(开启 lazyload 后,评论数将不显示)</td>
</tr>
<tr>
<td align="left">count</td>
<td align="left">是否在文章顶部显示评论数<br> livere、Giscus 和 utterances 不支持评论数显示</td>
</tr>
<tr>
<td align="left">card_post_count</td>
<td align="left">是否在首页文章卡片显示评论数<br>gitalk、livere 、Giscus 和 utterances 不支持评论数显示</td>
</tr>
</tbody></table>
<h2 id="Twikoo(推荐)"><a href="#Twikoo(推荐)" class="headerlink" title="Twikoo(推荐)"></a>Twikoo(推荐)</h2><blockquote>
<p>Twikoo 是一个简洁、安全、免费的静态网站评论系统,基于腾讯云开发。</p>
</blockquote>
<h3 id="效果预览"><a href="#效果预览" class="headerlink" title="效果预览"></a>效果预览</h3><div class="gallery">
<div class="fj-gallery data" data-rowHeight="220" data-limit="10">
<span class="gallery-data">[{"url":"https://bu.dusays.com/2022/07/29/62e2ba463b6ff.jpg","alt":"效果01.jpg"},{"url":"https://bu.dusays.com/2022/07/29/62e2ba4c8d1b4.jpg","alt":"效果02.jpg"},{"url":"https://bu.dusays.com/2022/07/29/62e2ba511d535.jpg","alt":"效果03.jpg"},{"url":"https://bu.dusays.com/2022/07/29/62e2ba52ab759.jpg","alt":"效果04.jpg"},{"url":"https://bu.dusays.com/2022/07/29/62e2ba541c6ff.jpg","alt":"效果-m-01.jpg"},{"url":"https://bu.dusays.com/2022/07/29/62e2ba56df910.jpg","alt":"效果-m-02.jpg"},{"url":"https://bu.dusays.com/2022/07/29/62e2ba5a3748a.jpg","alt":"msg-01.jpg"},{"url":"https://bu.dusays.com/2022/07/29/62e2ba5e2c564.jpg","alt":"msg-02.jpg"},{"url":"https://bu.dusays.com/2022/07/29/62e2ba62459e2.jpg","alt":"msg-03.jpg"}]</span>
</div>
<button class="gallery-load-more"><span>加载更多</span><i class="fa-solid fa-arrow-down"></i></button>
</div>
<h3 id="特色"><a href="#特色" class="headerlink" title="特色"></a>特色</h3><div class="tabs" id="span-"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#span--1">简单</button></li><li class="tab"><button type="button" data-href="#span--2">易用</button></li><li class="tab"><button type="button" data-href="#span--3">安全</button></li><li class="tab"><button type="button" data-href="#span--4">即时</button></li><li class="tab"><button type="button" data-href="#span--5">个性</button></li><li class="tab"><button type="button" data-href="#span--6">便捷管理</button></li><li class="tab"><button type="button" data-href="#span--7">缺点</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="span--1"><ul>
<li>免费搭建(使用云开发作为评论后台,每个用户均长期享受1个免费的标准型基础版1资源套餐)</li>
<li>简单部署(支持一键部署、手动部署、命令行部署)</li>
</ul><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--2"><ul>
<li>支持回复、点赞</li>
<li>无需额外适配,支持搭配浅色主题与深色主题使用</li>
<li>支持 API 调用,批量获取文章评论数、最新评论</li>
<li>访客在昵称栏输入 QQ 号,会自动补全 QQ 昵称和 QQ 邮箱</li>
<li>访客填写数字 QQ 邮箱,会使用 QQ 头像作为评论头像</li>
<li>支持评论框粘贴图片(可禁用)</li>
<li>支持插入图片(可禁用)</li>
<li>支持去不图床、云开发图床</li>
<li>支持插入表情(可禁用)</li>
<li>支持 Ctrl + Enter 快捷回复</li>
<li>评论框内容实时保存草稿,刷新不会丢失</li>
<li><a href="https://twikoo.js.org/faq.html#%E5%A6%82%E4%BD%95%E5%90%AF%E7%94%A8-katex-%E6%94%AF%E6%8C%81">支持 Katex 公式</a></li>
<li>支持按语言的代码高亮</li>
</ul><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--3"><ul>
<li>隐私信息安全(通过云函数控制敏感字段(邮箱、IP、环境配置等)不会泄露)</li>
<li>支持 Akismet 垃圾评论检测(需自行注册 <a href="https://akismet.com/">akismet.com</a>)</li>
<li>支持腾讯云内容安全垃圾评论检测(需自行注册 <a href="https://console.cloud.tencent.com/cms/text/overview">腾讯云内容安全</a>)</li>
<li>支持人工审核模式</li>
<li>防 XSS 注入</li>
<li>支持限制每个 IP 每 10 分钟最多发表多少条评论</li>
</ul><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--4"><ul>
<li>支持邮件提醒(访客和博主)</li>
<li>支持微信提醒(仅针对博主,基于 <a href="https://sc.ftqq.com/3.version">Server酱</a>,需自行注册)</li>
<li>支持 QQ 提醒(仅针对博主,基于 <a href="https://qmsg.zendee.cn/">Qmsg酱</a>,需自行注册)</li>
<li>支持 QQ 提醒(针对博主QQ或者群,基于 <a href="https://docs.go-cqhttp.org/">go-cqhttp</a>,需自己有服务器)</li>
</ul><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--5"><ul>
<li>支持自定义评论框背景图片</li>
<li>支持自定义“博主”标识文字</li>
<li>支持自定义通知邮件模板</li>
<li>支持自定义评论框提示信息(placeholder)</li>
<li>支持自定义表情列表(兼容 <a href="https://cdn.jsdelivr.net/npm/owo@1.0.2/demo/OwO.json">OwO 的数据格式</a>)</li>
<li>支持自定义【昵称】【邮箱】【网址】必填 / 选填</li>
<li>支持自定义代码高亮主题</li>
</ul><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--6"><ul>
<li>内嵌式管理面板,通过密码登录,可方便地查看评论、隐藏评论、删除评论、修改配置</li>
<li>支持隐藏管理入口,通过输入暗号显示</li>
<li>支持从 Valine、Artalk、Disqus 导入评论</li>
</ul><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--7"><ul>
<li>国外请求较慢</li>
<li>部署需要实名认证</li>
<li>不支持 IE</li>
</ul><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div>
<blockquote>
<p>本站是用 <a href="https://vercel.com/">Vercel</a> + <a href="https://www.mongodb.com/">MongoDB</a> 方案搭建 Twikoo 评论系统。<br>其他几种部署方式这里不做讲解,详情请参考:<a href="https://twikoo.js.org/">Twikoo 文档</a> 。</p>
</blockquote>
<h3 id="Vercel-部署"><a href="#Vercel-部署" class="headerlink" title="Vercel 部署"></a>Vercel 部署</h3><h4 id="视频教程"><a href="#视频教程" class="headerlink" title="视频教程"></a>视频教程</h4><div class="bilibili">
<iframe src="https://www.bilibili.com/video/BV1Fh411e7ZH/" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"> </iframe>
</div>
<h4 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h4><ol>
<li>申请 <a href="https://www.mongodb.com/cloud/atlas/register">MongoDB</a> 账号<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/30/62e4d6e7ce957.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/30/62e4d6efb8738.jpg"/></div></div>
填好图上所需内容,点击【Create your Atlas account】。</li>
<li>创建免费 MongoDB 数据库,区域推荐选择 【AWS / N. Virginia (us-east-1)】<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/30/62e4d7f1986b9.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/30/62e4d7fac54ff.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/30/62e4d7fe363b6.jpg"/></div></div></li>
<li>创建数据库用户(请记住这里的 password,后面步骤需要使用到),按步骤设置允许所有 IP(0.0.0.0/0)地址的连接(<a href="https://vercel.com/support/articles/how-to-allowlist-deployment-ip-address">为什么?</a>),填完信息后,点击【Finish and Close】<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/30/62e4d7ff47352.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/30/62e4d804d52c2.jpg"/></div></div></li>
<li>在 Clusters 页面点击 【Connect】,选择【Connect your appliction】,记录数据库连接字符串,请将连接字符串中的<code><password></code>修改为第三步中数据库密码,留着备用(将在第7步中用到)。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/30/62e4d807f00b7.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/30/62e4d80cbc69f.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/30/62e4d8121898f.jpg"/></div></div></li>
<li>申请 <a href="https://vercel.com/signup">Vercel</a> 账号,可以选择 Github 账号来同步</li>
<li>点击 <a href="https://vercel.com/import/project?template=https://github.com/imaegoo/twikoo/tree/main/src/server/vercel-min">此链接</a> 将 Twikoo 一键部署到 Vercel <div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/30/62e514b5559ac.jpg"/></div></div>
点击 Create,等待 Deploy完成,可看到如下效果:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/30/62e514b90c6fb.jpg"/></div></div></li>
<li>进入【Settings】->【Environment Variables】,添加环境变量【MONGODB_URI】,值为第 4 步的数据库连接字符串<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/30/62e514800dc82.jpg"/></div></div></li>
<li>进入【Deployments】,然后在任意一项后面点击更多(三个点),然后点击【Redeploy】,最后点击下面的【Redeploy】<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/30/62e5148248164.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/30/62e5168a4d321.jpg"/></div></div></li>
<li>进入【Overview】,点击【Domains】下方的链接,如果环境配置正确,可以看到 “Twikoo 云函数运行正常” 的提示<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/30/62e516ba54ee8.jpg"/></div></div>
Vercel Domains(包含 https:// 前缀,例如 <a href="https://xxx.vercel.app/">https://xxx.vercel.app</a>)即为您的环境 id<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/30/62e516dbde75f.jpg"/></div></div></li>
<li>在主题中写入 Twikoo 配置项。<br>在主题配置文件<code>_config.butterfly.yml</code>中修改以下内容,将你的环境id填入对应位置<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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"># Twikoo</span></span><br><span class="line"><span class="comment"># https://github.com/imaegoo/twikoo</span></span><br><span class="line"><span class="attr">twikoo:</span></span><br><span class="line"> <span class="attr">envId:</span> <span class="string">https://xxxxxx.vercel.app/</span> </span><br><span class="line"> <span class="attr">region:</span> </span><br><span class="line"> <span class="attr">visitor:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">option:</span></span><br></pre></td></tr></table></figure>
| 参数 | 解释 |</li>
</ol>
<p>| :—— | :—————————————————————– |<br>| envId | 环境id |<br>| region | 环境地域,默认为 ap-shanghai,如果您的环境地域不是上海,需传此参数 |<br>| visitor | 是否显示文章閲读数 |<br>| option | 可选配置 |</p>
<blockquote>
<p>开启 visitor 后,文章页的访问人数将改为 Twikoo 提供,而不是 不蒜子。<br>11. 重新编译运行,即可看到效果,点击评论区输入框下方的齿轮状按钮,设置你的管理密码,具体配置信息这里不做讲解,按照注解进行配置即可。</p>
</blockquote>
<h3 id="menhera-表情样式优化"><a href="#menhera-表情样式优化" class="headerlink" title="menhera 表情样式优化"></a>menhera 表情样式优化</h3><p>因为 twikoo 的默认宽度是 3em , 对于 menhera-chan 等其他长宽较大的表情根本无法看清,所以将它pc端评论后的表情加载设置成300px,设屏宽小于768px的,让它继承继承父元素的 100%宽 - 30px ,30px是为了与右侧有一段细微间隔,下图可以看到手机端的时候,表情和盒子最右侧是有一定距离的。</p>
<p>对于设屏宽小于768px,选择 menhera-chan 表情时,宽度太小,看不清图片,所以这里将宽度调整为设备宽的 50% 宽 - 10px。其他表情保持不变,只对 menhera-chan 表情做出改善。</p>
<p>若你的自定义表情中有其他跟 menhera-chan表情 类似的需求,可照着下面的 css 去改写,增加对应的类或者属性即可。</p>
<p>将以下代码复制到<code>custom.css</code>即可。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Twikoo 评论样式 */</span></span><br><span class="line"></span><br><span class="line"><span class="selector-class">.tk-input</span> <span class="selector-class">.el-textarea__inner</span> {</span><br><span class="line"> <span class="attribute">min-height</span>: <span class="number">120px</span> <span class="meta">!important</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#twikoo</span> <span class="selector-class">.OwO-body</span> {</span><br><span class="line"> <span class="attribute">max-width</span>: <span class="number">100%</span> <span class="meta">!important</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#twikoo</span> <span class="selector-class">.OwO</span> <span class="selector-class">.OwO-body</span> <span class="selector-class">.OwO-items</span><span class="selector-pseudo">:nth-child</span>(<span class="number">1</span>),</span><br><span class="line"><span class="selector-id">#twikoo</span> <span class="selector-class">.OwO</span> <span class="selector-class">.OwO-body</span> <span class="selector-class">.OwO-items</span><span class="selector-pseudo">:nth-child</span>(<span class="number">4</span>) {</span><br><span class="line"> <span class="attribute">max-height</span>: <span class="number">360px</span> <span class="meta">!important</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#twikoo</span> <span class="selector-class">.OwO-items</span> <span class="selector-tag">li</span><span class="selector-attr">[title|=menhera]</span> <span class="selector-tag">img</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100%</span> <span class="meta">!important</span>;</span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">5px</span> <span class="number">10px</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.tk-comment</span> <span class="selector-class">.tk-owo-emotion</span><span class="selector-attr">[alt*=menhera]</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">300px</span> <span class="meta">!important</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.tk-comment</span> <span class="selector-class">.vemoji</span><span class="selector-attr">[alt|=menhera]</span>,</span><br><span class="line"><span class="selector-class">.tk-comment</span> <span class="selector-class">.tk-owo-emotion</span><span class="selector-attr">[alt*=menhera]</span> {</span><br><span class="line"> <span class="attribute">max-width</span>: <span class="number">300px</span> <span class="meta">!important</span>;</span><br><span class="line"> <span class="attribute">max-height</span>: <span class="number">300px</span> <span class="meta">!important</span>;</span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">8px</span> <span class="number">1px</span>;</span><br><span class="line"> <span class="attribute">display</span>: block <span class="meta">!important</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">@media</span> screen <span class="keyword">and</span> (<span class="attribute">max-width</span>: <span class="number">768px</span>) {</span><br><span class="line"> <span class="selector-class">.tk-comment</span> <span class="selector-class">.vemoji</span><span class="selector-attr">[alt|=menhera]</span>, <span class="selector-class">.tk-comment</span> <span class="selector-class">.tk-owo-emotion</span><span class="selector-attr">[alt*=menhera]</span> {</span><br><span class="line"> <span class="attribute">max-width</span>: <span class="built_in">calc</span>(<span class="number">100%</span> - <span class="number">30px</span>) <span class="meta">!important</span>;</span><br><span class="line"> <span class="attribute">max-height</span>: <span class="built_in">calc</span>(<span class="number">100%</span> - <span class="number">30px</span>) <span class="meta">!important</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="selector-class">.OwO</span> <span class="selector-class">.OwO-body</span> <span class="selector-class">.OwO-items-image</span> <span class="selector-class">.OwO-item</span><span class="selector-attr">[title*=menhera]</span> {</span><br><span class="line"> <span class="attribute">max-width</span>: <span class="built_in">calc</span>(<span class="number">50%</span> - <span class="number">10px</span>);</span><br><span class="line"> <span class="attribute">box-sizing</span>: border-box;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>修改前的效果:</p>
<div class="gallery">
<div class="fj-gallery data" data-rowHeight="220" data-limit="10">
<span class="gallery-data">[{"url":"https://bu.dusays.com/2022/08/21/6301dd68dce0b.jpg","alt":"pc端 menher表情渲染效果.jpg"},{"url":"https://bu.dusays.com/2022/08/21/6301dd683ed04.jpg","alt":"pc端 选择menher表情.jpg"},{"url":"https://bu.dusays.com/2022/08/21/6301dd698b0a2.jpg","alt":"手机端 menher表情渲染效果.jpg"},{"url":"https://bu.dusays.com/2022/08/21/6301de6804542.jpg","alt":"手机端 选择menher表情.jpg"}]</span>
</div>
<button class="gallery-load-more"><span>加载更多</span><i class="fa-solid fa-arrow-down"></i></button>
</div>
<p>修改后的效果:</p>
<div class="gallery">
<div class="fj-gallery data" data-rowHeight="220" data-limit="10">
<span class="gallery-data">[{"url":"https://bu.dusays.com/2022/08/21/6301e0b6cca39.jpg","alt":"pc端 menher表情渲染效果.jpg"},{"url":"https://bu.dusays.com/2022/07/29/62e2ba52ab759.jpg","alt":"pc端 选择menher表情.jpg"},{"url":"https://bu.dusays.com/2022/07/29/62e2ba56df910.jpg","alt":"手机端 menher表情渲染效果.jpg"},{"url":"https://bu.dusays.com/2022/08/21/6301dd6843f4e.jpg","alt":"手机端 选择menher表情.jpg"}]</span>
</div>
<button class="gallery-load-more"><span>加载更多</span><i class="fa-solid fa-arrow-down"></i></button>
</div>
<h3 id="部署时遇到的常见错误"><a href="#部署时遇到的常见错误" class="headerlink" title="部署时遇到的常见错误"></a>部署时遇到的常见错误</h3><p>请参考 <a href="https://fe32.top/articles/hexo1612">Hexo + Butterfly 一些常见问题</a> 一文中关于【Twikoo】部分提及的内容。</p>
<h2 id="Disqus"><a href="#Disqus" class="headerlink" title="Disqus"></a>Disqus</h2><ol>
<li>前往 <a href="https://disqus.com/">Disqus官网</a> 注册账号<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/31/62e61fb8a5141.jpg"/></div></div></li>
<li>登入后点击首页的【GET STARTED】,选择【I want to install Disqus on my site】<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/31/62e61fba9a17c.jpg"/></div></div></li>
<li>输入下图中所需信息:Websit Name,Category,Language,点击【Create Site】<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/31/62e61fbd21052.jpg"/></div></div>
<table>
<thead>
<tr>
<th align="left">参数</th>
<th align="left">解释</th>
</tr>
</thead>
<tbody><tr>
<td align="left">Websit Name</td>
<td align="left">short name,且是唯一,像我是设置的 tzy1997-blog,short name 在配置评论时需要用到</td>
</tr>
<tr>
<td align="left">Category</td>
<td align="left">类别,自行选择</td>
</tr>
<tr>
<td align="left">Language</td>
<td align="left">语言,自行选择</td>
</tr>
</tbody></table>
</li>
<li>【Select Plan】选择 Basic<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/31/62e61fbfc3b95.jpg"/></div></div></li>
<li>接下来在【Configure Disqus】输入一些配置信息<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/31/62e61fc20599c.jpg"/></div></div></li>
<li>在【Setup Mederration】选择一个模式就好,最后点击【Complete Setup】<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/31/62e61fc411d22.jpg"/></div></div></li>
<li>在主题配置文件<code>_config.butterfly.yml</code>中修改以下内容,将你在第3步输入的【Websit Name】填入到 shortname<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># disqus</span></span><br><span class="line"><span class="comment"># https://disqus.com/</span></span><br><span class="line"><span class="attr">disqus:</span></span><br><span class="line"> <span class="attr">shortname:</span> <span class="string">tzy1997-blog</span></span><br></pre></td></tr></table></figure></li>
<li>重新编译运行,即可看到如下效果:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/31/62e61fc6da84d.jpg"/></div></div></li>
</ol>
<h2 id="Disqusjs"><a href="#Disqusjs" class="headerlink" title="Disqusjs"></a>Disqusjs</h2><blockquote>
<p>与Disqus一样,但由于Disqus在中国大陆无法访问, 使用Disqusjs可以在无法访问Disqus时显示评论。具体可参考<a href="https://github.com/SukkaW/DisqusJS">Disqusjs</a>。</p>
</blockquote>
<ol>
<li>配置 Disqus Application,在 <a href="https://disqus.com/api/applications/">Disqus API Application</a> 处注册一个 Application。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/31/62e640c320306.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/31/62e640c4f3174.jpg"/></div></div></li>
<li>点击进入新创建的 Application,获取你的 API Key(公钥)。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/31/62e640c53bedf.png"/></div></div></li>
<li>在 Application 的 Settings 页面设置你使用 DisqusJS 时的域名。Disqus API 会检查 API 请求的 Referrer 和 Origin。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/31/62e640c547760.png"/></div></div></li>
<li>在主题配置文件<code>_config.butterfly.yml</code>中配置以下内容:<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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"># Alternative Disqus - Render comments with Disqus API</span></span><br><span class="line"><span class="comment"># DisqusJS 評論系統,可以實現在網路審查地區載入 Disqus 評論列表,兼容原版</span></span><br><span class="line"><span class="comment"># https://github.com/SukkaW/DisqusJS</span></span><br><span class="line"><span class="attr">disqusjs:</span></span><br><span class="line"> <span class="attr">shortname:</span> </span><br><span class="line"> <span class="attr">siteName:</span> </span><br><span class="line"> <span class="attr">apikey:</span> </span><br><span class="line"> <span class="attr">api:</span> <span class="string">https://disqus.skk.moe/disqus/</span></span><br><span class="line"> <span class="attr">nocomment:</span> <span class="comment"># display when a blog post or an article has no comment attached</span></span><br><span class="line"> <span class="attr">admin:</span></span><br><span class="line"> <span class="attr">adminLabel:</span></span><br></pre></td></tr></table></figure>
各参数解释如下:<table>
<thead>
<tr>
<th align="left">参数</th>
<th align="left">解释</th>
</tr>
</thead>
<tbody><tr>
<td align="left">shortname</td>
<td align="left">你的 Disqus Forum 的 shortname,你可以在 <a href="https://disqus.com/admin/settings/general/">Disqus Admin - Settings - General - Shortname</a> 获取你的 shortname<br> 必须,无默认值</td>
</tr>
<tr>
<td align="left">siteName</td>
<td align="left">你站点的名称,将会显示在「评论基础模式」的 header 中;该配置应该和 <a href="https://disqus.com/admin/settings/general/">Disqus Admin - Settings - General - Website Name</a> 一致 <br> 非必须,无默认值</td>
</tr>
<tr>
<td align="left">apikey</td>
<td align="left">DisqusJS 向 API 发起请求时使用的 API Key,你应该在配置 Disqus Application 时获取了 API Key。<br> DisqusJS 支持填入一个 包含多个 API Key 的数组,每次请求时会随机使用其中一个;如果你只填入一个 API Key,可以填入 string 或 Array。<br> 必填,无默认值</td>
</tr>
<tr>
<td align="left">api</td>
<td align="left">DisqusJS 请求的 API Endpoint,通常情况下你应该配置一个 Disqus API 的反代并填入反代的地址。你也可以直接使用 DISQUS 官方 API 的 Endpoint <code>https://disqus.com/api/</code>,或是使用我搭建的 Disqus API 反代 Endpoint <code>https://disqus.skk.moe/disqus/</code>。如有必要可以阅读关于搭建反代的 <a href="https://github.com/SukkaW/DisqusJS#%E8%B0%83%E8%AF%95%E8%BF%9B%E9%98%B6%E4%BD%BF%E7%94%A8--%E5%BC%80%E5%8F%91%E7%9B%B8%E5%85%B3">相关内容</a><br> 建议,默认值为 <a href="https://disqus.skk.moe/disqus/">https://disqus.skk.moe/disqus/</a></td>
</tr>
<tr>
<td align="left">nocomment</td>
<td align="left">没有评论时的提示语(对应 Disqus Admin - Settings - Community - Comment Count Link - Zero comments)<br>非必须,默认值为 这里冷冷清清的,一条评论都没有</td>
</tr>
<tr>
<td align="left">admin</td>
<td align="left">你的站点的 Disqus Moderator 的用户名(也就是你的用户名)。你可以在 <a href="https://disqus.com/home/settings/account/">Disqus - Settings - Account - Username</a> 获取你的 Username <br> 非必须,无默认值</td>
</tr>
<tr>
<td align="left">adminLabel</td>
<td align="left">你想显示在 Disqus Moderator Badge 中的文字。该配置应和 <a href="https://disqus.com/admin/settings/community/">Disqus Admin - Settings - Community - Moderator Badge Text</a> 相同<br> 非必须,无默认值</td>
</tr>
</tbody></table>
</li>
<li>重新编译运行,即可看到如下效果:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/31/62e640c73cc13.jpg"/></div></div></li>
</ol>
<h2 id="Livere"><a href="#Livere" class="headerlink" title="Livere"></a>Livere</h2><ol>
<li>前往 <a href="https://livere.com/">来必力官网</a> 注册账号。</li>
<li>进入管理页面。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/31/62e65f8ee30f8.webp"/></div></div></li>
<li>选择默认的免费 City 版,点击【现在安装】<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/31/62e660384879d.jpg"/></div></div></li>
<li>输入基本的信息,点击【申请获取代码】<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/31/62e6608c87737.jpg"/></div></div></li>
<li>获取【data-uid】<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/31/62e6921de7f93.jpg"/></div></div></li>
<li>在主题配置文件<code>_config.butterfly.yml</code>中配置以下内容:<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># livere (來必力)</span></span><br><span class="line"><span class="comment"># https://www.livere.com/</span></span><br><span class="line"><span class="attr">livere:</span></span><br><span class="line"> <span class="attr">uid:</span> <span class="string">这里填你的uid</span></span><br></pre></td></tr></table></figure></li>
<li>重新编译运行,即可看到如下效果:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/31/62e6928a4e7a8.jpg"/></div></div></li>
<li>可以在管理界面查看 数据分析,进行评论管理,评论提醒等。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/31/62e6931b9b9e3.jpg"/></div></div></li>
</ol>
<h2 id="Gitalk"><a href="#Gitalk" class="headerlink" title="Gitalk"></a>Gitalk</h2><p>Gitalk 是一个基于 GitHub Issue 和 Preact 开发的评论插件。特性如下:</p>
<ul>
<li>使用 GitHub 登录</li>
<li>支持多语言 [en, zh-CN, zh-TW, es-ES, fr, ru, de, pl, ko, fa, ja]</li>
<li>支持个人或组织</li>
<li>无干扰模式(设置 distractionFreeMode 为 true 开启)</li>
<li>快捷键提交评论 (cmd|ctrl + enter)</li>
</ul>
<p>首先,您需要选择一个公共github存储库(已存在或创建一个新的github存储库)用于存储评论。</p>
<p>然后需要点击右上角头像【Settings】->【Developer settings】->【OAuth Apps】->【New OAuth App】 创建【GitHub Application】进行基本配置 ,找不到地方直接 <a href="https://github.com/settings/applications/new">点击这里申请</a>。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/03/62ea878812026.jpg"/></div></div>
<p>【Homepage URL】填写博客的仓库地址,例如我的填写<code>https://tzy13755126023.github.io</code>。<br>【Authorization callback URL】填写当前使用的域名,例如我的填写<code>https://fe32.top</code>,没有域名,跟【Homepage URL】保持一致即可。</p>
<p>然后可看到目标client_id, 继续点击【Generate a new client secret】即可得到目标【client_secret】。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/03/62ea878812026.jpg"/></div></div>
<p>大致的基本信息如下图:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/03/62ea878a8e6fe.jpg"/></div></div>
<p>最后在主题配置文件<code>_config.butterfly.yml</code>中配置以下内容:</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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"># gitalk</span></span><br><span class="line"><span class="comment"># https://github.com/gitalk/gitalk</span></span><br><span class="line"><span class="attr">gitalk:</span></span><br><span class="line"> <span class="attr">client_id:</span> <span class="string">'GitHub Application Client ID'</span></span><br><span class="line"> <span class="attr">client_secret:</span> <span class="string">'GitHub Application Client Secret'</span></span><br><span class="line"> <span class="attr">repo:</span> <span class="string">'GitHub repo'</span></span><br><span class="line"> <span class="attr">owner:</span> <span class="string">'GitHub repo owner'</span></span><br><span class="line"> <span class="attr">admin:</span> <span class="string">'GitHub repo owner and collaborators, only these guys can initialize github issues'</span></span><br><span class="line"> <span class="attr">language:</span> <span class="string">en</span> <span class="comment"># en, zh-CN, zh-TW, es-ES, fr, ru</span></span><br><span class="line"> <span class="attr">perPage:</span> <span class="number">10</span> <span class="comment"># Pagination size, with maximum 100.</span></span><br><span class="line"> <span class="attr">distractionFreeMode:</span> <span class="literal">false</span> <span class="comment"># Facebook-like distraction free mode.</span></span><br><span class="line"> <span class="attr">pagerDirection:</span> <span class="string">last</span> <span class="comment"># Comment sorting direction, available values are last and first.</span></span><br><span class="line"> <span class="attr">createIssueManually:</span> <span class="literal">true</span> <span class="comment"># Gitalk will create a corresponding github issue for your every single page automatically</span></span><br><span class="line"> <span class="attr">option:</span></span><br></pre></td></tr></table></figure>
<p>效果如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ea9fe2957cd.jpg"/></div></div>
<blockquote>
<p>详情可参考 <a href="https://github.com/gitalk/gitalk/blob/master/readme-cn.md">Gitalk Readme</a>。</p>
</blockquote>
<h2 id="Valine"><a href="#Valine" class="headerlink" title="Valine"></a>Valine</h2><p>Valine 诞生于2017年8月7日,是一款基于LeanCloud的快速、简洁且高效的无后端评论系统。</p>
<blockquote>
<p>由于 Valine 的国际版共享域名将于 2022 年 8 月 1 日起不再向中国大陆的最终用户提供服务,国际版共享域名仅服务于海外用户。本站已弃用 Valine ,改为 Twikoo。如果你更喜欢 Valine 的风格,你可以使用它的国区版。</p>
</blockquote>
<h3 id="参考教程"><a href="#参考教程" class="headerlink" title="参考教程"></a>参考教程</h3><p><a href="https://valine.js.org/">Valine 文档</a><br><a href="http://www.zhaojun.im/hexo-valine-admin/">Hexo 优化 — Valine 扩展之邮件通知</a><br><a href="https://blog.hclonely.com/posts/409d3090/">Valine 添加验证码、博主标签及评论微信、QQ 通知</a></p>
<p>效果如下:</p>
<div class="gallery">
<div class="fj-gallery data" data-rowHeight="220" data-limit="10">
<span class="gallery-data">[{"url":"https://bu.dusays.com/2021/08/26/84d3e2911b8ec.jpg","alt":""},{"url":"https://bu.dusays.com/2021/08/26/24353a0e2da4f.jpg","alt":""},{"url":"https://bu.dusays.com/2021/03/05/987c04fdd05f6.jpg","alt":""},{"url":"https://bu.dusays.com/2021/03/05/396f32b981353.jpg","alt":""},{"url":"https://bu.dusays.com/2021/03/05/72321b1f63e2c.jpg","alt":""},{"url":"https://bu.dusays.com/2021/03/05/564b64b674298.jpg","alt":""}]</span>
</div>
<button class="gallery-load-more"><span>加载更多</span><i class="fa-solid fa-arrow-down"></i></button>
</div>
<blockquote>
<p>详情可参考 <a href="https://fe32.top/articles/hexo1606">基于 Hexo 从零开始搭建个人博客(六)</a>,其中有提到过 Valine。</p>
</blockquote>
<h2 id="Waline"><a href="#Waline" class="headerlink" title="Waline"></a>Waline</h2><p>Waline 是一款从 Valine 衍生的带后端评论系统。可以将 Waline 等价成 With backend Valine。</p>
<p>具体配置可参考 <a href="https://waline.js.org/">waline 文档</a>,这里也不做详细讲解。</p>
<p>在主题配置文件<code>_config.butterfly.yml</code>中配置以下内容:</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># waline - A simple comment system with backend support fork from Valine</span></span><br><span class="line"><span class="comment"># https://waline.js.org/</span></span><br><span class="line"><span class="attr">waline:</span></span><br><span class="line"> <span class="attr">serverURL:</span> <span class="comment"># Waline server address url</span></span><br><span class="line"> <span class="attr">avatar:</span> <span class="string">monsterid</span> <span class="comment"># gravatar style https://zh-tw.gravatar.com/site/implement/images/#default-image</span></span><br><span class="line"> <span class="attr">emojiCDN:</span> <span class="comment"># emoji CDN</span></span><br><span class="line"> <span class="attr">bg:</span> <span class="comment"># waline background</span></span><br><span class="line"> <span class="attr">visitor:</span> <span class="literal">false</span></span><br><span class="line"> <span class="attr">option:</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>开启 pageview 后,文章页的访问人数将改为 Waline 提供,而不是 不蒜子。</p>
</blockquote>
<h2 id="Utterances"><a href="#Utterances" class="headerlink" title="Utterances"></a>Utterances</h2><p>与 Gitalk 一样,基于 GitHub issues 的评论工具。相对于 Gitalk,其相对需要权限较少。</p>
<blockquote>
<p>详细配置可参考 <a href="https://utteranc.es/">Utterances</a>。</p>
</blockquote>
<p>在主题配置文件<code>_config.butterfly.yml</code>中配置以下内容:</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># utterances</span></span><br><span class="line"><span class="comment"># https://utteranc.es/</span></span><br><span class="line"><span class="attr">utterances:</span></span><br><span class="line"> <span class="attr">repo:</span></span><br><span class="line"> <span class="comment"># Issue Mapping: pathname/url/title/og:title</span></span><br><span class="line"> <span class="attr">issue_term:</span> <span class="string">pathname</span></span><br><span class="line"> <span class="comment"># Theme: github-light/github-dark/github-dark-orange/icy-dark/dark-blue/photon-dark</span></span><br><span class="line"> <span class="attr">light_theme:</span> <span class="string">github-light</span></span><br><span class="line"> <span class="attr">dark_theme:</span> <span class="string">photon-dark</span></span><br></pre></td></tr></table></figure>
<p>效果如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/08/04/62ea9fe0e51b4.jpg"/></div></div>
<h2 id="Giscus"><a href="#Giscus" class="headerlink" title="Giscus"></a>Giscus</h2><p>一个基于 GitHub Discussions 的评论,详细配置请参考 <a href="https://giscus.app/zh-TW">Giscus 文档</a>。</p>
<p>在主题配置文件<code>_config.butterfly.yml</code>中配置以下内容:</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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"># Giscus</span></span><br><span class="line"><span class="comment"># https://giscus.app/</span></span><br><span class="line"><span class="attr">giscus:</span></span><br><span class="line"> <span class="attr">repo:</span></span><br><span class="line"> <span class="attr">repo_id:</span></span><br><span class="line"> <span class="attr">category_id:</span></span><br><span class="line"> <span class="attr">theme:</span></span><br><span class="line"> <span class="attr">light:</span> <span class="string">light</span></span><br><span class="line"> <span class="attr">dark:</span> <span class="string">dark</span></span><br><span class="line"> <span class="attr">option:</span></span><br></pre></td></tr></table></figure>
<p>效果如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif"
基于 Hexo 键入在线聊天功能
https://fe32.top/articles/hexo1610/
2022-07-17T05:44:49.000Z
2024-02-23T15:35:21.203Z
<blockquote>
<p>注意:本站的在线聊天系统已从 <a href="https://crisp.chat/zh/">crisp</a> 更换成 <a href="https://www.tidio.com/">tidio</a>。</p>
</blockquote>
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><!-- > 本站基于`Hexo`搭建,用的 🦋 [hexo-theme-butterfly](https://github.com/jerryc127/hexo-theme-butterfly.git) 主题 [v3.7.1](https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1),请注意最新的🦋 [hexo-theme-butterfly](https://github.com/jerryc127/hexo-theme-butterfly.git) 版本已经更新到 [v4.8.1](https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.8.1) 。<br>
如果你是 [v3.7.1](https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1) 之外的版本,可能有些地方会有出入,请留意。 -->
<blockquote>
<p>本站基于<code>Hexo</code>搭建,用的 🦋 <a href="https://github.com/jerryc127/hexo-theme-butterfly.git">hexo-theme-butterfly</a> 主题,已经升级到 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.7.0">v4.7.0</a> 。 请注意最新的🦋 <a href="https://github.com/jerryc127/hexo-theme-butterfly.git">hexo-theme-butterfly</a> 版本已经更新到 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.8.1">v4.8.1</a> 。<br><br>如果你是 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1">v3.7.1</a> 的版本,请移步 <a href="https://old.fe32.top/" target="_blank">v3.7.1</a> 站点进行浏览。</p>
</blockquote>
<blockquote>
<p>注意:我的博客根目录路径为 【G:/hexo-blog/blog-demo】,下文所说的根目录都是此路径,将用<code>[BlogRoot]</code>代替。如果不清楚根目录路径,请回到教程 <a href="https://fe32.top/articles/hexo1602/">基于 Hexo 从零开始搭建个人博客(二)</a>,查看你执行<code>hexo init xxx</code>这条命令时所选择的路径,例如我选择的路径是【G:/hexo-blog】,我的博客根目录即为【G:/hexo-blog/xxx】。<br><br>修改站点配置文件<code>_config.yml</code>,路径为【BlogRoot/_config.yml】。<br><br>修改主题配置文件<code>_config.butterfly.yml</code>,路径为【BlogRoot/_config.butterfly.yml】。</p>
</blockquote>
<h2 id="推荐阅读"><a href="#推荐阅读" class="headerlink" title="推荐阅读"></a>推荐阅读</h2><ul>
<li><a href="https://fe32.top/articles/hexo1601/">基于 Hexo 从零开始搭建个人博客(一)</a></li>
<li><a href="https://fe32.top/articles/hexo1602/">基于 Hexo 从零开始搭建个人博客(二)</a></li>
<li><a href="https://fe32.top/articles/hexo1603/">基于 Hexo 从零开始搭建个人博客(三)</a></li>
<li><a href="https://fe32.top/articles/hexo1604/">基于 Hexo 从零开始搭建个人博客(四)</a></li>
<li><a href="https://fe32.top/articles/hexo1605/">基于 Hexo 从零开始搭建个人博客(五)</a></li>
<li><a href="https://fe32.top/articles/hexo1606/">基于 Hexo 从零开始搭建个人博客(六)</a></li>
<li><a href="https://fe32.top/articles/hexo1607/">基于 Hexo 键入搜索功能</a></li>
<li><a href="https://fe32.top/articles/hexo1609/">基于 Hexo 键入分享功能</a></li>
<li><a href="https://fe32.top/articles/hexo1608/">Hexo + Butterfly 自定义右键菜单</a></li>
<li><a href="https://fe32.top/articles/hexo1612/">Hexo + Butterfly 一些常见问题</a></li>
<li><a href="https://fe32.top/articles/hexo1613/">请收下这只可爱的猫咪吧</a></li>
<li><a href="https://fe32.top/articles/hexo1614/">关于Vercel被墙导致获取Twikoo评论失败的解决方案</a></li>
<li><a href="https://fe32.top/articles/hexo1615/">飞只因太美,给你的首页装上吧!</a></li>
<li><a href="https://fe32.top/articles/hexo1617/">Hexo + Butterfly 自定义页脚</a></li>
<li><a href="https://fe32.top/articles/hexo1618/">Hexo + Butterfly 侧边栏公众号</a></li>
</ul>
<h2 id="本站效果"><a href="#本站效果" class="headerlink" title="本站效果"></a>本站效果</h2><p>注意: 由于<code>tidio</code>每月次数限制,本站于【2023/07/15】重新切换回<code>Crisp</code>。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2023/04/29/644d31c55b993.jpg" alt="本站 v4.7.0 用的是 tidio,效果仅做参考"/></div><span class="image-caption">本站 v4.7.0 用的是 tidio,效果仅做参考</span></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/17/62d3b90696c98.webp" alt="本站 v3.7.1 用的是 crisp,效果仅做参考"/></div><span class="image-caption">本站 v3.7.1 用的是 crisp,效果仅做参考</span></div>
<blockquote>
<p>从3.0开始,Butterfly主题内置了多种在线聊天工具。你可以选择开启一种,方便你与访客的交流。</p>
</blockquote>
<div class="tabs" id="span-"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#span--1">通用设置</button></li><li class="tab"><button type="button" data-href="#span--2">crisp (本站所用)</button></li><li class="tab"><button type="button" data-href="#span--3">chatra</button></li><li class="tab"><button type="button" data-href="#span--4">tidio</button></li><li class="tab"><button type="button" data-href="#span--5">Gitter</button></li><li class="tab"><button type="button" data-href="#span--6">daovoice</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="span--1"><p>关于这些在线聊天的工具,主题提供了一个按钮可以打开/关闭聊天窗口,这个聊天按钮将会出现在右下角里。你只需要把<code>chat_btn</code>打开就行。</p>
<p>修改主题配置文件<code>_config.butterfly.yml</code>,将<code>chat_btn</code>设置成<code>true</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Chat Button [recommend]</span></span><br><span class="line"><span class="comment"># It will create a button in the bottom right corner of website, and hide the origin button</span></span><br><span class="line"><span class="attr">chat_btn:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/17/62d3befac1c5c.webp"/></div></div>
<p>为了不影响访客的体验,主题提供一个<code>chat_hide_show</code>配置,设为<code>true</code>后,使用工具提供的按钮时,只有向上滚动才会显示聊天按钮,向下滚动时会隐藏按钮。</p>
<p>修改主题配置文件<code>_config.butterfly.yml</code>,将<code>chat_hide_show</code>设置成<code>true</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># The origin chat button is displayed when scrolling up, and the button is hidden when scrolling down</span></span><br><span class="line"><span class="attr">chat_hide_show:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>如果使用工具自带的聊天按钮,按钮位置可能会遮挡右下角图标,请配置<code>rightside-bottom</code>调正右下角图标位置。</p>
</blockquote><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--2"><p>打开<a href="https://crisp.chat/zh/">crisp官网</a>并注册账号。找到 【设置】->【网站设置】->【设置说明】,找到你的网站ID。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/17/62d3d2c60ed5d.jpg"/></div></div>
<p>修改主题配置文件<code>_config.butterfly.yml</code>,将<code>crisp</code>设置成<code>true</code>。并将你的网站ID填入<code>website_id</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># crisp</span></span><br><span class="line"><span class="comment"># https://crisp.chat/en/</span></span><br><span class="line"><span class="attr">crisp:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">website_id:</span> <span class="string">xxxxxxxx</span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/01/6296cf740e171.jpg"/></div></div>
<p>这里我发了一条内容为【你好,收到了吗?】的信息,可在PC和手机上接收消息(这里放手机效果图,毕竟电脑随身携带的可能性比较小),效果如下:</p>
<div class="gallery">
<div class="fj-gallery data" data-rowHeight="220" data-limit="10">
<span class="gallery-data">[{"url":"https://bu.dusays.com/2022/06/01/6296cf7fedc0d.jpg","alt":""},{"url":"https://bu.dusays.com/2022/06/01/6296cf75896a4.jpg","alt":""},{"url":"https://bu.dusays.com/2022/06/01/6296d0089ff3e.jpg","alt":""}]</span>
</div>
<button class="gallery-load-more"><span>加载更多</span><i class="fa-solid fa-arrow-down"></i></button>
</div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--3"><p>打开 <a href="https://chatra.com/">chatra官网</a> 并注册账号,这里填入邮箱就好,将会收到邮件让你去初始化密码。登进去之后,找到【Settings】->【Preferences】-> 【Public key】。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/17/62d3d88d07c85.jpg"/></div></div>
<p>修改主题配置文件<code>_config.butterfly.yml</code>,将<code>chatra</code>设置成<code>true</code>。并在<code>id</code>位置填入你的<code>Public key</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># chatra</span></span><br><span class="line"><span class="comment"># https://chatra.io/</span></span><br><span class="line"><span class="attr">chatra:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">id:</span> <span class="string">your</span> <span class="string">Public</span> <span class="string">key</span></span><br></pre></td></tr></table></figure>
<p><code>chatra</code>的样式你可以<code>Chat Widget</code>自行配置。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/17/62d3d9048e417.jpg"/></div></div>
<p>在站点中呈现的效果如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/17/62d3db4ada345.jpg"/></div></div>
<p>在网页和APP中都能收到信息,效果如下:</p>
<div class="gallery">
<div class="fj-gallery data" data-rowHeight="220" data-limit="10">
<span class="gallery-data">[{"url":"https://bu.dusays.com/2022/07/17/62d3dbc151517.jpg","alt":""},{"url":"https://bu.dusays.com/2022/07/17/62d3dbc7c5836.webp","alt":""}]</span>
</div>
<button class="gallery-load-more"><span>加载更多</span><i class="fa-solid fa-arrow-down"></i></button>
</div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--4"><p>打开 <a href="https://www.tidio.com/panel/register">tidio</a> 并注册账号。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/17/62d4163566e6f.jpg"/></div></div>
<p>登入账号后,你可以在【Settings】->【Developer】中找到【Public key】。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/17/62d417553a7a3.jpg"/></div></div>
<p>修改主题配置文件<code>_config.butterfly.yml</code>,将<code>tidio</code>设置成<code>true</code>。并在<code>public_key</code>位置填入你从<code>tidio</code>中获得的<code>Public key</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># tidio</span></span><br><span class="line"><span class="comment"># https://www.tidio.com/</span></span><br><span class="line"><span class="attr">tidio:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">public_key:</span> <span class="string">your</span> <span class="string">Public</span> <span class="string">key</span></span><br></pre></td></tr></table></figure>
<p><code>tidio</code>的样式你可以找到【Settings】->【Appearance】里面自行配置。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/17/62d417de40f92.jpg"/></div></div>
<p>在站点中呈现的效果如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/17/62d41870f2749.webp"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--5"><p>打开 <a href="https://gitter.im/">Gitter官网</a> 并注册账号,可以直接用 Github 账号登录。然后创建一个<code>community</code>或者<code>room</code>。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/18/62d4380bb4372.jpg"/></div></div>
<p>我这里创建的是<code>community</code>。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/18/62d4384de8d7f.jpg"/></div></div>
<p>复制名称,将名称填入主题配置文件中。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/18/62d438933f8d3.webp"/></div></div>
<p>修改主题配置文件<code>_config.butterfly.yml</code>,将<code>gitter</code>设置成<code>true</code>。并在<code>room</code>处填入复制过来的名称。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># gitter</span></span><br><span class="line"><span class="comment"># https://gitter.im/</span></span><br><span class="line"><span class="attr">gitter:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">room:</span> <span class="string">tzy1997-blog/community</span></span><br></pre></td></tr></table></figure>
<p>在站点中呈现的效果如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/18/62d43b02e26ef.webp"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--6"><p>打开 <a href="http://daovoice.io/">daovoice</a> 并注册账号。你可以在【应用设置】->【安装到网站】中找到【app id】。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/17/62d4235430223.jpg"/></div></div>
<p>修改主题配置文件<code>_config.butterfly.yml</code>,将<code>daovoice</code>设置成<code>true</code>。并将<code>app_id</code>填入对应的位置即可。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># daovoice</span></span><br><span class="line"><span class="comment"># http://daovoice.io/</span></span><br><span class="line"><span class="attr">daovoice:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">app_id:</span> <span class="string">4139c9af</span></span><br></pre></td></tr></table></figure>
<p>这里我们发了一条内容为【你好~,我是Hello world!】的消息,在站点中呈现的效果如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/17/62d423c7410ce.jpg"/></div></div>
<p>我们顺便看下站点发来的消息以及发给站点的消息,效果如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/17/62d424994bec4.jpg" alt="控制台收到站点发来的消息"/></div><span class="image-caption">控制台收到站点发来的消息</span></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/17/62d424a96265f.jpg" alt="站点收到控制台发来的消息"/></div><span class="image-caption">站点收到控制台发来的消息</span></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas
基于 Hexo 键入分享功能
https://fe32.top/articles/hexo1609/
2022-07-16T10:17:37.000Z
2024-02-23T15:35:21.204Z
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><!-- > 本站基于`Hexo`搭建,用的 🦋 [hexo-theme-butterfly](https://github.com/jerryc127/hexo-theme-butterfly.git) 主题 [v3.7.1](https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1),请注意最新的🦋 [hexo-theme-butterfly](https://github.com/jerryc127/hexo-theme-butterfly.git) 版本已经更新到 [v4.8.1](https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.8.1) 。<br>
如果你是 [v3.7.1](https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1) 之外的版本,可能有些地方会有出入,请留意。 -->
<blockquote>
<p>本站基于<code>Hexo</code>搭建,用的 🦋 <a href="https://github.com/jerryc127/hexo-theme-butterfly.git">hexo-theme-butterfly</a> 主题,已经升级到 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.7.0">v4.7.0</a> 。 请注意最新的🦋 <a href="https://github.com/jerryc127/hexo-theme-butterfly.git">hexo-theme-butterfly</a> 版本已经更新到 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.8.1">v4.8.1</a> 。<br><br>如果你是 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1">v3.7.1</a> 的版本,请移步 <a href="https://old.fe32.top/" target="_blank">v3.7.1</a> 站点进行浏览。</p>
</blockquote>
<blockquote>
<p>注意:我的博客根目录路径为 【G:/hexo-blog/blog-demo】,下文所说的根目录都是此路径,将用<code>[BlogRoot]</code>代替。如果不清楚根目录路径,请回到教程 <a href="https://fe32.top/articles/hexo1602/">基于 Hexo 从零开始搭建个人博客(二)</a>,查看你执行<code>hexo init xxx</code>这条命令时所选择的路径,例如我选择的路径是【G:/hexo-blog】,我的博客根目录即为【G:/hexo-blog/xxx】。<br><br>修改站点配置文件<code>_config.yml</code>,路径为【BlogRoot/_config.yml】。<br><br>修改主题配置文件<code>_config.butterfly.yml</code>,路径为【BlogRoot/_config.butterfly.yml】。</p>
</blockquote>
<h2 id="推荐阅读"><a href="#推荐阅读" class="headerlink" title="推荐阅读"></a>推荐阅读</h2><ul>
<li><a href="https://fe32.top/articles/hexo1601/">基于 Hexo 从零开始搭建个人博客(一)</a></li>
<li><a href="https://fe32.top/articles/hexo1602/">基于 Hexo 从零开始搭建个人博客(二)</a></li>
<li><a href="https://fe32.top/articles/hexo1603/">基于 Hexo 从零开始搭建个人博客(三)</a></li>
<li><a href="https://fe32.top/articles/hexo1604/">基于 Hexo 从零开始搭建个人博客(四)</a></li>
<li><a href="https://fe32.top/articles/hexo1605/">基于 Hexo 从零开始搭建个人博客(五)</a></li>
<li><a href="https://fe32.top/articles/hexo1606/">基于 Hexo 从零开始搭建个人博客(六)</a></li>
<li><a href="https://fe32.top/articles/hexo1607/">基于 Hexo 键入搜索功能</a></li>
<li><a href="https://fe32.top/articles/hexo1610/">基于 Hexo 键入在线聊天功能</a></li>
<li><a href="https://fe32.top/articles/hexo1608/">Hexo + Butterfly 自定义右键菜单</a></li>
<li><a href="https://fe32.top/articles/hexo1612/">Hexo + Butterfly 一些常见问题</a></li>
<li><a href="https://fe32.top/articles/hexo1613/">请收下这只可爱的猫咪吧</a></li>
<li><a href="https://fe32.top/articles/hexo1614/">关于Vercel被墙导致获取Twikoo评论失败的解决方案</a></li>
<li><a href="https://fe32.top/articles/hexo1615/">飞只因太美,给你的首页装上吧!</a></li>
<li><a href="https://fe32.top/articles/hexo1617/">Hexo + Butterfly 自定义页脚</a></li>
<li><a href="https://fe32.top/articles/hexo1618/">Hexo + Butterfly 侧边栏公众号</a></li>
</ul>
<blockquote>
<p>注意:主题集成了三种分享功能,分别是<code>AddThis</code>、<code>Sharejs</code>、<code>Addtoany</code>,只能从其中选择一个分享服务商。</p>
</blockquote>
<h2 id="AddThis"><a href="#AddThis" class="headerlink" title="AddThis"></a>AddThis</h2><ol>
<li>前往<a href="https://www.addthis.com/register?next=/dashboard#tool-config">AddThis 官网</a>注册账号,输入下图中信息即可。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/16/62d29950440cf.jpg"/></div></div></li>
<li>配置 WEBSITE TOOLS。<br>首先首先选择<code>Share Buttons</code>。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/16/62d2d5fa93073.jpg"/></div></div>
其次选择分享模型:Floating、Inline、Expanding、Image Sharing、Popup、Banner、Slider。每种模型的效果如下:<div class="tabs" id="span-"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#span--1">Floating</button></li><li class="tab"><button type="button" data-href="#span--2">Inline</button></li><li class="tab"><button type="button" data-href="#span--3">Expanding</button></li><li class="tab"><button type="button" data-href="#span--4">Image Sharing</button></li><li class="tab"><button type="button" data-href="#span--5">Popup</button></li><li class="tab"><button type="button" data-href="#span--6">Banner</button></li><li class="tab"><button type="button" data-href="#span--7">Slider</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="span--1"><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/16/62d2d66f924b9.jpg"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--2"><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/16/62d2d678e6c24.jpg"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--3"><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/16/62d2d69d83596.jpg"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--4"><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/16/62d2d6f4f3cf2.jpg"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--5"><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/16/62d2d6fea0e95.jpg"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--6"><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/16/62d2d705bc64d.jpg"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--7"><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/16/62d2d70d5fd0f.jpg"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div>
点击【Continue】然后对不同的模型做一些定制化的设置。例如,选择分享平台的个数、设置按钮的大小,颜色,设置图标的颜色、模型出现的位置等。这里只对 Floating 做出举例,具体效果请看下图:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/16/62d2db19750b1.jpg"/></div></div>
最后点击 【Activate Tool】 按钮即可。</li>
<li>找到【Get The Code】-> 【Just Copy】,可以看到你的 <code>pub-id</code>。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/16/62d2dd5b41f96.jpg" alt="图中的马赛克,即为你的pub-id"/></div><span class="image-caption">图中的马赛克,即为你的pub-id</span></div></li>
<li>修改主题配置文件<code>_config.butterfly.yml</code>。<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">addThis:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span> <span class="comment"># or false</span></span><br><span class="line"> <span class="attr">pubid:</span> <span class="string">你的pub-id</span></span><br></pre></td></tr></table></figure></li>
<li>重新编译运行,即可看到效果。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/16/62d2e07671db3.webp"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/16/62d2df7808999.webp"/></div></div></li>
</ol>
<h2 id="Sharejs"><a href="#Sharejs" class="headerlink" title="Sharejs"></a>Sharejs</h2><blockquote>
<p>如果你不了解 <a href="https://github.com/overtrue/share.js/">sharejs</a> ,可以看看它的文档。</p>
</blockquote>
<p>修改主题配置文件<code>_config.butterfly.yml</code></p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">sharejs:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">sites:</span> <span class="string">facebook,twitter,wechat,weibo,qq</span> <span class="comment">#想要显示的内容</span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/17/62d2e2b0471eb.jpg" alt="效果图"/></div><span class="image-caption">效果图</span></div>
<h2 id="Addtoany"><a href="#Addtoany" class="headerlink" title="Addtoany"></a>Addtoany</h2><blockquote>
<p>如果你不了解 <a href="https://www.addtoany.com/">Addtoany</a> ,可以看看它的文档。</p>
</blockquote>
<p>修改主题配置文件<code>_config.butterfly.yml</code></p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">addtoany:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">item:</span> <span class="string">facebook,twitter,wechat,sina_weibo,facebook_messenger,email,copy_link</span></span><br></pre></td></tr></table></figure>
<p>效果如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/17/62d3994e5611c.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif"
Hexo + Butterfly 自定义右键菜单
https://fe32.top/articles/hexo1608/
2022-07-13T15:37:41.000Z
2024-02-23T15:35:21.203Z
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><!-- > 本站基于`Hexo`搭建,用的 🦋 [hexo-theme-butterfly](https://github.com/jerryc127/hexo-theme-butterfly.git) 主题 [v3.7.1](https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1),请注意最新的🦋 [hexo-theme-butterfly](https://github.com/jerryc127/hexo-theme-butterfly.git) 版本已经更新到 [v4.8.1](https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.8.1) 。<br>
如果你是 [v3.7.1](https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1) 之外的版本,可能有些地方会有出入,请留意。 -->
<blockquote>
<p>本站基于<code>Hexo</code>搭建,用的 🦋 <a href="https://github.com/jerryc127/hexo-theme-butterfly.git">hexo-theme-butterfly</a> 主题,已经升级到 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.7.0">v4.7.0</a> 。 请注意最新的🦋 <a href="https://github.com/jerryc127/hexo-theme-butterfly.git">hexo-theme-butterfly</a> 版本已经更新到 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.8.1">v4.8.1</a> 。<br><br>如果你是 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1">v3.7.1</a> 的版本,请移步 <a href="https://old.fe32.top/" target="_blank">v3.7.1</a> 站点进行浏览。</p>
</blockquote>
<blockquote>
<p>注意:我的博客根目录路径为 【G:/hexo-blog/blog-demo】,下文所说的根目录都是此路径,将用<code>[BlogRoot]</code>代替。如果不清楚根目录路径,请回到教程 <a href="https://fe32.top/articles/hexo1602/">基于 Hexo 从零开始搭建个人博客(二)</a>,查看你执行<code>hexo init xxx</code>这条命令时所选择的路径,例如我选择的路径是【G:/hexo-blog】,我的博客根目录即为【G:/hexo-blog/xxx】。<br><br>修改站点配置文件<code>_config.yml</code>,路径为【BlogRoot/_config.yml】。<br><br>修改主题配置文件<code>_config.butterfly.yml</code>,路径为【BlogRoot/_config.butterfly.yml】。</p>
</blockquote>
<h2 id="推荐阅读"><a href="#推荐阅读" class="headerlink" title="推荐阅读"></a>推荐阅读</h2><ul>
<li><a href="https://fe32.top/articles/hexo1601/">基于 Hexo 从零开始搭建个人博客(一)</a></li>
<li><a href="https://fe32.top/articles/hexo1602/">基于 Hexo 从零开始搭建个人博客(二)</a></li>
<li><a href="https://fe32.top/articles/hexo1603/">基于 Hexo 从零开始搭建个人博客(三)</a></li>
<li><a href="https://fe32.top/articles/hexo1604/">基于 Hexo 从零开始搭建个人博客(四)</a></li>
<li><a href="https://fe32.top/articles/hexo1605/">基于 Hexo 从零开始搭建个人博客(五)</a></li>
<li><a href="https://fe32.top/articles/hexo1606/">基于 Hexo 从零开始搭建个人博客(六)</a></li>
<li><a href="https://fe32.top/articles/hexo1607/">基于 Hexo 键入搜索功能</a></li>
<li><a href="https://fe32.top/articles/hexo1609/">基于 Hexo 键入分享功能</a></li>
<li><a href="https://fe32.top/articles/hexo1610/">基于 Hexo 键入在线聊天功能</a></li>
<li><a href="https://fe32.top/articles/hexo1612/">Hexo + Butterfly 一些常见问题</a></li>
<li><a href="https://fe32.top/articles/hexo1613/">请收下这只可爱的猫咪吧</a></li>
<li><a href="https://fe32.top/articles/hexo1614/">关于Vercel被墙导致获取Twikoo评论失败的解决方案</a></li>
<li><a href="https://fe32.top/articles/hexo1615/">飞只因太美,给你的首页装上吧!</a></li>
<li><a href="https://fe32.top/articles/hexo1617/">Hexo + Butterfly 自定义页脚</a></li>
<li><a href="https://fe32.top/articles/hexo1618/">Hexo + Butterfly 侧边栏公众号</a></li>
</ul>
<h2 id="效果"><a href="#效果" class="headerlink" title="效果"></a>效果</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/14/62d02b9a980bc.jpg" alt="本站右键效果"/></div><span class="image-caption">本站右键效果</span></div>
<h2 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h2><ol>
<li>在<code>BlogRoot/themes/butterfly/layout/includes</code>文件夹下新建一个<code>right-menu</code>的文件夹,在此文件夹下新建一个<code>index.pug</code>文件。<br>具体位置如下图:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/14/62d02eefa3483.jpg"/></div></div>
将以下代码复制到文件中。<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">#rightMenu</span><br><span class="line"> .rightMenu-group.rightMenu-small</span><br><span class="line"> .rightMenu-item#menu-backward</span><br><span class="line"> i.fa-solid.fa-arrow-left</span><br><span class="line"> .rightMenu-item#menu-forward</span><br><span class="line"> i.fa-solid.fa-arrow-right</span><br><span class="line"> .rightMenu-item#menu-refresh</span><br><span class="line"> i.fa-solid.fa-arrow-rotate-right</span><br><span class="line"> .rightMenu-item#menu-home</span><br><span class="line"> i.fa-solid.fa-house</span><br><span class="line"> .rightMenu-group.rightMenu-line.rightMenuOther</span><br><span class="line"> a.rightMenu-item.menu-link(href='/archives/')</span><br><span class="line"> i.fa-solid.fa-archive</span><br><span class="line"> span='文章归档'</span><br><span class="line"> a.rightMenu-item.menu-link(href='/categories/')</span><br><span class="line"> i.fa-solid.fa-folder-open</span><br><span class="line"> span='文章分类'</span><br><span class="line"> a.rightMenu-item.menu-link(href='/tags/')</span><br><span class="line"> i.fa-solid.fa-tags</span><br><span class="line"> span='文章标签'</span><br><span class="line"> .rightMenu-group.rightMenu-line.rightMenuNormal</span><br><span class="line"> a.rightMenu-item.menu-link#menu-radompage(href='/random/index.html')</span><br><span class="line"> i.fa-solid.fa-shoe-prints</span><br><span class="line"> span='随便逛逛'</span><br><span class="line"> .rightMenu-item#menu-translate</span><br><span class="line"> i.fa-solid.fa-earth-asia</span><br><span class="line"> span='繁简切换'</span><br><span class="line"> .rightMenu-item#menu-darkmode</span><br><span class="line"> i.fa-solid.fa-moon</span><br><span class="line"> span='切换模式'</span><br><span class="line">#rightmenu-mask</span><br></pre></td></tr></table></figure></li>
<li>在<code>BlogRoot/themes/butterfly/layout/includes/layout.pug</code>中引入上一步中创建的<code>index.pug</code>文件。<br>具体位置如下图:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/14/62d02f04293e9.jpg"/></div></div></li>
<li>在<code>BlogRoot/themes/butterfly/source/js</code>文件夹下新建一个<code>rightMenu.js</code>,将以下代码复制到文件中。<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> rm = {};</span><br><span class="line">rm.<span class="property">showRightMenu</span> = <span class="keyword">function</span> (<span class="params">isTrue, x = <span class="number">0</span>, y = <span class="number">0</span></span>) {</span><br><span class="line"> <span class="keyword">let</span> $rightMenu = $(<span class="string">'#rightMenu'</span>);</span><br><span class="line"> $rightMenu.<span class="title function_">css</span>(<span class="string">'top'</span>, x + <span class="string">'px'</span>).<span class="title function_">css</span>(<span class="string">'left'</span>, y + <span class="string">'px'</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (isTrue) {</span><br><span class="line"> <span class="title function_">stopMaskScroll</span>()</span><br><span class="line"> $rightMenu.<span class="title function_">show</span>();</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $rightMenu.<span class="title function_">hide</span>();</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"><span class="keyword">let</span> rmWidth = $(<span class="string">'#rightMenu'</span>).<span class="title function_">width</span>();</span><br><span class="line"><span class="keyword">let</span> rmHeight = $(<span class="string">'#rightMenu'</span>).<span class="title function_">height</span>();</span><br><span class="line">rm.<span class="property">reloadrmSize</span> = <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> rmWidth = $(<span class="string">"#rightMenu"</span>).<span class="title function_">width</span>();</span><br><span class="line"> rmHeight = $(<span class="string">"#rightMenu"</span>).<span class="title function_">height</span>()</span><br><span class="line">};</span><br><span class="line"><span class="variable language_">window</span>.<span class="property">oncontextmenu</span> = <span class="keyword">function</span> (<span class="params">event</span>) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable language_">document</span>.<span class="property">body</span>.<span class="property">clientWidth</span> > <span class="number">768</span>) {</span><br><span class="line"> <span class="keyword">let</span> pageX = event.<span class="property">clientX</span> + <span class="number">10</span>; </span><br><span class="line"> <span class="keyword">let</span> pageY = event.<span class="property">clientY</span>;</span><br><span class="line"> <span class="keyword">let</span> $rightMenuNormal = $(<span class="string">".rightMenuNormal"</span>);</span><br><span class="line"> <span class="keyword">let</span> $rightMenuOther = $(<span class="string">".rightMenuOther"</span>);</span><br><span class="line"> <span class="keyword">let</span> $rightMenuReadmode = $(<span class="string">"#menu-readmode"</span>);</span><br><span class="line"> $rightMenuNormal.<span class="title function_">show</span>();</span><br><span class="line"> $rightMenuOther.<span class="title function_">show</span>();</span><br><span class="line"> rm.<span class="title function_">reloadrmSize</span>();</span><br><span class="line"> <span class="keyword">if</span> (pageX + rmWidth > <span class="variable language_">window</span>.<span class="property">innerWidth</span>) {</span><br><span class="line"> pageX -= rmWidth;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (pageY + rmHeight > <span class="variable language_">window</span>.<span class="property">innerHeight</span>) {</span><br><span class="line"> pageY -= rmHeight;</span><br><span class="line"> }</span><br><span class="line"> rm.<span class="title function_">showRightMenu</span>(<span class="literal">true</span>, pageY, pageX);</span><br><span class="line"> $(<span class="string">'#rightmenu-mask'</span>).<span class="title function_">attr</span>(<span class="string">'style'</span>, <span class="string">'display: flex'</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">removeRightMenu</span>(<span class="params"></span>) {</span><br><span class="line"> rm.<span class="title function_">showRightMenu</span>(<span class="literal">false</span>);</span><br><span class="line"> $(<span class="string">'#rightmenu-mask'</span>).<span class="title function_">attr</span>(<span class="string">'style'</span>, <span class="string">'display: none'</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">stopMaskScroll</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">"rightmenu-mask"</span>)) {</span><br><span class="line"> <span class="keyword">let</span> xscroll = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">"rightmenu-mask"</span>);</span><br><span class="line"> xscroll.<span class="title function_">addEventListener</span>(<span class="string">"mousewheel"</span>, <span class="keyword">function</span> (<span class="params">e</span>) {</span><br><span class="line"> <span class="title function_">removeRightMenu</span>();</span><br><span class="line"> }, <span class="literal">false</span>);</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">"rightMenu"</span>)) {</span><br><span class="line"> <span class="keyword">let</span> xscroll = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">"rightMenu"</span>);</span><br><span class="line"> xscroll.<span class="title function_">addEventListener</span>(<span class="string">"mousewheel"</span>, <span class="keyword">function</span> (<span class="params">e</span>) {</span><br><span class="line"> <span class="title function_">removeRightMenu</span>();</span><br><span class="line"> }, <span class="literal">false</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@name</span>: 切換模式</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">switchDarkMode</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="title function_">removeRightMenu</span>();</span><br><span class="line"> <span class="keyword">const</span> nowMode = <span class="variable language_">document</span>.<span class="property">documentElement</span>.<span class="title function_">getAttribute</span>(<span class="string">'data-theme'</span>) === <span class="string">'dark'</span> ? <span class="string">'dark'</span> : <span class="string">'light'</span></span><br><span class="line"> <span class="keyword">if</span> (nowMode === <span class="string">'light'</span>) {</span><br><span class="line"> <span class="title function_">activateDarkMode</span>();</span><br><span class="line"> saveToLocal.<span class="title function_">set</span>(<span class="string">'theme'</span>, <span class="string">'dark'</span>, <span class="number">2</span>);</span><br><span class="line"> <span class="variable constant_">GLOBAL_CONFIG</span>.<span class="property">Snackbar</span> !== <span class="literal">undefined</span> && btf.<span class="title function_">snackbarShow</span>(<span class="variable constant_">GLOBAL_CONFIG</span>.<span class="property">Snackbar</span>.<span class="property">day_to_night</span>);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="title function_">activateLightMode</span>();</span><br><span class="line"> saveToLocal.<span class="title function_">set</span>(<span class="string">'theme'</span>, <span class="string">'light'</span>, <span class="number">2</span>);</span><br><span class="line"> <span class="variable constant_">GLOBAL_CONFIG</span>.<span class="property">Snackbar</span> !== <span class="literal">undefined</span> && btf.<span class="title function_">snackbarShow</span>(<span class="variable constant_">GLOBAL_CONFIG</span>.<span class="property">Snackbar</span>.<span class="property">night_to_day</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">typeof</span> utterancesTheme === <span class="string">'function'</span> && <span class="title function_">utterancesTheme</span>();</span><br><span class="line"> <span class="keyword">typeof</span> <span class="variable constant_">FB</span> === <span class="string">'object'</span> && <span class="variable language_">window</span>.<span class="title function_">loadFBComment</span>();</span><br><span class="line"> <span class="variable language_">window</span>.<span class="property">DISQUS</span> && <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'disqus_thread'</span>).<span class="property">children</span>.<span class="property">length</span> && <span class="built_in">setTimeout</span>(<span class="function">() =></span> <span class="variable language_">window</span>.<span class="title function_">disqusReset</span>(), <span class="number">200</span>);</span><br><span class="line">};</span><br><span class="line"><span class="comment">/* eslint-disable no-undef */</span></span><br><span class="line"><span class="variable language_">document</span>.<span class="title function_">addEventListener</span>(<span class="string">'DOMContentLoaded'</span>, <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="title function_">translateInitialization</span>();</span><br><span class="line"> <span class="variable language_">document</span>.<span class="title function_">addEventListener</span>(<span class="string">'pjax:complete'</span>, translateInitialization);</span><br><span class="line">});</span><br><span class="line"><span class="keyword">const</span> translate = <span class="variable constant_">GLOBAL_CONFIG</span>.<span class="property">translate</span>;</span><br><span class="line"><span class="keyword">const</span> snackbarData = <span class="variable constant_">GLOBAL_CONFIG</span>.<span class="property">Snackbar</span>;</span><br><span class="line"><span class="keyword">const</span> defaultEncoding = translate.<span class="property">defaultEncoding</span>; <span class="comment">/* 網站默認語言,1: 繁體中文, 2: 簡體中文 */</span></span><br><span class="line"><span class="keyword">const</span> translateDelay = translate.<span class="property">translateDelay</span>; <span class="comment">/* 延遲時間,若不在前, 要設定延遲翻譯時間, 如100表示100ms,默認為0 */</span></span><br><span class="line"><span class="keyword">const</span> msgToTraditionalChinese = translate.<span class="property">msgToTraditionalChinese</span>; <span class="comment">/* 此處可以更改為你想要顯示的文字 */</span></span><br><span class="line"><span class="keyword">const</span> msgToSimplifiedChinese = translate.<span class="property">msgToSimplifiedChinese</span>; <span class="comment">/* 同上,但兩處均不建議更改 */</span></span><br><span class="line"><span class="keyword">let</span> currentEncoding = defaultEncoding;</span><br><span class="line"><span class="keyword">const</span> targetEncodingCookie = <span class="string">'translate-chn-cht'</span>;</span><br><span class="line"><span class="keyword">let</span> targetEncoding =</span><br><span class="line"> saveToLocal.<span class="title function_">get</span>(targetEncodingCookie) === <span class="literal">undefined</span></span><br><span class="line"> ? defaultEncoding</span><br><span class="line"> : <span class="title class_">Number</span>(saveToLocal.<span class="title function_">get</span>(<span class="string">'translate-chn-cht'</span>));</span><br><span class="line"><span class="keyword">let</span> translateButtonObject</span><br><span class="line"><span class="keyword">const</span> isSnackbar = <span class="variable constant_">GLOBAL_CONFIG</span>.<span class="property">Snackbar</span> !== <span class="literal">undefined</span>;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">translateText</span>(<span class="params">txt</span>) {</span><br><span class="line"> <span class="keyword">if</span> (txt === <span class="string">''</span> || txt == <span class="literal">null</span>) <span class="keyword">return</span> <span class="string">''</span>;</span><br><span class="line"> <span class="keyword">if</span> (currentEncoding === <span class="number">1</span> && targetEncoding === <span class="number">2</span>) <span class="keyword">return</span> <span class="title class_">Simplized</span>(txt);</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (currentEncoding === <span class="number">2</span> && targetEncoding === <span class="number">1</span>) { <span class="keyword">return</span> <span class="title class_">Traditionalized</span>(txt) } <span class="keyword">else</span> <span class="keyword">return</span> txt;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">translateBody</span>(<span class="params">fobj</span>) {</span><br><span class="line"> <span class="keyword">let</span> objs;</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> fobj === <span class="string">'object'</span>) objs = fobj.<span class="property">childNodes</span>;</span><br><span class="line"> <span class="keyword">else</span> objs = <span class="variable language_">document</span>.<span class="property">body</span>.<span class="property">childNodes</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < objs.<span class="property">length</span>; i++) {</span><br><span class="line"> <span class="keyword">const</span> obj = objs.<span class="title function_">item</span>(i);</span><br><span class="line"> <span class="keyword">if</span> (</span><br><span class="line"> <span class="string">'||BR|HR|'</span>.<span class="title function_">indexOf</span>(<span class="string">'|'</span> + obj.<span class="property">tagName</span> + <span class="string">'|'</span>) > <span class="number">0</span> ||</span><br><span class="line"> obj === translateButtonObject</span><br><span class="line"> ) { <span class="keyword">continue</span> }</span><br><span class="line"> <span class="keyword">if</span> (obj.<span class="property">title</span> !== <span class="string">''</span> && obj.<span class="property">title</span> != <span class="literal">null</span>) { obj.<span class="property">title</span> = <span class="title function_">translateText</span>(obj.<span class="property">title</span>) };</span><br><span class="line"> <span class="keyword">if</span> (obj.<span class="property">alt</span> !== <span class="string">''</span> && obj.<span class="property">alt</span> != <span class="literal">null</span>) obj.<span class="property">alt</span> = <span class="title function_">translateText</span>(obj.<span class="property">alt</span>);</span><br><span class="line"> <span class="keyword">if</span> (obj.<span class="property">placeholder</span> !== <span class="string">''</span> && obj.<span class="property">placeholder</span> != <span class="literal">null</span>) obj.<span class="property">placeholder</span> = <span class="title function_">translateText</span>(obj.<span class="property">placeholder</span>);</span><br><span class="line"> <span class="keyword">if</span> (</span><br><span class="line"> obj.<span class="property">tagName</span> === <span class="string">'INPUT'</span> &&</span><br><span class="line"> obj.<span class="property">value</span> !== <span class="string">''</span> &&</span><br><span class="line"> obj.<span class="property">type</span> !== <span class="string">'text'</span> &&</span><br><span class="line"> obj.<span class="property">type</span> !== <span class="string">'hidden'</span></span><br><span class="line"> ) { obj.<span class="property">value</span> = <span class="title function_">translateText</span>(obj.<span class="property">value</span>) }</span><br><span class="line"> <span class="keyword">if</span> (obj.<span class="property">nodeType</span> === <span class="number">3</span>) obj.<span class="property">data</span> = <span class="title function_">translateText</span>(obj.<span class="property">data</span>);</span><br><span class="line"> <span class="keyword">else</span> <span class="title function_">translateBody</span>(obj);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">translatePage</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">if</span> (targetEncoding === <span class="number">1</span>) {</span><br><span class="line"> currentEncoding = <span class="number">1</span>;</span><br><span class="line"> targetEncoding = <span class="number">2</span>;</span><br><span class="line"> saveToLocal.<span class="title function_">set</span>(targetEncodingCookie, targetEncoding, <span class="number">2</span>);</span><br><span class="line"> <span class="title function_">translateBody</span>();</span><br><span class="line"> <span class="keyword">if</span> (isSnackbar) btf.<span class="title function_">snackbarShow</span>(snackbarData.<span class="property">cht_to_chs</span>);</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (targetEncoding === <span class="number">2</span>) {</span><br><span class="line"> currentEncoding = <span class="number">2</span>;</span><br><span class="line"> targetEncoding = <span class="number">1</span>;</span><br><span class="line"> saveToLocal.<span class="title function_">set</span>(targetEncodingCookie, targetEncoding, <span class="number">2</span>);</span><br><span class="line"> <span class="title function_">translateBody</span>();</span><br><span class="line"> <span class="keyword">if</span> (isSnackbar) btf.<span class="title function_">snackbarShow</span>(snackbarData.<span class="property">chs_to_cht</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">JTPYStr</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">'万与丑专业丛东丝丢两严丧个丬丰临为丽举么义乌乐乔习乡书买乱争于亏云亘亚产亩亲亵亸亿仅从仑仓仪们价众优伙会伛伞伟传伤伥伦伧伪伫体余佣佥侠侣侥侦侧侨侩侪侬俣俦俨俩俪俭债倾偬偻偾偿傥傧储傩儿兑兖党兰关兴兹养兽冁内冈册写军农冢冯冲决况冻净凄凉凌减凑凛几凤凫凭凯击凼凿刍划刘则刚创删别刬刭刽刿剀剂剐剑剥剧劝办务劢动励劲劳势勋勐勚匀匦匮区医华协单卖卢卤卧卫却卺厂厅历厉压厌厍厕厢厣厦厨厩厮县参叆叇双发变叙叠叶号叹叽吁后吓吕吗吣吨听启吴呒呓呕呖呗员呙呛呜咏咔咙咛咝咤咴咸哌响哑哒哓哔哕哗哙哜哝哟唛唝唠唡唢唣唤唿啧啬啭啮啰啴啸喷喽喾嗫呵嗳嘘嘤嘱噜噼嚣嚯团园囱围囵国图圆圣圹场坂坏块坚坛坜坝坞坟坠垄垅垆垒垦垧垩垫垭垯垱垲垴埘埙埚埝埯堑堕塆墙壮声壳壶壸处备复够头夸夹夺奁奂奋奖奥妆妇妈妩妪妫姗姜娄娅娆娇娈娱娲娴婳婴婵婶媪嫒嫔嫱嬷孙学孪宁宝实宠审宪宫宽宾寝对寻导寿将尔尘尧尴尸尽层屃屉届属屡屦屿岁岂岖岗岘岙岚岛岭岳岽岿峃峄峡峣峤峥峦崂崃崄崭嵘嵚嵛嵝嵴巅巩巯币帅师帏帐帘帜带帧帮帱帻帼幂幞干并广庄庆庐庑库应庙庞废庼廪开异弃张弥弪弯弹强归当录彟彦彻径徕御忆忏忧忾怀态怂怃怄怅怆怜总怼怿恋恳恶恸恹恺恻恼恽悦悫悬悭悯惊惧惨惩惫惬惭惮惯愍愠愤愦愿慑慭憷懑懒懔戆戋戏戗战戬户扎扑扦执扩扪扫扬扰抚抛抟抠抡抢护报担拟拢拣拥拦拧拨择挂挚挛挜挝挞挟挠挡挢挣挤挥挦捞损捡换捣据捻掳掴掷掸掺掼揸揽揿搀搁搂搅携摄摅摆摇摈摊撄撑撵撷撸撺擞攒敌敛数斋斓斗斩断无旧时旷旸昙昼昽显晋晒晓晔晕晖暂暧札术朴机杀杂权条来杨杩杰极构枞枢枣枥枧枨枪枫枭柜柠柽栀栅标栈栉栊栋栌栎栏树栖样栾桊桠桡桢档桤桥桦桧桨桩梦梼梾检棂椁椟椠椤椭楼榄榇榈榉槚槛槟槠横樯樱橥橱橹橼檐檩欢欤欧歼殁殇残殒殓殚殡殴毁毂毕毙毡毵氇气氢氩氲汇汉污汤汹沓沟没沣沤沥沦沧沨沩沪沵泞泪泶泷泸泺泻泼泽泾洁洒洼浃浅浆浇浈浉浊测浍济浏浐浑浒浓浔浕涂涌涛涝涞涟涠涡涢涣涤润涧涨涩淀渊渌渍渎渐渑渔渖渗温游湾湿溃溅溆溇滗滚滞滟滠满滢滤滥滦滨滩滪漤潆潇潋潍潜潴澜濑濒灏灭灯灵灾灿炀炉炖炜炝点炼炽烁烂烃烛烟烦烧烨烩烫烬热焕焖焘煅煳熘爱爷牍牦牵牺犊犟状犷犸犹狈狍狝狞独狭狮狯狰狱狲猃猎猕猡猪猫猬献獭玑玙玚玛玮环现玱玺珉珏珐珑珰珲琎琏琐琼瑶瑷璇璎瓒瓮瓯电画畅畲畴疖疗疟疠疡疬疮疯疱疴痈痉痒痖痨痪痫痴瘅瘆瘗瘘瘪瘫瘾瘿癞癣癫癯皑皱皲盏盐监盖盗盘眍眦眬着睁睐睑瞒瞩矫矶矾矿砀码砖砗砚砜砺砻砾础硁硅硕硖硗硙硚确硷碍碛碜碱碹磙礼祎祢祯祷祸禀禄禅离秃秆种积称秽秾稆税稣稳穑穷窃窍窑窜窝窥窦窭竖竞笃笋笔笕笺笼笾筑筚筛筜筝筹签简箓箦箧箨箩箪箫篑篓篮篱簖籁籴类籼粜粝粤粪粮糁糇紧絷纟纠纡红纣纤纥约级纨纩纪纫纬纭纮纯纰纱纲纳纴纵纶纷纸纹纺纻纼纽纾线绀绁绂练组绅细织终绉绊绋绌绍绎经绐绑绒结绔绕绖绗绘给绚绛络绝绞统绠绡绢绣绤绥绦继绨绩绪绫绬续绮绯绰绱绲绳维绵绶绷绸绹绺绻综绽绾绿缀缁缂缃缄缅缆缇缈缉缊缋缌缍缎缏缐缑缒缓缔缕编缗缘缙缚缛缜缝缞缟缠缡缢缣缤缥缦缧缨缩缪缫缬缭缮缯缰缱缲缳缴缵罂网罗罚罢罴羁羟羡翘翙翚耢耧耸耻聂聋职聍联聩聪肃肠肤肷肾肿胀胁胆胜胧胨胪胫胶脉脍脏脐脑脓脔脚脱脶脸腊腌腘腭腻腼腽腾膑臜舆舣舰舱舻艰艳艹艺节芈芗芜芦苁苇苈苋苌苍苎苏苘苹茎茏茑茔茕茧荆荐荙荚荛荜荞荟荠荡荣荤荥荦荧荨荩荪荫荬荭荮药莅莜莱莲莳莴莶获莸莹莺莼萚萝萤营萦萧萨葱蒇蒉蒋蒌蓝蓟蓠蓣蓥蓦蔷蔹蔺蔼蕲蕴薮藁藓虏虑虚虫虬虮虽虾虿蚀蚁蚂蚕蚝蚬蛊蛎蛏蛮蛰蛱蛲蛳蛴蜕蜗蜡蝇蝈蝉蝎蝼蝾螀螨蟏衅衔补衬衮袄袅袆袜袭袯装裆裈裢裣裤裥褛褴襁襕见观觃规觅视觇览觉觊觋觌觍觎觏觐觑觞触觯詟誉誊讠计订讣认讥讦讧讨让讪讫训议讯记讱讲讳讴讵讶讷许讹论讻讼讽设访诀证诂诃评诅识诇诈诉诊诋诌词诎诏诐译诒诓诔试诖诗诘诙诚诛诜话诞诟诠诡询诣诤该详诧诨诩诪诫诬语诮误诰诱诲诳说诵诶请诸诹诺读诼诽课诿谀谁谂调谄谅谆谇谈谊谋谌谍谎谏谐谑谒谓谔谕谖谗谘谙谚谛谜谝谞谟谠谡谢谣谤谥谦谧谨谩谪谫谬谭谮谯谰谱谲谳谴谵谶谷豮贝贞负贠贡财责贤败账货质贩贪贫贬购贮贯贰贱贲贳贴贵贶贷贸费贺贻贼贽贾贿赀赁赂赃资赅赆赇赈赉赊赋赌赍赎赏赐赑赒赓赔赕赖赗赘赙赚赛赜赝赞赟赠赡赢赣赪赵赶趋趱趸跃跄跖跞践跶跷跸跹跻踊踌踪踬踯蹑蹒蹰蹿躏躜躯车轧轨轩轪轫转轭轮软轰轱轲轳轴轵轶轷轸轹轺轻轼载轾轿辀辁辂较辄辅辆辇辈辉辊辋辌辍辎辏辐辑辒输辔辕辖辗辘辙辚辞辩辫边辽达迁过迈运还这进远违连迟迩迳迹适选逊递逦逻遗遥邓邝邬邮邹邺邻郁郄郏郐郑郓郦郧郸酝酦酱酽酾酿释里鉅鉴銮錾钆钇针钉钊钋钌钍钎钏钐钑钒钓钔钕钖钗钘钙钚钛钝钞钟钠钡钢钣钤钥钦钧钨钩钪钫钬钭钮钯钰钱钲钳钴钵钶钷钸钹钺钻钼钽钾钿铀铁铂铃铄铅铆铈铉铊铋铍铎铏铐铑铒铕铗铘铙铚铛铜铝铞铟铠铡铢铣铤铥铦铧铨铪铫铬铭铮铯铰铱铲铳铴铵银铷铸铹铺铻铼铽链铿销锁锂锃锄锅锆锇锈锉锊锋锌锍锎锏锐锑锒锓锔锕锖锗错锚锜锞锟锠锡锢锣锤锥锦锨锩锫锬锭键锯锰锱锲锳锴锵锶锷锸锹锺锻锼锽锾锿镀镁镂镃镆镇镈镉镊镌镍镎镏镐镑镒镕镖镗镙镚镛镜镝镞镟镠镡镢镣镤镥镦镧镨镩镪镫镬镭镮镯镰镱镲镳镴镶长门闩闪闫闬闭问闯闰闱闲闳间闵闶闷闸闹闺闻闼闽闾闿阀阁阂阃阄阅阆阇阈阉阊阋阌阍阎阏阐阑阒阓阔阕阖阗阘阙阚阛队阳阴阵阶际陆陇陈陉陕陧陨险随隐隶隽难雏雠雳雾霁霉霭靓静靥鞑鞒鞯鞴韦韧韨韩韪韫韬韵页顶顷顸项顺须顼顽顾顿颀颁颂颃预颅领颇颈颉颊颋颌颍颎颏颐频颒颓颔颕颖颗题颙颚颛颜额颞颟颠颡颢颣颤颥颦颧风飏飐飑飒飓飔飕飖飗飘飙飚飞飨餍饤饥饦饧饨饩饪饫饬饭饮饯饰饱饲饳饴饵饶饷饸饹饺饻饼饽饾饿馀馁馂馃馄馅馆馇馈馉馊馋馌馍馎馏馐馑馒馓馔馕马驭驮驯驰驱驲驳驴驵驶驷驸驹驺驻驼驽驾驿骀骁骂骃骄骅骆骇骈骉骊骋验骍骎骏骐骑骒骓骔骕骖骗骘骙骚骛骜骝骞骟骠骡骢骣骤骥骦骧髅髋髌鬓魇魉鱼鱽鱾鱿鲀鲁鲂鲄鲅鲆鲇鲈鲉鲊鲋鲌鲍鲎鲏鲐鲑鲒鲓鲔鲕鲖鲗鲘鲙鲚鲛鲜鲝鲞鲟鲠鲡鲢鲣鲤鲥鲦鲧鲨鲩鲪鲫鲬鲭鲮鲯鲰鲱鲲鲳鲴鲵鲶鲷鲸鲹鲺鲻鲼鲽鲾鲿鳀鳁鳂鳃鳄鳅鳆鳇鳈鳉鳊鳋鳌鳍鳎鳏鳐鳑鳒鳓鳔鳕鳖鳗鳘鳙鳛鳜鳝鳞鳟鳠鳡鳢鳣鸟鸠鸡鸢鸣鸤鸥鸦鸧鸨鸩鸪鸫鸬鸭鸮鸯鸰鸱鸲鸳鸴鸵鸶鸷鸸鸹鸺鸻鸼鸽鸾鸿鹀鹁鹂鹃鹄鹅鹆鹇鹈鹉鹊鹋鹌鹍鹎鹏鹐鹑鹒鹓鹔鹕鹖鹗鹘鹚鹛鹜鹝鹞鹟鹠鹡鹢鹣鹤鹥鹦鹧鹨鹩鹪鹫鹬鹭鹯鹰鹱鹲鹳鹴鹾麦麸黄黉黡黩黪黾'</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">FTPYStr</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">'萬與醜專業叢東絲丟兩嚴喪個爿豐臨為麗舉麼義烏樂喬習鄉書買亂爭於虧雲亙亞產畝親褻嚲億僅從侖倉儀們價眾優夥會傴傘偉傳傷倀倫傖偽佇體餘傭僉俠侶僥偵側僑儈儕儂俁儔儼倆儷儉債傾傯僂僨償儻儐儲儺兒兌兗黨蘭關興茲養獸囅內岡冊寫軍農塚馮衝決況凍淨淒涼淩減湊凜幾鳳鳧憑凱擊氹鑿芻劃劉則剛創刪別剗剄劊劌剴劑剮劍剝劇勸辦務勱動勵勁勞勢勳猛勩勻匭匱區醫華協單賣盧鹵臥衛卻巹廠廳曆厲壓厭厙廁廂厴廈廚廄廝縣參靉靆雙發變敘疊葉號歎嘰籲後嚇呂嗎唚噸聽啟吳嘸囈嘔嚦唄員咼嗆嗚詠哢嚨嚀噝吒噅鹹呱響啞噠嘵嗶噦嘩噲嚌噥喲嘜嗊嘮啢嗩唕喚呼嘖嗇囀齧囉嘽嘯噴嘍嚳囁嗬噯噓嚶囑嚕劈囂謔團園囪圍圇國圖圓聖壙場阪壞塊堅壇壢壩塢墳墜壟壟壚壘墾坰堊墊埡墶壋塏堖塒塤堝墊垵塹墮壪牆壯聲殼壺壼處備複夠頭誇夾奪奩奐奮獎奧妝婦媽嫵嫗媯姍薑婁婭嬈嬌孌娛媧嫻嫿嬰嬋嬸媼嬡嬪嬙嬤孫學孿寧寶實寵審憲宮寬賓寢對尋導壽將爾塵堯尷屍盡層屭屜屆屬屢屨嶼歲豈嶇崗峴嶴嵐島嶺嶽崠巋嶨嶧峽嶢嶠崢巒嶗崍嶮嶄嶸嶔崳嶁脊巔鞏巰幣帥師幃帳簾幟帶幀幫幬幘幗冪襆幹並廣莊慶廬廡庫應廟龐廢廎廩開異棄張彌弳彎彈強歸當錄彠彥徹徑徠禦憶懺憂愾懷態慫憮慪悵愴憐總懟懌戀懇惡慟懨愷惻惱惲悅愨懸慳憫驚懼慘懲憊愜慚憚慣湣慍憤憒願懾憖怵懣懶懍戇戔戲戧戰戩戶紮撲扡執擴捫掃揚擾撫拋摶摳掄搶護報擔擬攏揀擁攔擰撥擇掛摯攣掗撾撻挾撓擋撟掙擠揮撏撈損撿換搗據撚擄摑擲撣摻摜摣攬撳攙擱摟攪攜攝攄擺搖擯攤攖撐攆擷擼攛擻攢敵斂數齋斕鬥斬斷無舊時曠暘曇晝曨顯晉曬曉曄暈暉暫曖劄術樸機殺雜權條來楊榪傑極構樅樞棗櫪梘棖槍楓梟櫃檸檉梔柵標棧櫛櫳棟櫨櫟欄樹棲樣欒棬椏橈楨檔榿橋樺檜槳樁夢檮棶檢欞槨櫝槧欏橢樓欖櫬櫚櫸檟檻檳櫧橫檣櫻櫫櫥櫓櫞簷檁歡歟歐殲歿殤殘殞殮殫殯毆毀轂畢斃氈毿氌氣氫氬氳彙漢汙湯洶遝溝沒灃漚瀝淪滄渢溈滬濔濘淚澩瀧瀘濼瀉潑澤涇潔灑窪浹淺漿澆湞溮濁測澮濟瀏滻渾滸濃潯濜塗湧濤澇淶漣潿渦溳渙滌潤澗漲澀澱淵淥漬瀆漸澠漁瀋滲溫遊灣濕潰濺漵漊潷滾滯灩灄滿瀅濾濫灤濱灘澦濫瀠瀟瀲濰潛瀦瀾瀨瀕灝滅燈靈災燦煬爐燉煒熗點煉熾爍爛烴燭煙煩燒燁燴燙燼熱煥燜燾煆糊溜愛爺牘犛牽犧犢強狀獷獁猶狽麅獮獰獨狹獅獪猙獄猻獫獵獼玀豬貓蝟獻獺璣璵瑒瑪瑋環現瑲璽瑉玨琺瓏璫琿璡璉瑣瓊瑤璦璿瓔瓚甕甌電畫暢佘疇癤療瘧癘瘍鬁瘡瘋皰屙癰痙癢瘂癆瘓癇癡癉瘮瘞瘺癟癱癮癭癩癬癲臒皚皺皸盞鹽監蓋盜盤瞘眥矓著睜睞瞼瞞矚矯磯礬礦碭碼磚硨硯碸礪礱礫礎硜矽碩硤磽磑礄確鹼礙磧磣堿镟滾禮禕禰禎禱禍稟祿禪離禿稈種積稱穢穠穭稅穌穩穡窮竊竅窯竄窩窺竇窶豎競篤筍筆筧箋籠籩築篳篩簹箏籌簽簡籙簀篋籜籮簞簫簣簍籃籬籪籟糴類秈糶糲粵糞糧糝餱緊縶糸糾紆紅紂纖紇約級紈纊紀紉緯紜紘純紕紗綱納紝縱綸紛紙紋紡紵紖紐紓線紺絏紱練組紳細織終縐絆紼絀紹繹經紿綁絨結絝繞絰絎繪給絢絳絡絕絞統綆綃絹繡綌綏絛繼綈績緒綾緓續綺緋綽緔緄繩維綿綬繃綢綯綹綣綜綻綰綠綴緇緙緗緘緬纜緹緲緝縕繢緦綞緞緶線緱縋緩締縷編緡緣縉縛縟縝縫縗縞纏縭縊縑繽縹縵縲纓縮繆繅纈繚繕繒韁繾繰繯繳纘罌網羅罰罷羆羈羥羨翹翽翬耮耬聳恥聶聾職聹聯聵聰肅腸膚膁腎腫脹脅膽勝朧腖臚脛膠脈膾髒臍腦膿臠腳脫腡臉臘醃膕齶膩靦膃騰臏臢輿艤艦艙艫艱豔艸藝節羋薌蕪蘆蓯葦藶莧萇蒼苧蘇檾蘋莖蘢蔦塋煢繭荊薦薘莢蕘蓽蕎薈薺蕩榮葷滎犖熒蕁藎蓀蔭蕒葒葤藥蒞蓧萊蓮蒔萵薟獲蕕瑩鶯蓴蘀蘿螢營縈蕭薩蔥蕆蕢蔣蔞藍薊蘺蕷鎣驀薔蘞藺藹蘄蘊藪槁蘚虜慮虛蟲虯蟣雖蝦蠆蝕蟻螞蠶蠔蜆蠱蠣蟶蠻蟄蛺蟯螄蠐蛻蝸蠟蠅蟈蟬蠍螻蠑螿蟎蠨釁銜補襯袞襖嫋褘襪襲襏裝襠褌褳襝褲襇褸襤繈襴見觀覎規覓視覘覽覺覬覡覿覥覦覯覲覷觴觸觶讋譽謄訁計訂訃認譏訐訌討讓訕訖訓議訊記訒講諱謳詎訝訥許訛論訩訟諷設訪訣證詁訶評詛識詗詐訴診詆謅詞詘詔詖譯詒誆誄試詿詩詰詼誠誅詵話誕詬詮詭詢詣諍該詳詫諢詡譸誡誣語誚誤誥誘誨誑說誦誒請諸諏諾讀諑誹課諉諛誰諗調諂諒諄誶談誼謀諶諜謊諫諧謔謁謂諤諭諼讒諮諳諺諦謎諞諝謨讜謖謝謠謗諡謙謐謹謾謫譾謬譚譖譙讕譜譎讞譴譫讖穀豶貝貞負貟貢財責賢敗賬貨質販貪貧貶購貯貫貳賤賁貰貼貴貺貸貿費賀貽賊贄賈賄貲賃賂贓資賅贐賕賑賚賒賦賭齎贖賞賜贔賙賡賠賧賴賵贅賻賺賽賾贗讚贇贈贍贏贛赬趙趕趨趲躉躍蹌蹠躒踐躂蹺蹕躚躋踴躊蹤躓躑躡蹣躕躥躪躦軀車軋軌軒軑軔轉軛輪軟轟軲軻轤軸軹軼軤軫轢軺輕軾載輊轎輈輇輅較輒輔輛輦輩輝輥輞輬輟輜輳輻輯轀輸轡轅轄輾轆轍轔辭辯辮邊遼達遷過邁運還這進遠違連遲邇逕跡適選遜遞邐邏遺遙鄧鄺鄔郵鄒鄴鄰鬱郤郟鄶鄭鄆酈鄖鄲醞醱醬釅釃釀釋裏钜鑒鑾鏨釓釔針釘釗釙釕釷釺釧釤鈒釩釣鍆釹鍚釵鈃鈣鈈鈦鈍鈔鍾鈉鋇鋼鈑鈐鑰欽鈞鎢鉤鈧鈁鈥鈄鈕鈀鈺錢鉦鉗鈷缽鈳鉕鈽鈸鉞鑽鉬鉭鉀鈿鈾鐵鉑鈴鑠鉛鉚鈰鉉鉈鉍鈹鐸鉶銬銠鉺銪鋏鋣鐃銍鐺銅鋁銱銦鎧鍘銖銑鋌銩銛鏵銓鉿銚鉻銘錚銫鉸銥鏟銃鐋銨銀銣鑄鐒鋪鋙錸鋱鏈鏗銷鎖鋰鋥鋤鍋鋯鋨鏽銼鋝鋒鋅鋶鐦鐧銳銻鋃鋟鋦錒錆鍺錯錨錡錁錕錩錫錮鑼錘錐錦鍁錈錇錟錠鍵鋸錳錙鍥鍈鍇鏘鍶鍔鍤鍬鍾鍛鎪鍠鍰鎄鍍鎂鏤鎡鏌鎮鎛鎘鑷鐫鎳鎿鎦鎬鎊鎰鎔鏢鏜鏍鏰鏞鏡鏑鏃鏇鏐鐔钁鐐鏷鑥鐓鑭鐠鑹鏹鐙鑊鐳鐶鐲鐮鐿鑔鑣鑞鑲長門閂閃閆閈閉問闖閏闈閑閎間閔閌悶閘鬧閨聞闥閩閭闓閥閣閡閫鬮閱閬闍閾閹閶鬩閿閽閻閼闡闌闃闠闊闋闔闐闒闕闞闤隊陽陰陣階際陸隴陳陘陝隉隕險隨隱隸雋難雛讎靂霧霽黴靄靚靜靨韃鞽韉韝韋韌韍韓韙韞韜韻頁頂頃頇項順須頊頑顧頓頎頒頌頏預顱領頗頸頡頰頲頜潁熲頦頤頻頮頹頷頴穎顆題顒顎顓顏額顳顢顛顙顥纇顫顬顰顴風颺颭颮颯颶颸颼颻飀飄飆飆飛饗饜飣饑飥餳飩餼飪飫飭飯飲餞飾飽飼飿飴餌饒餉餄餎餃餏餅餑餖餓餘餒餕餜餛餡館餷饋餶餿饞饁饃餺餾饈饉饅饊饌饢馬馭馱馴馳驅馹駁驢駔駛駟駙駒騶駐駝駑駕驛駘驍罵駰驕驊駱駭駢驫驪騁驗騂駸駿騏騎騍騅騌驌驂騙騭騤騷騖驁騮騫騸驃騾驄驏驟驥驦驤髏髖髕鬢魘魎魚魛魢魷魨魯魴魺鮁鮃鯰鱸鮋鮓鮒鮊鮑鱟鮍鮐鮭鮚鮳鮪鮞鮦鰂鮜鱠鱭鮫鮮鮺鯗鱘鯁鱺鰱鰹鯉鰣鰷鯀鯊鯇鮶鯽鯒鯖鯪鯕鯫鯡鯤鯧鯝鯢鯰鯛鯨鯵鯴鯔鱝鰈鰏鱨鯷鰮鰃鰓鱷鰍鰒鰉鰁鱂鯿鰠鼇鰭鰨鰥鰩鰟鰜鰳鰾鱈鱉鰻鰵鱅鰼鱖鱔鱗鱒鱯鱤鱧鱣鳥鳩雞鳶鳴鳲鷗鴉鶬鴇鴆鴣鶇鸕鴨鴞鴦鴒鴟鴝鴛鴬鴕鷥鷙鴯鴰鵂鴴鵃鴿鸞鴻鵐鵓鸝鵑鵠鵝鵒鷳鵜鵡鵲鶓鵪鶤鵯鵬鵮鶉鶊鵷鷫鶘鶡鶚鶻鶿鶥鶩鷊鷂鶲鶹鶺鷁鶼鶴鷖鸚鷓鷚鷯鷦鷲鷸鷺鸇鷹鸌鸏鸛鸘鹺麥麩黃黌黶黷黲黽'</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Traditionalized</span>(<span class="params">cc</span>) {</span><br><span class="line"> <span class="keyword">let</span> str = <span class="string">''</span>;</span><br><span class="line"> <span class="keyword">const</span> ss = <span class="title class_">JTPYStr</span>();</span><br><span class="line"> <span class="keyword">const</span> tt = <span class="title class_">FTPYStr</span>();</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < cc.<span class="property">length</span>; i++) {</span><br><span class="line"> <span class="keyword">if</span> (cc.<span class="title function_">charCodeAt</span>(i) > <span class="number">10000</span> && ss.<span class="title function_">indexOf</span>(cc.<span class="title function_">charAt</span>(i)) !== -<span class="number">1</span>) { str += tt.<span class="title function_">charAt</span>(ss.<span class="title function_">indexOf</span>(cc.<span class="title function_">charAt</span>(i))) } <span class="keyword">else</span> str += cc.<span class="title function_">charAt</span>(i)</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">return</span> str;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Simplized</span>(<span class="params">cc</span>) {</span><br><span class="line"> <span class="keyword">let</span> str = <span class="string">''</span>;</span><br><span class="line"> <span class="keyword">const</span> ss = <span class="title class_">JTPYStr</span>();</span><br><span class="line"> <span class="keyword">const</span> tt = <span class="title class_">FTPYStr</span>();</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < cc.<span class="property">length</span>; i++) {</span><br><span class="line"> <span class="keyword">if</span> (cc.<span class="title function_">charCodeAt</span>(i) > <span class="number">10000</span> && tt.<span class="title function_">indexOf</span>(cc.<span class="title function_">charAt</span>(i)) !== -<span class="number">1</span>) { str += ss.<span class="title function_">charAt</span>(tt.<span class="title function_">indexOf</span>(cc.<span class="title function_">charAt</span>(i))) } <span class="keyword">else</span> str += cc.<span class="title function_">charAt</span>(i)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> str;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">translateInitialization</span>(<span class="params"></span>) {</span><br><span class="line"> translateButtonObject = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'menu-translate'</span>);</span><br><span class="line"> <span class="keyword">if</span> (translateButtonObject) {</span><br><span class="line"> <span class="keyword">if</span> (currentEncoding !== targetEncoding) {</span><br><span class="line"> <span class="built_in">setTimeout</span>(translateBody, translateDelay);</span><br><span class="line"> }</span><br><span class="line"> translateButtonObject.<span class="title function_">addEventListener</span>(<span class="string">'click'</span>, translatePage, <span class="literal">false</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">$(<span class="string">'#menu-backward'</span>).<span class="title function_">on</span>(<span class="string">'click'</span>, <span class="keyword">function</span> (<span class="params"></span>) { <span class="variable language_">window</span>.<span class="property">history</span>.<span class="title function_">back</span>(); });</span><br><span class="line">$(<span class="string">'#menu-forward'</span>).<span class="title function_">on</span>(<span class="string">'click'</span>, <span class="keyword">function</span> (<span class="params"></span>) { <span class="variable language_">window</span>.<span class="property">history</span>.<span class="title function_">forward</span>(); });</span><br><span class="line">$(<span class="string">'#menu-refresh'</span>).<span class="title function_">on</span>(<span class="string">'click'</span>, <span class="keyword">function</span> (<span class="params"></span>) { <span class="variable language_">window</span>.<span class="property">location</span>.<span class="title function_">reload</span>(); });</span><br><span class="line">$(<span class="string">'#menu-darkmode'</span>).<span class="title function_">on</span>(<span class="string">'click'</span>, <span class="keyword">function</span> (<span class="params"></span>) { <span class="title function_">switchDarkMode</span>() });</span><br><span class="line">$(<span class="string">'#menu-home'</span>).<span class="title function_">on</span>(<span class="string">'click'</span>, <span class="keyword">function</span> (<span class="params"></span>) { <span class="variable language_">window</span>.<span class="property">location</span>.<span class="property">href</span> = <span class="variable language_">window</span>.<span class="property">location</span>.<span class="property">origin</span>; });</span><br><span class="line"><span class="comment">/* 简体繁体切换 */</span></span><br><span class="line">$(<span class="string">'#menu-translate'</span>).<span class="title function_">on</span>(<span class="string">'click'</span>, <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="title function_">removeRightMenu</span>();</span><br><span class="line"> <span class="title function_">translateInitialization</span>();</span><br><span class="line">});</span><br><span class="line">$(<span class="string">".menu-link"</span>).<span class="title function_">on</span>(<span class="string">"click"</span>, <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="title function_">removeRightMenu</span>()</span><br><span class="line">});</span><br><span class="line">$(<span class="string">"#rightmenu-mask"</span>).<span class="title function_">on</span>(<span class="string">"click"</span>, <span class="keyword">function</span> (<span class="params"></span>) { <span class="title function_">removeRightMenu</span>() });</span><br><span class="line">$(<span class="string">"#rightmenu-mask"</span>).<span class="title function_">contextmenu</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="title function_">removeRightMenu</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">});</span><br></pre></td></tr></table></figure></li>
<li>在<code>BlogRoot/themes/butterfly/source/css</code>文件夹下新建一个<code>rightMenu.css</code>,将以下代码复制到文件中。<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="selector-id">#rightMenu</span> {</span><br><span class="line"> <span class="attribute">display</span>: none;</span><br><span class="line"> <span class="attribute">position</span>: fixed;</span><br><span class="line"> <span class="attribute">padding</span>: <span class="number">0</span> .<span class="number">25rem</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">9rem</span>;</span><br><span class="line"> <span class="attribute">height</span>: fit-content;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">10%</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">10%</span>;</span><br><span class="line"> <span class="attribute">background-color</span>: <span class="built_in">rgba</span>(<span class="number">238</span>, <span class="number">255</span>, <span class="number">255</span>, .<span class="number">85</span>);</span><br><span class="line"> -webkit-backdrop-<span class="attribute">filter</span>: <span class="built_in">blur</span>(<span class="number">20px</span>);</span><br><span class="line"> backdrop-<span class="attribute">filter</span>: <span class="built_in">blur</span>(<span class="number">20px</span>);</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#363636</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">12px</span>;</span><br><span class="line"> <span class="attribute">z-index</span>: <span class="number">99994</span>;</span><br><span class="line"> <span class="attribute">border</span>: <span class="number">#e3e8f7</span>;</span><br><span class="line"> user-select: none;</span><br><span class="line"> <span class="attribute">box-shadow</span>: <span class="built_in">rgba</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, .<span class="number">05</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#rightMenu</span> <span class="selector-tag">a</span> {</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#363636</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#rightMenu</span> <span class="selector-class">.rightMenu-group</span> {</span><br><span class="line"> <span class="attribute">padding</span>: .<span class="number">35rem</span> .<span class="number">3rem</span>;</span><br><span class="line"> <span class="attribute">transition</span>: .<span class="number">3s</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#rightMenu</span> <span class="selector-class">.rightMenu-line</span> {</span><br><span class="line"> <span class="attribute">border-top</span>: <span class="number">1px</span> dashed <span class="number">#4259ef23</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#rightMenu</span> <span class="selector-class">.rightMenu-group</span><span class="selector-class">.rightMenu-small</span> {</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><br><span class="line"></span><br><span class="line"><span class="selector-id">#rightMenu</span> <span class="selector-class">.rightMenu-group</span> <span class="selector-class">.rightMenu-item</span> {</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">8px</span>;</span><br><span class="line"> <span class="attribute">transition</span>: .<span class="number">3s</span>;</span><br><span class="line"> <span class="attribute">cursor</span>: pointer</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#rightMenu</span> <span class="selector-class">.rightMenu-line</span> <span class="selector-class">.rightMenu-item</span> {</span><br><span class="line"> <span class="attribute">margin</span>: .<span class="number">25rem</span> <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">padding</span>: .<span class="number">25rem</span> <span class="number">0</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#rightMenu</span> <span class="selector-class">.rightMenu-group</span><span class="selector-class">.rightMenu-line</span> <span class="selector-class">.rightMenu-item</span> {</span><br><span class="line"> <span class="attribute">display</span>: flex</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#rightMenu</span> <span class="selector-class">.rightMenu-group</span> <span class="selector-class">.rightMenu-item</span><span class="selector-pseudo">:hover</span> {</span><br><span class="line"> <span class="attribute">background-color</span>: <span class="number">#6f42c1</span>;</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#fff</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#rightMenu</span> <span class="selector-class">.rightMenu-group</span> <span class="selector-class">.rightMenu-item</span><span class="selector-pseudo">:active</span> {</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">scale</span>(.<span class="number">97</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#rightMenu</span> <span class="selector-class">.rightMenu-group</span> <span class="selector-class">.rightMenu-item</span> <span class="selector-tag">i</span> {</span><br><span class="line"> <span class="attribute">display</span>: inline-block;</span><br><span class="line"> <span class="attribute">text-align</span>: center;</span><br><span class="line"> <span class="attribute">line-height</span>: <span class="number">1.5rem</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">1.5rem</span>;</span><br><span class="line"> <span class="attribute">padding</span>: <span class="number">0</span> .<span class="number">25rem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#rightMenu</span> <span class="selector-class">.rightMenu-line</span> <span class="selector-class">.rightMenu-item</span> <span class="selector-tag">i</span> {</span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">0</span> .<span class="number">25rem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#rightMenu</span> <span class="selector-class">.rightMenu-group</span> <span class="selector-class">.rightMenu-item</span> <span class="selector-tag">span</span> {</span><br><span class="line"> <span class="attribute">line-height</span>: <span class="number">1.5rem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.rightMenu-small</span> <span class="selector-class">.rightMenu-item</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">30px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">30px</span>;</span><br><span class="line"> <span class="attribute">line-height</span>: <span class="number">30px</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#rightmenu-mask</span> {</span><br><span class="line"> <span class="attribute">position</span>: fixed;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100vw</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">100vh</span>;</span><br><span class="line"> <span class="attribute">background</span>: <span class="number">0</span> <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">display</span>: none;</span><br><span class="line"> <span class="attribute">z-index</span>: <span class="number">101</span>;</span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">0</span> <span class="meta">!important</span>;</span><br><span class="line"> <span class="attribute">z-index</span>: <span class="number">99993</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></li>
<li>在主题配置文件<code>_config.butterfly.yml</code>中引入<code>Jquery</code>、<code>rightMenu.js</code>和<code>rightMenu.css</code>。<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">inject:</span></span><br><span class="line"> <span class="attr">head:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string"><link</span> <span class="string">rel="stylesheet"</span> <span class="string">href="/css/rightMenu.css"></span></span><br><span class="line"> <span class="attr">bottom:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string"><script</span> <span class="string">defer</span> <span class="string">src="https://npm.elemecdn.com/jquery@latest/dist/jquery.min.js"></script></span></span><br><span class="line"> <span class="bullet">-</span> <span class="string"><script</span> <span class="string">defer</span> <span class="string">data-pjax</span> <span class="string">src="/js/rightMenu.js"></script></span></span><br></pre></td></tr></table></figure></li>
<li>最后重新编译运行即可看见效果。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/15/62d03ec1d3ed6.jpg"/></div></div>
<blockquote>
<p>需要注意的是,如果点击繁简切换,切换模式,出现了错误,请检查下主题的这两个功能是否开启。在主题配置文件<code>_config.butterfly.yml</code>中搜索<code>translate</code>和<code>darkmode</code>,将<code>enable</code>设置为<code>true</code>,在重新编译运行。</p>
</blockquote>
</li>
</ol>
<h2 id="扩展"><a href="#扩展" class="headerlink" title="扩展"></a>扩展</h2><p>这个章节将讲述如何去扩展右键的功能。通过上面的步骤,我们已经实现了下图中的功能。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/15/62d03ec1d3ed6.jpg"/></div></div>
<p>如果想在自定义右键上新增一个<code>打印页面</code>的功能。该如何去实现呢?</p>
<ol>
<li>增加DOM。(为了描述的更清晰,将沿用上面提到过的代码,<code>+</code>表示在此基础上新增的代码。)<br>在<code>BlogRoot/themes/butterfly/layout/includes/right-menu/index.pug</code>中新加如下代码:<figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">#rightMenu</span><br><span class="line"> .rightMenu-group.rightMenu-small</span><br><span class="line"> .rightMenu-item#menu-backward</span><br><span class="line"> i.fa-solid.fa-arrow-left</span><br><span class="line"> .rightMenu-item#menu-forward</span><br><span class="line"> i.fa-solid.fa-arrow-right</span><br><span class="line"> .rightMenu-item#menu-refresh</span><br><span class="line"> i.fa-solid.fa-arrow-rotate-right</span><br><span class="line"> .rightMenu-item#menu-home</span><br><span class="line"> i.fa-solid.fa-house</span><br><span class="line"> .rightMenu-group.rightMenu-line.rightMenuOther</span><br><span class="line"> a.rightMenu-item.menu-link(href='/archives/')</span><br><span class="line"> i.fa-solid.fa-archive</span><br><span class="line"> span='文章归档'</span><br><span class="line"> a.rightMenu-item.menu-link(href='/categories/')</span><br><span class="line"> i.fa-solid.fa-folder-open</span><br><span class="line"> span='文章分类'</span><br><span class="line"> a.rightMenu-item.menu-link(href='/tags/')</span><br><span class="line"> i.fa-solid.fa-tags</span><br><span class="line"> span='文章标签'</span><br><span class="line"> .rightMenu-group.rightMenu-line.rightMenuNormal</span><br><span class="line"> a.rightMenu-item.menu-link#menu-radompage(href='/random/index.html')</span><br><span class="line"> i.fa-solid.fa-shoe-prints</span><br><span class="line"> span='随便逛逛'</span><br><span class="line"> .rightMenu-item#menu-translate</span><br><span class="line"> i.fa-solid.fa-earth-asia</span><br><span class="line"> span='繁简切换'</span><br><span class="line"> .rightMenu-item#menu-darkmode</span><br><span class="line"> i.fa-solid.fa-moon</span><br><span class="line"> span='切换模式'</span><br><span class="line"><span class="addition">+ .rightMenu-item#menu-print</span></span><br><span class="line"><span class="addition">+ i.fa-solid.fa-print.fa-fw</span></span><br><span class="line"><span class="addition">+ span='打印页面'</span></span><br><span class="line">#rightmenu-mask</span><br></pre></td></tr></table></figure>
有兴趣的同学可以按下<kbd>F12</kbd> 打开控制台,找到<code>Elements</code>,并找到<code>#rightMenu</code>的盒子,你会发现新增的pug语法最终会被编译成:<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></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"rightMenu-item"</span> <span class="attr">id</span>=<span class="string">"menu-print"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">i</span> <span class="attr">class</span>=<span class="string">"fa-solid fa-print fa-fw"</span>></span><span class="tag"></<span class="name">i</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span>></span>打印頁面<span class="tag"></<span class="name">span</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/15/62d0472b9c38b.jpg"/></div></div>
记住这个<code>id</code>为<code>menu-print</code>的属性,下面将会用到。</li>
<li>在<code>BlogRoot/themes/butterfly/source/js/rightMenu.js</code>中写入实现方法。<figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">$('#menu-translate').on('click', function () {</span><br><span class="line"> removeRightMenu();</span><br><span class="line"> translateInitialization();</span><br><span class="line">});</span><br><span class="line">$(".menu-link").on("click", function () {</span><br><span class="line"> removeRightMenu()</span><br><span class="line">});</span><br><span class="line"><span class="addition">+ $("#menu-print").on("click", function () {</span></span><br><span class="line"><span class="addition">+ removeRightMenu();</span></span><br><span class="line"><span class="addition">+ window.print();</span></span><br><span class="line"><span class="addition">+ });</span></span><br><span class="line">$("#rightmenu-mask").on("click", function () { removeRightMenu() });</span><br><span class="line">$("#rightmenu-mask").contextmenu(function () {</span><br><span class="line"> removeRightMenu();</span><br><span class="line"> return false;</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
不难发现,新增的代码实际上是在<code>id</code>为<code>menu-print</code>的盒子上绑了一个点击事件,后面的方法则是触发点击事件后要执行的过程。<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="comment">// 在这里执行你想实现的操作</span></span><br><span class="line"> <span class="comment">// removeRightMenu();</span></span><br><span class="line"> <span class="comment">// window.print();</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></li>
<li>此时点击鼠标右键,会出现新加的一项功能<code>打印页面</code>。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/15/62d03ec280ddd.jpg"/></div></div></li>
</ol>
<p>到此,你学废了吗?遇到问题的话请在评论区留言。</p>
<h2 id="推荐阅读-1"><a href="#推荐阅读-1" class="headerlink" title="推荐阅读"></a>推荐阅读</h2><table>
<thead>
<tr>
<th align="center">参考方向</th>
<th align="center">教程原贴</th>
</tr>
</thead>
<tbody><tr>
<td align="center">ZHHEO</td>
<td align="center"><a href="https://blog.zhheo.com/p/5e931b65.html">Butterfly 魔改:自定义右键菜单</a></td>
</tr>
<tr>
<td align="center">LYX</td>
<td align="center"><a
基于 Hexo 键入搜索功能
https://fe32.top/articles/hexo1607/
2022-07-10T10:06:16.000Z
2024-02-23T15:35:21.204Z
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><!-- > 本站基于`Hexo`搭建,用的 🦋 [hexo-theme-butterfly](https://github.com/jerryc127/hexo-theme-butterfly.git) 主题 [v3.7.1](https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1),请注意最新的🦋 [hexo-theme-butterfly](https://github.com/jerryc127/hexo-theme-butterfly.git) 版本已经更新到 [v4.8.1](https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.8.1) 。<br>
如果你是 [v3.7.1](https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1) 之外的版本,可能有些地方会有出入,请留意。 -->
<blockquote>
<p>本站基于<code>Hexo</code>搭建,用的 🦋 <a href="https://github.com/jerryc127/hexo-theme-butterfly.git">hexo-theme-butterfly</a> 主题,已经升级到 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.7.0">v4.7.0</a> 。 请注意最新的🦋 <a href="https://github.com/jerryc127/hexo-theme-butterfly.git">hexo-theme-butterfly</a> 版本已经更新到 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.8.1">v4.8.1</a> 。<br><br>如果你是 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1">v3.7.1</a> 的版本,请移步 <a href="https://old.fe32.top/" target="_blank">v3.7.1</a> 站点进行浏览。</p>
</blockquote>
<blockquote>
<p>注意:我的博客根目录路径为 【G:/hexo-blog/blog-demo】,下文所说的根目录都是此路径,将用<code>[BlogRoot]</code>代替。如果不清楚根目录路径,请回到教程 <a href="https://fe32.top/articles/hexo1602/">基于 Hexo 从零开始搭建个人博客(二)</a>,查看你执行<code>hexo init xxx</code>这条命令时所选择的路径,例如我选择的路径是【G:/hexo-blog】,我的博客根目录即为【G:/hexo-blog/xxx】。<br><br>修改站点配置文件<code>_config.yml</code>,路径为【BlogRoot/_config.yml】。<br><br>修改主题配置文件<code>_config.butterfly.yml</code>,路径为【BlogRoot/_config.butterfly.yml】。</p>
</blockquote>
<h2 id="推荐阅读"><a href="#推荐阅读" class="headerlink" title="推荐阅读"></a>推荐阅读</h2><ul>
<li><a href="https://fe32.top/articles/hexo1601/">基于 Hexo 从零开始搭建个人博客(一)</a></li>
<li><a href="https://fe32.top/articles/hexo1602/">基于 Hexo 从零开始搭建个人博客(二)</a></li>
<li><a href="https://fe32.top/articles/hexo1603/">基于 Hexo 从零开始搭建个人博客(三)</a></li>
<li><a href="https://fe32.top/articles/hexo1604/">基于 Hexo 从零开始搭建个人博客(四)</a></li>
<li><a href="https://fe32.top/articles/hexo1605/">基于 Hexo 从零开始搭建个人博客(五)</a></li>
<li><a href="https://fe32.top/articles/hexo1606/">基于 Hexo 从零开始搭建个人博客(六)</a></li>
<li><a href="https://fe32.top/articles/hexo1609/">基于 Hexo 键入分享功能</a></li>
<li><a href="https://fe32.top/articles/hexo1610/">基于 Hexo 键入在线聊天功能</a></li>
<li><a href="https://fe32.top/articles/hexo1608/">Hexo + Butterfly 自定义右键菜单</a></li>
<li><a href="https://fe32.top/articles/hexo1612/">Hexo + Butterfly 一些常见问题</a></li>
<li><a href="https://fe32.top/articles/hexo1613/">请收下这只可爱的猫咪吧</a></li>
<li><a href="https://fe32.top/articles/hexo1614/">关于Vercel被墙导致获取Twikoo评论失败的解决方案</a></li>
<li><a href="https://fe32.top/articles/hexo1615/">飞只因太美,给你的首页装上吧!</a></li>
<li><a href="https://fe32.top/articles/hexo1617/">Hexo + Butterfly 自定义页脚</a></li>
<li><a href="https://fe32.top/articles/hexo1618/">Hexo + Butterfly 侧边栏公众号</a></li>
</ul>
<h2 id="Local-search"><a href="#Local-search" class="headerlink" title="Local search"></a>Local search</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/11/62cc415c9c150.jpg" alt="效果图"/></div><span class="image-caption">效果图</span></div>
<p>步骤如下:</p>
<ol>
<li>安装依赖。<br>前往博客根目录,打开cmd命令窗口执行<code>npm install hexo-generator-search --save</code>。<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 hexo-generator-search --save</span><br></pre></td></tr></table></figure></li>
<li>注入配置。<br>修改站点配置文件<code>_config.yml</code>,添加如下代码:<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">search:</span></span><br><span class="line"> <span class="attr">path:</span> <span class="string">search.xml</span></span><br><span class="line"> <span class="attr">field:</span> <span class="string">post</span></span><br><span class="line"> <span class="attr">content:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure></li>
<li>主题中开启搜索。<br>在主题配置文件<code>_config.butterfly.yml</code>中修改以下内容:<figure class="highlight diff"><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">local_search:</span><br><span class="line"><span class="deletion">- enable: false</span></span><br><span class="line"><span class="addition">+ enable: true</span></span><br></pre></td></tr></table></figure></li>
<li>重新编译运行,即可看到效果。<br>前往博客根目录,打开cmd命令窗口依次执行如下命令:<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">hexo cl && hexo generate</span><br><span class="line">hexo s -p 8000</span><br></pre></td></tr></table></figure>
详情可参考 <a href="https://github.com/wzpan/hexo-generator-search">hexo-generator-search</a></li>
</ol>
<h2 id="Algolia(推荐)"><a href="#Algolia(推荐)" class="headerlink" title="Algolia(推荐)"></a>Algolia(推荐)</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/11/62cc4784318c3.jpg" alt="效果图"/></div><span class="image-caption">效果图</span></div>
<blockquote>
<p>关于 Algolia 搜索功能,这里有两种插件,一个是 <a href="https://github.com/thom4parisot/hexo-algolia">hexo-algolia</a> ,一个是 <a href="https://github.com/LouisBarranqueiro/hexo-algoliasearch">hexo-algoliasearch</a>。第一种亲测只能对匹配文章title,不能匹配文章内容查询到结果,所以推荐第二种。下面分别对这两种插件做不同的说明。</p>
</blockquote>
<h3 id="获取-Algolia-账号"><a href="#获取-Algolia-账号" class="headerlink" title="获取 Algolia 账号"></a>获取 Algolia 账号</h3><ol>
<li>注册 Algolia。<br>进入<a href="https://www.algolia.com/">官网地址</a> 注册,也可以直接用<code>Github</code>授权登录。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/12/62cd8ab928023.jpg"/></div></div></li>
<li>新建 Index。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/12/62cd8abcf39d0.jpg"/></div></div></li>
<li>创建拥有一定权限的<code>api key</code>(如果选择第二种插件,可忽略这一步)。<br>进入【Settings > API Keys】。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/12/62cd8ac08634e.jpg"/></div></div>
进入【All API Keys > API Keys】,点击【New API Key】。在ACL里面增加删除和新增Object的权限(按理说只用这两个权限就行,下图中我多加了几个),然后填上 indices 栏目中的 index name ,选刚才你创建的那个index,其余默认就行。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/12/62cd8ac422090.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/12/62cd8ac713689.jpg"/></div></div>
点击【Create】,这样就得到了一个 api key。注意一下,这个key将会在下面的步骤中用到。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/13/62cec7a818fd8.jpg"/></div></div></li>
</ol>
<h3 id="安装依赖-amp-amp-写入配置"><a href="#安装依赖-amp-amp-写入配置" class="headerlink" title="安装依赖 && 写入配置"></a>安装依赖 && 写入配置</h3><div class="tabs" id="span-"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#span--1">hexo-algoliasearch(推荐)</button></li><li class="tab"><button type="button" data-href="#span--2">hexo-algolia</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="span--1"><ol>
<li>安装 Algolia 依赖。<br>前往博客根目录,打开cmd命令窗口执行<code>npm install hexo-algoliasearch --save</code>。<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 hexo-algoliasearch --save</span><br></pre></td></tr></table></figure></li>
<li>注入配置。<br>修改站点配置文件<code>_config.yml</code>,添加如下代码:<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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="attr">algolia:</span></span><br><span class="line"> <span class="attr">appId:</span> <span class="string">"your applicationID"</span></span><br><span class="line"> <span class="attr">apiKey:</span> <span class="string">"your Search-Only API Key"</span></span><br><span class="line"> <span class="attr">adminApiKey:</span> <span class="string">"your Admin API Key"</span></span><br><span class="line"> <span class="attr">chunkSize:</span> <span class="number">5000</span></span><br><span class="line"> <span class="attr">indexName:</span> <span class="string">"your indexName"</span></span><br><span class="line"> <span class="attr">fields:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">content:strip:truncate,0,500</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">excerpt:strip</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">gallery</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">permalink</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">photos</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">slug</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">tags</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">title</span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/13/62cece7b31f5f.jpg"/></div></div>
【applicationID】填入图中位置的 <span class='p blue'>Applicaiton ID</span>,【apiKey】填入图中位置的 <span class='p blue'>Search-Only API Key</span>,【Admin API Key】填入图中位置的 <span class='p blue'>Admin API Key</span>,【indexName】填入前面创建的索引名称。</li>
<li>执行<code>hexo algolia</code>。<br>前往博客根目录,打开cmd命令窗口执行<code>hexo algolia</code>。<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">hexo algolia</span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/16/62d2846f76321.jpg"/></div></div>
到如下信息,证明成功了,可以去 Algolia 网站上查看,索引已经上传成功了。
<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">INFO 128 files generated <span class="keyword">in</span> 2.33 s</span><br><span class="line">INFO Clearing index on Algolia...</span><br><span class="line">INFO Index cleared.</span><br><span class="line">INFO Indexing posts on Algolia...</span><br><span class="line">INFO 65 posts indexed.</span><br></pre></td></tr></table></figure></li>
<li>主题中写入 Alogolia 配置项。<br>在主题配置文件<code>_config.butterfly.yml</code>中修改以下内容:<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="attr">algolia_search:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">hits:</span></span><br><span class="line"> <span class="attr">per_page:</span> <span class="number">10</span></span><br><span class="line"> <span class="attr">labels:</span></span><br><span class="line"> <span class="attr">input_placeholder:</span> <span class="string">Search</span> <span class="string">for</span> <span class="string">Posts</span></span><br><span class="line"> <span class="attr">hits_empty:</span> <span class="string">"我们没有找到任何搜索结果: ${query}"</span></span><br><span class="line"> <span class="attr">hits_stats:</span> <span class="string">"找到${hits}条结果(用时${time} ms)"</span></span><br></pre></td></tr></table></figure></li>
<li>重新编译运行,即可看到效果。<br>前往博客根目录,打开cmd命令窗口依次执行如下命令:<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">hexo cl && hexo generate</span><br><span class="line">hexo s -p 8000</span><br></pre></td></tr></table></figure></li>
</ol><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--2"><ol>
<li>安装 Algolia 依赖。<br>前往博客根目录,打开cmd命令窗口执行<code>npm install hexo-algolia --save</code>。<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 hexo-algolia --save</span><br></pre></td></tr></table></figure></li>
<li>注入配置。<br>修改站点配置文件<code>_config.yml</code>,添加如下代码:<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">algolia:</span></span><br><span class="line"> <span class="attr">applicationID:</span> <span class="string">'your applicationID'</span></span><br><span class="line"> <span class="attr">apiKey:</span> <span class="string">'your Search-Only API Key'</span></span><br><span class="line"> <span class="attr">indexName:</span> <span class="string">'your indexName'</span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/13/62cece7b31f5f.jpg"/></div></div>
【applicationID】填入图中位置的 <span class='p blue'>Applicaiton ID</span>,【apiKey】填入图中位置的 <span class='p blue'>Search-Only API Key</span>,
【indexName】填入前面创建的索引名称。</li>
<li>上传数据到 Algolia。<br>前往博客根目录,打开<code>Git</code>,依次执行如下命令:<br>【your apiKey】替换为刚才自己创建拥有权限的<code>api key</code>。<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="built_in">export</span> HEXO_ALGOLIA_INDEXING_KEY=your apiKey</span><br><span class="line">hexo algolia</span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/13/62ced0f13aac0.jpg"/></div></div>
到如下信息,证明成功了,可以去 Algolia 网站上查看,索引已经上传成功了。
<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">INFO [hexo-algolia] Testing HEXO_ALGOLIA_INDEXING_KEY permissions.</span><br><span class="line">INFO Start processing</span><br><span class="line">INFO [hexo-algolia] 7 records to index (post, page).</span><br><span class="line">INFO [hexo-algolia] Indexing chunk 1 of 1 (7 records)</span><br><span class="line">INFO [hexo-algolia] Indexing <span class="keyword">done</span>.</span><br></pre></td></tr></table></figure></li>
<li>主题中写入 Alogolia 配置项。<br>在主题配置文件<code>_config.butterfly.yml</code>中修改以下内容:<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="attr">algolia_search:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">hits:</span></span><br><span class="line"> <span class="attr">per_page:</span> <span class="number">10</span></span><br><span class="line"> <span class="attr">labels:</span></span><br><span class="line"> <span class="attr">input_placeholder:</span> <span class="string">Search</span> <span class="string">for</span> <span class="string">Posts</span></span><br><span class="line"> <span class="attr">hits_empty:</span> <span class="string">"我们没有找到任何搜索结果: ${query}"</span></span><br><span class="line"> <span class="attr">hits_stats:</span> <span class="string">"找到${hits}条结果(用时${time} ms)"</span></span><br></pre></td></tr></table></figure></li>
<li>重新编译运行,即可看到效果。<br>前往博客根目录,打开cmd命令窗口依次执行如下命令:<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">hexo cl && hexo generate</span><br><span class="line">hexo s -p 8000</span><br></pre></td></tr></table></figure></li>
</ol><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas
前端基础进阶(十七):详解 ES6 Modules
https://fe32.top/articles/jsnb9544/
2022-07-01T15:38:20.000Z
2023-05-17T10:01:12.000Z
<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如 Ruby 的<code>require</code>、Python 的<code>import</code>,甚至就连 CSS 都有<code>@import</code>,但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。</p>
<p>在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。</p>
<p>ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。</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">// CommonJS模块</span></span><br><span class="line"><span class="keyword">let</span> { stat, exists, readfile } = <span class="built_in">require</span>(<span class="string">'fs'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 等同于</span></span><br><span class="line"><span class="keyword">let</span> _fs = <span class="built_in">require</span>(<span class="string">'fs'</span>);</span><br><span class="line"><span class="keyword">let</span> stat = _fs.<span class="property">stat</span>;</span><br><span class="line"><span class="keyword">let</span> exists = _fs.<span class="property">exists</span>;</span><br><span class="line"><span class="keyword">let</span> readfile = _fs.<span class="property">readfile</span>;</span><br></pre></td></tr></table></figure>
<p>上面代码的实质是整体加载<code>fs</code>模块(即加载<code>fs</code>的所有方法),生成一个对象(<code>_fs</code>),然后再从这个对象上面读取 3 个方法。这种加载称为“运行时加载”,因为只有运行时才能得到这个对象,导致完全没办法在编译时做“静态优化”。</p>
<p>ES6 模块不是对象,而是通过<code>export</code>命令显式指定输出的代码,再通过<code>import</code>命令输入。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ES6模块</span></span><br><span class="line"><span class="keyword">import</span> { stat, exists, readFile } <span class="keyword">from</span> <span class="string">'fs'</span>;</span><br></pre></td></tr></table></figure>
<p>上面代码的实质是从<code>fs</code>模块加载 3 个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。当然,这也导致了没法引用 ES6 模块本身,因为它不是对象。</p>
<p>由于 ES6 模块是编译时加载,使得静态分析成为可能。有了它,就能进一步拓宽 JavaScript 的语法,比如引入宏(macro)和类型检验(type system)这些只能靠静态分析实现的功能。</p>
<p>除了静态加载带来的各种好处,ES6 模块还有以下好处。</p>
<ul>
<li>不再需要<code>UMD</code>模块格式了,将来服务器和浏览器都会支持 ES6 模块格式。目前,通过各种工具库,其实已经做到了这一点。</li>
<li>将来浏览器的新 API 就能用模块格式提供,不再必须做成全局变量或者<code>navigator</code>对象的属性。</li>
<li>不再需要对象作为命名空间(比如<code>Math</code>对象),未来这些功能可以通过模块提供。</li>
</ul>
<h2 id="严格模式"><a href="#严格模式" class="headerlink" title="严格模式"></a>严格模式</h2><p>ES6 的模块自动采用严格模式,不管你有没有在模块头部加上<code>"use strict";</code>。</p>
<p>严格模式主要有以下限制。</p>
<ul>
<li>变量必须声明后再使用</li>
<li>函数的参数不能有同名属性,否则报错</li>
<li>不能使用<code>with</code>语句</li>
<li>不能对只读属性赋值,否则报错</li>
<li>不能使用前缀 0 表示八进制数,否则报错</li>
<li>不能删除不可删除的属性,否则报错</li>
<li>不能删除变量<code>delete prop</code>,会报错,只能删除属性<code>delete global[prop]</code></li>
<li>eval不会在它的外层作用域引入变量</li>
<li>eval和arguments不能被重新赋值</li>
<li>arguments不会自动反映函数参数的变化</li>
<li>不能使用<code>arguments.callee</code></li>
<li>不能使用<code>arguments.caller</code></li>
<li>禁止<code>this</code>指向全局对象</li>
<li>不能使用<code>fn.caller</code>和<code>fn.arguments</code>获取函数调用的堆栈</li>
<li>增加了保留字(比如<code>protected</code>、<code>static</code>和<code>interface</code>)</li>
</ul>
<p>上面这些限制,模块都必须遵守。由于严格模式是 ES5 引入的,不属于 ES6,所以请参阅相关 ES5 书籍,本书不再详细介绍了。</p>
<p>其中,尤其需要注意<code>this</code>的限制。ES6 模块之中,顶层的<code>this</code>指向<code>undefined</code>,即不应该在顶层代码使用<code>this</code>。</p>
<h2 id="export-命令"><a href="#export-命令" class="headerlink" title="export 命令"></a>export 命令</h2><p>模块功能主要由两个命令构成:<code>export</code>和<code>import</code>。<code>expor</code>t命令用于规定模块的对外接口,<code>import</code>命令用于输入其他模块提供的功能。</p>
<p>一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用<code>export</code>关键字输出该变量。下面是一个 JS 文件,里面使用<code>export</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="comment">// profile.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">var</span> firstName = <span class="string">'Michael'</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">var</span> lastName = <span class="string">'Jackson'</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">var</span> year = <span class="number">1958</span>;</span><br></pre></td></tr></table></figure>
<p>上面代码是<code>profile.js</code>文件,保存了用户信息。ES6 将其视为一个模块,里面用<code>export</code>命令对外部输出了三个变量。</p>
<p><code>export</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// profile.js</span></span><br><span class="line"><span class="keyword">var</span> firstName = <span class="string">'Michael'</span>;</span><br><span class="line"><span class="keyword">var</span> lastName = <span class="string">'Jackson'</span>;</span><br><span class="line"><span class="keyword">var</span> year = <span class="number">1958</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> { firstName, lastName, year };</span><br></pre></td></tr></table></figure>
<p>上面代码在<code>export</code>命令后面,使用大括号指定所要输出的一组变量。它与前一种写法(直接放置在<code>var</code>语句前)是等价的,但是应该优先考虑使用这种写法。因为这样就可以在脚本尾部,一眼看清楚输出了哪些变量。</p>
<p><code>export</code>命令除了输出变量,还可以输出函数或类(class)。</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">export</span> <span class="keyword">function</span> <span class="title function_">multiply</span>(<span class="params">x, y</span>) {</span><br><span class="line"> <span class="keyword">return</span> x * y;</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<p>上面代码对外输出一个函数<code>multiply</code>。</p>
<p>通常情况下,<code>export</code>输出的变量就是本来的名字,但是可以使用<code>as</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="keyword">function</span> <span class="title function_">v1</span>(<span class="params"></span>) { ... }</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">v2</span>(<span class="params"></span>) { ... }</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> {</span><br><span class="line"> v1 <span class="keyword">as</span> streamV1,</span><br><span class="line"> v2 <span class="keyword">as</span> streamV2,</span><br><span class="line"> v2 <span class="keyword">as</span> streamLatestVersion</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<p>上面代码使用<code>as</code>关键字,重命名了函数<code>v1</code>和<code>v2</code>的对外接口。重命名后,<code>v2</code>可以用不同的名字输出两次。</p>
<p>需要特别注意的是,<code>export</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 报错</span></span><br><span class="line"><span class="keyword">export</span> <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 报错</span></span><br><span class="line"><span class="keyword">var</span> m = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">export</span> m;</span><br></pre></td></tr></table></figure>
<p>上面两种写法都会报错,因为没有提供对外的接口。第一种写法直接输出 1,第二种写法通过变量<code>m</code>,还是直接输出 1。<code>1</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">export</span> <span class="keyword">var</span> m = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 写法二</span></span><br><span class="line"><span class="keyword">var</span> m = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">export</span> {m};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 写法三</span></span><br><span class="line"><span class="keyword">var</span> n = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">export</span> {n <span class="keyword">as</span> m};</span><br></pre></td></tr></table></figure>
<p>上面三种写法都是正确的,规定了对外的接口<code>m</code>。其他脚本可以通过这个接口,取到值<code>1</code>。它们的实质是,在接口名与模块内部变量之间,建立了一一对应的关系。</p>
<p>同样的,<code>function</code>和<code>class</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">function</span> <span class="title function_">f</span>(<span class="params"></span>) {}</span><br><span class="line"><span class="keyword">export</span> f;</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">function</span> <span class="title function_">f</span>(<span class="params"></span>) {};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 正确</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">f</span>(<span class="params"></span>) {}</span><br><span class="line"><span class="keyword">export</span> {f};</span><br></pre></td></tr></table></figure>
<p>另外,<code>export</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">export</span> <span class="keyword">var</span> foo = <span class="string">'bar'</span>;</span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="function">() =></span> foo = <span class="string">'baz'</span>, <span class="number">500</span>);</span><br></pre></td></tr></table></figure>
<p>上面代码输出变量<code>foo</code>,值为<code>bar</code>,500 毫秒之后变成<code>baz</code>。</p>
<p>这一点与 CommonJS 规范完全不同。CommonJS 模块输出的是值的缓存,不存在动态更新,详见下文《Module 的加载实现》一节。</p>
<p>最后,<code>export</code>命令可以出现在模块的任何位置,只要处于模块顶层就可以。如果处于块级作用域内,就会报错,下一节的<code>import</code>命令也是如此。这是因为处于条件代码块之中,就没法做静态优化了,违背了 ES6 模块的设计初衷。</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">function</span> <span class="title function_">foo</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">export</span> <span class="keyword">default</span> <span class="string">'bar'</span> <span class="comment">// SyntaxError</span></span><br><span class="line">}</span><br><span class="line"><span class="title function_">foo</span>()</span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>export</code>语句放在函数之中,结果报错。</p>
<h2 id="import-命令"><a href="#import-命令" class="headerlink" title="import 命令"></a>import 命令</h2><p>使用<code>export</code>命令定义了模块的对外接口以后,其他 JS 文件就可以通过<code>import</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></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> { firstName, lastName, year } <span class="keyword">from</span> <span class="string">'./profile.js'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">setName</span>(<span class="params">element</span>) {</span><br><span class="line"> element.<span class="property">textContent</span> = firstName + <span class="string">' '</span> + lastName;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面代码的<code>import</code>命令,用于加载<code>profile.js</code>文件,并从中输入变量。<code>import</code>命令接受一对大括号,里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块(<code>profile.js</code>)对外接口的名称相同。</p>
<p>如果想为输入的变量重新取一个名字,<code>import</code>命令要使用<code>as</code>关键字,将输入的变量重命名。</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> { lastName <span class="keyword">as</span> surname } <span class="keyword">from</span> <span class="string">'./profile.js'</span>;</span><br></pre></td></tr></table></figure>
<p><code>import</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="keyword">import</span> {a} <span class="keyword">from</span> <span class="string">'./xxx.js'</span></span><br><span class="line"></span><br><span class="line">a = {}; <span class="comment">// Syntax Error : 'a' is read-only;</span></span><br></pre></td></tr></table></figure>
<p>上面代码中,脚本加载了变量<code>a</code>,对其重新赋值就会报错,因为<code>a</code>是一个只读的接口。但是,如果<code>a</code>是一个对象,改写<code>a</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="keyword">import</span> {a} <span class="keyword">from</span> <span class="string">'./xxx.js'</span></span><br><span class="line"></span><br><span class="line">a.<span class="property">foo</span> = <span class="string">'hello'</span>; <span class="comment">// 合法操作</span></span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>a</code>的属性可以成功改写,并且其他模块也可以读到改写后的值。不过,这种写法很难查错,建议凡是输入的变量,都当作完全只读,不要轻易改变它的属性。</p>
<p><code>import</code>后面的<code>from</code>指定模块文件的位置,可以是相对路径,也可以是绝对路径。如果不带有路径,只是一个模块名,那么必须有配置文件,告诉 JavaScript 引擎该模块的位置。</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> { myMethod } <span class="keyword">from</span> <span class="string">'util'</span>;</span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>util</code>是模块文件名,由于不带有路径,必须通过配置,告诉引擎怎么取到这个模块。</p>
<blockquote>
<p>注意,<code>import</code>命令具有提升效果,会提升到整个模块的头部,首先执行。</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">foo</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> { foo } <span class="keyword">from</span> <span class="string">'my_module'</span>;</span><br></pre></td></tr></table></figure>
<p>上面的代码不会报错,因为<code>import</code>的执行早于<code>foo</code>的调用。这种行为的本质是,<code>import</code>命令是编译阶段执行的,在代码运行之前。</p>
<p>由于<code>import</code>是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 报错</span></span><br><span class="line"><span class="keyword">import</span> { <span class="string">'f'</span> + <span class="string">'oo'</span> } <span class="keyword">from</span> <span class="string">'my_module'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 报错</span></span><br><span class="line"><span class="keyword">let</span> <span class="variable language_">module</span> = <span class="string">'my_module'</span>;</span><br><span class="line"><span class="keyword">import</span> { foo } <span class="keyword">from</span> <span class="variable language_">module</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 报错</span></span><br><span class="line"><span class="keyword">if</span> (x === <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">import</span> { foo } <span class="keyword">from</span> <span class="string">'module1'</span>;</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">import</span> { foo } <span class="keyword">from</span> <span class="string">'module2'</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面三种写法都会报错,因为它们用到了表达式、变量和<code>if</code>结构。在静态分析阶段,这些语法都是没法得到值的。</p>
<p>最后,<code>import</code>语句会执行所加载的模块,因此可以有下面的写法。</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">'lodash'</span>;</span><br></pre></td></tr></table></figure>
<p>上面代码仅仅执行<code>lodash</code>模块,但是不输入任何值。</p>
<p>如果多次重复执行同一句<code>import</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> <span class="string">'lodash'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">'lodash'</span>;</span><br></pre></td></tr></table></figure>
<p>上面代码加载了两次<code>lodash</code>,但是只会执行一次。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> { foo } <span class="keyword">from</span> <span class="string">'my_module'</span>;</span><br><span class="line"><span class="keyword">import</span> { bar } <span class="keyword">from</span> <span class="string">'my_module'</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> { foo, bar } <span class="keyword">from</span> <span class="string">'my_module'</span>;</span><br></pre></td></tr></table></figure>
<p>上面代码中,虽然<code>foo</code>和<code>bar</code>在两个语句中加载,但是它们对应的是同一个<code>my_module</code>模块。也就是说,<code>import</code>语句是 Singleton 模式。</p>
<p>目前阶段,通过 Babel 转码,CommonJS 模块的<code>requir</code>e命令和 ES6 模块的<code>import</code>命令,可以写在同一个模块里面,但是最好不要这样做。因为<code>import</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="built_in">require</span>(<span class="string">'core-js/modules/es6.symbol'</span>);</span><br><span class="line"><span class="built_in">require</span>(<span class="string">'core-js/modules/es6.promise'</span>);</span><br><span class="line"><span class="keyword">import</span> <span class="title class_">React</span> <span class="keyword">from</span> <span class="string">'React'</span>;</span><br></pre></td></tr></table></figure>
<h2 id="模块的整体加载"><a href="#模块的整体加载" class="headerlink" title="模块的整体加载"></a>模块的整体加载</h2><p>除了指定加载某个输出值,还可以使用整体加载,即用星号(<code>*</code>)指定一个对象,所有输出值都加载在这个对象上面。</p>
<p>下面是一个<code>circle.js</code>文件,它输出两个方法<code>area</code>和<code>circumference</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// circle.js</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">area</span>(<span class="params">radius</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title class_">Math</span>.<span class="property">PI</span> * radius * radius;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">circumference</span>(<span class="params">radius</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">2</span> * <span class="title class_">Math</span>.<span class="property">PI</span> * radius;</span><br><span class="line">}</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// main.js</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> { area, circumference } <span class="keyword">from</span> <span class="string">'./circle'</span>;</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'圆面积:'</span> + <span class="title function_">area</span>(<span class="number">4</span>));</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'圆周长:'</span> + <span class="title function_">circumference</span>(<span class="number">14</span>));</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="keyword">import</span> * <span class="keyword">as</span> circle <span class="keyword">from</span> <span class="string">'./circle'</span>;</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'圆面积:'</span> + circle.<span class="title function_">area</span>(<span class="number">4</span>));</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'圆周长:'</span> + circle.<span class="title function_">circumference</span>(<span class="number">14</span>));</span><br></pre></td></tr></table></figure>
<p>注意,模块整体加载所在的那个对象(上例是<code>circle</code>),应该是可以静态分析的,所以不允许运行时改变。下面的写法都是不允许的。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> circle <span class="keyword">from</span> <span class="string">'./circle'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 下面两行都是不允许的</span></span><br><span class="line">circle.<span class="property">foo</span> = <span class="string">'hello'</span>;</span><br><span class="line">circle.<span class="property">area</span> = <span class="keyword">function</span> (<span class="params"></span>) {};</span><br></pre></td></tr></table></figure>
<h2 id="export-default-命令"><a href="#export-default-命令" class="headerlink" title="export default 命令"></a>export default 命令</h2><p>从前面的例子可以看出,使用<code>import</code>命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。但是,用户肯定希望快速上手,未必愿意阅读文档,去了解模块有哪些属性和方法。</p>
<p>为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到<code>export default</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="comment">// export-default.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'foo'</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面代码是一个模块文件<code>export-default.js</code>,它的默认输出是一个函数。</p>
<p>其他模块加载该模块时,<code>import</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">// import-default.js</span></span><br><span class="line"><span class="keyword">import</span> customName <span class="keyword">from</span> <span class="string">'./export-default'</span>;</span><br><span class="line"><span class="title function_">customName</span>(); <span class="comment">// 'foo'</span></span><br></pre></td></tr></table></figure>
<p>上面代码的<code>import</code>命令,可以用任意名称指向<code>export-default.js</code>输出的方法,这时就不需要知道原模块输出的函数名。需要注意的是,这时<code>import</code>命令后面,不使用大括号。</p>
<p><code>export default</code>命令用在非匿名函数前,也是可以的。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// export-default.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'foo'</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 或者写成</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'foo'</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> foo;</span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>foo</code>函数的函数名<code>foo</code>,在模块外部是无效的。加载的时候,视同匿名函数加载。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 第一组</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">crc32</span>(<span class="params"></span>) { <span class="comment">// 输出</span></span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> crc32 <span class="keyword">from</span> <span class="string">'crc32'</span>; <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">function</span> <span class="title function_">crc32</span>(<span class="params"></span>) { <span class="comment">// 输出</span></span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> {crc32} <span class="keyword">from</span> <span class="string">'crc32'</span>; <span class="comment">// 输入</span></span><br></pre></td></tr></table></figure>
<p>上面代码的两组写法,第一组是使用<code>export default</code>时,对应的<code>import</code>语句不需要使用大括号;第二组是不使用<code>export default</code>时,对应的<code>import</code>语句需要使用大括号。</p>
<p><code>export default</code>命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此<code>export default</code>命令只能使用一次。所以,<code>import</code>命令后面才不用加大括号,因为只可能唯一对应<code>export default</code>命令。</p>
<p>本质上,<code>export default</code>就是输出一个叫做<code>default</code>的变量或方法,然后系统允许你为它取任意名字。所以,下面的写法是有效的。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// modules.js</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">add</span>(<span class="params">x, y</span>) {</span><br><span class="line"> <span class="keyword">return</span> x * y;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">export</span> {add <span class="keyword">as</span> <span class="keyword">default</span>};</span><br><span class="line"><span class="comment">// 等同于</span></span><br><span class="line"><span class="comment">// export default add;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// app.js</span></span><br><span class="line"><span class="keyword">import</span> { <span class="keyword">default</span> <span class="keyword">as</span> foo } <span class="keyword">from</span> <span class="string">'modules'</span>;</span><br><span class="line"><span class="comment">// 等同于</span></span><br><span class="line"><span class="comment">// import foo from 'modules';</span></span><br></pre></td></tr></table></figure>
<p>正是因为<code>export default</code>命令其实只是输出一个叫做<code>default</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 正确</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">var</span> a = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 正确</span></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> a;</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">default</span> <span class="keyword">var</span> a = <span class="number">1</span>;</span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>export default a</code>的含义是将变量<code>a</code>的值赋给变量<code>default</code>。所以,最后一种写法会报错。</p>
<p>同样地,因为<code>export default</code>命令的本质是将后面的值,赋给<code>default</code>变量,所以可以直接将一个值写在<code>export default</code>之后。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 正确</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="number">42</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="number">42</span>;</span><br></pre></td></tr></table></figure>
<p>上面代码中,后一句报错是因为没有指定对外的接口,而前一句指定对外接口为default。</p>
<p>有了<code>export default</code>命令,输入模块时就非常直观了,以输入 lodash 模块为例。</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="keyword">from</span> <span class="string">'lodash'</span>;</span><br></pre></td></tr></table></figure>
<p>如果想在一条<code>import</code>语句中,同时输入默认方法和其他接口,可以写成下面这样。</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> _, { each, forEach } <span class="keyword">from</span> <span class="string">'lodash'</span>;</span><br></pre></td></tr></table></figure>
<p>对应上面代码的<code>export</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> (<span class="params">obj</span>) {</span><br><span class="line"> <span class="comment">// ···</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">each</span>(<span class="params">obj, iterator, context</span>) {</span><br><span class="line"> <span class="comment">// ···</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> { each <span class="keyword">as</span> forEach };</span><br></pre></td></tr></table></figure>
<p>上面代码的最后一行的意思是,暴露出<code>forEach</code>接口,默认指向<code>each</code>接口,即<code>forEach</code>和<code>each</code>指向同一个方法。</p>
<p><code>export default</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// MyClass.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">class</span> { ... }</span><br><span class="line"></span><br><span class="line"><span class="comment">// main.js</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">MyClass</span> <span class="keyword">from</span> <span class="string">'MyClass'</span>;</span><br><span class="line"><span class="keyword">let</span> o = <span class="keyword">new</span> <span class="title class_">MyClass</span>();</span><br></pre></td></tr></table></figure>
<h2 id="export-与-import-的复合写法"><a href="#export-与-import-的复合写法" class="headerlink" title="export 与 import 的复合写法"></a>export 与 import 的复合写法</h2><p>如果在一个模块之中,先输入后输出同一个模块,<code>import</code>语句可以与<code>export</code>语句写在一起。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> { foo, bar } <span class="keyword">from</span> <span class="string">'my_module'</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> { foo, bar } <span class="keyword">from</span> <span class="string">'my_module'</span>;</span><br><span class="line"><span class="keyword">export</span> { foo, bar };</span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>export</code>和<code>import</code>语句可以结合在一起,写成一行。但需要注意的是,写成一行以后,<code>foo</code>和<code>bar</code>实际上并没有被导入当前模块,只是相当于对外转发了这两个接口,导致当前模块不能直接使用<code>foo</code>和<code>bar</code>。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 接口改名</span></span><br><span class="line"><span class="keyword">export</span> { foo <span class="keyword">as</span> myFoo } <span class="keyword">from</span> <span class="string">'my_module'</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">from</span> <span class="string">'my_module'</span>;</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> { <span class="keyword">default</span> } <span class="keyword">from</span> <span class="string">'foo'</span>;</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> { es6 <span class="keyword">as</span> <span class="keyword">default</span> } <span class="keyword">from</span> <span class="string">'./someModule'</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> { es6 } <span class="keyword">from</span> <span class="string">'./someModule'</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> es6;</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> { <span class="keyword">default</span> <span class="keyword">as</span> es6 } <span class="keyword">from</span> <span class="string">'./someModule'</span>;</span><br></pre></td></tr></table></figure>
<p>ES2020 之前,有一种<code>import</code>语句,没有对应的复合写法。</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="keyword">as</span> someIdentifier <span class="keyword">from</span> <span class="string">"someModule"</span>;</span><br></pre></td></tr></table></figure>
<p><a href="https://github.com/tc39/proposal-export-ns-from">ES2020</a> 补上了这个写法。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> * <span class="keyword">as</span> ns <span class="keyword">from</span> <span class="string">"mod"</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> * <span class="keyword">as</span> ns <span class="keyword">from</span> <span class="string">"mod"</span>;</span><br><span class="line"><span class="keyword">export</span> {ns};</span><br></pre></td></tr></table></figure>
<h2 id="模块的继承"><a href="#模块的继承" class="headerlink" title="模块的继承"></a>模块的继承</h2><p>模块之间也可以继承。假设有一个<code>circleplus</code>模块,继承了<code>circle</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">// circleplus.js</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> * <span class="keyword">from</span> <span class="string">'circle'</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">var</span> e = <span class="number">2.71828182846</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span>(<span class="params">x</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title class_">Math</span>.<span class="title function_">exp</span>(x);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面代码中的<code>export *</code>,表示再输出<code>circle</code>模块的所有属性和方法。注意,<code>export *</code>命令会忽略<code>circle</code>模块的<code>default</code>方法。然后,上面代码又输出了自定义的<code>e</code>变量和默认方法。</p>
<p>这时,也可以将<code>circle</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">// circleplus.js</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> { area <span class="keyword">as</span> circleArea } <span class="keyword">from</span> <span class="string">'circle'</span>;</span><br></pre></td></tr></table></figure>
<p>上面代码表示,只输出<code>circle</code>模块的<code>area</code>方法,且将其改名为<code>circleArea</code>。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// main.js</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> math <span class="keyword">from</span> <span class="string">'circleplus'</span>;</span><br><span class="line"><span class="keyword">import</span> exp <span class="keyword">from</span> <span class="string">'circleplus'</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">exp</span>(math.<span class="property">e</span>));</span><br></pre></td></tr></table></figure>
<p>上面代码中的<code>import exp</code>表示,将<code>circleplus</code>模块的默认方法加载为<code>exp</code>方法。</p>
<h2 id="跨模块常量"><a href="#跨模块常量" class="headerlink" title="跨模块常量"></a>跨模块常量</h2><p>介绍<code>const</code>命令的时候说过,<code>const</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"><span class="comment">// constants.js 模块</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> A = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> B = <span class="number">3</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> C = <span class="number">4</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// test1.js 模块</span></span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> constants <span class="keyword">from</span> <span class="string">'./constants'</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(constants.<span class="property">A</span>); <span class="comment">// 1</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(constants.<span class="property">B</span>); <span class="comment">// 3</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// test2.js 模块</span></span><br><span class="line"><span class="keyword">import</span> {A, B} <span class="keyword">from</span> <span class="string">'./constants'</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(A); <span class="comment">// 1</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(B); <span class="comment">// 3</span></span><br></pre></td></tr></table></figure>
<p>如果要使用的常量非常多,可以建一个专门的<code>constants</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// constants/db.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> db = {</span><br><span class="line"> <span class="attr">url</span>: <span class="string">'http://my.couchdbserver.local:5984'</span>,</span><br><span class="line"> <span class="attr">admin_username</span>: <span class="string">'admin'</span>,</span><br><span class="line"> <span class="attr">admin_password</span>: <span class="string">'admin password'</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// constants/user.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> users = [<span class="string">'root'</span>, <span class="string">'admin'</span>, <span class="string">'staff'</span>, <span class="string">'ceo'</span>, <span class="string">'chief'</span>, <span class="string">'moderator'</span>];</span><br></pre></td></tr></table></figure>
<p>然后,将这些文件输出的常量,合并在<code>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></pre></td><td class="code"><pre><span class="line"><span class="comment">// constants/index.js</span></span><br><span class="line"><span class="keyword">export</span> {db} <span class="keyword">from</span> <span class="string">'./db'</span>;</span><br><span class="line"><span class="keyword">export</span> {users} <span class="keyword">from</span> <span class="string">'./users'</span>;</span><br></pre></td></tr></table></figure>
<p>使用的时候,直接加载<code>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></pre></td><td class="code"><pre><span class="line"><span class="comment">// script.js</span></span><br><span class="line"><span class="keyword">import</span> {db, users} <span class="keyword">from</span> <span class="string">'./constants/index'</span>;</span><br></pre></td></tr></table></figure>
<h2 id="import"><a href="#import" class="headerlink" title="import()"></a>import()</h2><h3 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h3><p>前面介绍过,<code>import</code>命令会被 JavaScript 引擎静态分析,先于模块内的其他语句执行(<code>import</code>命令叫做“连接” binding 其实更合适)。所以,下面的代码会报错。</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">// 报错</span></span><br><span class="line"><span class="keyword">if</span> (x === <span class="number">2</span>) {</span><br><span class="line"> <span class="keyword">import</span> <span class="title class_">MyModual</span> <span class="keyword">from</span> <span class="string">'./myModual'</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面代码中,引擎处理<code>import</code>语句是在编译时,这时不会去分析或执行<code>if</code>语句,所以<code>import</code>语句放在<code>if</code>代码块之中毫无意义,因此会报句法错误,而不是执行时错误。也就是说,<code>import</code>和<code>export</code>命令只能在模块的顶层,不能在代码块之中(比如,在<code>if</code>代码块之中,或在函数之中)。</p>
<p>这样的设计,固然有利于编译器提高效率,但也导致无法在运行时加载模块。在语法上,条件加载就不可能实现。如果<code>import</code>命令要取代 Node 的<code>require</code>方法,这就形成了一个障碍。因为<code>require</code>是运行时加载模块,<code>import</code>命令无法取代<code>require</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">const</span> path = <span class="string">'./'</span> + fileName;</span><br><span class="line"><span class="keyword">const</span> myModual = <span class="built_in">require</span>(path);</span><br></pre></td></tr></table></figure>
<p>上面的语句就是动态加载,<code>require</code>到底加载哪一个模块,只有运行时才知道。<code>import</code>命令做不到这一点。</p>
<p><a href="https://github.com/tc39/proposal-dynamic-import">ES2020提案</a> 引入<code>import()</code>函数,支持动态加载模块。</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>(specifier)</span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>import</code>函数的参数<code>specifier</code>,指定所要加载的模块的位置。<code>import</code>命令能够接受什么参数,<code>import()</code>函数就能接受什么参数,两者区别主要是后者为动态加载。</p>
<p><code>import()</code>返回一个 Promise 对象。下面是一个例子。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> main = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">'main'</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span>(<span class="string">`./section-modules/<span class="subst">${someVariable}</span>.js`</span>)</span><br><span class="line"> .<span class="title function_">then</span>(<span class="function"><span class="params">module</span> =></span> {</span><br><span class="line"> <span class="variable language_">module</span>.<span class="title function_">loadPageInto</span>(main);</span><br><span class="line"> })</span><br><span class="line"> .<span class="title function_">catch</span>(<span class="function"><span class="params">err</span> =></span> {</span><br><span class="line"> main.<span class="property">textContent</span> = err.<span class="property">message</span>;</span><br><span class="line"> });</span><br></pre></td></tr></table></figure>
<p><code>import()</code>函数可以用在任何地方,不仅仅是模块,非模块的脚本也可以使用。它是运行时执行,也就是说,什么时候运行到这一句,就会加载指定的模块。另外,<code>import()</code>函数与所加载的模块没有静态连接关系,这点也是与<code>import</code>语句不相同。<code>import()</code>类似于 Node.js 的<code>require()</code>方法,区别主要是前者是异步加载,后者是同步加载。</p>
<p>由于<code>import()</code>返回 Promise 对象,所以需要使用<code>then()</code>方法指定处理函数。考虑到代码的清晰,更推荐使用<code>await</code>命令。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">renderWidget</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">const</span> container = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'widget'</span>);</span><br><span class="line"> <span class="keyword">if</span> (container !== <span class="literal">null</span>) {</span><br><span class="line"> <span class="comment">// 等同于</span></span><br><span class="line"> <span class="comment">// import("./widget").then(widget => {</span></span><br><span class="line"> <span class="comment">// widget.render(container);</span></span><br><span class="line"> <span class="comment">// });</span></span><br><span class="line"> <span class="keyword">const</span> widget = <span class="keyword">await</span> <span class="keyword">import</span>(<span class="string">'./widget.js'</span>);</span><br><span class="line"> widget.<span class="title function_">render</span>(container);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">renderWidget</span>();</span><br></pre></td></tr></table></figure>
<p>上面示例中,<code>await</code>命令后面就是使用<code>import()</code>,对比<code>then()</code>的写法明显更简洁易读。</p>
<h3 id="适用场合"><a href="#适用场合" class="headerlink" title="适用场合"></a>适用场合</h3><p>下面是<code>import()</code>的一些适用场合。</p>
<ol>
<li>按需加载。<br><code>import()</code>可以在需要的时候,再加载某个模块。<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">button.<span class="title function_">addEventListener</span>(<span class="string">'click'</span>, <span class="function"><span class="params">event</span> =></span> {</span><br><span class="line"> <span class="keyword">import</span>(<span class="string">'./dialogBox.js'</span>)</span><br><span class="line"> .<span class="title function_">then</span>(<span class="function"><span class="params">dialogBox</span> =></span> {</span><br><span class="line"> dialogBox.<span class="title function_">open</span>();</span><br><span class="line"> })</span><br><span class="line"> .<span class="title function_">catch</span>(<span class="function"><span class="params">error</span> =></span> {</span><br><span class="line"> <span class="comment">/* Error handling */</span></span><br><span class="line"> })</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
上面代码中,<code>import()</code>方法放在<code>click</code>事件的监听函数之中,只有用户点击了按钮,才会加载这个模块。</li>
<li>条件加载<br><code>import()</code>可以放在<code>if</code>代码块,根据不同的情况,加载不同的模块。<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (condition) {</span><br><span class="line"> <span class="keyword">import</span>(<span class="string">'moduleA'</span>).<span class="title function_">then</span>(...);</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">import</span>(<span class="string">'moduleB'</span>).<span class="title function_">then</span>(...);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
上面代码中,如果满足条件,就加载模块 A,否则加载模块 B。</li>
<li>动态的模块路径<br><code>import()</code>允许模块路径动态生成。<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>(<span class="title function_">f</span>())</span><br><span class="line">.<span class="title function_">then</span>(...);</span><br></pre></td></tr></table></figure>
上面代码中,根据函数<code>f</code>的返回结果,加载不同的模块。</li>
</ol>
<h3 id="注意点"><a href="#注意点" class="headerlink" title="注意点"></a>注意点</h3><p><code>import()</code>加载模块成功以后,这个模块会作为一个对象,当作<code>then</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>(<span class="string">'./myModule.js'</span>)</span><br><span class="line">.<span class="title function_">then</span>(<span class="function">(<span class="params">{export1, export2}</span>) =></span> {</span><br><span class="line"> <span class="comment">// ...·</span></span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>export1</code>和<code>export2</code>都是<code>myModule.js</code>的输出接口,可以解构获得。</p>
<p>如果模块有<code>default</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>(<span class="string">'./myModule.js'</span>)</span><br><span class="line">.<span class="title function_">then</span>(<span class="function"><span class="params">myModule</span> =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(myModule.<span class="property">default</span>);</span><br><span class="line">});</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="keyword">import</span>(<span class="string">'./myModule.js'</span>)</span><br><span class="line">.<span class="title function_">then</span>(<span class="function">(<span class="params">{<span class="keyword">default</span>: theDefault}</span>) =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(theDefault);</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<p>如果想同时加载多个模块,可以采用下面的写法。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Promise</span>.<span class="title function_">all</span>([</span><br><span class="line"> <span class="keyword">import</span>(<span class="string">'./module1.js'</span>),</span><br><span class="line"> <span class="keyword">import</span>(<span class="string">'./module2.js'</span>),</span><br><span class="line"> <span class="keyword">import</span>(<span class="string">'./module3.js'</span>),</span><br><span class="line">])</span><br><span class="line">.<span class="title function_">then</span>(<span class="function">(<span class="params">[module1, module2, module3]</span>) =></span> {</span><br><span class="line"> ···</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<p><code>import()</code>也可以用在 async 函数之中。</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="keyword">async</span> <span class="keyword">function</span> <span class="title function_">main</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">const</span> myModule = <span class="keyword">await</span> <span class="keyword">import</span>(<span class="string">'./myModule.js'</span>);</span><br><span class="line"> <span class="keyword">const</span> {export1, export2} = <span class="keyword">await</span> <span class="keyword">import</span>(<span class="string">'./myModule.js'</span>);</span><br><span class="line"> <span class="keyword">const</span> [module1, module2, module3] =</span><br><span class="line"> <span class="keyword">await</span> <span class="title class_">Promise</span>.<span class="title function_">all</span>([</span><br><span class="line"> <span class="keyword">import</span>(<span class="string">'./module1.js'</span>),</span><br><span class="line"> <span class="keyword">import</span>(<span class="string">'./module2.js'</span>),</span><br><span class="line"> <span class="keyword">import</span>(<span class="string">'./module3.js'</span>),</span><br><span class="line"> ]);</span><br><span class="line">}</span><br><span class="line"><span class="title function_">main</span>();</span><br></pre></td></tr></table></figure>
<h2 id="Module-的加载实现"><a href="#Module-的加载实现" class="headerlink" title="Module 的加载实现"></a>Module 的加载实现</h2><h3 id="浏览器加载"><a href="#浏览器加载" class="headerlink" title="浏览器加载"></a>浏览器加载</h3><h4 id="传统方法"><a href="#传统方法" class="headerlink" title="传统方法"></a>传统方法</h4><p>HTML 网页中,浏览器通过<code><script></code>标签加载 JavaScript 脚本。</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></pre></td><td class="code"><pre><span class="line"><span class="comment"><!-- 页面内嵌的脚本 --></span></span><br><span class="line"><span class="tag"><<span class="name">script</span> <span class="attr">type</span>=<span class="string">"application/javascript"</span>></span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="comment">// module code</span></span></span><br><span class="line"><span class="language-javascript"></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"></span><br><span class="line"><span class="comment"><!-- 外部脚本 --></span></span><br><span class="line"><span class="tag"><<span class="name">script</span> <span class="attr">type</span>=<span class="string">"application/javascript"</span> <span class="attr">src</span>=<span class="string">"path/to/myModule.js"</span>></span></span><br><span class="line"><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure>
<p>上面代码中,由于浏览器脚本的默认语言是 JavaScript,因此<code>type="application/javascript"</code>可以省略。</p>
<p>默认情况下,浏览器是同步加载 JavaScript 脚本,即渲染引擎遇到<code><script></code>标签就会停下来,等到执行完脚本,再继续向下渲染。如果是外部脚本,还必须加入脚本下载的时间。</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></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">script</span> <span class="attr">src</span>=<span class="string">"path/to/myModule.js"</span> <span class="attr">defer</span>></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"><<span class="name">script</span> <span class="attr">src</span>=<span class="string">"path/to/myModule.js"</span> <span class="attr">async</span>></span><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure>
<p>上面代码中,<code><script></code>标签打开<code>defer</code>或<code>async</code>属性,脚本就会异步加载。渲染引擎遇到这一行命令,就会开始下载外部脚本,但不会等它下载和执行,而是直接执行后面的命令。</p>
<p><code>defer</code>与<code>async</code>的区别是:<code>defer</code>要等到整个页面在内存中正常渲染结束(DOM 结构完全生成,以及其他脚本执行完成),才会执行;<code>async</code>一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。一句话,<code>defer</code>是“渲染完再执行”,<code>async</code>是“下载完就执行”。另外,如果有多个<code>defer</code>脚本,会按照它们在页面出现的顺序加载,而多个<code>async</code>脚本是不能保证加载顺序的。</p>
<h4 id="加载规则"><a href="#加载规则" class="headerlink" title="加载规则"></a>加载规则</h4><p>浏览器加载 ES6 模块,也使用<code><script></code>标签,但是要加入<code>type="module"</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"><<span class="name">script</span> <span class="attr">type</span>=<span class="string">"module"</span> <span class="attr">src</span>=<span class="string">"./foo.js"</span>></span><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure>
<p>上面代码在网页中插入一个模块<code>foo.js</code>,由于<code>type</code>属性设为<code>module</code>,所以浏览器知道这是一个 ES6 模块。</p>
<p>浏览器对于带有<code>type="module</code>“的<code><script></code>,都是异步加载,不会造成堵塞浏览器,即等到整个页面渲染完,再执行模块脚本,等同于打开了<code><script></code>标签的<code>defer</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"><<span class="name">script</span> <span class="attr">type</span>=<span class="string">"module"</span> <span class="attr">src</span>=<span class="string">"./foo.js"</span>></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="comment"><!-- 等同于 --></span></span><br><span class="line"><span class="tag"><<span class="name">script</span> <span class="attr">type</span>=<span class="string">"module"</span> <span class="attr">src</span>=<span class="string">"./foo.js"</span> <span class="attr">defer</span>></span><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure>
<p>如果网页有多个<code><script type="module"></code>,它们会按照在页面出现的顺序依次执行。</p>
<p><code><script></code>标签的<code>async</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"><<span class="name">script</span> <span class="attr">type</span>=<span class="string">"module"</span> <span class="attr">src</span>=<span class="string">"./foo.js"</span> <span class="attr">async</span>></span><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure>
<p>一旦使用了<code>async</code>属性,<code><script type="module"></code>就不会按照在页面出现的顺序执行,而是只要该模块加载完成,就执行该模块。</p>
<p>ES6 模块也允许内嵌在网页中,语法行为与加载外部脚本完全一致。</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"><<span class="name">script</span> <span class="attr">type</span>=<span class="string">"module"</span>></span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">import</span> utils <span class="keyword">from</span> <span class="string">"./utils.js"</span>;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="comment">// other code</span></span></span><br><span class="line"><span class="language-javascript"></span><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure>
<p>举例来说,jQuery 就支持模块加载。</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></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">script</span> <span class="attr">type</span>=<span class="string">"module"</span>></span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">import</span> $ <span class="keyword">from</span> <span class="string">"./jquery/src/jquery.js"</span>;</span></span><br><span class="line"><span class="language-javascript"> $(<span class="string">'#message'</span>).<span class="title function_">text</span>(<span class="string">'Hi from jQuery!'</span>);</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure>
<p>对于外部的模块脚本(上例是<code>foo.js</code>),有几点需要注意。</p>
<ul>
<li>代码是在模块作用域之中运行,而不是在全局作用域运行。模块内部的顶层变量,外部不可见。</li>
<li>模块脚本自动采用严格模式,不管有没有声明<code>use strict</code>。</li>
<li>模块之中,可以使用<code>import</code>命令加载其他模块(<code>.js</code>后缀不可省略,需要提供绝对 URL 或相对 URL),也可以使用<code>export</code>命令输出对外接口。</li>
<li>模块之中,顶层的<code>this</code>关键字返回<code>undefined</code>,而不是指向<code>window</code>。也就是说,在模块顶层使用<code>this</code>关键字,是无意义的。</li>
<li>同一个模块如果加载多次,将只执行一次。</li>
</ul>
<p>下面是一个示例模块。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> utils <span class="keyword">from</span> <span class="string">'https://example.com/js/utils.js'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> x = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(x === <span class="variable language_">window</span>.<span class="property">x</span>); <span class="comment">//false</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span> === <span class="literal">undefined</span>); <span class="comment">// true</span></span><br></pre></td></tr></table></figure>
<p>利用顶层的<code>this</code>等于<code>undefined</code>这个语法点,可以侦测当前代码是否在 ES6 模块之中。</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">const</span> isNotModuleScript = <span class="variable language_">this</span> !== <span class="literal">undefined</span>;</span><br></pre></td></tr></table></figure>
<h3 id="ES6-模块与-CommonJS-模块的差异"><a href="#ES6-模块与-CommonJS-模块的差异" class="headerlink" title="ES6 模块与 CommonJS 模块的差异"></a>ES6 模块与 CommonJS 模块的差异</h3><p>讨论 Node.js 加载 ES6 模块之前,必须了解 ES6 模块与 CommonJS 模块完全不同。</p>
<p>它们有三个重大差异。</p>
<ul>
<li>CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。</li>
<li>CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。</li>
<li>CommonJS 模块的<code>require()</code>是同步加载模块,ES6 模块的<code>import</code>命令是异步加载,有一个独立的模块依赖的解析阶段。</li>
</ul>
<p>第二个差异是因为 CommonJS 加载的是一个对象(即<code>module.exports</code>属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。</p>
<p>下面重点解释第一个差异。</p>
<p>CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。请看下面这个模块文件<code>lib.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></pre></td><td class="code"><pre><span class="line"><span class="comment">// lib.js</span></span><br><span class="line"><span class="keyword">var</span> counter = <span class="number">3</span>;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">incCounter</span>(<span class="params"></span>) {</span><br><span class="line"> counter++;</span><br><span class="line">}</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = {</span><br><span class="line"> <span class="attr">counter</span>: counter,</span><br><span class="line"> <span class="attr">incCounter</span>: incCounter,</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<p>上面代码输出内部变量<code>counter</code>和改写这个变量的内部方法<code>incCounter</code>。然后,在<code>main.js</code>里面加载这个模块。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// main.js</span></span><br><span class="line"><span class="keyword">var</span> mod = <span class="built_in">require</span>(<span class="string">'./lib'</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(mod.<span class="property">counter</span>); <span class="comment">// 3</span></span><br><span class="line">mod.<span class="title function_">incCounter</span>();</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(mod.<span class="property">counter</span>); <span class="comment">// 3</span></span><br></pre></td></tr></table></figure>
<p>上面代码说明,<code>lib.js</code>模块加载以后,它的内部变化就影响不到输出的<code>mod.counter</code>了。这是因为<code>mod.counter</code>是一个原始类型的值,会被缓存。除非写成一个函数,才能得到内部变动后的值。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// lib.js</span></span><br><span class="line"><span class="keyword">var</span> counter = <span class="number">3</span>;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">incCounter</span>(<span class="params"></span>) {</span><br><span class="line"> counter++;</span><br><span class="line">}</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = {</span><br><span class="line"> <span class="keyword">get</span> <span class="title function_">counter</span>() {</span><br><span class="line"> <span class="keyword">return</span> counter</span><br><span class="line"> },</span><br><span class="line"> <span class="attr">incCounter</span>: incCounter,</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<p>上面代码中,输出的<code>counter</code>属性实际上是一个取值器函数。现在再执行<code>main.js</code>,就可以正确读取内部变量<code>counter</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">$ node main.<span class="property">js</span></span><br><span class="line"><span class="number">3</span></span><br><span class="line"><span class="number">4</span></span><br></pre></td></tr></table></figure>
<p>ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令<code>import</code>,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,ES6 的<code>import</code>有点像 Unix 系统的“符号连接”,原始值变了,<code>import</code>加载的值也会跟着变。因此,ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。</p>
<p>还是举上面的例子。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// lib.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">let</span> counter = <span class="number">3</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">incCounter</span>(<span class="params"></span>) {</span><br><span class="line"> counter++;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// main.js</span></span><br><span class="line"><span class="keyword">import</span> { counter, incCounter } <span class="keyword">from</span> <span class="string">'./lib'</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(counter); <span class="comment">// 3</span></span><br><span class="line"><span class="title function_">incCounter</span>();</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(counter); <span class="comment">// 4</span></span><br></pre></td></tr></table></figure>
<p>上面代码说明,ES6 模块输入的变量<code>counter</code>是活的,完全反应其所在模块<code>lib.js</code>内部的变化。</p>
<p>再举一个出现在<code>export</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">// m1.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">var</span> foo = <span class="string">'bar'</span>;</span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="function">() =></span> foo = <span class="string">'baz'</span>, <span class="number">500</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// m2.js</span></span><br><span class="line"><span class="keyword">import</span> {foo} <span class="keyword">from</span> <span class="string">'./m1.js'</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(foo);</span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="function">() =></span> <span class="variable language_">console</span>.<span class="title function_">log</span>(foo), <span class="number">500</span>);</span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>m1.js</code>的变量<code>foo</code>,在刚加载时等于<code>bar</code>,过了 500 毫秒,又变为等于<code>baz</code>。</p>
<p>让我们看看,<code>m2.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">$ babel-node m2.<span class="property">js</span></span><br><span class="line"></span><br><span class="line">bar</span><br><span class="line">baz</span><br></pre></td></tr></table></figure>
<p>上面代码表明,ES6 模块不会缓存运行结果,而是动态地去被加载的模块取值,并且变量总是绑定其所在的模块。</p>
<p>由于 ES6 输入的模块变量,只是一个“符号连接”,所以这个变量是只读的,对它进行重新赋值会报错。</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">// lib.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">let</span> obj = {};</span><br><span class="line"></span><br><span class="line"><span class="comment">// main.js</span></span><br><span class="line"><span class="keyword">import</span> { obj } <span class="keyword">from</span> <span class="string">'./lib'</span>;</span><br><span class="line"></span><br><span class="line">obj.<span class="property">prop</span> = <span class="number">123</span>; <span class="comment">// OK</span></span><br><span class="line">obj = {}; <span class="comment">// TypeError</span></span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>main.j</code>s从<code>lib.js</code>输入变量<code>obj</code>,可以对<code>obj</code>添加属性,但是重新赋值就会报错。因为变量<code>obj</code>指向的地址是只读的,不能重新赋值,这就好比<code>main.js</code>创造了一个名为<code>obj</code>的<code>const</code>变量。</p>
<p>最后,<code>export</code>通过接口,输出的是同一个值。不同的脚本加载这个接口,得到的都是同样的实例。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// mod.js</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">C</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">sum</span> = <span class="number">0</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">add</span> = <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">sum</span> += <span class="number">1</span>;</span><br><span class="line"> };</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">show</span> = <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>.<span class="property">sum</span>);</span><br><span class="line"> };</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">let</span> c = <span class="keyword">new</span> <span class="title function_">C</span>();</span><br></pre></td></tr></table></figure>
<p>上面的脚本<code>mod.js</code>,输出的是一个<code>C</code>的实例。不同的脚本加载这个模块,得到的都是同一个实例。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// x.js</span></span><br><span class="line"><span class="keyword">import</span> {c} <span class="keyword">from</span> <span class="string">'./mod'</span>;</span><br><span class="line">c.<span class="title function_">add</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// y.js</span></span><br><span class="line"><span class="keyword">import</span> {c} <span class="keyword">from</span> <span class="string">'./mod'</span>;</span><br><span class="line">c.<span class="title function_">show</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// main.js</span></span><br><span class="line"><span class="keyword">import</span> <span class="string">'./x'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">'./y'</span>;</span><br></pre></td></tr></table></figure>
<p>现在执行<code>main.js</code>,输出的是<code>1</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">$ babel-node main.<span class="property">js</span></span><br><span class="line"><span class="number">1</span></span><br></pre></td></tr></table></figure>
<p>这就证明了<code>x.js</code>和<code>y.js</code>加载的都是<code>C</code>的同一个实例。</p>
<h3 id="Node-js-的模块加载方法"><a href="#Node-js-的模块加载方法" class="headerlink" title="Node.js 的模块加载方法"></a>Node.js 的模块加载方法</h3><p>JavaScript 现在有两种模块。一种是 ES6 模块,简称 ESM;另一种是 CommonJS 模块,简称 CJS。</p>
<p>CommonJS 模块是 Node.js 专用的,与 ES6 模块不兼容。语法上面,两者最明显的差异是,CommonJS 模块使用<code>require()</code>和<code>module.exports</code>,ES6 模块使用<code>import</code>和<code>export</code>。</p>
<p>它们采用不同的加载方案。从 Node.js v13.2 版本开始,Node.js 已经默认打开了 ES6 模块支持。</p>
<p>Node.js 要求 ES6 模块采用<code>.mjs</code>后缀文件名。也就是说,只要脚本文件里面使用<code>import</code>或者<code>export</code>命令,那么就必须采用<code>.mjs</code>后缀名。Node.js 遇到<code>.mjs</code>文件,就认为它是 ES6 模块,默认启用严格模式,不必在每个模块文件顶部指定<code>"use strict"</code>。</p>
<p>如果不希望将后缀名改成<code>.mjs</code>,可以在项目的<code>package.json</code>文件中,指定<code>type</code>字段为<code>module</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><br><span class="line"> <span class="string">"type"</span>: <span class="string">"module"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>一旦设置了以后,该项目的 JS 脚本,就被解释成 ES6 模块。</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="title class_">ES6</span> 模块</span><br><span class="line">$ node my-app.<span class="property">js</span></span><br></pre></td></tr></table></figure>
<p>如果这时还要使用 CommonJS 模块,那么需要将 CommonJS 脚本的后缀名都改成<code>.cjs</code>。如果没有<code>type</code>字段,或者<code>type</code>字段为<code>commonjs</code>,则<code>.js</code>脚本会被解释成 CommonJS 模块。</p>
<p>总结为一句话:<code>.mjs</code>文件总是以 ES6 模块加载,<code>.cjs</code>文件总是以 CommonJS 模块加载,<code>.js</code>文件的加载取决于<code>package.json</code>里面<code>type</code>字段的设置。</p>
<blockquote>
<p>注意,ES6 模块与 CommonJS 模块尽量不要混用。<code>require</code>命令不能加载<code>.mjs</code>文件,会报错,只有<code>import</code>命令才可以加载<code>.mjs</code>文件。反过来,<code>.mjs</code>文件里面也不能使用<code>require</code>命令,必须使用<code>import</code>。</p>
</blockquote>
<h4 id="package-json-的-main-字段"><a href="#package-json-的-main-字段" class="headerlink" title="package.json 的 main 字段"></a>package.json 的 main 字段</h4><p><code>package.json</code>文件有两个字段可以指定模块的入口文件:<code>main</code>和<code>exports</code>。比较简单的模块,可以只使用<code>main</code>字段,指定模块加载的入口文件。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ./node_modules/es-module-package/package.json</span></span><br><span class="line">{</span><br><span class="line"> <span class="string">"type"</span>: <span class="string">"module"</span>,</span><br><span class="line"> <span class="string">"main"</span>: <span class="string">"./src/index.js"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面代码指定项目的入口脚本为<code>./src/index.js</code>,它的格式为 ES6 模块。如果没有<code>type</code>字段,<code>index.js</code>就会被解释为 CommonJS 模块。</p>
<p>然后,<code>import</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="comment">// ./my-app.mjs</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> { something } <span class="keyword">from</span> <span class="string">'es-module-package'</span>;</span><br><span class="line"><span class="comment">// 实际加载的是 ./node_modules/es-module-package/src/index.js</span></span><br></pre></td></tr></table></figure>
<p>上面代码中,运行该脚本以后,Node.js 就会到<code>./node_modules</code>目录下面,寻找<code>es-module-package</code>模块,然后根据该模块<code>package.json</code>的<code>main</code>字段去执行入口文件。</p>
<p>这时,如果用 CommonJS 模块的<code>require()</code>命令去加载<code>es-module-package</code>模块会报错,因为 CommonJS 模块不能处理<code>export</code>命令。</p>
<h4 id="package-json-的-exports-字段"><a href="#package-json-的-exports-字段" class="headerlink" title="package.json 的 exports 字段"></a>package.json 的 exports 字段</h4><p><code>exports</code>字段的优先级高于<code>main</code>字段。它有多种用法。</p>
<ol>
<li>子目录别名<br><code>package.json</code>文件的<code>exports</code>字段可以指定脚本或子目录的别名。<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">// ./node_modules/es-module-package/package.json</span></span><br><span class="line">{</span><br><span class="line"> <span class="string">"exports"</span>: {</span><br><span class="line"> <span class="string">"./submodule"</span>: <span class="string">"./src/submodule.js"</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
上面的代码指定<code>src/submodule.js</code>别名为<code>submodule</code>,然后就可以从别名加载这个文件。<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> submodule <span class="keyword">from</span> <span class="string">'es-module-package/submodule'</span>;</span><br><span class="line"><span class="comment">// 加载 ./node_modules/es-module-package/src/submodule.js</span></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></pre></td><td class="code"><pre><span class="line"><span class="comment">// ./node_modules/es-module-package/package.json</span></span><br><span class="line">{</span><br><span class="line"> <span class="string">"exports"</span>: {</span><br><span class="line"> <span class="string">"./features/"</span>: <span class="string">"./src/features/"</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> feature <span class="keyword">from</span> <span class="string">'es-module-package/features/x.js'</span>;</span><br><span class="line"><span class="comment">// 加载 ./node_modules/es-module-package/src/features/x.js</span></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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 报错</span></span><br><span class="line"><span class="keyword">import</span> submodule <span class="keyword">from</span> <span class="string">'es-module-package/private-module.js'</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> submodule <span class="keyword">from</span> <span class="string">'./node_modules/es-module-package/private-module.js'</span>;</span><br></pre></td></tr></table></figure></li>
<li>main 的别名<br><code>exports</code>字段的别名如果是<code>.</code>,就代表模块的主入口,优先级高于<code>main</code>字段,并且可以直接简写成<code>exports</code>字段的值。<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="string">"exports"</span>: {</span><br><span class="line"> <span class="string">"."</span>: <span class="string">"./main.js"</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 等同于</span></span><br><span class="line">{</span><br><span class="line"> <span class="string">"exports"</span>: <span class="string">"./main.js"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
由于<code>exports</code>字段只有支持 ES6 的 Node.js 才认识,所以可以用来兼容旧版本的 Node.js。<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><br><span class="line"> <span class="string">"main"</span>: <span class="string">"./main-legacy.cjs"</span>,</span><br><span class="line"> <span class="string">"exports"</span>: {</span><br><span class="line"> <span class="string">"."</span>: <span class="string">"./main-modern.cjs"</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
上面代码中,老版本的 Node.js (不支持 ES6 模块)的入口文件是<code>main-legacy.cjs</code>,新版本的 Node.js 的入口文件是<code>main-modern.cjs</code>。</li>
<li>条件加载<br>利用<code>.</code>这个别名,可以为 ES6 模块和 CommonJS 指定不同的入口。目前,这个功能需要在 Node.js 运行的时候,打开<code>--experimental-conditional-exports</code>标志。<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><br><span class="line"> <span class="string">"type"</span>: <span class="string">"module"</span>,</span><br><span class="line"> <span class="string">"exports"</span>: {</span><br><span class="line"> <span class="string">"."</span>: {</span><br><span class="line"> <span class="string">"require"</span>: <span class="string">"./main.cjs"</span>,</span><br><span class="line"> <span class="string">"default"</span>: <span class="string">"./main.js"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
上面代码中,别名<code>.</code>的<code>require</code>条件指定<code>require()</code>命令的入口文件(即 CommonJS 的入口),<code>default</code>条件指定其他情况的入口(即 ES6 的入口)。<br>上面的写法可以简写如下。<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="string">"exports"</span>: {</span><br><span class="line"> <span class="string">"require"</span>: <span class="string">"./main.cjs"</span>,</span><br><span class="line"> <span class="string">"default"</span>: <span class="string">"./main.js"</span></span><br><span class="line"> }</span><br><span class="line">}</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></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="comment">// 报错</span></span><br><span class="line"> <span class="string">"exports"</span>: {</span><br><span class="line"> <span class="string">"./feature"</span>: <span class="string">"./lib/feature.js"</span>,</span><br><span class="line"> <span class="string">"require"</span>: <span class="string">"./main.cjs"</span>,</span><br><span class="line"> <span class="string">"default"</span>: <span class="string">"./main.js"</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li>
</ol>
<h4 id="CommonJS-模块加载-ES6-模块"><a href="#CommonJS-模块加载-ES6-模块" class="headerlink" title="CommonJS 模块加载 ES6 模块"></a>CommonJS 模块加载 ES6 模块</h4><p>CommonJS 的<code>require()</code>命令不能加载 ES6 模块,会报错,只能使用<code>import()</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="keyword">async</span> () => {</span><br><span class="line"> <span class="keyword">await</span> <span class="keyword">import</span>(<span class="string">'./my-app.mjs'</span>);</span><br><span class="line">})();</span><br></pre></td></tr></table></figure>
<p>上面代码可以在 CommonJS 模块中运行。</p>
<p><code>require()</code>不支持 ES6 模块的一个原因是,它是同步加载,而 ES6 模块内部可以使用顶层<code>await</code>命令,导致无法被同步加载。</p>
<h4 id="ES6-模块加载-CommonJS-模块"><a href="#ES6-模块加载-CommonJS-模块" class="headerlink" title="ES6 模块加载 CommonJS 模块"></a>ES6 模块加载 CommonJS 模块</h4><p>ES6 模块的<code>import</code>命令可以加载 CommonJS 模块,但是只能整体加载,不能只加载单一的输出项。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 正确</span></span><br><span class="line"><span class="keyword">import</span> packageMain <span class="keyword">from</span> <span class="string">'commonjs-package'</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> { method } <span class="keyword">from</span> <span class="string">'commonjs-package'</span>;</span><br></pre></td></tr></table></figure>
<p>这是因为 ES6 模块需要支持静态代码分析,而 CommonJS 模块的输出接口是<code>module.exports</code>,是一个对象,无法被静态分析,所以只能整体加载。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> packageMain <span class="keyword">from</span> <span class="string">'commonjs-package'</span>;</span><br><span class="line"><span class="keyword">const</span> { method } = packageMain;</span><br></pre></td></tr></table></figure>
<p>还有一种变通的加载方法,就是使用 Node.js 内置的<code>module.createRequire()</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">// cjs.cjs</span></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="string">'cjs'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// esm.mjs</span></span><br><span class="line"><span class="keyword">import</span> { createRequire } <span class="keyword">from</span> <span class="string">'module'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="built_in">require</span> = <span class="title function_">createRequire</span>(<span class="keyword">import</span>.<span class="property">meta</span>.<span class="property">url</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> cjs = <span class="built_in">require</span>(<span class="string">'./cjs.cjs'</span>);</span><br><span class="line">cjs === <span class="string">'cjs'</span>; <span class="comment">// true</span></span><br></pre></td></tr></table></figure>
<p>上面代码中,ES6 模块通过<code>module.createRequire()</code>方法可以加载 CommonJS 模块。但是,这种写法等于将 ES6 和 CommonJS 混在一起了,所以不建议使用。</p>
<h4 id="同时支持两种格式的模块"><a href="#同时支持两种格式的模块" class="headerlink" title="同时支持两种格式的模块"></a>同时支持两种格式的模块</h4><p>一个模块同时要支持 CommonJS 和 ES6 两种格式,也很容易。</p>
<p>如果原始模块是 ES6 格式,那么需要给出一个整体输出接口,比如<code>export default obj</code>,使得 CommonJS 可以用<code>import()</code>进行加载。</p>
<p>如果原始模块是 CommonJS 格式,那么可以加一个包装层。</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> cjsModule <span class="keyword">from</span> <span class="string">'../index.js'</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> foo = cjsModule.<span class="property">foo</span>;</span><br></pre></td></tr></table></figure>
<p>上面代码先整体输入 CommonJS 模块,然后再根据需要输出具名接口。</p>
<p>你可以把这个文件的后缀名改为<code>.mjs</code>,或者将它放在一个子目录,再在这个子目录里面放一个单独的<code>package.json</code>文件,指明<code>{ type: "module" }</code>。</p>
<p>另一种做法是在<code>package.json</code>文件的<code>exports</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="string">"exports"</span>:{</span><br><span class="line"> <span class="string">"require"</span>: <span class="string">"./index.js"</span>,</span><br><span class="line"> <span class="string">"import"</span>: <span class="string">"./esm/wrapper.js"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面代码指定<code>require()</code>和<code>import</code>,加载该模块会自动切换到不一样的入口文件。</p>
<h4 id="Node-js-的内置模块"><a href="#Node-js-的内置模块" class="headerlink" title="Node.js 的内置模块"></a>Node.js 的内置模块</h4><p>Node.js 的内置模块可以整体加载,也可以加载指定的输出项。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 整体加载</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">EventEmitter</span> <span class="keyword">from</span> <span class="string">'events'</span>;</span><br><span class="line"><span class="keyword">const</span> e = <span class="keyword">new</span> <span class="title class_">EventEmitter</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> { readFile } <span class="keyword">from</span> <span class="string">'fs'</span>;</span><br><span class="line"><span class="title function_">readFile</span>(<span class="string">'./foo.txt'</span>, <span class="function">(<span class="params">err, source</span>) =></span> {</span><br><span class="line"> <span class="keyword">if</span> (err) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">error</span>(err);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(source);</span><br><span class="line"> }</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<h4 id="加载路径"><a href="#加载路径" class="headerlink" title="加载路径"></a>加载路径</h4><p>ES6 模块的加载路径必须给出脚本的完整路径,不能省略脚本的后缀名。<code>import</code>命令和<code>package.json</code>文件的<code>main</code>字段如果省略脚本的后缀名,会报错。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ES6 模块中将报错</span></span><br><span class="line"><span class="keyword">import</span> { something } <span class="keyword">from</span> <span class="string">'./index'</span>;</span><br></pre></td></tr></table></figure>
<p>为了与浏览器的<code>import</code>加载规则相同,Node.js 的<code>.mjs</code>文件支持 URL 路径。</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">'./foo.mjs?query=1'</span>; <span class="comment">// 加载 ./foo 传入参数 ?query=1</span></span><br></pre></td></tr></table></figure>
<p>上面代码中,脚本路径带有参数<code>?query=1</code>,Node 会按 URL 规则解读。同一个脚本只要参数不同,就会被加载多次,并且保存成不同的缓存。由于这个原因,只要文件名中含有<code>:</code>、<code>%</code>、<code>#</code>、<code>?</code>等特殊字符,最好对这些字符进行转义。</p>
<p>目前,Node.js 的<code>import</code>命令只支持加载本地模块(<code>file:</code>协议)和<code>data:</code>协议,不支持加载远程模块。另外,脚本路径只支持相对路径,不支持绝对路径(即以<code>/</code>或<code>//</code>开头的路径)。</p>
<h4 id="内部变量"><a href="#内部变量" class="headerlink" title="内部变量"></a>内部变量</h4><p>ES6 模块应该是通用的,同一个模块不用修改,就可以用在浏览器环境和服务器环境。为了达到这个目标,Node.js 规定 ES6 模块之中不能使用 CommonJS 模块的特有的一些内部变量。</p>
<p>首先,就是<code>this</code>关键字。ES6 模块之中,顶层的<code>this</code>指向<code>undefined</code>;CommonJS 模块的顶层this指向当前模块,这是两者的一个重大差异。</p>
<p>其次,以下这些顶层变量在 ES6 模块之中都是不存在的。</p>
<ul>
<li>arguments</li>
<li>require</li>
<li>module</li>
<li>exports</li>
<li>__filename</li>
<li>__dirname</li>
</ul>
<h3 id="循环加载"><a href="#循环加载" class="headerlink" title="循环加载"></a>循环加载</h3><p>“循环加载”(circular dependency)指的是,<code>a</code>脚本的执行依赖<code>b</code>脚本,而<code>b</code>脚本的执行又依赖<code>a</code>脚本。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// a.js</span></span><br><span class="line"><span class="keyword">var</span> b = <span class="built_in">require</span>(<span class="string">'b'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// b.js</span></span><br><span class="line"><span class="keyword">var</span> a = <span class="built_in">require</span>(<span class="string">'a'</span>);</span><br></pre></td></tr></table></figure>
<p>通常,“循环加载”表示存在强耦合,如果处理不好,还可能导致递归加载,使得程序无法执行,因此应该避免出现。</p>
<p>但是实际上,这是很难避免的,尤其是依赖关系复杂的大项目,很容易出现<code>a</code>依赖<code>b</code>,<code>b</code>依赖<code>c</code>,<code>c</code>又依赖<code>a</code>这样的情况。这意味着,模块加载机制必须考虑“循环加载”的情况。</p>
<p>对于 JavaScript 语言来说,目前最常见的两种模块格式 CommonJS 和 ES6,处理“循环加载”的方法是不一样的,返回的结果也不一样。</p>
<h4 id="CommonJS-模块的加载原理"><a href="#CommonJS-模块的加载原理" class="headerlink" title="CommonJS 模块的加载原理"></a>CommonJS 模块的加载原理</h4><p>介绍 ES6 如何处理“循环加载”之前,先介绍目前最流行的 CommonJS 模块格式的加载原理。</p>
<p>CommonJS 的一个模块,就是一个脚本文件。<code>require</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></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="attr">id</span>: <span class="string">'...'</span>,</span><br><span class="line"> <span class="attr">exports</span>: { ... },</span><br><span class="line"> <span class="attr">loaded</span>: <span class="literal">true</span>,</span><br><span class="line"> ...</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面代码就是 Node 内部加载模块后生成的一个对象。该对象的<code>id</code>属性是模块名,<code>exports</code>属性是模块输出的各个接口,<code>loaded</code>属性是一个布尔值,表示该模块的脚本是否执行完毕。其他还有很多属性,这里都省略了。</p>
<p>以后需要用到这个模块的时候,就会到<code>exports</code>属性上面取值。即使再次执行<code>require</code>命令,也不会再次执行该模块,而是到缓存之中取值。也就是说,CommonJS 模块无论加载多少次,都只会在第一次加载时运行一次,以后再加载,就返回第一次运行的结果,除非手动清除系统缓存。</p>
<h4 id="CommonJS-模块的循环加载"><a href="#CommonJS-模块的循环加载" class="headerlink" title="CommonJS 模块的循环加载"></a>CommonJS 模块的循环加载</h4><p>CommonJS 模块的重要特性是加载时执行,即脚本代码在<code>require</code>的时候,就会全部执行。一旦出现某个模块被”循环加载”,就只输出已经执行的部分,还未执行的部分不会输出。</p>
<p>让我们来看,<a href="https://nodejs.org/api/modules.html#modules_cycles">Node 官方文档</a>里面的例子。脚本文件<code>a.js</code>代码如下。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">exports</span>.<span class="property">done</span> = <span class="literal">false</span>;</span><br><span class="line"><span class="keyword">var</span> b = <span class="built_in">require</span>(<span class="string">'./b.js'</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'在 a.js 之中,b.done = %j'</span>, b.<span class="property">done</span>);</span><br><span class="line"><span class="built_in">exports</span>.<span class="property">done</span> = <span class="literal">true</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'a.js 执行完毕'</span>);</span><br></pre></td></tr></table></figure>
<p>上面代码之中,<code>a.js</code>脚本先输出一个<code>done</code>变量,然后加载另一个脚本文件<code>b.js</code>。注意,此时<code>a.js</code>代码就停在这里,等待<code>b.js</code>执行完毕,再往下执行。</p>
<p>再看<code>b.js</code>的代码。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">exports</span>.<span class="property">done</span> = <span class="literal">false</span>;</span><br><span class="line"><span class="keyword">var</span> a = <span class="built_in">require</span>(<span class="string">'./a.js'</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'在 b.js 之中,a.done = %j'</span>, a.<span class="property">done</span>);</span><br><span class="line"><span class="built_in">exports</span>.<span class="property">done</span> = <span class="literal">true</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'b.js 执行完毕'</span>);</span><br></pre></td></tr></table></figure>
<p>上面代码之中,<code>b.js</code>执行到第二行,就会去加载<code>a.js</code>,这时,就发生了“循环加载”。系统会去<code>a.js</code>模块对应对象的<code>exports</code>属性取值,可是因为<code>a.js</code>还没有执行完,从<code>exports</code>属性只能取回已经执行的部分,而不是最后的值。</p>
<p><code>a.js</code>已经执行的部分,只有一行。</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">exports</span>.<span class="property">done</span> = <span class="literal">false</span>;</span><br></pre></td></tr></table></figure>
<p>因此,对于<code>b.js</code>来说,它从<code>a.js</code>只输入一个变量<code>done</code>,值为<code>false</code>。</p>
<p>然后,<code>b.js</code>接着往下执行,等到全部执行完毕,再把执行权交还给<code>a.js</code>。于是,<code>a.js</code>接着往下执行,直到执行完毕。我们写一个脚本<code>main.js</code>,验证这个过程。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="built_in">require</span>(<span class="string">'./a.js'</span>);</span><br><span class="line"><span class="keyword">var</span> b = <span class="built_in">require</span>(<span class="string">'./b.js'</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'在 main.js 之中, a.done=%j, b.done=%j'</span>, a.<span class="property">done</span>, b.<span class="property">done</span>);</span><br></pre></td></tr></table></figure>
<p>执行<code>main.js</code>,运行结果如下。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$ node main.<span class="property">js</span></span><br><span class="line"></span><br><span class="line">在 b.<span class="property">js</span> 之中,a.<span class="property">done</span> = <span class="literal">false</span></span><br><span class="line">b.<span class="property">js</span> 执行完毕</span><br><span class="line">在 a.<span class="property">js</span> 之中,b.<span class="property">done</span> = <span class="literal">true</span></span><br><span class="line">a.<span class="property">js</span> 执行完毕</span><br><span class="line">在 main.<span class="property">js</span> 之中, a.<span class="property">done</span>=<span class="literal">true</span>, b.<span class="property">done</span>=<span class="literal">true</span></span><br></pre></td></tr></table></figure>
<p>上面的代码证明了两件事。一是,在<code>b.js</code>之中,<code>a.js</code>没有执行完毕,只执行了第一行。二是,<code>main.js</code>执行到第二行时,不会再次执行<code>b.js</code>,而是输出缓存的<code>b.js</code>的执行结果,即它的第四行。</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">exports</span>.<span class="property">done</span> = <span class="literal">true</span>;</span><br></pre></td></tr></table></figure>
<p>总之,CommonJS 输入的是被输出值的拷贝,不是引用。</p>
<p>另外,由于 CommonJS 模块遇到循环加载时,返回的是当前已经执行的部分的值,而不是代码全部执行后的值,两者可能会有差异。所以,输入变量的时候,必须非常小心。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="built_in">require</span>(<span class="string">'a'</span>); <span class="comment">// 安全的写法</span></span><br><span class="line"><span class="keyword">var</span> foo = <span class="built_in">require</span>(<span class="string">'a'</span>).<span class="property">foo</span>; <span class="comment">// 危险的写法</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">exports</span>.<span class="property">good</span> = <span class="keyword">function</span> (<span class="params">arg</span>) {</span><br><span class="line"> <span class="keyword">return</span> a.<span class="title function_">foo</span>(<span class="string">'good'</span>, arg); <span class="comment">// 使用的是 a.foo 的最新值</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="built_in">exports</span>.<span class="property">bad</span> = <span class="keyword">function</span> (<span class="params">arg</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">foo</span>(<span class="string">'bad'</span>, arg); <span class="comment">// 使用的是一个部分加载时的值</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<p>上面代码中,如果发生循环加载,<code>require('a').foo</code>的值很可能后面会被改写,改用<code>require('a')</code>会更保险一点。</p>
<h5 id="ES6-模块的循环加载"><a href="#ES6-模块的循环加载" class="headerlink" title="ES6 模块的循环加载"></a>ES6 模块的循环加载</h5><p>ES6 处理“循环加载”与 CommonJS 有本质的不同。ES6 模块是动态引用,如果使用<code>import</code>从一个模块加载变量(即<code>import foo from 'foo'</code>),那些变量不会被缓存,而是成为一个指向被加载模块的引用,需要开发者自己保证,真正取值的时候能够取到值。</p>
<p>请看下面这个例子。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// a.mjs</span></span><br><span class="line"><span class="keyword">import</span> {bar} <span class="keyword">from</span> <span class="string">'./b'</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'a.mjs'</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(bar);</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">let</span> foo = <span class="string">'foo'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// b.mjs</span></span><br><span class="line"><span class="keyword">import</span> {foo} <span class="keyword">from</span> <span class="string">'./a'</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'b.mjs'</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(foo);</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">let</span> bar = <span class="string">'bar'</span>;</span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>a.mjs</code>加载<code>b.mjs</code>,<code>b.mjs</code>又加载<code>a.mjs</code>,构成循环加载。执行<code>a.mjs</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">$ node --experimental-modules a.<span class="property">mjs</span></span><br><span class="line">b.<span class="property">mjs</span></span><br><span class="line"><span class="title class_">ReferenceError</span>: foo is not defined</span><br></pre></td></tr></table></figure>
<p>上面代码中,执行<code>a.mjs</code>以后会报错,<code>foo</code>变量未定义,这是为什么?</p>
<p>让我们一行行来看,ES6 循环加载是怎么处理的。首先,执行<code>a.mjs</code>以后,引擎发现它加载了<code>b.mjs</code>,因此会优先执行<code>b.mjs</code>,然后再执行<code>a.mjs</code>。接着,执行<code>b.mjs</code>的时候,已知它从<code>a.mjs</code>输入了<code>foo</code>接口,这时不会去执行<code>a.mjs</code>,而是认为这个接口已经存在了,继续往下执行。执行到第三行<code>console.log(foo)</code>的时候,才发现这个接口根本没定义,因此报错。</p>
<p>解决这个问题的方法,就是让<code>b.mjs</code>运行的时候,<code>foo</code>已经有定义了。这可以通过将<code>foo</code>写成函数来解决。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// a.mjs</span></span><br><span class="line"><span class="keyword">import</span> {bar} <span class="keyword">from</span> <span class="string">'./b'</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'a.mjs'</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">bar</span>());</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) { <span class="keyword">return</span> <span class="string">'foo'</span> }</span><br><span class="line"><span class="keyword">export</span> {foo};</span><br><span class="line"></span><br><span class="line"><span class="comment">// b.mjs</span></span><br><span class="line"><span class="keyword">import</span> {foo} <span class="keyword">from</span> <span class="string">'./a'</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'b.mjs'</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">foo</span>());</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">bar</span>(<span class="params"></span>) { <span class="keyword">return</span> <span class="string">'bar'</span> }</span><br><span class="line"><span class="keyword">export</span> {bar};</span><br></pre></td></tr></table></figure>
<p>这时再执行<code>a.mjs</code>就可以得到预期结果。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ node --experimental-modules a.<span class="property">mjs</span></span><br><span class="line">b.<span class="property">mjs</span></span><br><span class="line">foo</span><br><span class="line">a.<span class="property">mjs</span></span><br><span class="line">bar</span><br></pre></td></tr></table></figure>
<p>这是因为函数具有提升作用,在执行<code>import {bar} from './b'</code>时,函数<code>foo</code>就已经有定义了,所以<code>b.mjs</code>加载的时候不会报错。这也意味着,如果把函数<code>foo</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// a.mjs</span></span><br><span class="line"><span class="keyword">import</span> {bar} <span class="keyword">from</span> <span class="string">'./b'</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'a.mjs'</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">bar</span>());</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">foo</span> = (<span class="params"></span>) => <span class="string">'foo'</span>;</span><br><span class="line"><span class="keyword">export</span> {foo};</span><br></pre></td></tr></table></figure>
<p>上面代码的第四行,改成了函数表达式,就不具有提升作用,执行就会报错。</p>
<p>我们再来看 ES6 模块加载器 <a href="https://github.com/ModuleLoader/es6-module-loader/blob/master/docs/circular-references-bindings.md">SystemJS</a> 给出的一个例子。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// even.js</span></span><br><span class="line"><span class="keyword">import</span> { odd } <span class="keyword">from</span> <span class="string">'./odd'</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">var</span> counter = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">even</span>(<span class="params">n</span>) {</span><br><span class="line"> counter++;</span><br><span class="line"> <span class="keyword">return</span> n === <span class="number">0</span> || <span class="title function_">odd</span>(n - <span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// odd.js</span></span><br><span class="line"><span class="keyword">import</span> { even } <span class="keyword">from</span> <span class="string">'./even'</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">odd</span>(<span class="params">n</span>) {</span><br><span class="line"> <span class="keyword">return</span> n !== <span class="number">0</span> && <span class="title function_">even</span>(n - <span class="number">1</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>even.js</code>里面的函数<code>even</code>有一个参数<code>n</code>,只要不等于 0,就会减去 1,传入加载的<code>odd()</code>。<code>odd.js</code>也会做类似操作。</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></pre></td><td class="code"><pre><span class="line">$ babel-node</span><br><span class="line">> <span class="keyword">import</span> * <span class="keyword">as</span> m <span class="keyword">from</span> <span class="string">'./even.js'</span>;</span><br><span class="line">> m.<span class="title function_">even</span>(<span class="number">10</span>);</span><br><span class="line"><span class="literal">true</span></span><br><span class="line">> m.<span class="property">counter</span></span><br><span class="line"><span class="number">6</span></span><br><span class="line">> m.<span class="title function_">even</span>(<span class="number">20</span>)</span><br><span class="line"><span class="literal">true</span></span><br><span class="line">> m.<span class="property">counter</span></span><br><span class="line"><span class="number">17</span></span><br></pre></td></tr></table></figure>
<p>上面代码中,参数<code>n</code>从 10 变为 0 的过程中,<code>even()</code>一共会执行 6 次,所以变量<code>counter</code>等于 6。第二次调用<code>even()</code>时,参数<code>n</code>从 20 变为 0,<code>even()</code>一共会执行 11 次,加上前面的 6 次,所以变量<code>counter</code>等于 17。</p>
<p>这个例子要是改写成 CommonJS,就根本无法执行,会报错。</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">// even.js</span></span><br><span class="line"><span class="keyword">var</span> odd = <span class="built_in">require</span>(<span class="string">'./odd'</span>);</span><br><span class="line"><span class="keyword">var</span> counter = <span class="number">0</span>;</span><br><span class="line"><span class="built_in">exports</span>.<span class="property">counter</span> = counter;</span><br><span class="line"><span class="built_in">exports</span>.<span class="property">even</span> = <span class="keyword">function</span> (<span class="params">n</span>) {</span><br><span class="line"> counter++;</span><br><span class="line"> <span class="keyword">return</span> n == <span class="number">0</span> || <span class="title function_">odd</span>(n - <span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// odd.js</span></span><br><span class="line"><span class="keyword">var</span> even = <span class="built_in">require</span>(<span class="string">'./even'</span>).<span class="property">even</span>;</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="keyword">function</span> (<span class="params">n</span>) {</span><br><span class="line"> <span class="keyword">return</span> n != <span class="number">0</span> && <span class="title function_">even</span>(n - <span class="number">1</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>even.js</code>加载<code>odd.js</code>,而<code>odd.js</code>又去加载<code>even.js</code>,形成“循环加载”。这时,执行引擎就会输出<code>even.js</code>已经执行的部分(不存在任何结果),所以在<code>odd.js</code>之中,变量<code>even</code>等于<code>undefined</code>,等到后面调用<code>even(n - 1)</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">$ node</span><br><span class="line">> <span class="keyword">var</span> m = <span class="built_in">require</span>(<span class="string">'./even'</span>);</span><br><span class="line">> m.<span class="title function_">even</span>(<span class="number">10</span>)</span><br><span class="line"><span class="title class_">TypeError</span>: even is not a <span class="keyword">function</span></span><br></pre></td></tr></table></figure>
<p>参考链接</p>
<ul>
<li><a href="https://es6.ruanyifeng.com/#docs/module">Module 的语法</a></li>
<li><a href="https://es6.ruanyifeng.com/#docs/module-loader">Module
前端基础进阶(十六):ES6常用基础合集
https://fe32.top/articles/jsnb9543/
2022-06-30T15:16:39.000Z
2023-05-17T10:01:12.000Z
<h2 id="变量声明方式-let-x2F-const"><a href="#变量声明方式-let-x2F-const" class="headerlink" title="变量声明方式 let/const"></a>变量声明方式 let/const</h2><p>与 var 不同,新的变量声明方式带来了一些不一样的特性,其中最重要的两个特性就是提供了块级作用域与不再具备变量提升。通过 2 个简单的例子来说明这两点。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="keyword">let</span> a = <span class="number">20</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(a); <span class="comment">// a is not defined</span></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></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="keyword">let</span> _a = <span class="number">20</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(a); <span class="comment">// a is not defined</span></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></pre></td><td class="code"><pre><span class="line"><span class="comment">// ES5</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(a); <span class="comment">// undefined</span></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">20</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ES6</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(a); <span class="comment">// a is not defined</span></span><br><span class="line"><span class="keyword">let</span> a = <span class="number">20</span>;</span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/30/62bdc11480eb3.webp" alt="变量提升demo示例"/></div><span class="image-caption">变量提升demo示例</span></div>
<p class='p blue'>我们常常使用let来声明一个值会被改变的变量,而使用const来声明一个值不会被改变的变量,也可以称之为常量。</p>
<p>当值为基础数据类型时,那么这里的值,就是指值本身。<br>而当值对应的为引用数据类型时,那么这里说的值,则表示指向该对象的引用。这里需要注意,正因为该值为一个引用,只需要保证引用不变就可以,仍然可以改变该引用所指向的对象。</p>
<p>当我们试图改变 const 声明的变量时,则会报错。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> a = <span class="literal">null</span>;</span><br><span class="line">a = <span class="number">20</span>;</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> obDev = {</span><br><span class="line"> <span class="attr">a</span>: <span class="number">20</span>,</span><br><span class="line"> <span class="attr">b</span>: <span class="number">30</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">obDev.<span class="property">a</span> = <span class="number">30</span>;</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(obDev); <span class="comment">// Object {a: 30, b: 30}</span></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> fn = <span class="keyword">function</span>(<span class="params"></span>) {}</span><br><span class="line"><span class="keyword">const</span> a = obDev.<span class="property">a</span>;</span><br><span class="line">... ...</span><br></pre></td></tr></table></figure>
<h2 id="箭头函数的使用"><a href="#箭头函数的使用" class="headerlink" title="箭头函数的使用"></a>箭头函数的使用</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// es5</span></span><br><span class="line"><span class="keyword">var</span> fn = <span class="keyword">function</span>(<span class="params">a, b</span>) {</span><br><span class="line"> <span class="keyword">return</span> a + b;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// es6 箭头函数写法,当函数直接被return时,可以省略函数体的括号</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">fn</span> = (<span class="params">a, b</span>) => a + b;</span><br><span class="line"></span><br><span class="line"><span class="comment">// es5</span></span><br><span class="line"><span class="keyword">var</span> foo = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">20</span>;</span><br><span class="line"> <span class="keyword">var</span> b = <span class="number">30</span>;</span><br><span class="line"> <span class="keyword">return</span> a + b;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// es6</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">foo</span> = (<span class="params"></span>) => {</span><br><span class="line"> <span class="keyword">const</span> a = <span class="number">20</span>;</span><br><span class="line"> <span class="keyword">const</span> b = <span class="number">30</span>;</span><br><span class="line"> <span class="keyword">return</span> a + b;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<blockquote>
<p>箭头函数可以替换函数表达式,但是不能替换函数声明。</p>
</blockquote>
<p>其次还有一个至关重要的一点,就是箭头函数中,没有this。如果你在箭头函数中使用了this,那么该this一定就是外层的this。因此我们也就无从谈起用call/apply/bind来改变this指向。记住这个特性,能让你在react组件之间传值时少走无数弯路。</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">var</span> person = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'tom'</span>,</span><br><span class="line"> <span class="attr">getName</span>: <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">name</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 我们试图用ES6的写法来重构上面的对象</span></span><br><span class="line"><span class="keyword">const</span> person = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'tom'</span>,</span><br><span class="line"> <span class="attr">getName</span>: <span class="function">() =></span> <span class="variable language_">this</span>.<span class="property">name</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 但是编译结果却是</span></span><br><span class="line"><span class="keyword">var</span> person = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'tom'</span>,</span><br><span class="line"> <span class="attr">getName</span>: <span class="keyword">function</span> <span class="title function_">getName</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">undefined</span>.<span class="property">name</span>;</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<blockquote>
<p>在ES6中,会默认采用严格模式,因此this也不会自动指向window对象了,而箭头函数本身并没有this,因此this就只能是undefined,这种情况,如果你还想用this,就不要用使用箭头函数的写法。</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 可以稍做改动</span></span><br><span class="line"><span class="keyword">const</span> person = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'tom'</span>,</span><br><span class="line"> <span class="attr">getName</span>: <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">setTimeout</span>(<span class="function">() =></span> <span class="variable language_">this</span>.<span class="property">name</span>, <span class="number">1000</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 编译之后变成</span></span><br><span class="line"><span class="keyword">var</span> person = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'tom'</span>,</span><br><span class="line"> <span class="attr">getName</span>: <span class="keyword">function</span> <span class="title function_">getName</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> _this = <span class="variable language_">this</span>; <span class="comment">// 使用了我们在es5时常用的方式保存this引用</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> _this.<span class="property">name</span>;</span><br><span class="line"> }, <span class="number">1000</span>);</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<h2 id="模板字符串"><a href="#模板字符串" class="headerlink" title="模板字符串"></a>模板字符串</h2><p>模板字符串是为了解决使用<code>+</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// es6</span></span><br><span class="line"><span class="keyword">const</span> a = <span class="number">20</span>;</span><br><span class="line"><span class="keyword">const</span> b = <span class="number">30</span>;</span><br><span class="line"><span class="keyword">const</span> string = <span class="string">`<span class="subst">${a}</span>+<span class="subst">${b}</span>=<span class="subst">${a+b}</span>`</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// es5</span></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">20</span>;</span><br><span class="line"><span class="keyword">var</span> b = <span class="number">30</span>;</span><br><span class="line"><span class="keyword">var</span> string = a + <span class="string">"+"</span> + b + <span class="string">"="</span> + (a + b);</span><br></pre></td></tr></table></figure>
<h2 id="解析结构"><a href="#解析结构" class="headerlink" title="解析结构"></a>解析结构</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 首先有这么一个对象</span></span><br><span class="line"><span class="keyword">const</span> props = {</span><br><span class="line"> <span class="attr">className</span>: <span class="string">'tiger-button'</span>,</span><br><span class="line"> <span class="attr">loading</span>: <span class="literal">false</span>,</span><br><span class="line"> <span class="attr">clicked</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">disabled</span>: <span class="string">'disabled'</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>当我们想要取得其中的2个值:<code>loading</code>与<code>clicked</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// es5</span></span><br><span class="line"><span class="keyword">var</span> loading = props.<span class="property">loading</span>;</span><br><span class="line"><span class="keyword">var</span> clicked = props.<span class="property">clicked</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// es6</span></span><br><span class="line"><span class="keyword">const</span> { loading, clicked } = props;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 给一个默认值,当props对象中找不到loading时,loading就等于该默认值</span></span><br><span class="line"><span class="keyword">const</span> { loading = <span class="literal">false</span>, clicked } = props;</span><br></pre></td></tr></table></figure>
<p>是不是简单了许多?正是由于解析结构大大减少了代码量,因此它大受欢迎,在很多代码中它的影子随处可见。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 比如</span></span><br><span class="line"><span class="comment">// section1</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">React</span>, { <span class="title class_">Component</span> } <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// section2</span></span><br><span class="line"><span class="keyword">export</span> { <span class="keyword">default</span> } <span class="keyword">from</span> <span class="string">'./Button'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// section3</span></span><br><span class="line"><span class="keyword">const</span> { click, loading } = <span class="variable language_">this</span>.<span class="property">props</span>;</span><br><span class="line"><span class="keyword">const</span> { isCheck } = <span class="variable language_">this</span>.<span class="property">state</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// more 任何获取对象属性值的场景都可以使用解析结构来减少我们的代码量</span></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></pre></td><td class="code"><pre><span class="line"><span class="comment">// es6</span></span><br><span class="line"><span class="keyword">const</span> arr = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];</span><br><span class="line"><span class="keyword">const</span> [a, b, c] = arr;</span><br><span class="line"></span><br><span class="line"><span class="comment">// es5</span></span><br><span class="line"><span class="keyword">var</span> arr = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];</span><br><span class="line"><span class="keyword">var</span> a = arr[<span class="number">0</span>];</span><br><span class="line"><span class="keyword">var</span> b = arr[<span class="number">1</span>];</span><br><span class="line"><span class="keyword">var</span> c = arr[<span class="number">2</span>];</span><br></pre></td></tr></table></figure>
<p>数组以序列号一一对应,这是一个有序的对应关系。<br>而对象根据属性名一一对应,这是一个无序的对应关系。</p>
<p>根据这个特性,使用解析结构从对象中获取属性值更加具有可用性。</p>
<h2 id="函数默认参数"><a href="#函数默认参数" class="headerlink" title="函数默认参数"></a>函数默认参数</h2><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">add</span>(<span class="params">x, y</span>) {</span><br><span class="line"> <span class="keyword">var</span> x = x || <span class="number">20</span>;</span><br><span class="line"> <span class="keyword">var</span> y = y || <span class="number">30</span>;</span><br><span class="line"> <span class="keyword">return</span> x + y;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">add</span>()); <span class="comment">// 50</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>这种方式并不是没有缺点,比如传入一个x值为false,这个时候任然会取到默认值,就不是我们的本意了。</p>
</blockquote>
<p>ES6的默认值写法:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">add</span>(<span class="params">x = <span class="number">20</span>, y = <span class="number">30</span></span>) {</span><br><span class="line"> <span class="keyword">return</span> x + y;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">add</span>());</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title class_">ButtonGroupProps</span> = {</span><br><span class="line"> <span class="attr">size</span>: <span class="string">'normal'</span>,</span><br><span class="line"> <span class="attr">className</span>: <span class="string">'xxxx-button-group'</span>,</span><br><span class="line"> <span class="attr">borderColor</span>: <span class="string">'#333'</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">ButtonGroup</span>(<span class="params">props = ButtonGroupProps</span>) {</span><br><span class="line"> ... ...</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="扩展运算符"><a href="#扩展运算符" class="headerlink" title="扩展运算符"></a>扩展运算符</h2><p>扩展运算符(spread)是三个点(…)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。</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> arr1 = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];</span><br><span class="line"><span class="keyword">const</span> arr2 = [...arr1, <span class="number">10</span>, <span class="number">20</span>, <span class="number">30</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 这样,arr2 就变成了[1, 2, 3, 10, 20, 30];</span></span><br></pre></td></tr></table></figure>
<p>当然,展开对象数据也是可以得到类似的结果:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> obj1 = {</span><br><span class="line"> <span class="attr">a</span>: <span class="number">1</span>,</span><br><span class="line"> <span class="attr">b</span>: <span class="number">2</span>,</span><br><span class="line"> <span class="attr">c</span>: <span class="number">3</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> obj2 = {</span><br><span class="line"> ...obj1,</span><br><span class="line"> <span class="attr">d</span>: <span class="number">4</span>,</span><br><span class="line"> <span class="attr">e</span>: <span class="number">5</span>,</span><br><span class="line"> <span class="attr">f</span>: <span class="number">6</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 结果类似于 const obj2 = Object.assign({}, obj1, {d: 4})</span></span><br></pre></td></tr></table></figure>
<p>展开运算符还常常运用在解析结构之中,例如我们在Raect封装组件的时候常常不确定props到底还有多少数据会传进来,就会利用展开运算符来处理剩余的数据。</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">// 这种方式在react中十分常用</span></span><br><span class="line"><span class="keyword">const</span> props = {</span><br><span class="line"> <span class="attr">size</span>: <span class="number">1</span>,</span><br><span class="line"> <span class="attr">src</span>: <span class="string">'xxxx'</span>,</span><br><span class="line"> <span class="attr">mode</span>: <span class="string">'si'</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> { size, ...others } = props;</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(others)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 然后再利用暂开运算符传递给下一个元素,再以后封装react组件时会大量使用到这种方式,正在学习react的同学一定要搞懂这种使用方式</span></span><br><span class="line"><button {...others} size={size} /></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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 所有参数之和</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">add</span> = (<span class="params">a, b, ...more</span>) => {</span><br><span class="line"> <span class="keyword">return</span> more.<span class="title function_">reduce</span>(<span class="function">(<span class="params">m, n</span>) =></span> m + n) + a + b</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">add</span>(<span class="number">1</span>, <span class="number">23</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>)) <span class="comment">// 39</span></span><br></pre></td></tr></table></figure>
<h2 id="对象字面量-与-class"><a href="#对象字面量-与-class" class="headerlink" title="对象字面量 与 class"></a>对象字面量 与 class</h2><p>ES6针对对象字面量做了许多简化语法的处理。</p>
<p class='p blue'>1. 当属性与值的变量同名时</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="keyword">const</span> name = <span class="string">'Jane'</span>;</span><br><span class="line"><span class="keyword">const</span> age = <span class="number">20</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// es6</span></span><br><span class="line"><span class="keyword">const</span> person = {</span><br><span class="line"> name,</span><br><span class="line"> age</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// es5</span></span><br><span class="line"><span class="keyword">var</span> person = {</span><br><span class="line"> <span class="attr">name</span>: name,</span><br><span class="line"> <span class="attr">age</span>: age</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<p>那么这种方式在任何地方都可以使用,比如在一个模块对外提供接口时</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">getName</span> = (<span class="params"></span>) => person.<span class="property">name</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">getAge</span> = (<span class="params"></span>) => person.<span class="property">age</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// commonJS的方式</span></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = { getName, getAge }</span><br><span class="line"></span><br><span class="line"><span class="comment">// ES6 modules的方式</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> { getName, getAge }</span><br></pre></td></tr></table></figure>
<p class='p blue'>2. 除了属性之外,对象字面量写法中的方法也可以有简写方式。</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"><span class="comment">// es6</span></span><br><span class="line"><span class="keyword">const</span> person = {</span><br><span class="line"> name,</span><br><span class="line"> age,</span><br><span class="line"> <span class="title function_">getName</span>(<span class="params"></span>) { <span class="comment">// 只要不使用箭头函数,this就还是我们熟悉的this</span></span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">name</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// es5</span></span><br><span class="line"><span class="keyword">var</span> person = {</span><br><span class="line"> <span class="attr">name</span>: name,</span><br><span class="line"> <span class="attr">age</span>: age,</span><br><span class="line"> <span class="attr">getName</span>: <span class="keyword">function</span> <span class="title function_">getName</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">name</span>;</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<p>在 Ant-Design 的源码实现中,就大量使用了这种方式来拼接当前元素的 className,例如:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> alertCls = <span class="title function_">classNames</span>(prefixCls, {</span><br><span class="line"> [<span class="string">`<span class="subst">${prefixCls}</span>-<span class="subst">${type}</span>`</span>]: <span class="literal">true</span>,</span><br><span class="line"> [<span class="string">`<span class="subst">${prefixCls}</span>-close`</span>]: !<span class="variable language_">this</span>.<span class="property">state</span>.<span class="property">closing</span>,</span><br><span class="line"> [<span class="string">`<span class="subst">${prefixCls}</span>-with-description`</span>]: !!description,</span><br><span class="line"> [<span class="string">`<span class="subst">${prefixCls}</span>-no-icon`</span>]: !showIcon,</span><br><span class="line"> [<span class="string">`<span class="subst">${prefixCls}</span>-banner`</span>]: !!banner,</span><br><span class="line"> }, className);</span><br></pre></td></tr></table></figure>
<blockquote>
<p>Ant-Design是一个认可度非常高的UI组件库,官方使用react的方式进行了实现,除此之外,还有vue也有对应的实现,有兴趣的同学可以去他们的官网了解学习。<a href="https://ant.design/index-cn">https://ant.design/index-cn</a></p>
</blockquote>
<p class='p blue'>3. Class</p>
<p>ES6 为我们创建对象提供了新的语法糖,这就是 Class 语法。如果你对ES5中面向对象的方式比较熟悉的话,Class 掌握起来也是非常迅速的,因为除了写法的不同,它并不会增加新的难以理解的知识点。我们先利用一个简单的例子来看看写法的不同。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ES5</span></span><br><span class="line"><span class="comment">// 构造函数</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Person</span>(<span class="params">name, age</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">age</span> = age;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 原型方法</span></span><br><span class="line"><span class="title class_">Person</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">getName</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">name</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// ES6</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span> {</span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params">name, age</span>) { <span class="comment">// 构造函数</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">age</span> = age;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="title function_">getName</span>(<span class="params"></span>) { <span class="comment">// 原型方法</span></span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">name</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>除此之外,还需要特别注意在实际使用中的几种写法方式的不同,在下面的例子注释中,我说明了他们分别对应的 ES5中 的含义。</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="keyword">class</span> <span class="title class_">Person</span> {</span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params">name, age</span>) { <span class="comment">// 构造函数</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">age</span> = age;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="title function_">getName</span>(<span class="params"></span>) { <span class="comment">// 这种写法表示将方法添加到原型中</span></span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">name</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">static</span> a = <span class="number">20</span>; <span class="comment">// 等同于 Person.a = 20</span></span><br><span class="line"></span><br><span class="line"> c = <span class="number">20</span>; <span class="comment">// 表示在构造函数中添加属性 在构造函数中等同于 this.c = 20</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 箭头函数的写法表示在构造函数中添加方法,在构造函数中等同于this.getAge = function() {}</span></span><br><span class="line"> getAge = <span class="function">() =></span> <span class="variable language_">this</span>.<span class="property">age</span> </span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>箭头函数需要注意的仍然是this的指向问题,因为箭头函数this指向不能被改变的特性,因此在react组件中常常利用这个特性来在不同的组件进行传值会更加方便。</p>
<p class='p blue'>4. 继承 extends</p>
<p>相比ES5,ES6的继承就要简单很多,我们直接来看一个例子。</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="keyword">class</span> <span class="title class_">Person</span> {</span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params">name, age</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">age</span> = age;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="title function_">getName</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">name</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// Student类继承Person类</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Student</span> <span class="keyword">extends</span> <span class="title class_ inherited__">Person</span> {</span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params">name, age, gender, classes</span>) {</span><br><span class="line"> <span class="variable language_">super</span>(name, age);</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">gender</span> = gender;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">classes</span> = classes;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="title function_">getGender</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">gender</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>我们只需要一个<code>extends</code>关键字,就可以实现继承了,不用像ES5那样去担心构造函数继承和原型继承,除此之外,我们还需要关注一个叫做super的方法。</p>
<p>在继承的构造函数中,我们必须如上面的例子那么调用一次<code>super</code>方法,它表示构造函数的继承,与ES5中利用<code>call/apply</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 构造函数中</span></span><br><span class="line"><span class="comment">// es6</span></span><br><span class="line"><span class="variable language_">super</span>(name, age);</span><br><span class="line"></span><br><span class="line"><span class="comment">// es5</span></span><br><span class="line"><span class="title class_">Person</span>.<span class="title function_">call</span>(<span class="variable language_">this</span>);</span><br></pre></td></tr></table></figure>
<blockquote>
<p>super还可以直接调用父级的原型方法,<code>super.getName</code>,但是我自己从来没这样用过,也就不扩展说了。<br>继承在react中有大量的使用场景,许多组件都利用继承来创建。</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="title class_">React</span>, { <span class="title class_">Component</span> } <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">App</span> <span class="keyword">extends</span> <span class="title class_ inherited__">Component</span> {</span><br><span class="line"></span><br><span class="line"> defaultProps = {}</span><br><span class="line"> state = {}</span><br><span class="line"> <span class="title function_">componentWillMount</span>(<span class="params"></span>) {}</span><br><span class="line"> <span class="title function_">componentDidMount</span>(<span class="params"></span>) {}</span><br><span class="line"></span><br><span class="line"> btnClick = <span class="function"><span class="params">e</span> =></span> {}</span><br><span class="line"></span><br><span class="line"> <span class="title function_">render</span>(<span class="params"></span>) {}</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="Promise"><a href="#Promise" class="headerlink" title="Promise"></a>Promise</h2><p><a href="https://fe32.top/articles/jsnb9542/">前端基础进阶(十五):详解 Promise对象</a></p>
<h2 id="模块-Modules"><a href="#模块-Modules" class="headerlink" title="模块 Modules"></a>模块 Modules</h2><p><a href="https://fe32.top/articles/jsnb9542/">前端基础进阶(十五):详解 ES6
前端基础进阶(十五):详解 Promise对象
https://fe32.top/articles/jsnb9542/
2022-06-29T14:08:16.000Z
2023-05-17T10:01:12.000Z
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>文章的开头,主要分析一下,为什么会有 Promise 出现。</p>
<p>在实际的使用中,有非常多的应用场景我们不能立即知道应该如何继续往下执行。最常见的一个场景就是 ajax 请求。通俗来说,由于网速的不同,可能你得到返回值的时间也是不同的,这个时候我们就需要等待,结果出来了之后才知道怎么样继续下去。</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">// 简单的ajax原生实现</span></span><br><span class="line"><span class="keyword">var</span> url = <span class="string">'https://hq.tigerbrokers.com/fundamental/finance_calendar/getType/2017-02-26/2017-06-10'</span>;</span><br><span class="line"><span class="keyword">var</span> result;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> <span class="variable constant_">XHR</span> = <span class="keyword">new</span> <span class="title class_">XMLHttpRequest</span>();</span><br><span class="line"><span class="variable constant_">XHR</span>.<span class="title function_">open</span>(<span class="string">'GET'</span>, url, <span class="literal">true</span>);</span><br><span class="line"><span class="variable constant_">XHR</span>.<span class="title function_">send</span>();</span><br><span class="line"></span><br><span class="line"><span class="variable constant_">XHR</span>.<span class="property">onreadystatechange</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable constant_">XHR</span>.<span class="property">readyState</span> == <span class="number">4</span> && <span class="variable constant_">XHR</span>.<span class="property">status</span> == <span class="number">200</span>) {</span><br><span class="line"> result = <span class="variable constant_">XHR</span>.<span class="property">response</span>;</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(result);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>在 ajax 的原生实现中,利用了<code>onreadystatechange</code>事件,当该事件触发并且符合一定条件时,才能拿到想要的数据,之后才能开始处理数据。</p>
<p>这样做看上去并没有什么麻烦,但如果这个时候,我们还需要另外一个 ajax 请求,这个新 ajax 请求的其中一个参数,得从上一个 ajax 请求中获取,这个时候就不得不等待上一个接口请求完成之后,再请求后一个接口。如下:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> url = <span class="string">'https://hq.tigerbrokers.com/fundamental/finance_calendar/getType/2017-02-26/2017-06-10'</span>;</span><br><span class="line"><span class="keyword">var</span> result;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> <span class="variable constant_">XHR</span> = <span class="keyword">new</span> <span class="title class_">XMLHttpRequest</span>();</span><br><span class="line"><span class="variable constant_">XHR</span>.<span class="title function_">open</span>(<span class="string">'GET'</span>, url, <span class="literal">true</span>);</span><br><span class="line"><span class="variable constant_">XHR</span>.<span class="title function_">send</span>();</span><br><span class="line"></span><br><span class="line"><span class="variable constant_">XHR</span>.<span class="property">onreadystatechange</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable constant_">XHR</span>.<span class="property">readyState</span> == <span class="number">4</span> && <span class="variable constant_">XHR</span>.<span class="property">status</span> == <span class="number">200</span>) {</span><br><span class="line"> result = <span class="variable constant_">XHR</span>.<span class="property">response</span>;</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(result);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 伪代码</span></span><br><span class="line"> <span class="keyword">var</span> url2 = <span class="string">'http:xxx.yyy.com/zzz?ddd='</span> + result.<span class="property">someParams</span>;</span><br><span class="line"> <span class="keyword">var</span> <span class="title class_">XHR2</span> = <span class="keyword">new</span> <span class="title class_">XMLHttpRequest</span>();</span><br><span class="line"> <span class="title class_">XHR2</span>.<span class="title function_">open</span>(<span class="string">'GET'</span>, url, <span class="literal">true</span>);</span><br><span class="line"> <span class="title class_">XHR2</span>.<span class="title function_">send</span>();</span><br><span class="line"> <span class="title class_">XHR2</span>.<span class="property">onreadystatechange</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> ...</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>当出现第三个 ajax (甚至更多)仍然依赖上一个请求时,我们的代码就变成了一场灾难。这场灾难,往往也被称为 <span class='p blue'>回调地狱</span> 。</p>
<p>因此需要一个叫做 Promise 的东西,来解决这个问题。</p>
<p>当然,除了回调地狱之外,还有一个非常重要的需求:<span class='p blue'>为了代码更加具有可读性和可维护性,需要将数据请求与数据处理明确的区分开来</span> 。上面的写法,是完全没有区分开,当数据变得复杂时,也许连自己都无法轻松维护自己的代码了。这也是模块化过程中,必须要掌握的一个重要技能,请一定重视。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 一个简单的封装</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">want</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'这是你想要执行的代码'</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">fn</span>(<span class="params">want</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'这里表示执行了一大堆各种代码'</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 其它代码执行完毕,最后执行回调函数</span></span><br><span class="line"> want && <span class="title function_">want</span>();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">fn</span>(want);</span><br></pre></td></tr></table></figure>
<p>利用回调函数封装,是我们在初学 JavaScript 时常常会使用的技能。确保我们想要的代码压后执行,除了利用函数调用栈的执行顺序之外,还可以利用上一篇文章所述的队列机制。</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="keyword">function</span> <span class="title function_">want</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'这是你想要执行的代码'</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">fn</span>(<span class="params">want</span>) {</span><br><span class="line"> <span class="comment">// 将想要执行的代码放入队列中,根据事件循环的机制,我们就不用非得将它放到最后面了,由你自由选择</span></span><br><span class="line"> want && <span class="built_in">setTimeout</span>(want, <span class="number">0</span>);</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'这里表示执行了一大堆各种代码'</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">fn</span>(want);</span><br></pre></td></tr></table></figure>
<p>如果浏览器已经支持了原生的 Promise 对象,那么我们就知道,浏览器的js引擎里已经有了 Promise 队列,这样就可以利用 Promise 将任务放在它的队列中去。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">want</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'这是你想要执行的代码'</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">fn</span>(<span class="params">want</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'这里表示执行了一大堆各种代码'</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 返回Promise对象</span></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve, reject</span>) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> want == <span class="string">'function'</span>) {</span><br><span class="line"> <span class="title function_">resolve</span>(want);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="title function_">reject</span>(<span class="string">'TypeError: '</span>+ want +<span class="string">'不是一个函数'</span>)</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">fn</span>(want).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params">want</span>) {</span><br><span class="line"> <span class="title function_">want</span>();</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="title function_">fn</span>(<span class="string">'1234'</span>).<span class="title function_">catch</span>(<span class="keyword">function</span>(<span class="params">err</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(err);</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<p>看上去变得更加复杂了。可是代码变得更加健壮,处理了错误输入的情况。</p>
<h2 id="Promise"><a href="#Promise" class="headerlink" title="Promise"></a>Promise</h2><h3 id="Promise-的含义"><a href="#Promise-的含义" class="headerlink" title="Promise 的含义"></a>Promise 的含义</h3><p>Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了<code>Promise</code>对象。</p>
<p>所谓<code>Promise</code>,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。</p>
<p><code>Promise</code>对象有以下两个特点。</p>
<ol>
<li>对象的状态不受外界影响。<code>Promise</code>对象代表一个异步操作,有三种状态:<code>pending</code>(进行中)、<code>fulfilled</code>(已成功)和<code>rejected</code>(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是<code>Promise</code>这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。</li>
<li>一旦状态改变,就不会再变,任何时候都可以得到这个结果。<code>Promise</code>对象的状态改变,只有两种可能:从<code>pending</code>变为<code>fulfilled</code>和从<code>pending</code>变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。</li>
</ol>
<blockquote>
<p>注意,为了行文方便,本章后面的<code>resolved</code>统一只指<code>fulfilled</code>状态,不包含<code>rejected</code>状态。</p>
</blockquote>
<p>有了<code>Promise</code>对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,<code>Promise</code>对象提供统一的接口,使得控制异步操作更加容易。</p>
<p><code>Promise</code>也有一些缺点。首先,无法取消<code>Promise</code>,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,<code>Promise</code>内部抛出的错误,不会反应到外部。第三,当处于<code>pending</code>状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。</p>
<p>如果某些事件不断地反复发生,一般来说,使用 Stream 模式是比部署Promise更好的选择。</p>
<h3 id="基本用法"><a href="#基本用法" class="headerlink" title="基本用法"></a>基本用法</h3><p>ES6 规定,<code>Promise</code>对象是一个构造函数,用来生成<code>Promise</code>实例。</p>
<p>下面代码创造了一个<code>Promise</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> promise = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve, reject</span>) {</span><br><span class="line"> <span class="comment">// ... some code</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="comment">/* 异步操作成功 */</span>){</span><br><span class="line"> <span class="title function_">resolve</span>(value);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="title function_">reject</span>(error);</span><br><span class="line"> }</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<p><code>Promise</code>构造函数接受一个函数作为参数,该函数的两个参数分别是<code>resolve</code>和<code>reject</code>。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。</p>
<p><code>resolve</code>函数的作用是,将<code>Promise</code>对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;<code>reject</code>函数的作用是,将<code>Promise</code>对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。</p>
<p><code>Promise</code>实例生成以后,可以用<code>then</code>方法分别指定<code>resolved</code>状态和<code>rejected</code>状态的回调函数。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">promise.<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params">value</span>) {</span><br><span class="line"> <span class="comment">// success</span></span><br><span class="line">}, <span class="keyword">function</span>(<span class="params">error</span>) {</span><br><span class="line"> <span class="comment">// failure</span></span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<p><code>then</code>方法可以接受两个回调函数作为参数。第一个回调函数是<code>Promise</code>对象的状态变为<code>resolved</code>时调用,第二个回调函数是<code>Promise</code>对象的状态变为<code>rejected</code>时调用。这两个函数都是可选的,不一定要提供。它们都接受<code>Promise</code>对象传出的值作为参数。</p>
<p>下面是一个<code>Promise</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">timeout</span>(<span class="params">ms</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> <span class="built_in">setTimeout</span>(resolve, ms, <span class="string">'done'</span>);</span><br><span class="line"> });</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">timeout</span>(<span class="number">100</span>).<span class="title function_">then</span>(<span class="function">(<span class="params">value</span>) =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(value);</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>timeout</code>方法返回一个<code>Promise</code>实例,表示一段时间以后才会发生的结果。过了指定的时间(<code>ms</code>参数)以后,<code>Promise</code>实例的状态变为<code>resolved</code>,就会触发<code>then</code>方法绑定的回调函数。</p>
<p>Promise 新建后就会立即执行。</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="keyword">let</span> promise = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve, reject</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Promise'</span>);</span><br><span class="line"> <span class="title function_">resolve</span>();</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">promise.<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'resolved.'</span>);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Hi!'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Promise</span></span><br><span class="line"><span class="comment">// Hi!</span></span><br><span class="line"><span class="comment">// resolved</span></span><br></pre></td></tr></table></figure>
<p>上面代码中,Promise 新建后立即执行,所以首先输出的是<code>Promise</code>。然后,<code>then</code>方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以<code>resolved</code>最后输出。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">loadImageAsync</span>(<span class="params">url</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve, reject</span>) {</span><br><span class="line"> <span class="keyword">const</span> image = <span class="keyword">new</span> <span class="title class_">Image</span>();</span><br><span class="line"></span><br><span class="line"> image.<span class="property">onload</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="title function_">resolve</span>(image);</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> image.<span class="property">onerror</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="title function_">reject</span>(<span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">'Could not load image at '</span> + url));</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> image.<span class="property">src</span> = url;</span><br><span class="line"> });</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面代码中,使用<code>Promise</code>包装了一个图片加载的异步操作。如果加载成功,就调用<code>resolve</code>方法,否则就调用<code>reject</code>方法。</p>
<p>下面是一个用<code>Promise</code>对象实现的 Ajax 操作的例子。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> getJSON = <span class="keyword">function</span>(<span class="params">url</span>) {</span><br><span class="line"> <span class="keyword">const</span> promise = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve, reject</span>){</span><br><span class="line"> <span class="keyword">const</span> handler = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">readyState</span> !== <span class="number">4</span>) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">status</span> === <span class="number">200</span>) {</span><br><span class="line"> <span class="title function_">resolve</span>(<span class="variable language_">this</span>.<span class="property">response</span>);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="title function_">reject</span>(<span class="keyword">new</span> <span class="title class_">Error</span>(<span class="variable language_">this</span>.<span class="property">statusText</span>));</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">const</span> client = <span class="keyword">new</span> <span class="title class_">XMLHttpRequest</span>();</span><br><span class="line"> client.<span class="title function_">open</span>(<span class="string">"GET"</span>, url);</span><br><span class="line"> client.<span class="property">onreadystatechange</span> = handler;</span><br><span class="line"> client.<span class="property">responseType</span> = <span class="string">"json"</span>;</span><br><span class="line"> client.<span class="title function_">setRequestHeader</span>(<span class="string">"Accept"</span>, <span class="string">"application/json"</span>);</span><br><span class="line"> client.<span class="title function_">send</span>();</span><br><span class="line"></span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> promise;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="title function_">getJSON</span>(<span class="string">"/posts.json"</span>).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params">json</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Contents: '</span> + json);</span><br><span class="line">}, <span class="keyword">function</span>(<span class="params">error</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">'出错了'</span>, error);</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>getJSON</code>是对 XMLHttpRequest 对象的封装,用于发出一个针对 JSON 数据的 HTTP 请求,并且返回一个<code>Promise</code>对象。需要注意的是,在<code>getJSON</code>内部,<code>resolve</code>函数和<code>reject</code>函数调用时,都带有参数。</p>
<p>如果调用<code>resolve</code>函数和<code>reject</code>函数时带有参数,那么它们的参数会被传递给回调函数。<code>reject</code>函数的参数通常是<code>Error</code>对象的实例,表示抛出的错误;<code>resolve</code>函数的参数除了正常的值以外,还可能是另一个 Promise 实例,比如像下面这样。</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> p1 = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span> (<span class="params">resolve, reject</span>) {</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> p2 = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span> (<span class="params">resolve, reject</span>) {</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line"> <span class="title function_">resolve</span>(p1);</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>p1</code>和<code>p2</code>都是 Promise 的实例,但是<code>p2</code>的<code>resolve</code>方法将<code>p1</code>作为参数,即一个异步操作的结果是返回另一个异步操作。</p>
<p>注意,这时<code>p1</code>的状态就会传递给<code>p2</code>,也就是说,<code>p1</code>的状态决定了<code>p2</code>的状态。如果<code>p1</code>的状态是<code>pending</code>,那么<code>p2</code>的回调函数就会等待<code>p1</code>的状态改变;如果<code>p1</code>的状态已经是<code>resolved</code>或者<code>rejected</code>,那么<code>p2</code>的回调函数将会立刻执行。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> p1 = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span> (<span class="params">resolve, reject</span>) {</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="function">() =></span> <span class="title function_">reject</span>(<span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">'fail'</span>)), <span class="number">3000</span>)</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> p2 = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span> (<span class="params">resolve, reject</span>) {</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="function">() =></span> <span class="title function_">resolve</span>(p1), <span class="number">1000</span>)</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">p2</span><br><span class="line"> .<span class="title function_">then</span>(<span class="function"><span class="params">result</span> =></span> <span class="variable language_">console</span>.<span class="title function_">log</span>(result))</span><br><span class="line"> .<span class="title function_">catch</span>(<span class="function"><span class="params">error</span> =></span> <span class="variable language_">console</span>.<span class="title function_">log</span>(error))</span><br><span class="line"><span class="comment">// Error: fail</span></span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>p1</code>是一个 Promise,3 秒之后变为<code>rejected</code>。<code>p2</code>的状态在 1 秒之后改变,<code>resolve</code>方法返回的是<code>p1</code>。由于<code>p2</code>返回的是另一个 Promise,导致<code>p2</code>自己的状态无效了,由<code>p1</code>的状态决定<code>p2</code>的状态。所以,后面的then语句都变成针对后者(<code>p1</code>)。又过了 2 秒,<code>p1</code>变为<code>rejected</code>,导致触发<code>catch</code>方法指定的回调函数。</p>
<blockquote>
<p>注意,调用<code>resolve</code>或<code>reject</code>并不会终结 Promise 的参数函数的执行。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> <span class="title function_">resolve</span>(<span class="number">1</span>);</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">2</span>);</span><br><span class="line">}).<span class="title function_">then</span>(<span class="function"><span class="params">r</span> =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(r);</span><br><span class="line">});</span><br><span class="line"><span class="comment">// 2</span></span><br><span class="line"><span class="comment">// 1</span></span><br></pre></td></tr></table></figure>
<p>上面代码中,调用<code>resolve(1)</code>以后,后面的<code>console.log(2)</code>还是会执行,并且会首先打印出来。这是因为立即 resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。</p>
<p>一般来说,调用<code>resolve</code>或<code>reject</code>以后,Promise 的使命就完成了,后继操作应该放到<code>then</code>方法里面,而不应该直接写在<code>resolve</code>或<code>reject</code>的后面。所以,最好在它们前面加上<code>return</code>语句,这样就不会有意外。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">resolve</span>(<span class="number">1</span>);</span><br><span class="line"> <span class="comment">// 后面的语句不会执行</span></span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">2</span>);</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<h3 id="Promise-prototype-then"><a href="#Promise-prototype-then" class="headerlink" title="Promise.prototype.then()"></a>Promise.prototype.then()</h3><p>Promise 实例具有<code>then</code>方法,也就是说,<code>then</code>方法是定义在原型对象<code>Promise.prototype</code>上的。它的作用是为 Promise 实例添加状态改变时的回调函数。前面说过,<code>then</code>方法的第一个参数是<code>resolved</code>状态的回调函数,第二个参数是<code>rejected</code>状态的回调函数,它们都是可选的。</p>
<p>then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即<code>then</code>方法后面再调用另一个<code>then</code>方法。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">getJSON</span>(<span class="string">"/posts.json"</span>).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params">json</span>) {</span><br><span class="line"> <span class="keyword">return</span> json.<span class="property">post</span>;</span><br><span class="line">}).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params">post</span>) {</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<p>上面的代码使用<code>then</code>方法,依次指定了两个回调函数。第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。</p>
<p>采用链式的<code>then</code>,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个<code>Promise</code>对象(即有异步操作),这时后一个回调函数,就会等待该<code>Promise</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="title function_">getJSON</span>(<span class="string">"/post/1.json"</span>).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params">post</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">getJSON</span>(post.<span class="property">commentURL</span>);</span><br><span class="line">}).<span class="title function_">then</span>(<span class="keyword">function</span> (<span class="params">comments</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"resolved: "</span>, comments);</span><br><span class="line">}, <span class="keyword">function</span> (<span class="params">err</span>){</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"rejected: "</span>, err);</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<p>上面代码中,第一个<code>then</code>方法指定的回调函数,返回的是另一个<code>Promise</code>对象。这时,第二个then方法指定的回调函数,就会等待这个新的<code>Promise</code>对象状态发生变化。如果变为<code>resolved</code>,就调用第一个回调函数,如果状态变为<code>rejected</code>,就调用第二个回调函数。</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="title function_">getJSON</span>(<span class="string">"/post/1.json"</span>).<span class="title function_">then</span>(</span><br><span class="line"> <span class="function"><span class="params">post</span> =></span> <span class="title function_">getJSON</span>(post.<span class="property">commentURL</span>)</span><br><span class="line">).<span class="title function_">then</span>(</span><br><span class="line"> <span class="function"><span class="params">comments</span> =></span> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"resolved: "</span>, comments),</span><br><span class="line"> <span class="function"><span class="params">err</span> =></span> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"rejected: "</span>, err)</span><br><span class="line">);</span><br></pre></td></tr></table></figure>
<h3 id="Promise-prototype-catch"><a href="#Promise-prototype-catch" class="headerlink" title="Promise.prototype.catch()"></a>Promise.prototype.catch()</h3><p><code>Promise.prototype.catch()</code>方法是<code>.then(null, rejection)</code>或<code>.then(undefined, rejection)</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></pre></td><td class="code"><pre><span class="line"><span class="title function_">getJSON</span>(<span class="string">'/posts.json'</span>).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params">posts</span>) {</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}).<span class="title function_">catch</span>(<span class="keyword">function</span>(<span class="params">error</span>) {</span><br><span class="line"> <span class="comment">// 处理 getJSON 和 前一个回调函数运行时发生的错误</span></span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'发生错误!'</span>, error);</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>getJSON()</code>方法返回一个 Promise 对象,如果该对象状态变为<code>resolved</code>,则会调用<code>then()</code>方法指定的回调函数;如果异步操作抛出错误,状态就会变为<code>rejected</code>,就会调用<code>catch()</code>方法指定的回调函数,处理这个错误。另外,<code>then()</code>方法指定的回调函数,如果运行中抛出错误,也会被<code>catch()</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></pre></td><td class="code"><pre><span class="line">p.<span class="title function_">then</span>(<span class="function">(<span class="params">val</span>) =></span> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'fulfilled:'</span>, val))</span><br><span class="line"> .<span class="title function_">catch</span>(<span class="function">(<span class="params">err</span>) =></span> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'rejected'</span>, err));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 等同于</span></span><br><span class="line">p.<span class="title function_">then</span>(<span class="function">(<span class="params">val</span>) =></span> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'fulfilled:'</span>, val))</span><br><span class="line"> .<span class="title function_">then</span>(<span class="literal">null</span>, <span class="function">(<span class="params">err</span>) =></span> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"rejected:"</span>, err));</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> promise = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve, reject</span>) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">'test'</span>);</span><br><span class="line">});</span><br><span class="line">promise.<span class="title function_">catch</span>(<span class="keyword">function</span>(<span class="params">error</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(error);</span><br><span class="line">});</span><br><span class="line"><span class="comment">// Error: test</span></span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>promise</code>抛出一个错误,就被<code>catch()</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 写法一</span></span><br><span class="line"><span class="keyword">const</span> promise = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve, reject</span>) {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">'test'</span>);</span><br><span class="line"> } <span class="keyword">catch</span>(e) {</span><br><span class="line"> <span class="title function_">reject</span>(e);</span><br><span class="line"> }</span><br><span class="line">});</span><br><span class="line">promise.<span class="title function_">catch</span>(<span class="keyword">function</span>(<span class="params">error</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(error);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="comment">// 写法二</span></span><br><span class="line"><span class="keyword">const</span> promise = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve, reject</span>) {</span><br><span class="line"> <span class="title function_">reject</span>(<span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">'test'</span>));</span><br><span class="line">});</span><br><span class="line">promise.<span class="title function_">catch</span>(<span class="keyword">function</span>(<span class="params">error</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(error);</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<p>比较上面两种写法,可以发现<code>reject()</code>方法的作用,等同于抛出错误。</p>
<p>如果 Promise 状态已经变成<code>resolved</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="keyword">const</span> promise = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve, reject</span>) {</span><br><span class="line"> <span class="title function_">resolve</span>(<span class="string">'ok'</span>);</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">'test'</span>);</span><br><span class="line">});</span><br><span class="line">promise</span><br><span class="line"> .<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params">value</span>) { <span class="variable language_">console</span>.<span class="title function_">log</span>(value) })</span><br><span class="line"> .<span class="title function_">catch</span>(<span class="keyword">function</span>(<span class="params">error</span>) { <span class="variable language_">console</span>.<span class="title function_">log</span>(error) });</span><br><span class="line"><span class="comment">// ok</span></span><br></pre></td></tr></table></figure>
<p>上面代码中,Promise 在<code>resolve</code>语句后面,再抛出错误,不会被捕获,等于没有抛出。因为 Promise 的状态一旦改变,就永久保持该状态,不会再变了。</p>
<p>Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个<code>catch</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="title function_">getJSON</span>(<span class="string">'/post/1.json'</span>).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params">post</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">getJSON</span>(post.<span class="property">commentURL</span>);</span><br><span class="line">}).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params">comments</span>) {</span><br><span class="line"> <span class="comment">// some code</span></span><br><span class="line">}).<span class="title function_">catch</span>(<span class="keyword">function</span>(<span class="params">error</span>) {</span><br><span class="line"> <span class="comment">// 处理前面三个Promise产生的错误</span></span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<p>上面代码中,一共有三个 Promise 对象:一个由<code>getJSON()</code>产生,两个由<code>then()</code>产生。它们之中任何一个抛出的错误,都会被最后一个<code>catch()</code>捕获。</p>
<p>一般来说,不要在<code>then()</code>方法里面定义 Reject 状态的回调函数(即<code>then</code>的第二个参数),总是使用<code>catch</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// bad</span></span><br><span class="line">promise</span><br><span class="line"> .<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params">data</span>) {</span><br><span class="line"> <span class="comment">// success</span></span><br><span class="line"> }, <span class="keyword">function</span>(<span class="params">err</span>) {</span><br><span class="line"> <span class="comment">// error</span></span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"><span class="comment">// good</span></span><br><span class="line">promise</span><br><span class="line"> .<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params">data</span>) { <span class="comment">//cb</span></span><br><span class="line"> <span class="comment">// success</span></span><br><span class="line"> })</span><br><span class="line"> .<span class="title function_">catch</span>(<span class="keyword">function</span>(<span class="params">err</span>) {</span><br><span class="line"> <span class="comment">// error</span></span><br><span class="line"> });</span><br></pre></td></tr></table></figure>
<p>上面代码中,第二种写法要好于第一种写法,理由是第二种写法可以捕获前面<code>then</code>方法执行中的错误,也更接近同步的写法(<code>try/catch</code>)。因此,建议总是使用<code>catch()</code>方法,而不使用<code>then()</code>方法的第二个参数。</p>
<p>跟传统的<code>try/catch</code>代码块不同的是,如果没有使用<code>catch()</code>方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应。</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="keyword">const</span> someAsyncThing = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve, reject</span>) {</span><br><span class="line"> <span class="comment">// 下面一行会报错,因为x没有声明</span></span><br><span class="line"> <span class="title function_">resolve</span>(x + <span class="number">2</span>);</span><br><span class="line"> });</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="title function_">someAsyncThing</span>().<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'everything is great'</span>);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="function">() =></span> { <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">123</span>) }, <span class="number">2000</span>);</span><br><span class="line"><span class="comment">// Uncaught (in promise) ReferenceError: x is not defined</span></span><br><span class="line"><span class="comment">// 123</span></span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>someAsyncThing()</code>函数产生的 Promise 对象,内部有语法错误。浏览器运行到这一行,会打印出错误提示<code>ReferenceError: x is not defined</code>,但是不会退出进程、终止脚本执行,2 秒之后还是会输出<code>123</code>。这就是说,Promise 内部的错误不会影响到 Promise 外部的代码,通俗的说法就是“Promise 会吃掉错误”。</p>
<p>这个脚本放在服务器执行,退出码就是0(即表示执行成功)。不过,Node.js 有一个<code>unhandledRejection</code>事件,专门监听未捕获的<code>reject</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">process.<span class="title function_">on</span>(<span class="string">'unhandledRejection'</span>, <span class="keyword">function</span> (<span class="params">err, p</span>) {</span><br><span class="line"> <span class="keyword">throw</span> err;</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>unhandledRejection</code>事件的监听函数有两个参数,第一个是错误对象,第二个是报错的 Promise 实例,它可以用来了解发生错误的环境信息。</p>
<blockquote>
<p>注意,Node 有计划在未来废除<code>unhandledRejection</code>事件。如果 Promise 内部有未捕获的错误,会直接终止进程,并且进程的退出码不为 0。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> promise = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span> (<span class="params">resolve, reject</span>) {</span><br><span class="line"> <span class="title function_">resolve</span>(<span class="string">'ok'</span>);</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) { <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">'test'</span>) }, <span class="number">0</span>)</span><br><span class="line">});</span><br><span class="line">promise.<span class="title function_">then</span>(<span class="keyword">function</span> (<span class="params">value</span>) { <span class="variable language_">console</span>.<span class="title function_">log</span>(value) });</span><br><span class="line"><span class="comment">// ok</span></span><br><span class="line"><span class="comment">// Uncaught Error: test</span></span><br></pre></td></tr></table></figure>
<p>上面代码中,Promise 指定在下一轮“事件循环”再抛出错误。到了那个时候,Promise 的运行已经结束了,所以这个错误是在 Promise 函数体外抛出的,会冒泡到最外层,成了未捕获的错误。</p>
<p>一般总是建议,Promise 对象后面要跟<code>catch()</code>方法,这样可以处理 Promise 内部发生的错误。<code>catch()</code>方法返回的还是一个 Promise 对象,因此后面还可以接着调用<code>then()</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> someAsyncThing = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve, reject</span>) {</span><br><span class="line"> <span class="comment">// 下面一行会报错,因为x没有声明</span></span><br><span class="line"> <span class="title function_">resolve</span>(x + <span class="number">2</span>);</span><br><span class="line"> });</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="title function_">someAsyncThing</span>()</span><br><span class="line">.<span class="title function_">catch</span>(<span class="keyword">function</span>(<span class="params">error</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'oh no'</span>, error);</span><br><span class="line">})</span><br><span class="line">.<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'carry on'</span>);</span><br><span class="line">});</span><br><span class="line"><span class="comment">// oh no [ReferenceError: x is not defined]</span></span><br><span class="line"><span class="comment">// carry on</span></span><br></pre></td></tr></table></figure>
<p>上面代码运行完<code>catch()</code>方法指定的回调函数,会接着运行后面那个<code>then()</code>方法指定的回调函数。如果没有报错,则会跳过<code>catch()</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="title class_">Promise</span>.<span class="title function_">resolve</span>()</span><br><span class="line">.<span class="title function_">catch</span>(<span class="keyword">function</span>(<span class="params">error</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'oh no'</span>, error);</span><br><span class="line">})</span><br><span class="line">.<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'carry on'</span>);</span><br><span class="line">});</span><br><span class="line"><span class="comment">// carry on</span></span><br></pre></td></tr></table></figure>
<p>上面的代码因为没有报错,跳过了<code>catch()</code>方法,直接执行后面的<code>then()</code>方法。此时,要是<code>then()</code>方法里面报错,就与前面的<code>catch()</code>无关了。</p>
<p><code>catch()</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> someAsyncThing = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve, reject</span>) {</span><br><span class="line"> <span class="comment">// 下面一行会报错,因为x没有声明</span></span><br><span class="line"> <span class="title function_">resolve</span>(x + <span class="number">2</span>);</span><br><span class="line"> });</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="title function_">someAsyncThing</span>().<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">someOtherAsyncThing</span>();</span><br><span class="line">}).<span class="title function_">catch</span>(<span class="keyword">function</span>(<span class="params">error</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'oh no'</span>, error);</span><br><span class="line"> <span class="comment">// 下面一行会报错,因为 y 没有声明</span></span><br><span class="line"> y + <span class="number">2</span>;</span><br><span class="line">}).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'carry on'</span>);</span><br><span class="line">});</span><br><span class="line"><span class="comment">// oh no [ReferenceError: x is not defined]</span></span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>catch()</code>方法抛出一个错误,因为后面没有别的<code>catch()</code>方法了,导致这个错误不会被捕获,也不会传递到外层。如果改写一下,结果就不一样了。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">someAsyncThing</span>().<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">someOtherAsyncThing</span>();</span><br><span class="line">}).<span class="title function_">catch</span>(<span class="keyword">function</span>(<span class="params">error</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'oh no'</span>, error);</span><br><span class="line"> <span class="comment">// 下面一行会报错,因为y没有声明</span></span><br><span class="line"> y + <span class="number">2</span>;</span><br><span class="line">}).<span class="title function_">catch</span>(<span class="keyword">function</span>(<span class="params">error</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'carry on'</span>, error);</span><br><span class="line">});</span><br><span class="line"><span class="comment">// oh no [ReferenceError: x is not defined]</span></span><br><span class="line"><span class="comment">// carry on [ReferenceError: y is not defined]</span></span><br></pre></td></tr></table></figure>
<p>上面代码中,第二个<code>catch()</code>方法用来捕获前一个<code>catch()</code>方法抛出的错误。</p>
<h3 id="Promise-prototype-finally"><a href="#Promise-prototype-finally" class="headerlink" title="Promise.prototype.finally()"></a>Promise.prototype.finally()</h3><p><code>finally()</code>方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。</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">promise</span><br><span class="line">.<span class="title function_">then</span>(<span class="function"><span class="params">result</span> =></span> {···})</span><br><span class="line">.<span class="title function_">catch</span>(<span class="function"><span class="params">error</span> =></span> {···})</span><br><span class="line">.<span class="title function_">finally</span>(<span class="function">() =></span> {···});</span><br></pre></td></tr></table></figure>
<p>上面代码中,不管<code>promise</code>最后的状态,在执行完<code>then</code>或<code>catch</code>指定的回调函数以后,都会执行<code>finally</code>方法指定的回调函数。</p>
<p>下面是一个例子,服务器使用 Promise 处理请求,然后使用<code>finally</code>方法关掉服务器。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">server.<span class="title function_">listen</span>(port)</span><br><span class="line"> .<span class="title function_">then</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line"> })</span><br><span class="line"> .<span class="title function_">finally</span>(server.<span class="property">stop</span>);</span><br></pre></td></tr></table></figure>
<p><code>finally</code>方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是<code>fulfilled</code>还是<code>rejected</code>。这表明,<code>finally</code>方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。</p>
<p><code>finally</code>本质上是<code>then</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></pre></td><td class="code"><pre><span class="line">promise</span><br><span class="line">.<span class="title function_">finally</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="comment">// 语句</span></span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="comment">// 等同于</span></span><br><span class="line">promise</span><br><span class="line">.<span class="title function_">then</span>(</span><br><span class="line"> <span class="function"><span class="params">result</span> =></span> {</span><br><span class="line"> <span class="comment">// 语句</span></span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> },</span><br><span class="line"> <span class="function"><span class="params">error</span> =></span> {</span><br><span class="line"> <span class="comment">// 语句</span></span><br><span class="line"> <span class="keyword">throw</span> error;</span><br><span class="line"> }</span><br><span class="line">);</span><br></pre></td></tr></table></figure>
<p>上面代码中,如果不使用<code>finally</code>方法,同样的语句需要为成功和失败两种情况各写一次。有了<code>finally</code>方法,则只需要写一次。</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></pre></td><td class="code"><pre><span class="line"><span class="title class_">Promise</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">finally</span> = <span class="keyword">function</span> (<span class="params">callback</span>) {</span><br><span class="line"> <span class="keyword">let</span> P = <span class="variable language_">this</span>.<span class="property">constructor</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="title function_">then</span>(</span><br><span class="line"> <span class="function"><span class="params">value</span> =></span> P.<span class="title function_">resolve</span>(<span class="title function_">callback</span>()).<span class="title function_">then</span>(<span class="function">() =></span> value),</span><br><span class="line"> <span class="function"><span class="params">reason</span> =></span> P.<span class="title function_">resolve</span>(<span class="title function_">callback</span>()).<span class="title function_">then</span>(<span class="function">() =></span> { <span class="keyword">throw</span> reason })</span><br><span class="line"> );</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<p>上面代码中,不管前面的 Promise 是<code>fulfilled</code>还是<code>rejected</code>,都会执行回调函数<code>callback</code>。</p>
<p>从上面的实现还可以看到,<code>finally</code>方法总是会返回原来的值。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// resolve 的值是 undefined</span></span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">resolve</span>(<span class="number">2</span>).<span class="title function_">then</span>(<span class="function">() =></span> {}, <span class="function">() =></span> {})</span><br><span class="line"></span><br><span class="line"><span class="comment">// resolve 的值是 2</span></span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">resolve</span>(<span class="number">2</span>).<span class="title function_">finally</span>(<span class="function">() =></span> {})</span><br><span class="line"></span><br><span class="line"><span class="comment">// reject 的值是 undefined</span></span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">reject</span>(<span class="number">3</span>).<span class="title function_">then</span>(<span class="function">() =></span> {}, <span class="function">() =></span> {})</span><br><span class="line"></span><br><span class="line"><span class="comment">// reject 的值是 3</span></span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">reject</span>(<span class="number">3</span>).<span class="title function_">finally</span>(<span class="function">() =></span> {})</span><br></pre></td></tr></table></figure>
<h3 id="Promise-all"><a href="#Promise-all" class="headerlink" title="Promise.all()"></a>Promise.all()</h3><p><code>Promise.all()</code>方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。</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">const</span> p = <span class="title class_">Promise</span>.<span class="title function_">all</span>([p1, p2, p3]);</span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>Promise.all()</code>方法接受一个数组作为参数,<code>p1</code>、<code>p2</code>、<code>p3</code>都是 Promise 实例,如果不是,就会先调用下面讲到的<code>Promise.resolve</code>方法,将参数转为 Promise 实例,再进一步处理。另外,<code>Promise.all()</code>方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。</p>
<p><code>p</code>的状态由<code>p1</code>、<code>p2</code>、<code>p3</code>决定,分成两种情况。</p>
<ol>
<li><p>只有<code>p1</code>、<code>p2</code>、<code>p3</code>的状态都变成<code>fulfilled</code>,<code>p</code>的状态才会变成<code>fulfilled</code>,此时<code>p1</code>、<code>p2</code>、<code>p3</code>的返回值组成一个数组,传递给<code>p</code>的回调函数。</p>
</li>
<li><p>只要<code>p1</code>、<code>p2</code>、<code>p3</code>之中有一个被<code>rejected</code>,<code>p</code>的状态就变成<code>rejected</code>,此时第一个被<code>reject</code>的实例的返回值,会传递给<code>p</code>的回调函数。</p>
</li>
</ol>
<p>下面是一个具体的例子。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 生成一个Promise对象的数组</span></span><br><span class="line"><span class="keyword">const</span> promises = [<span class="number">2</span>, <span class="number">3</span>, <span class="number">5</span>, <span class="number">7</span>, <span class="number">11</span>, <span class="number">13</span>].<span class="title function_">map</span>(<span class="keyword">function</span> (<span class="params">id</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">getJSON</span>(<span class="string">'/post/'</span> + id + <span class="string">".json"</span>);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">all</span>(promises).<span class="title function_">then</span>(<span class="keyword">function</span> (<span class="params">posts</span>) {</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}).<span class="title function_">catch</span>(<span class="keyword">function</span>(<span class="params">reason</span>){</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>promises</code>是包含 6 个 Promise 实例的数组,只有这 6 个实例的状态都变成<code>fulfilled</code>,或者其中有一个变为<code>rejected</code>,才会调用<code>Promise.all</code>方法后面的回调函数。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> databasePromise = <span class="title function_">connectDatabase</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> booksPromise = databasePromise</span><br><span class="line"> .<span class="title function_">then</span>(findAllBooks);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> userPromise = databasePromise</span><br><span class="line"> .<span class="title function_">then</span>(getCurrentUser);</span><br><span class="line"></span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">all</span>([</span><br><span class="line"> booksPromise,</span><br><span class="line"> userPromise</span><br><span class="line">])</span><br><span class="line">.<span class="title function_">then</span>(<span class="function">(<span class="params">[books, user]</span>) =></span> <span class="title function_">pickTopRecommendations</span>(books, user));</span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>booksPromise</code>和<code>userPromise</code>是两个异步操作,只有等到它们的结果都返回了,才会触发<code>pickTopRecommendations</code>这个回调函数。</p>
<p>注意,如果作为参数的 Promise 实例,自己定义了<code>catch</code>方法,那么它一旦被<code>rejected</code>,并不会触发<code>Promise.all()</code>的<code>catch</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> p1 = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> <span class="title function_">resolve</span>(<span class="string">'hello'</span>);</span><br><span class="line">})</span><br><span class="line">.<span class="title function_">then</span>(<span class="function"><span class="params">result</span> =></span> result)</span><br><span class="line">.<span class="title function_">catch</span>(<span class="function"><span class="params">e</span> =></span> e);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> p2 = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">'报错了'</span>);</span><br><span class="line">})</span><br><span class="line">.<span class="title function_">then</span>(<span class="function"><span class="params">result</span> =></span> result)</span><br><span class="line">.<span class="title function_">catch</span>(<span class="function"><span class="params">e</span> =></span> e);</span><br><span class="line"></span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">all</span>([p1, p2])</span><br><span class="line">.<span class="title function_">then</span>(<span class="function"><span class="params">result</span> =></span> <span class="variable language_">console</span>.<span class="title function_">log</span>(result))</span><br><span class="line">.<span class="title function_">catch</span>(<span class="function"><span class="params">e</span> =></span> <span class="variable language_">console</span>.<span class="title function_">log</span>(e));</span><br><span class="line"><span class="comment">// ["hello", Error: 报错了]</span></span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>p1</code>会<code>resolved</code>,<code>p2</code>首先会<code>rejected</code>,但是<code>p</code>2有自己的<code>catch</code>方法,该方法返回的是一个新的 Promise 实例,p2指向的实际上是这个实例。该实例执行完<code>catch</code>方法后,也会变成<code>resolved</code>,导致<code>Promise.all()</code>方法参数里面的两个实例都会<code>resolved</code>,因此会调用<code>then</code>方法指定的回调函数,而不会调用<code>catch</code>方法指定的回调函数。</p>
<p>如果<code>p2</code>没有自己的<code>catch</code>方法,就会调用<code>Promise.all()</code>的<code>catch</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"><span class="keyword">const</span> p1 = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> <span class="title function_">resolve</span>(<span class="string">'hello'</span>);</span><br><span class="line">})</span><br><span class="line">.<span class="title function_">then</span>(<span class="function"><span class="params">result</span> =></span> result);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> p2 = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">'报错了'</span>);</span><br><span class="line">})</span><br><span class="line">.<span class="title function_">then</span>(<span class="function"><span class="params">result</span> =></span> result);</span><br><span class="line"></span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">all</span>([p1, p2])</span><br><span class="line">.<span class="title function_">then</span>(<span class="function"><span class="params">result</span> =></span> <span class="variable language_">console</span>.<span class="title function_">log</span>(result))</span><br><span class="line">.<span class="title function_">catch</span>(<span class="function"><span class="params">e</span> =></span> <span class="variable language_">console</span>.<span class="title function_">log</span>(e));</span><br><span class="line"><span class="comment">// Error: 报错了</span></span><br></pre></td></tr></table></figure>
<h3 id="Promise-race"><a href="#Promise-race" class="headerlink" title="Promise.race()"></a>Promise.race()</h3><p><code>Promise.race()</code>方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。</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">const</span> p = <span class="title class_">Promise</span>.<span class="title function_">race</span>([p1, p2, p3]);</span><br></pre></td></tr></table></figure>
<p>上面代码中,只要<code>p1</code>、<code>p2</code>、<code>p3</code>之中有一个实例率先改变状态,<code>p</code>的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给<code>p</code>的回调函数。</p>
<p><code>Promise.race()</code>方法的参数与<code>Promise.all()</code>方法一样,如果不是 Promise 实例,就会先调用下面讲到的<code>Promise.resolve()</code>方法,将参数转为 Promise 实例,再进一步处理。</p>
<p>下面是一个例子,如果指定时间内没有获得结果,就将 Promise 的状态变为<code>reject</code>,否则变为<code>resolve</code>。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> p = <span class="title class_">Promise</span>.<span class="title function_">race</span>([</span><br><span class="line"> <span class="title function_">fetch</span>(<span class="string">'/resource-that-may-take-a-while'</span>),</span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span> (<span class="params">resolve, reject</span>) {</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="function">() =></span> <span class="title function_">reject</span>(<span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">'request timeout'</span>)), <span class="number">5000</span>)</span><br><span class="line"> })</span><br><span class="line">]);</span><br><span class="line"></span><br><span class="line">p</span><br><span class="line">.<span class="title function_">then</span>(<span class="variable language_">console</span>.<span class="property">log</span>)</span><br><span class="line">.<span class="title function_">catch</span>(<span class="variable language_">console</span>.<span class="property">error</span>);</span><br></pre></td></tr></table></figure>
<p>上面代码中,如果 5 秒之内<code>fetch</code>方法无法返回结果,变量<code>p</code>的状态就会变为<code>rejected</code>,从而触发<code>catch</code>方法指定的回调函数。</p>
<h3 id="Promise-allSettled"><a href="#Promise-allSettled" class="headerlink" title="Promise.allSettled()"></a>Promise.allSettled()</h3><p>有时候,我们希望等到一组异步操作都结束了,不管每一个操作是成功还是失败,再进行下一步操作。但是,现有的 Promise 方法很难实现这个要求。</p>
<p><code>Promise.all()</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> urls = [url_1, url_2, url_3];</span><br><span class="line"><span class="keyword">const</span> requests = urls.<span class="title function_">map</span>(<span class="function"><span class="params">x</span> =></span> <span class="title function_">fetch</span>(x));</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">await</span> <span class="title class_">Promise</span>.<span class="title function_">all</span>(requests);</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'所有请求都成功。'</span>);</span><br><span class="line">} <span class="keyword">catch</span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'至少一个请求失败,其他请求可能还没结束。'</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面示例中,<code>Promise.all()</code>可以确定所有请求都成功了,但是只要有一个请求失败,它就会报错,而不管另外的请求是否结束。</p>
<p>为了解决这个问题,<a href="https://github.com/tc39/proposal-promise-allSettled">ES2020</a> 引入了<code>Promise.allSettled()</code>方法,用来确定一组异步操作是否都结束了(不管成功或失败)。所以,它的名字叫做”Settled“,包含了”fulfilled“和”rejected“两种情况。</p>
<p><code>Promise.allSettled()</code>方法接受一个数组作为参数,数组的每个成员都是一个 Promise 对象,并返回一个新的 Promise 对象。只有等到参数数组的所有 Promise 对象都发生状态变更(不管是<code>fulfilled</code>还是<code>rejected</code>),返回的 Promise 对象才会发生状态变更。</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> promises = [</span><br><span class="line"> <span class="title function_">fetch</span>(<span class="string">'/api-1'</span>),</span><br><span class="line"> <span class="title function_">fetch</span>(<span class="string">'/api-2'</span>),</span><br><span class="line"> <span class="title function_">fetch</span>(<span class="string">'/api-3'</span>),</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line"><span class="keyword">await</span> <span class="title class_">Promise</span>.<span class="title function_">allSettled</span>(promises);</span><br><span class="line"><span class="title function_">removeLoadingIndicator</span>();</span><br></pre></td></tr></table></figure>
<p>上面示例中,数组<code>promises</code>包含了三个请求,只有等到这三个请求都结束了(不管请求成功还是失败),<code>removeLoadingIndicator()</code>才会执行。</p>
<p>该方法返回的新的 Promise 实例,一旦发生状态变更,状态总是<code>fulfilled</code>,不会变成<code>rejected</code>。状态变成<code>fulfilled</code>后,它的回调函数会接收到一个数组作为参数,该数组的每个成员对应前面数组的每个 Promise 对象。</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> resolved = <span class="title class_">Promise</span>.<span class="title function_">resolve</span>(<span class="number">42</span>);</span><br><span class="line"><span class="keyword">const</span> rejected = <span class="title class_">Promise</span>.<span class="title function_">reject</span>(-<span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> allSettledPromise = <span class="title class_">Promise</span>.<span class="title function_">allSettled</span>([resolved, rejected]);</span><br><span class="line"></span><br><span class="line">allSettledPromise.<span class="title function_">then</span>(<span class="keyword">function</span> (<span class="params">results</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(results);</span><br><span class="line">});</span><br><span class="line"><span class="comment">// [</span></span><br><span class="line"><span class="comment">// { status: 'fulfilled', value: 42 },</span></span><br><span class="line"><span class="comment">// { status: 'rejected', reason: -1 }</span></span><br><span class="line"><span class="comment">// ]</span></span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>Promise.allSettled()</code>的返回值<code>allSettledPromise</code>,状态只可能变成<code>fulfilled</code>。它的回调函数接收到的参数是数组<code>results</code>。该数组的每个成员都是一个对象,对应传入<code>Promise.allSettled()</code>的数组里面的两个 Promise 对象。</p>
<p><code>results</code>的每个成员是一个对象,对象的格式是固定的,对应异步操作的结果。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 异步操作成功时</span></span><br><span class="line">{<span class="attr">status</span>: <span class="string">'fulfilled'</span>, <span class="attr">value</span>: value}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 异步操作失败时</span></span><br><span class="line">{<span class="attr">status</span>: <span class="string">'rejected'</span>, <span class="attr">reason</span>: reason}</span><br></pre></td></tr></table></figure>
<p>成员对象的<code>status</code>属性的值只可能是字符串<code>fulfilled</code>或字符串<code>rejected</code>,用来区分异步操作是成功还是失败。如果是成功(<code>fulfilled</code>),对象会有<code>value</code>属性,如果是失败(<code>rejected</code>),会有<code>reason</code>属性,对应两种状态时前面异步操作的返回值。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> promises = [ <span class="title function_">fetch</span>(<span class="string">'index.html'</span>), <span class="title function_">fetch</span>(<span class="string">'https://does-not-exist/'</span>) ];</span><br><span class="line"><span class="keyword">const</span> results = <span class="keyword">await</span> <span class="title class_">Promise</span>.<span class="title function_">allSettled</span>(promises);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 过滤出成功的请求</span></span><br><span class="line"><span class="keyword">const</span> successfulPromises = results.<span class="title function_">filter</span>(<span class="function"><span class="params">p</span> =></span> p.<span class="property">status</span> === <span class="string">'fulfilled'</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> errors = results</span><br><span class="line"> .<span class="title function_">filter</span>(<span class="function"><span class="params">p</span> =></span> p.<span class="property">status</span> === <span class="string">'rejected'</span>)</span><br><span class="line"> .<span class="title function_">map</span>(<span class="function"><span class="params">p</span> =></span> p.<span class="property">reason</span>);</span><br></pre></td></tr></table></figure>
<h3 id="Promise-any"><a href="#Promise-any" class="headerlink" title="Promise.any()"></a>Promise.any()</h3><p>ES2021 引入了<code>Promise.any()</code><a href="https://github.com/tc39/proposal-promise-any">方法</a>。该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。</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="title class_">Promise</span>.<span class="title function_">any</span>([</span><br><span class="line"> <span class="title function_">fetch</span>(<span class="string">'https://v8.dev/'</span>).<span class="title function_">then</span>(<span class="function">() =></span> <span class="string">'home'</span>),</span><br><span class="line"> <span class="title function_">fetch</span>(<span class="string">'https://v8.dev/blog'</span>).<span class="title function_">then</span>(<span class="function">() =></span> <span class="string">'blog'</span>),</span><br><span class="line"> <span class="title function_">fetch</span>(<span class="string">'https://v8.dev/docs'</span>).<span class="title function_">then</span>(<span class="function">() =></span> <span class="string">'docs'</span>)</span><br><span class="line">]).<span class="title function_">then</span>(<span class="function">(<span class="params">first</span>) =></span> { <span class="comment">// 只要有一个 fetch() 请求成功</span></span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(first);</span><br><span class="line">}).<span class="title function_">catch</span>(<span class="function">(<span class="params">error</span>) =></span> { <span class="comment">// 所有三个 fetch() 全部请求失败</span></span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(error);</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<p>只要参数实例有一个变成<code>fulfilled</code>状态,包装实例就会变成<code>fulfilled</code>状态;如果所有参数实例都变成<code>rejected</code>状态,包装实例就会变成<code>rejected</code>状态。</p>
<p><code>Promise.any()</code>跟<code>Promise.race()</code>方法很像,只有一点不同,就是<code>Promise.any()</code>不会因为某个 Promise 变成<code>rejected</code>状态而结束,必须等到所有参数 Promise 变成<code>rejected</code>状态才会结束。</p>
<p>下面是<code>Promise()</code>与<code>await</code>命令结合使用的例子。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> promises = [</span><br><span class="line"> <span class="title function_">fetch</span>(<span class="string">'/endpoint-a'</span>).<span class="title function_">then</span>(<span class="function">() =></span> <span class="string">'a'</span>),</span><br><span class="line"> <span class="title function_">fetch</span>(<span class="string">'/endpoint-b'</span>).<span class="title function_">then</span>(<span class="function">() =></span> <span class="string">'b'</span>),</span><br><span class="line"> <span class="title function_">fetch</span>(<span class="string">'/endpoint-c'</span>).<span class="title function_">then</span>(<span class="function">() =></span> <span class="string">'c'</span>),</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">const</span> first = <span class="keyword">await</span> <span class="title class_">Promise</span>.<span class="title function_">any</span>(promises);</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(first);</span><br><span class="line">} <span class="keyword">catch</span> (error) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(error);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>Promise.any()</code>方法的参数数组包含三个 Promise 操作。其中只要有一个变成<code>fulfilled</code>,<code>Promise.any()</code>返回的 Promise 对象就变成<code>fulfilled</code>。如果所有三个操作都变成<code>rejected</code>,那么<code>await</code>命令就会抛出错误。</p>
<p><code>Promise.any()</code>抛出的错误是一个 AggregateError 实例(详见《对象的扩展》一章),这个 AggregateError 实例对象的<code>errors</code>属性是一个数组,包含了所有成员的错误。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> resolved = <span class="title class_">Promise</span>.<span class="title function_">resolve</span>(<span class="number">42</span>);</span><br><span class="line"><span class="keyword">var</span> rejected = <span class="title class_">Promise</span>.<span class="title function_">reject</span>(-<span class="number">1</span>);</span><br><span class="line"><span class="keyword">var</span> alsoRejected = <span class="title class_">Promise</span>.<span class="title function_">reject</span>(<span class="title class_">Infinity</span>);</span><br><span class="line"></span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">any</span>([resolved, rejected, alsoRejected]).<span class="title function_">then</span>(<span class="keyword">function</span> (<span class="params">result</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(result); <span class="comment">// 42</span></span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">any</span>([rejected, alsoRejected]).<span class="title function_">catch</span>(<span class="keyword">function</span> (<span class="params">results</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(results <span class="keyword">instanceof</span> <span class="title class_">AggregateError</span>); <span class="comment">// true</span></span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(results.<span class="property">errors</span>); <span class="comment">// [-1, Infinity]</span></span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<h3 id="Promise-resolve"><a href="#Promise-resolve" class="headerlink" title="Promise.resolve()"></a>Promise.resolve()</h3><p>有时需要将现有对象转为 Promise 对象,<code>Promise.resolve()</code>方法就起到这个作用。</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">const</span> jsPromise = <span class="title class_">Promise</span>.<span class="title function_">resolve</span>($.<span class="title function_">ajax</span>(<span class="string">'/whatever.json'</span>));</span><br></pre></td></tr></table></figure>
<p>上面代码将 jQuery 生成的<code>deferred</code>对象,转为一个新的 Promise 对象。</p>
<p><code>Promise.resolve()</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="title class_">Promise</span>.<span class="title function_">resolve</span>(<span class="string">'foo'</span>)</span><br><span class="line"><span class="comment">// 等价于</span></span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function"><span class="params">resolve</span> =></span> <span class="title function_">resolve</span>(<span class="string">'foo'</span>))</span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">resolve</span>()方法的参数分成四种情况。</span><br></pre></td></tr></table></figure>
<ol>
<li>参数是一个 Promise 实例<br>如果参数是 Promise 实例,那么<code>Promise.resolve</code>将不做任何修改、原封不动地返回这个实例。</li>
<li>参数是一个<code>thenable</code>对象<br><code>thenable</code>对象指的是具有<code>then</code>方法的对象,比如下面这个对象。<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> thenable = {</span><br><span class="line"> <span class="attr">then</span>: <span class="keyword">function</span>(<span class="params">resolve, reject</span>) {</span><br><span class="line"> <span class="title function_">resolve</span>(<span class="number">42</span>);</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<code>Promise.resolve()</code>方法会将这个对象转为 Promise 对象,然后就立即执行<code>thenable</code>对象的<code>then()</code>方法。<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">let</span> thenable = {</span><br><span class="line"> <span class="attr">then</span>: <span class="keyword">function</span>(<span class="params">resolve, reject</span>) {</span><br><span class="line"> <span class="title function_">resolve</span>(<span class="number">42</span>);</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> p1 = <span class="title class_">Promise</span>.<span class="title function_">resolve</span>(thenable);</span><br><span class="line">p1.<span class="title function_">then</span>(<span class="keyword">function</span> (<span class="params">value</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(value); <span class="comment">// 42</span></span><br><span class="line">});</span><br></pre></td></tr></table></figure>
上面代码中,<code>thenable</code>对象的<code>then()</code>方法执行后,对象<code>p1</code>的状态就变为<code>resolved</code>,从而立即执行最后那个<code>then()</code>方法指定的回调函数,输出42。</li>
<li>参数不是具有<code>then()</code>方法的对象,或根本就不是对象<br>如果参数是一个原始值,或者是一个不具有<code>then()</code>方法的对象,则<code>Promise.resolve()</code>方法返回一个新的 Promise 对象,状态为<code>resolved</code>。<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> p = <span class="title class_">Promise</span>.<span class="title function_">resolve</span>(<span class="string">'Hello'</span>);</span><br><span class="line"></span><br><span class="line">p.<span class="title function_">then</span>(<span class="keyword">function</span> (<span class="params">s</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(s)</span><br><span class="line">});</span><br><span class="line"><span class="comment">// Hello</span></span><br></pre></td></tr></table></figure>
上面代码生成一个新的 Promise 对象的实例<code>p</code>。由于字符串<code>Hello</code>不属于异步操作(判断方法是字符串对象不具有 then 方法),返回 Promise 实例的状态从一生成就是<code>resolved</code>,所以回调函数会立即执行。<code>Promise.resolve()</code>方法的参数,会同时传给回调函数。</li>
<li>不带有任何参数<br><code>Promise.resolve()</code>方法允许调用时不带参数,直接返回一个<code>resolved</code>状态的 Promise 对象。<br>所以,如果希望得到一个 Promise 对象,比较方便的方法就是直接调用<code>Promise.resolve()</code>方法。<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> p = <span class="title class_">Promise</span>.<span class="title function_">resolve</span>();</span><br><span class="line"></span><br><span class="line">p.<span class="title function_">then</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">});</span><br></pre></td></tr></table></figure>
上面代码的变量<code>p</code>就是一个 Promise 对象。<blockquote>
<p>需要注意的是,立即<code>resolve()</code>的 Promise 对象,是在本轮“事件循环”(event loop)的结束时执行,而不是在下一轮“事件循环”的开始时。</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'three'</span>);</span><br><span class="line">}, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">resolve</span>().<span class="title function_">then</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'two'</span>);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'one'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// one</span></span><br><span class="line"><span class="comment">// two</span></span><br><span class="line"><span class="comment">// three</span></span><br></pre></td></tr></table></figure>
上面代码中,<code>setTimeout(fn, 0)</code>在下一轮“事件循环”开始时执行,<code>Promise.resolve()</code>在本轮“事件循环”结束时执行,<code>console.log('one')</code>则是立即执行,因此最先输出。</li>
</ol>
<h3 id="Promise-reject"><a href="#Promise-reject" class="headerlink" title="Promise.reject()"></a>Promise.reject()</h3><p><code>Promise.reject(reason)</code>方法也会返回一个新的 Promise 实例,该实例的状态为<code>rejected</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="keyword">const</span> p = <span class="title class_">Promise</span>.<span class="title function_">reject</span>(<span class="string">'出错了'</span>);</span><br><span class="line"><span class="comment">// 等同于</span></span><br><span class="line"><span class="keyword">const</span> p = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> <span class="title function_">reject</span>(<span class="string">'出错了'</span>))</span><br><span class="line"></span><br><span class="line">p.<span class="title function_">then</span>(<span class="literal">null</span>, <span class="keyword">function</span> (<span class="params">s</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(s)</span><br><span class="line">});</span><br><span class="line"><span class="comment">// 出错了</span></span><br></pre></td></tr></table></figure>
<p>上面代码生成一个 Promise 对象的实例p,状态为`rejected,回调函数会立即执行。</p>
<p><code>Promise.reject()</code>方法的参数,会原封不动地作为<code>reject</code>的理由,变成后续方法的参数。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Promise</span>.<span class="title function_">reject</span>(<span class="string">'出错了'</span>)</span><br><span class="line">.<span class="title function_">catch</span>(<span class="function"><span class="params">e</span> =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(e === <span class="string">'出错了'</span>)</span><br><span class="line">})</span><br><span class="line"><span class="comment">// true</span></span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>Promise.reject()</code>方法的参数是一个字符串,后面<code>catch()</code>方法的参数<code>e</code>就是这个字符串。</p>
<h2 id="应用"><a href="#应用" class="headerlink" title="应用"></a>应用</h2><h3 id="加载图片"><a href="#加载图片" class="headerlink" title="加载图片"></a>加载图片</h3><p>我们可以将图片的加载写成一个<code>Promise</code>,一旦加载完成,<code>Promise</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="keyword">const</span> preloadImage = <span class="keyword">function</span> (<span class="params">path</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span> (<span class="params">resolve, reject</span>) {</span><br><span class="line"> <span class="keyword">const</span> image = <span class="keyword">new</span> <span class="title class_">Image</span>();</span><br><span class="line"> image.<span class="property">onload</span> = resolve;</span><br><span class="line"> image.<span class="property">onerror</span> = reject;</span><br><span class="line"> image.<span class="property">src</span> = path;</span><br><span class="line"> });</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<h3 id="Generator-函数与-Promise-的结合"><a href="#Generator-函数与-Promise-的结合" class="headerlink" title="Generator 函数与 Promise 的结合"></a>Generator 函数与 Promise 的结合</h3><p>使用 Generator 函数管理流程,遇到异步操作的时候,通常返回一个<code>Promise</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">getFoo</span> () {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span> (<span class="params">resolve, reject</span>){</span><br><span class="line"> <span class="title function_">resolve</span>(<span class="string">'foo'</span>);</span><br><span class="line"> });</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> g = <span class="keyword">function</span>* () {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">const</span> foo = <span class="keyword">yield</span> <span class="title function_">getFoo</span>();</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(foo);</span><br><span class="line"> } <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(e);</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">run</span> (generator) {</span><br><span class="line"> <span class="keyword">const</span> it = <span class="title function_">generator</span>();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">go</span>(<span class="params">result</span>) {</span><br><span class="line"> <span class="keyword">if</span> (result.<span class="property">done</span>) <span class="keyword">return</span> result.<span class="property">value</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> result.<span class="property">value</span>.<span class="title function_">then</span>(<span class="keyword">function</span> (<span class="params">value</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">go</span>(it.<span class="title function_">next</span>(value));</span><br><span class="line"> }, <span class="keyword">function</span> (<span class="params">error</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">go</span>(it.<span class="keyword">throw</span>(error));</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="title function_">go</span>(it.<span class="title function_">next</span>());</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">run</span>(g);</span><br></pre></td></tr></table></figure>
<p>上面代码的 Generator 函数<code>g</code>之中,有一个异步操作<code>getFoo</code>,它返回的就是一个<code>Promise</code>对象。函数<code>run</code>用来处理这个<code>Promise</code>对象,并调用下一个<code>next</code>方法。</p>
<h2 id="Promise-try"><a href="#Promise-try" class="headerlink" title="Promise.try()"></a>Promise.try()</h2><p>实际开发中,经常遇到一种情况:不知道或者不想区分,函数<code>f</code>是同步函数还是异步操作,但是想用 Promise 来处理它。因为这样就可以不管<code>f</code>是否包含异步操作,都用<code>then</code>方法指定下一步流程,用<code>catch</code>方法处理<code>f</code>抛出的错误。一般就会采用下面的写法。</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="title class_">Promise</span>.<span class="title function_">resolve</span>().<span class="title function_">then</span>(f)</span><br></pre></td></tr></table></figure>
<p>上面的写法有一个缺点,就是如果f是同步函数,那么它会在本轮事件循环的末尾执行。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">f</span> = (<span class="params"></span>) => <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'now'</span>);</span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">resolve</span>().<span class="title function_">then</span>(f);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'next'</span>);</span><br><span class="line"><span class="comment">// next</span></span><br><span class="line"><span class="comment">// now</span></span><br></pre></td></tr></table></figure>
<p>上面代码中,函数<code>f</code>是同步的,但是用 Promise 包装了以后,就变成异步执行了。</p>
<p>那么有没有一种方法,让同步函数同步执行,异步函数异步执行,并且让它们具有统一的 API 呢?回答是可以的,并且还有两种写法。第一种写法是用<code>async</code>函数来写。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">f</span> = (<span class="params"></span>) => <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'now'</span>);</span><br><span class="line">(<span class="keyword">async</span> () => <span class="title function_">f</span>())();</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'next'</span>);</span><br><span class="line"><span class="comment">// now</span></span><br><span class="line"><span class="comment">// next</span></span><br></pre></td></tr></table></figure>
<p>上面代码中,第二行是一个立即执行的匿名函数,会立即执行里面的<code>async</code>函数,因此如果f是同步的,就会得到同步的结果;如果<code>f</code>是异步的,就可以用<code>then</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">async</span> () => <span class="title function_">f</span>())()</span><br><span class="line">.<span class="title function_">then</span>(...)</span><br></pre></td></tr></table></figure>
<p>需要注意的是,<code>async () => f()</code>会吃掉f()抛出的错误。所以,如果想捕获错误,要使用<code>promise.catch</code>方法。</p>
<p>第二种写法是使用<code>new Promise()</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">f</span> = (<span class="params"></span>) => <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'now'</span>);</span><br><span class="line">(</span><br><span class="line"> <span class="function">() =></span> <span class="keyword">new</span> <span class="title class_">Promise</span>(</span><br><span class="line"> <span class="function"><span class="params">resolve</span> =></span> <span class="title function_">resolve</span>(<span class="title function_">f</span>())</span><br><span class="line"> )</span><br><span class="line">)();</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'next'</span>);</span><br><span class="line"><span class="comment">// now</span></span><br><span class="line"><span class="comment">// next</span></span><br></pre></td></tr></table></figure>
<p>上面代码也是使用立即执行的匿名函数,执行<code>new Promise()</code>。这种情况下,同步函数也是同步执行的。</p>
<p>鉴于这是一个很常见的需求,所以现在有一个<a href="https://github.com/ljharb/proposal-promise-try">提案</a>,提供<code>Promise.try</code>方法替代上面的写法。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">f</span> = (<span class="params"></span>) => <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'now'</span>);</span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">try</span>(f);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'next'</span>);</span><br><span class="line"><span class="comment">// now</span></span><br><span class="line"><span class="comment">// next</span></span><br></pre></td></tr></table></figure>
<p>事实上,<code>Promise.try</code>存在已久,Promise 库<code>Bluebird</code>、<code>Q</code>和<code>when</code>,早就提供了这个方法。</p>
<p>由于<code>Promise.try</code>为所有操作提供了统一的处理机制,所以如果想用<code>then</code>方法管理流程,最好都用<code>Promise.try</code>包装一下。这样有<a href="http://cryto.net/~joepie91/blog/2016/05/11/what-is-promise-try-and-why-does-it-matter/">许多好处</a>,其中一点就是可以更好地管理异常。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">f</span> = (<span class="params"></span>) => <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'now'</span>);</span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">try</span>(f);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'next'</span>);</span><br><span class="line"><span class="comment">// now</span></span><br><span class="line"><span class="comment">// next</span></span><br></pre></td></tr></table></figure>
<p>上面代码中,<code>database.users.get()</code>返回一个 Promise 对象,如果抛出异步错误,可以用<code>catch</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">database.<span class="property">users</span>.<span class="title function_">get</span>({<span class="attr">id</span>: userId})</span><br><span class="line">.<span class="title function_">then</span>(...)</span><br><span class="line">.<span class="title function_">catch</span>(...)</span><br></pre></td></tr></table></figure>
<p>但是<code>database.users.get()</code>可能还会抛出同步错误(比如数据库连接错误,具体要看实现方法),这时你就不得不用<code>try...catch</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="keyword">try</span> {</span><br><span class="line"> database.<span class="property">users</span>.<span class="title function_">get</span>({<span class="attr">id</span>: userId})</span><br><span class="line"> .<span class="title function_">then</span>(...)</span><br><span class="line"> .<span class="title function_">catch</span>(...)</span><br><span class="line">} <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面这样的写法就很笨拙了,这时就可以统一用<code>promise.catch()</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="title class_">Promise</span>.<span class="title function_">try</span>(<span class="function">() =></span> database.<span class="property">users</span>.<span class="title function_">get</span>({<span class="attr">id</span>: userId}))</span><br><span class="line"> .<span class="title function_">then</span>(...)</span><br><span class="line"> .<span class="title function_">catch</span>(...)</span><br></pre></td></tr></table></figure>
<p>事实上,<code>Promise.try</code>就是模拟<code>try</code>代码块,就像<code>promise.catch</code>模拟的是<code>catch</code>代码块。</p>
<p>参考链接</p>
<ul>
<li><a href="https://www.jianshu.com/p/fe5f173276bd">前端基础进阶(十五):透彻掌握Promise的使用,读这篇就够了</a></li>
<li><a href="https://es6.ruanyifeng.com/#docs/promise">Promise 对象</a></li>
</ul>
<p>推荐阅读</p>
<ul>
<li><a href="https://juejin.cn/post/6844903665686282253">Promise实现原理(附源码)</a></li>
<li><a href="https://juejin.cn/post/6844903607968481287">史上最通俗易懂的Promise</a></li>
<li><a href="https://juejin.cn/post/6924188714419634190">看懂此文,手写十种Promise!</a></li>
<li><a href="https://juejin.cn/post/6994594642280857630">看了就会,手写Promise原理,最通俗易懂的版本</a></li>
<li><a href="https://juejin.cn/post/6844904096525189128">Promise/async/Generator实现原理解析</a></li>
<li><a href="https://juejin.cn/post/6945319439772434469">从一道让我失眠的 Promise 面试题开始,深入分析 Promise 实现细节</a></li>
<li><a
前端基础进阶(十四):深入核心,详解事件循环机制
https://fe32.top/articles/jsnb9541/
2022-06-28T14:47:43.000Z
2023-05-17T10:01:12.000Z
<blockquote>
<p>推荐阅读</p>
</blockquote>
<ul>
<li><a href="https://zhuanlan.zhihu.com/p/25407758">这个前端面试在搞事</a></li>
<li><a href="https://juejin.cn/post/6844903470466629640">80% 应聘者都不及格的 JS 面试题</a></li>
<li><a href="https://fe32.top/articles/js0320ub/">熟悉事件循环?那谈谈为什么会分为宏任务和微任务</a></li>
</ul>
<p>学习事件循环机制之前,希望你已经懂得了如下概念:</p>
<ul>
<li>执行上下文(Execution context)</li>
<li>函数调用栈(call stack)</li>
<li>队列数据结构(queue)</li>
<li>Promise(将在下一篇文章专门总结Promise的详细使用)</li>
</ul>
<blockquote>
<p>因为chrome浏览器中新标准中的事件循环机制与nodejs类似,因此此处就整合nodejs一起来理解,其中会介绍到几个nodejs有,但是浏览器中没有的API,大家只需要了解就好,不一定非要知道它是如何使用。比如<code>process.nextTick</code>,<code>setImmediate</code></p>
</blockquote>
<p>我们知道JavaScript的一大特点就是单线程,而这个线程中拥有唯一的一个事件循环。</p>
<blockquote>
<p>当然新标准中的<code>web worker</code>涉及到了多线程,这里就不讨论了。</p>
</blockquote>
<p>JavaScript代码的执行过程中,除了依靠函数调用栈来搞定函数的执行顺序外,还依靠任务队列(task queue)来搞定另外一些代码的执行。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/28/62bb1f361ef50.webp" alt="队列数据结构"/></div><span class="image-caption">队列数据结构</span></div>
<ul>
<li>一个线程中,事件循环是唯一的,但是任务队列可以拥有多个。</li>
<li>任务队列又分为macro-task(宏任务)与micro-task(微任务),在最新标准中,它们被分别称为task与jobs。</li>
<li>macro-task大概包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。</li>
<li>micro-task大概包括: process.nextTick, Promise, Object.observe(已废弃), MutationObserver(html5新特性)</li>
<li>setTimeout/Promise等我们称之为任务源。而进入任务队列的是它们指定的具体执行任务。</li>
</ul>
<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">// setTimeout中的回调函数才是进入任务队列的任务</span></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'xxxx'</span>);</span><br><span class="line">})</span><br><span class="line"><span class="comment">// 非常多的同学对于setTimeout的理解存在偏差。所以大概说一下误解:</span></span><br><span class="line"><span class="comment">// setTimeout作为一个任务分发器,这个函数会立即执行,而它所要分发的任务,也就是它的第一个参数,才是延迟执行</span></span><br></pre></td></tr></table></figure>
<ul>
<li>来自不同任务源的任务会进入到不同的任务队列。其中setTimeout与setInterval是同源的。</li>
<li>事件循环的顺序,决定了JavaScript代码的执行顺序。它从script(整体代码)开始第一次循环。之后全局上下文进入函数调用栈。直到调用栈清空(只剩全局),然后执行所有的micro-task。当所有可执行的micro-task执行完毕之后。循环再次从macro-task开始,找到其中一个任务队列执行完毕,然后再执行所有的micro-task,这样一直循环下去。</li>
<li>其中每一个任务的执行,无论是macro-task还是micro-task,都是借助函数调用栈来完成。纯文字表述确实有点干涩,因此,这里我们通过2个例子,来逐步理解事件循环的具体顺序。</li>
</ul>
<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"><span class="comment">// demo01 出自于上面我引用文章的一个例子,我们来根据上面的结论,一步一步分析具体的执行过程。</span></span><br><span class="line"><span class="comment">// 为了方便理解,我以打印出来的字符作为当前的任务名称</span></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'timeout1'</span>);</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'promise1'</span>);</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">var</span> i = <span class="number">0</span>; i < <span class="number">1000</span>; i++) {</span><br><span class="line"> i == <span class="number">99</span> && <span class="title function_">resolve</span>();</span><br><span class="line"> }</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'promise2'</span>);</span><br><span class="line">}).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'then1'</span>);</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'global1'</span>);</span><br></pre></td></tr></table></figure>
<p>第一步:事件循环从宏任务队列开始,这个时候,宏任务队列中,只有一个script(整体代码)任务。每一个任务的执行顺序,都依靠函数调用栈来搞定,而当遇到任务源时,则会先分发任务到对应的队列中去,所以,上面例子的第一步执行如下图所示。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/28/62bb207892528.webp" alt="首先script任务开始执行,全局上下文入栈"/></div><span class="image-caption">首先script任务开始执行,全局上下文入栈</span></div>
<p>第二步:script任务执行时首先遇到了setTimeout,setTimeout为一个宏任务源,它的作用就是将任务分发到对应的队列中。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'timeout1'</span>);</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/28/62bb20b833355.webp" alt="宏任务timeout1进入setTimeout队列"/></div><span class="image-caption">宏任务timeout1进入setTimeout队列</span></div>
<p>第三步:script执行时遇到Promise实例。Promise构造函数中的第一个参数,是在new的时候执行,因此不会进入任何其它的队列,而是直接在当前任务直接执行了,而后续的.then则会被分发到micro-task的Promise队列中去。</p>
<p>因此,构造函数执行时,里面的参数进入函数调用栈执行。for循环不会进入任何队列,因此代码会依次执行,所以这里的promise1和promise2会依次输出。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/28/62bb20cad4e8d.webp" alt="promise1入栈执行,这时promise1被最先输出"/></div><span class="image-caption">promise1入栈执行,这时promise1被最先输出</span></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/28/62bb20dc9faff.webp" alt="resolve在for循环中入栈执行"/></div><span class="image-caption">resolve在for循环中入栈执行</span></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/28/62bb20f1a26f1.webp" alt="构造函数执行完毕的过程中,resolve执行完毕出栈,promise2输出,promise1页出栈,then执行时,Promise任务then1进入对应队列"/></div><span class="image-caption">构造函数执行完毕的过程中,resolve执行完毕出栈,promise2输出,promise1页出栈,then执行时,Promise任务then1进入对应队列</span></div>
<p>script任务继续往下执行,最后只有一句输出了globa1,然后,全局任务就执行完毕了。</p>
<p>第四步:第一个宏任务script执行完毕之后,就开始执行所有的可执行的微任务。这个时候,微任务中,只有Promise队列中的一个任务then1,因此直接执行就行了,执行结果输出then1,当然,它的执行,也是进入函数调用栈中执行的。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/28/62bb22dc0ec14.webp" alt="执行所有的微任务"/></div><span class="image-caption">执行所有的微任务</span></div>
<p>第五步:当所有的micro-tast执行完毕之后,表示第一轮的循环就结束了。这个时候就得开始第二轮的循环。第二轮循环仍然从宏任务macro-task开始。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/28/62bb22e023472.webp" alt="微任务被清空"/></div><span class="image-caption">微任务被清空</span></div>
<p>这个时候,发现宏任务中,只有在setTimeout队列中还要一个timeout1的任务等待执行。因此就直接执行即可。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/28/62bb22f12672b.webp" alt="timeout1入栈执行"/></div><span class="image-caption">timeout1入栈执行</span></div>
<p>这个时候宏任务队列与微任务队列中都没有任务了,所以代码就不会再输出其它东西了。上面这个例子的输出结果就显而易见。大家可以自行尝试体会。</p>
<p>这个例子比较简单,涉及到的队列任务并不多,因此读懂了它还不能全面的了解到事件循环机制的全貌。所以我下面弄了一个复杂一点的例子,再给大家解析一番,相信读懂之后,事件循环这个问题,再面试中再次被问到就难不倒大家了。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// demo02</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'golb1'</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'timeout1'</span>);</span><br><span class="line"> process.<span class="title function_">nextTick</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'timeout1_nextTick'</span>);</span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'timeout1_promise'</span>);</span><br><span class="line"> <span class="title function_">resolve</span>();</span><br><span class="line"> }).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'timeout1_then'</span>)</span><br><span class="line"> })</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="title function_">setImmediate</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'immediate1'</span>);</span><br><span class="line"> process.<span class="title function_">nextTick</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'immediate1_nextTick'</span>);</span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'immediate1_promise'</span>);</span><br><span class="line"> <span class="title function_">resolve</span>();</span><br><span class="line"> }).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'immediate1_then'</span>)</span><br><span class="line"> })</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">process.<span class="title function_">nextTick</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'glob1_nextTick'</span>);</span><br><span class="line">})</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'glob1_promise'</span>);</span><br><span class="line"> <span class="title function_">resolve</span>();</span><br><span class="line">}).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'glob1_then'</span>)</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'timeout2'</span>);</span><br><span class="line"> process.<span class="title function_">nextTick</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'timeout2_nextTick'</span>);</span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'timeout2_promise'</span>);</span><br><span class="line"> <span class="title function_">resolve</span>();</span><br><span class="line"> }).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'timeout2_then'</span>)</span><br><span class="line"> })</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">process.<span class="title function_">nextTick</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'glob2_nextTick'</span>);</span><br><span class="line">})</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'glob2_promise'</span>);</span><br><span class="line"> <span class="title function_">resolve</span>();</span><br><span class="line">}).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'glob2_then'</span>)</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="title function_">setImmediate</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'immediate2'</span>);</span><br><span class="line"> process.<span class="title function_">nextTick</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'immediate2_nextTick'</span>);</span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'immediate2_promise'</span>);</span><br><span class="line"> <span class="title function_">resolve</span>();</span><br><span class="line"> }).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'immediate2_then'</span>)</span><br><span class="line"> })</span><br><span class="line">})</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>我们一步一步来分析代码执行过程。</p>
<p>第一步:宏任务script首先执行。全局入栈。glob1输出。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/29/62bb260be4e18.webp" alt="script首先执行"/></div><span class="image-caption">script首先执行</span></div>
<p>第二步,执行过程遇到setTimeout。setTimeout作为任务分发器,将任务分发到对应的宏任务队列中。</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="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'timeout1'</span>);</span><br><span class="line"> process.<span class="title function_">nextTick</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'timeout1_nextTick'</span>);</span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'timeout1_promise'</span>);</span><br><span class="line"> <span class="title function_">resolve</span>();</span><br><span class="line"> }).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'timeout1_then'</span>)</span><br><span class="line"> })</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/29/62bb26118a9f2.webp" alt="timeout1进入对应队列"/></div><span class="image-caption">timeout1进入对应队列</span></div>
<p>第三步:执行过程遇到setImmediate。setImmediate也是一个宏任务分发器,将任务分发到对应的任务队列中。setImmediate的任务队列会在setTimeout队列的后面执行。</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="title function_">setImmediate</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'immediate1'</span>);</span><br><span class="line"> process.<span class="title function_">nextTick</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'immediate1_nextTick'</span>);</span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'immediate1_promise'</span>);</span><br><span class="line"> <span class="title function_">resolve</span>();</span><br><span class="line"> }).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'immediate1_then'</span>)</span><br><span class="line"> })</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/29/62bb2623d2600.webp" alt="进入setImmediate队列"/></div><span class="image-caption">进入setImmediate队列</span></div>
<p>第四步:执行遇到nextTick,process.nextTick是一个微任务分发器,它会将任务分发到对应的微任务队列中去。</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">process.<span class="title function_">nextTick</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'glob1_nextTick'</span>);</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/29/62bb2630b5943.webp" alt="nextTick"/></div><span class="image-caption">nextTick</span></div>
<p>第五步:执行遇到Promise。Promise的then方法会将任务分发到对应的微任务队列中,但是它构造函数中的方法会直接执行。因此,glob1_promise会第二个输出。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'glob1_promise'</span>);</span><br><span class="line"> <span class="title function_">resolve</span>();</span><br><span class="line">}).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'glob1_then'</span>)</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/29/62bb2640cec3a.webp" alt="先是函数调用栈的变化"/></div><span class="image-caption">先是函数调用栈的变化</span></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/29/62bb26499a37e.webp" alt="然后glob1_then任务进入队列"/></div><span class="image-caption">然后glob1_then任务进入队列</span></div>
<p>第六步:执行遇到第二个setTimeout。</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="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'timeout2'</span>);</span><br><span class="line"> process.<span class="title function_">nextTick</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'timeout2_nextTick'</span>);</span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'timeout2_promise'</span>);</span><br><span class="line"> <span class="title function_">resolve</span>();</span><br><span class="line"> }).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'timeout2_then'</span>)</span><br><span class="line"> })</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/29/62bb265159e15.webp" alt="timeout2进入对应队列"/></div><span class="image-caption">timeout2进入对应队列</span></div>
<p>第七步:先后遇到nextTick与Promise。</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">process.<span class="title function_">nextTick</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'glob2_nextTick'</span>);</span><br><span class="line">})</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'glob2_promise'</span>);</span><br><span class="line"> <span class="title function_">resolve</span>();</span><br><span class="line">}).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'glob2_then'</span>)</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/29/62bb265c3f721.webp" alt="glob2_nextTick与Promise任务分别进入各自的队列"/></div><span class="image-caption">glob2_nextTick与Promise任务分别进入各自的队列</span></div>
<p>第八步:再次遇到setImmediate。</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="title function_">setImmediate</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'immediate2'</span>);</span><br><span class="line"> process.<span class="title function_">nextTick</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'immediate2_nextTick'</span>);</span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'immediate2_promise'</span>);</span><br><span class="line"> <span class="title function_">resolve</span>();</span><br><span class="line"> }).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'immediate2_then'</span>)</span><br><span class="line"> })</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/29/62bb2668a2b37.webp" alt="nextTick"/></div><span class="image-caption">nextTick</span></div>
<p>这个时候,script中的代码就执行完毕了,执行过程中,遇到不同的任务分发器,就将任务分发到各自对应的队列中去。接下来,将会执行所有的微任务队列中的任务。</p>
<p>其中,nextTick队列会比Promie先执行。nextTick中的可执行任务执行完毕之后,才会开始执行Promise队列中的任务。</p>
<p>当所有可执行的微任务执行完毕之后,这一轮循环就表示结束了。下一轮循环继续从宏任务队列开始执行。</p>
<p>这个时候,script已经执行完毕,所以就从setTimeout队列开始执行。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/29/62bb26730bef2.webp" alt="第二轮循环初始状态"/></div><span class="image-caption">第二轮循环初始状态</span></div>
<p>setTimeout任务的执行,也依然是借助函数调用栈来完成,并且遇到任务分发器的时候也会将任务分发到对应的队列中去。</p>
<p>只有当setTimeout中所有的任务执行完毕之后,才会再次开始执行微任务队列。并且清空所有的可执行微任务。</p>
<p>setTiemout队列产生的微任务执行完毕之后,循环则回过头来开始执行setImmediate队列。仍然是先将setImmediate队列中的任务执行完毕,再执行所产生的微任务。</p>
<p>当setImmediate队列执行产生的微任务全部执行之后,第二轮循环也就结束了。</p>
<p>当然,这些顺序都是v8的一些实现。我们也可以根据上面的规则,来尝试实现一下事件循环的机制。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 用数组模拟一个队列</span></span><br><span class="line"><span class="keyword">var</span> tasks = [];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 模拟一个事件分发器</span></span><br><span class="line"><span class="keyword">var</span> addFn1 = <span class="keyword">function</span>(<span class="params">task</span>) {</span><br><span class="line"> tasks.<span class="title function_">push</span>(task);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 执行所有的任务</span></span><br><span class="line"><span class="keyword">var</span> flush = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> tasks.<span class="title function_">map</span>(<span class="keyword">function</span>(<span class="params">task</span>) {</span><br><span class="line"> <span class="title function_">task</span>();</span><br><span class="line"> })</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 最后利用setTimeout/或者其它你认为合适的方式丢入事件循环中</span></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="title function_">flush</span>();</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="comment">// 当然,也可以不用丢进事件循环,而是我们自己手动在适当的时机去执行对应的某一个方法</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> dispatch = <span class="keyword">function</span>(<span class="params">name</span>) {</span><br><span class="line"> tasks.<span class="title function_">map</span>(<span class="keyword">function</span>(<span class="params">item</span>) {</span><br><span class="line"> <span class="keyword">if</span>(item.<span class="property">name</span> == name) {</span><br><span class="line"> item.<span class="title function_">handler</span>();</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 当然,我们把任务丢进去的时候,多保存一个name即可。</span></span><br><span class="line"><span class="comment">// 这时候,task的格式就如下</span></span><br><span class="line">demoTask = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'demo'</span>,</span><br><span class="line"> <span class="attr">handler</span>: <span class="keyword">function</span>(<span class="params"></span>) {}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 于是,一个订阅-通知的设计模式就这样轻松的被实现了</span></span><br></pre></td></tr></table></figure>
<p>这样,我们就模拟了一个任务队列。还可以定义另外一个队列,利用上面的各种方式来规定它们的优先级。</p>
<blockquote>
<p>需要注意的是,这里的执行顺序,或者执行的优先级在不同的场景里由于实现的不同会导致不同的结果,包括<code>node</code>的不同版本,不同浏览器等都有不同的结果。</p>
</blockquote>
<div class="note no-icon flat"><p>原文链接:<a
前端基础进阶(十三):详细图解jQuery对象,以及如何扩展jQuery插件
https://fe32.top/articles/jsnb9540/
2022-06-26T14:21:56.000Z
2023-05-17T10:01:12.000Z
<p>jQuery在前端史上有它非常超然的历史地位,许多从中学到的技巧在实践开发中仍然非常好用。简单的了解它有助于我们更加深入的理解JavaScript。如果你能够从中看明白jquery是如何一步步被取代的,那么,我想你的收益远不止学会使用了一个库那么简单。</p>
<p class='p blue'>因此,我的态度是,项目中你可以不用,但是我建议你了解了解jQuery的思想。</p>
<p>这篇文章的主要目的,是从面向对象的角度,跟大家分享jquery对象是如何封装的。</p>
<p>使用jQuery对象时,我们这样写:</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">// 声明一个jQuery对象</span></span><br><span class="line">$(<span class="string">'.target'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取元素的css属性</span></span><br><span class="line">$(<span class="string">'.target'</span>).<span class="title function_">css</span>(<span class="string">'width'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取元素的位置信息</span></span><br><span class="line">$(<span class="string">'.target'</span>).<span class="title function_">offset</span>()</span><br></pre></td></tr></table></figure>
<p>在使用之初可能会有许多疑问,比如<code>$</code>是怎么回事?为什么不用<code>new</code>就可以直接声明一个对象?等等。了解之后,才知道原来这正是jQuery对象创建的巧妙之处。</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><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></pre></td><td class="code"><pre><span class="line">;</span><br><span class="line">(<span class="keyword">function</span> (<span class="params">ROOT</span>) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 构造函数</span></span><br><span class="line"> <span class="keyword">var</span> jQuery = <span class="keyword">function</span> (<span class="params">selector</span>) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 在jQuery中直接返回new过的实例,这里的init是jQuery的真正构造函数</span></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> jQuery.<span class="property">fn</span>.<span class="title function_">init</span>(selector)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> jQuery.<span class="property">fn</span> = jQuery.<span class="property"><span class="keyword">prototype</span></span> = {</span><br><span class="line"> <span class="attr">constructor</span>: jQuery,</span><br><span class="line"></span><br><span class="line"> <span class="attr">version</span>: <span class="string">'1.0.0'</span>,</span><br><span class="line"></span><br><span class="line"> <span class="attr">init</span>: <span class="keyword">function</span> (<span class="params">selector</span>) {</span><br><span class="line"> <span class="comment">// 在jquery中这里有一个复杂的判断,但是这里我做了简化</span></span><br><span class="line"> <span class="keyword">var</span> elem, selector;</span><br><span class="line"> elem = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(selector);</span><br><span class="line"> <span class="variable language_">this</span>[<span class="number">0</span>] = elem;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 在jquery中返回一个由所有原型属性方法组成的数组,我们这里简化,直接返回this即可</span></span><br><span class="line"> <span class="comment">// return jQuery.makeArray(selector, this);</span></span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>;</span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 在原型上添加一堆方法</span></span><br><span class="line"> <span class="attr">toArray</span>: <span class="keyword">function</span> (<span class="params"></span>) { },</span><br><span class="line"> <span class="attr">get</span>: <span class="keyword">function</span> (<span class="params"></span>) { },</span><br><span class="line"> <span class="attr">each</span>: <span class="keyword">function</span> (<span class="params"></span>) { },</span><br><span class="line"> <span class="attr">ready</span>: <span class="keyword">function</span> (<span class="params"></span>) { },</span><br><span class="line"> <span class="attr">first</span>: <span class="keyword">function</span> (<span class="params"></span>) { },</span><br><span class="line"> <span class="attr">slice</span>: <span class="keyword">function</span> (<span class="params"></span>) { }</span><br><span class="line"> <span class="comment">// ... ...</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> jQuery.<span class="property">fn</span>.<span class="property">init</span>.<span class="property"><span class="keyword">prototype</span></span> = jQuery.<span class="property">fn</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 实现jQuery的两种扩展方式</span></span><br><span class="line"> jQuery.<span class="property">extend</span> = jQuery.<span class="property">fn</span>.<span class="property">extend</span> = <span class="keyword">function</span> (<span class="params">options</span>) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 在jquery源码中会根据参数不同进行很多判断,我们这里就直接走一种方式,所以就不用判断了</span></span><br><span class="line"> <span class="keyword">var</span> target = <span class="variable language_">this</span>;</span><br><span class="line"> <span class="keyword">var</span> copy;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (name <span class="keyword">in</span> options) {</span><br><span class="line"> copy = options[name];</span><br><span class="line"> target[name] = copy;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> target;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// jQuery中利用上面实现的扩展机制,添加了许多方法,其中</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 直接添加在构造函数上,被称为工具方法</span></span><br><span class="line"> jQuery.<span class="title function_">extend</span>({</span><br><span class="line"> <span class="attr">isFunction</span>: <span class="keyword">function</span> (<span class="params"></span>) { },</span><br><span class="line"> <span class="attr">type</span>: <span class="keyword">function</span> (<span class="params"></span>) { },</span><br><span class="line"> <span class="attr">parseHTML</span>: <span class="keyword">function</span> (<span class="params"></span>) { },</span><br><span class="line"> <span class="attr">parseJSON</span>: <span class="keyword">function</span> (<span class="params"></span>) { },</span><br><span class="line"> <span class="attr">ajax</span>: <span class="keyword">function</span> (<span class="params"></span>) { }</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 添加到原型上</span></span><br><span class="line"> jQuery.<span class="property">fn</span>.<span class="title function_">extend</span>({</span><br><span class="line"> <span class="attr">queue</span>: <span class="keyword">function</span> (<span class="params"></span>) { },</span><br><span class="line"> <span class="attr">promise</span>: <span class="keyword">function</span> (<span class="params"></span>) { },</span><br><span class="line"> <span class="attr">attr</span>: <span class="keyword">function</span> (<span class="params"></span>) { },</span><br><span class="line"> <span class="attr">prop</span>: <span class="keyword">function</span> (<span class="params"></span>) { },</span><br><span class="line"> <span class="attr">addClass</span>: <span class="keyword">function</span> (<span class="params"></span>) { },</span><br><span class="line"> <span class="attr">removeClass</span>: <span class="keyword">function</span> (<span class="params"></span>) { },</span><br><span class="line"> <span class="attr">val</span>: <span class="keyword">function</span> (<span class="params"></span>) { },</span><br><span class="line"> <span class="attr">css</span>: <span class="keyword">function</span> (<span class="params"></span>) { }</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> <span class="comment">// $符号的由来,实际上它就是jQuery,一个简化的写法,在这里我们还可以替换成其他可用字符</span></span><br><span class="line"> <span class="variable constant_">ROOT</span>.<span class="property">jQuery</span> = <span class="variable constant_">ROOT</span>.<span class="property">$</span> = jQuery;</span><br><span class="line">})(<span class="variable language_">window</span>);</span><br></pre></td></tr></table></figure>
<p>在上面的代码中,我封装了一个简化版的jQuery对象,给大家简单展示了jQuery的整体骨架。</p>
<p>在代码中可以看到,jQuery自身对于原型的处理使用了一些巧妙的方式,比如<code>jQuery.fn = jQuery.prototype</code>,<code>jQuery.fn.init.prototype = jQuery.fn</code>等,这几句正是jQuery对象的关键所在。看图分析。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/27/62b9b68796743.webp" alt="jQuery对象核心图"/></div><span class="image-caption">jQuery对象核心图</span></div>
<h2 id="对象封装分析"><a href="#对象封装分析" class="headerlink" title="对象封装分析"></a>对象封装分析</h2><p>在上面的实现中,首先在jQuery构造函数里声明了一个fn属性,并将其指向了原型<code>jQuery.prototype</code>。然后在原型中添加了init方法。</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">jQuery.<span class="property">fn</span> = jQuery.<span class="property"><span class="keyword">prototype</span></span> = {</span><br><span class="line"> <span class="attr">init</span>: {}</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>之后又将init的原型,指向了<code>jQuery.prototype</code>。</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">jQuery.<span class="property">fn</span>.<span class="property">init</span>.<span class="property"><span class="keyword">prototype</span></span> = jQuery.<span class="property">fn</span>;</span><br></pre></td></tr></table></figure>
<p>而在构造函数jQuery中,返回了init的实例对象。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> jQuery = <span class="keyword">function</span> (<span class="params">selector</span>) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 在jQuery中直接返回new过的实例,这里的init是jQuery的真正构造函数</span></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> jQuery.<span class="property">fn</span>.<span class="title function_">init</span>(selector)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>最后对外暴露入口时,将字符<code>$</code>与<code>jQuery</code>对等起来。</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="variable constant_">ROOT</span>.<span class="property">jQuery</span> = <span class="variable constant_">ROOT</span>.<span class="property">$</span> = jQuery;</span><br></pre></td></tr></table></figure>
<p>因此当我们直接使用<code>$('#test')</code>创建一个对象时,实际上是创建了一个init的实例,这里的真正构造函数是原型中的init方法。</p>
<blockquote>
<p>注意:许多对jQuery内部实现不太了解的盆友,常常会毫无节制使用$(),比如对于同一个元素的不同操作。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> width = <span class="built_in">parseInt</span>($(<span class="string">'#test'</span>).<span class="title function_">css</span>(<span class="string">'width'</span>));</span><br><span class="line"><span class="keyword">if</span>(width > <span class="number">20</span>) {</span><br><span class="line"> $(<span class="string">'#test'</span>).<span class="title function_">css</span>(<span class="string">'backgroundColor'</span>, <span class="string">'red'</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>通过上面的一系列分析,我们知道每当执行$()时,就会重新生成一个init的实例对象,因此当我们这样没有节制的使用jQuery是非常不正确的,虽然看上去方便了一些,但是对于内存的消耗非常大。正确的做法是既然是同一个对象,那么就用一个变量保存起来后续使用即可。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> $test = $(<span class="string">'#test'</span>);</span><br><span class="line"><span class="keyword">var</span> width = <span class="built_in">parseInt</span>($test.<span class="title function_">css</span>(<span class="string">'width'</span>));</span><br><span class="line"><span class="keyword">if</span>(width > <span class="number">20</span>) {</span><br><span class="line"> $test.<span class="title function_">css</span>(<span class="string">'backgroundColor'</span>, <span class="string">'red'</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="扩展方法分析"><a href="#扩展方法分析" class="headerlink" title="扩展方法分析"></a>扩展方法分析</h2><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></pre></td><td class="code"><pre><span class="line">jQuery.<span class="property">extend</span> = jQuery.<span class="property">fn</span>.<span class="property">extend</span> = <span class="keyword">function</span> (<span class="params">options</span>) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 在jquery源码中会根据参数不同进行很多判断,我们这里就直接走一种方式,所以就不用判断了</span></span><br><span class="line"> <span class="keyword">var</span> target = <span class="variable language_">this</span>;</span><br><span class="line"> <span class="keyword">var</span> copy;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (name <span class="keyword">in</span> options) {</span><br><span class="line"> copy = options[name];</span><br><span class="line"> target[name] = copy;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> target;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>传入的参数options为一个<code>key: value</code>模式的对象,通过<code>for in</code>遍历options,将key作为jQuery的新属性,value作为该新属性所对应的新方法,分别添加到jQuery方法和jQuery.fn中。</p>
<p>当我们通过<code>jQuery.extend</code>扩展jQuery时,方法被添加到了jQuery构造函数中,当通过<code>jQuery.fn.extend</code>扩展jQuery时,方法被添加到了jQuery原型中。</p>
<p>上面的例子中,简单展示了在jQuery内部,许多方法的实现都是通过这两个扩展方法来完成的。</p>
<p>有一个朋友留言给我,说他被静态方法,工具方法和实例方法这几个概念困扰了很久,到底它们有什么区别?</p>
<p>在封装对象时,属性和方法可以具体放置的三个位置,并且对于这三个位置的不同做了一个详细的解读。</p>
<p>{ % image <a href="https://bu.dusays.com/2022/06/27/62b9cdd2affcc.webp">https://bu.dusays.com/2022/06/27/62b9cdd2affcc.webp</a> % }</p>
<p>在实现jQuery扩展方法时,一部分方法需要扩展到构造函数中,一部分方法需要扩展到原型中,当我们通读jQuery源码时,还发现有一些方法放在了模块作用域中,至于为什么会有这样的区别,建议大家回过头去读读前一篇文章。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 模块内部</span></span><br><span class="line"><span class="keyword">const</span> a = <span class="number">20</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Person</span>(<span class="params">name, age</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">age</span> = age;</span><br><span class="line"> <span class="comment">// 构造函数方法,每声明一个实例,都会重新创建一次,属于实例独有</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">getName</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">name</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 原型方法,仅在原型创建时声明一次,属于所有实例共享</span></span><br><span class="line"><span class="title class_">Person</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">getAge</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">age</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 工具方法,直接挂载在构造函数名上,仅声明一次,无法直接访问实例内部属性与方法</span></span><br><span class="line"><span class="title class_">Person</span>.<span class="property">each</span> = <span class="keyword">function</span>(<span class="params"></span>) {}</span><br></pre></td></tr></table></figure>
<p>如上例中,each就是一个工具方法,或者说静态方法。</p>
<p>工具方法的特性也和工具一词非常贴近,他们与实例的自身属性毫无关联,仅仅只是实现一些通用的功能,我们可以通过<code>$.each</code>与<code>$('div').each</code>这2个方法来体会工具方法与实例方法的不同之处。</p>
<p>在实际开发中,我们运用得非常多的一个工具库就是<code>lodash.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="title function_">ajax</span>()</span><br><span class="line">$.<span class="title function_">isFunction</span>()</span><br><span class="line">$.<span class="title function_">each</span>()</span><br><span class="line">... ...</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="string">'#test'</span>).<span class="title function_">css</span>()</span><br><span class="line">$(<span class="string">'#test'</span>).<span class="title function_">attr</span>()</span><br><span class="line">$(<span class="string">'div'</span>).<span class="title function_">each</span>()</span><br></pre></td></tr></table></figure>
<p>这样,那位同学的疑问就被搞定啦。我们在学习的时候,一定不要过分去纠结一些概念,而要明白这些概念背后的具体场景是怎么回事儿,那么学习这件事情就不会在一些奇奇怪怪的地方卡住了。</p>
<p>所以通过$.extend扩展的方法,其实就是对工具方法的扩展,而通过$.fn.extend扩展的方法,就是对于实例方法的扩展。</p>
<h2 id="jQuery插件的实现"><a href="#jQuery插件的实现" class="headerlink" title="jQuery插件的实现"></a>jQuery插件的实现</h2><p>在前面我跟大家分享了jQuery如何实现,以及他们的方法如何扩展,并且前一篇文章分享了拖拽对象的具体实现。所以建议大家自己动手尝试将拖拽扩展成为jQuery的一个实例方法,这就是一个jQuery插件了。</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><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Drag对象简化代码,完整源码可在上一篇文章中查看</span></span><br><span class="line">;</span><br><span class="line">(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 构造</span></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">Drag</span>(<span class="params">selector</span>) { }</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 原型</span></span><br><span class="line"> <span class="title class_">Drag</span>.<span class="property"><span class="keyword">prototype</span></span> = {</span><br><span class="line"> <span class="attr">constructor</span>: <span class="title class_">Drag</span>,</span><br><span class="line"></span><br><span class="line"> <span class="attr">init</span>: <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="comment">// 初始时需要做些什么事情</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">setDrag</span>();</span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 稍作改造,仅用于获取当前元素的属性,类似于getName</span></span><br><span class="line"> <span class="attr">getStyle</span>: <span class="keyword">function</span> (<span class="params">property</span>) { },</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 用来获取当前元素的位置信息,注意与之前的不同之处</span></span><br><span class="line"> <span class="attr">getPosition</span>: <span class="keyword">function</span> (<span class="params"></span>) { },</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 用来设置当前元素的位置</span></span><br><span class="line"> <span class="attr">setPostion</span>: <span class="keyword">function</span> (<span class="params">pos</span>) { },</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 该方法用来绑定事件</span></span><br><span class="line"> <span class="attr">setDrag</span>: <span class="keyword">function</span> (<span class="params"></span>) { }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 一种对外暴露的方式</span></span><br><span class="line"> <span class="variable language_">window</span>.<span class="property">Drag</span> = <span class="title class_">Drag</span>;</span><br><span class="line">})();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 通过扩展方法将拖拽扩展为jQuery的一个实例方法</span></span><br><span class="line">(<span class="keyword">function</span> (<span class="params">$</span>) {</span><br><span class="line"> $.fn.<span class="title function_">extend</span>({</span><br><span class="line"> <span class="attr">becomeDrag</span>: <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">Drag</span>(<span class="variable language_">this</span>[<span class="number">0</span>]);</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>; <span class="comment">// 注意:为了保证jQuery所有的方法都能够链式访问,每一个方法的最后都需要返回this,即返回jQuery实例</span></span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line">})(jQuery);</span><br></pre></td></tr></table></figure>
<div class="note no-icon flat"><p>原文链接:<a
前端基础进阶(十二):面向对象实战之封装拖拽对象
https://fe32.top/articles/jsnb9539/
2022-06-26T12:27:39.000Z
2023-05-17T10:01:12.000Z
<p>前几篇文章,跟大家分享了JavaScript的一些基础知识,这篇文章,将会进入第一个实战环节:利用前面几章的所涉及到的知识,封装一个拖拽对象。为了能够帮助大家了解更多的方式与进行对比,在这里将使用三种不同的方式来实现拖拽。</p>
<ul>
<li>不封装对象直接实现;</li>
<li>利用原生JavaScript封装拖拽对象;</li>
<li>通过扩展jQuery来实现拖拽对象。</li>
</ul>
<blockquote>
<p>本文的例子将会放置于<a href="https://codepen.io/">codepen.io</a>中,供大家在阅读时直接查看。如果对于codepen不了解的同学,可以花点时间稍微了解一下。</p>
</blockquote>
<h2 id="如何让一个DOM元素动起来"><a href="#如何让一个DOM元素动起来" class="headerlink" title="如何让一个DOM元素动起来"></a>如何让一个DOM元素动起来</h2><p>我们常常通过修改元素的<code>top</code>,<code>left</code>,<code>translate</code>来其的位置发生改变。在下面的例子中,每点击一次按钮,对应的元素就会移动5px。大家可点击查看。</p>
<p><a href="http://codepen.io/yangbo5207/pen/YZxGpO">点击查看一个让元素动起来的小例子</a></p>
<blockquote>
<p>由于修改一个元素top/left值会引起页面重绘,而translate不会,因此从性能优化上来判断,我们会优先使用translate属性。</p>
</blockquote>
<h2 id="如何获取当前浏览器支持的transform兼容写法"><a href="#如何获取当前浏览器支持的transform兼容写法" class="headerlink" title="如何获取当前浏览器支持的transform兼容写法"></a>如何获取当前浏览器支持的transform兼容写法</h2><p>transform是css3的属性,当使用它时就不得不面对兼容性的问题。不同版本浏览器的兼容写法大致有如下几种:</p>
<p><code>['transform', 'webkitTransform', 'MozTransform', 'msTransform', 'OTransform']</code></p>
<p>因此需要判断当前浏览器环境支持的transform属性是哪一种,方法如下:</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 获取当前浏览器支持的transform兼容写法</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">getTransform</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> transform = <span class="string">''</span>,</span><br><span class="line"> divStyle = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">'div'</span>).<span class="property">style</span>,</span><br><span class="line"> <span class="comment">// 可能涉及到的几种兼容性写法,通过循环找出浏览器识别的那一个</span></span><br><span class="line"> transformArr = [<span class="string">'transform'</span>, <span class="string">'webkitTransform'</span>, <span class="string">'MozTransform'</span>, <span class="string">'msTransform'</span>, <span class="string">'OTransform'</span>],</span><br><span class="line"></span><br><span class="line"> i = <span class="number">0</span>,</span><br><span class="line"> len = transformArr.<span class="property">length</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (; i < len; i++) {</span><br><span class="line"> <span class="keyword">if</span> (transformArr[i] <span class="keyword">in</span> divStyle) {</span><br><span class="line"> <span class="comment">// 找到之后立即返回,结束函数</span></span><br><span class="line"> <span class="keyword">return</span> transform = transformArr[i];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 如果没有找到,就直接返回空字符串</span></span><br><span class="line"> <span class="keyword">return</span> transform;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>该方法用于获取浏览器支持的transform属性。如果返回的为空字符串,则表示当前浏览器并不支持transform,这个时候就需要使用left,top值来改变元素的位置。如果支持,就改变transform的值。</p>
<h2 id="如何获取元素的初始位置"><a href="#如何获取元素的初始位置" class="headerlink" title="如何获取元素的初始位置"></a>如何获取元素的初始位置</h2><p>首先需要获取到目标元素的初始位置,因此这里需要一个专门用来获取元素样式的功能函数。</p>
<p>但是获取元素样式在IE浏览器与其他浏览器有一些不同,我们需要一个兼容性的写法。</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">function</span> <span class="title function_">getStyle</span>(<span class="params">elem, property</span>) {</span><br><span class="line"> <span class="comment">// ie通过currentStyle来获取元素的样式,其他浏览器通过getComputedStyle来获取</span></span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">document</span>.<span class="property">defaultView</span>.<span class="property">getComputedStyle</span> ? <span class="variable language_">document</span>.<span class="property">defaultView</span>.<span class="title function_">getComputedStyle</span>(elem, <span class="literal">false</span>)[property] : elem.<span class="property">currentStyle</span>[property];</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>有了这个方法之后,就可以开始动手写获取目标元素初始位置的方法了。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">getTargetPos</span>(<span class="params">elem</span>) {</span><br><span class="line"> <span class="keyword">var</span> pos = { <span class="attr">x</span>: <span class="number">0</span>, <span class="attr">y</span>: <span class="number">0</span> };</span><br><span class="line"> <span class="keyword">var</span> transform = <span class="title function_">getTransform</span>();</span><br><span class="line"> <span class="keyword">if</span> (transform) {</span><br><span class="line"> <span class="keyword">var</span> transformValue = <span class="title function_">getStyle</span>(elem, transform);</span><br><span class="line"> <span class="keyword">if</span> (transformValue == <span class="string">'none'</span>) {</span><br><span class="line"> elem.<span class="property">style</span>[transform] = <span class="string">'translate(0, 0)'</span>;</span><br><span class="line"> <span class="keyword">return</span> pos;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">var</span> temp = transformValue.<span class="title function_">match</span>(<span class="regexp">/-?\d+/g</span>);</span><br><span class="line"> <span class="keyword">return</span> pos = {</span><br><span class="line"> <span class="attr">x</span>: <span class="built_in">parseInt</span>(temp[<span class="number">4</span>].<span class="title function_">trim</span>()),</span><br><span class="line"> <span class="attr">y</span>: <span class="built_in">parseInt</span>(temp[<span class="number">5</span>].<span class="title function_">trim</span>())</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">if</span> (<span class="title function_">getStyle</span>(elem, <span class="string">'position'</span>) == <span class="string">'static'</span>) {</span><br><span class="line"> elem.<span class="property">style</span>.<span class="property">position</span> = <span class="string">'relative'</span>;</span><br><span class="line"> <span class="keyword">return</span> pos;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">var</span> x = <span class="built_in">parseInt</span>(<span class="title function_">getStyle</span>(elem, <span class="string">'left'</span>) ? <span class="title function_">getStyle</span>(elem, <span class="string">'left'</span>) : <span class="number">0</span>);</span><br><span class="line"> <span class="keyword">var</span> y = <span class="built_in">parseInt</span>(<span class="title function_">getStyle</span>(elem, <span class="string">'top'</span>) ? <span class="title function_">getStyle</span>(elem, <span class="string">'top'</span>) : <span class="number">0</span>);</span><br><span class="line"> <span class="keyword">return</span> pos = {</span><br><span class="line"> <span class="attr">x</span>: x,</span><br><span class="line"> <span class="attr">y</span>: y</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>在拖拽过程中,需要不停的设置目标元素的新位置,这样它才会移动起来,因此我们需要一个设置目标元素位置的方法。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// pos = { x: 200, y: 100 }</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">setTargetPos</span>(<span class="params">elem, pos</span>) {</span><br><span class="line"> <span class="keyword">var</span> transform = <span class="title function_">getTransform</span>();</span><br><span class="line"> <span class="keyword">if</span> (transform) {</span><br><span class="line"> elem.<span class="property">style</span>[transform] = <span class="string">'translate('</span> + pos.<span class="property">x</span> + <span class="string">'px, '</span> + pos.<span class="property">y</span> + <span class="string">'px)'</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> elem.<span class="property">style</span>.<span class="property">left</span> = pos.<span class="property">x</span> + <span class="string">'px'</span>;</span><br><span class="line"> elem.<span class="property">style</span>.<span class="property">top</span> = pos.<span class="property">y</span> + <span class="string">'px'</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> elem;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="我们需要用到哪些事件?"><a href="#我们需要用到哪些事件?" class="headerlink" title="我们需要用到哪些事件?"></a>我们需要用到哪些事件?</h2><p>在pc上的浏览器中,结合<code>mousedown</code>、<code>mousemove</code>、<code>mouseup</code>这三个事件可以帮助我们实现拖拽。</p>
<ul>
<li>mousedown 鼠标按下时触发</li>
<li>mousemove 鼠标按下后拖动时触发</li>
<li>mouseup 鼠标松开时触发</li>
</ul>
<blockquote>
<p>而在移动端,分别与之对应的则是<code>touchstart</code>、<code>touchmove</code>、<code>touchend</code>。</p>
</blockquote>
<p>当元素绑定这些事件时,有一个事件对象会作为参数传递给回调函数,通过事件对象,我们可以获取到当前鼠标的精确位置,鼠标位置信息是实现拖拽的关键。</p>
<blockquote>
<p>事件对象十分重要,其中包含了非常多的有用的信息,在这里就不扩展了,大家可以在函数中将事件对象打印出来查看其中的具体属性,这个方法对于记不清事件对象重要属性的童鞋非常有用。</p>
</blockquote>
<h2 id="拖拽的原理"><a href="#拖拽的原理" class="headerlink" title="拖拽的原理"></a>拖拽的原理</h2><p>当事件触发时,可以通过事件对象获取到鼠标的精切位置。这是实现拖拽的关键。当鼠标按下(mousedown触发)时,需要记住鼠标的初始位置与目标元素的初始位置,我们的目标就是实现当鼠标移动时,目标元素也跟着移动,根据常理我们可以得出如下关系:</p>
<blockquote>
<p>移动后的鼠标位置 - 鼠标初始位置 = 移动后的目标元素位置 - 目标元素的初始位置</p>
</blockquote>
<p>如果鼠标位置的差值我们用dis来表示,那么目标元素的位置就等于:</p>
<blockquote>
<p>移动后目标元素的位置 = dis + 目标元素的初始位置。</p>
</blockquote>
<p>通过事件对象,可以精确的知道鼠标的当前位置,因此当鼠标拖动(mousemove)时,我们可以不停的计算出鼠标移动的差值,以此来求出目标元素的当前位置。这个过程,就实现了拖拽。</p>
<p>而在鼠标松开(mouseup)结束拖拽时,我们需要处理一些收尾工作。详情见代码。</p>
<h2 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h2><ol>
<li>准备工作<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">var</span> oElem = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'target'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 声明2个变量用来保存鼠标初始位置的x,y坐标</span></span><br><span class="line"><span class="keyword">var</span> startX = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">var</span> startY = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 声明2个变量用来保存目标元素初始位置的x,y坐标</span></span><br><span class="line"><span class="keyword">var</span> sourceX = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">var</span> sourceY = <span class="number">0</span>;</span><br></pre></td></tr></table></figure></li>
<li>功能函数<br>之前已经贴过代码,就不再重复贴了。<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 获取当前浏览器支持的transform兼容写法</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">getTransform</span>(<span class="params"></span>) {}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取元素属性</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">getStyle</span>(<span class="params">elem, property</span>) {}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取元素的初始位置</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">getTargetPos</span>(<span class="params">elem</span>) {}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 设置元素的初始位置</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">setTargetPos</span>(<span class="params">elem, potions</span>) {}</span><br></pre></td></tr></table></figure></li>
<li>声明三个事件的回调函数<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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 绑定在mousedown上的回调,event为传入的事件对象</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">start</span>(<span class="params">event</span>) {</span><br><span class="line"> <span class="comment">// 获取鼠标初始位置</span></span><br><span class="line"> startX = event.<span class="property">pageX</span>;</span><br><span class="line"> startY = event.<span class="property">pageY</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 获取元素初始位置</span></span><br><span class="line"> <span class="keyword">var</span> pos = <span class="title function_">getTargetPos</span>(oElem);</span><br><span class="line"></span><br><span class="line"> sourceX = pos.<span class="property">x</span>;</span><br><span class="line"> sourceY = pos.<span class="property">y</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 绑定</span></span><br><span class="line"> <span class="variable language_">document</span>.<span class="title function_">addEventListener</span>(<span class="string">'mousemove'</span>, move, <span class="literal">false</span>);</span><br><span class="line"> <span class="variable language_">document</span>.<span class="title function_">addEventListener</span>(<span class="string">'mouseup'</span>, end, <span class="literal">false</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">move</span>(<span class="params">event</span>) {</span><br><span class="line"> <span class="comment">// 获取鼠标当前位置</span></span><br><span class="line"> <span class="keyword">var</span> currentX = event.<span class="property">pageX</span>;</span><br><span class="line"> <span class="keyword">var</span> currentY = event.<span class="property">pageY</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 计算差值</span></span><br><span class="line"> <span class="keyword">var</span> distanceX = currentX - startX;</span><br><span class="line"> <span class="keyword">var</span> distanceY = currentY - startY;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 计算并设置元素当前位置</span></span><br><span class="line"> <span class="title function_">setTargetPos</span>(oElem, {</span><br><span class="line"> <span class="attr">x</span>: (sourceX + distanceX).<span class="title function_">toFixed</span>(),</span><br><span class="line"> <span class="attr">y</span>: (sourceY + distanceY).<span class="title function_">toFixed</span>()</span><br><span class="line"> })</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">end</span>(<span class="params">event</span>) {</span><br><span class="line"> <span class="variable language_">document</span>.<span class="title function_">removeEventListener</span>(<span class="string">'mousemove'</span>, move);</span><br><span class="line"> <span class="variable language_">document</span>.<span class="title function_">removeEventListener</span>(<span class="string">'mouseup'</span>, end);</span><br><span class="line"> <span class="comment">// do other things</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
OK,一个简单的拖拽,就这样愉快的实现了。点击下面的链接,可以在线查看该例子的demo。</li>
</ol>
<p><a href="https://codepen.io/yangbo5207/pen/WpEoyd">使用原生js实现拖拽</a></p>
<h2 id="封装拖拽对象"><a href="#封装拖拽对象" class="headerlink" title="封装拖拽对象"></a>封装拖拽对象</h2><p>在<a href="https://fe32.top/articles/jsnb9538/">前端基础进阶(十一):详解面向对象、构造函数、原型与原型链</a>一文中跟大家分享了面向对象如何实现,基于那些基础知识,我们来将上面实现的拖拽封装为一个拖拽对象。目标是只要声明一个拖拽实例,传入的目标元素将自动具备可以被拖拽的功能。</p>
<p>在实际开发中,一个对象常常会被单独放在一个js文件中,这个js文件将单独作为一个模块,利用各种模块的方式组织起来使用。当然这里没有复杂的模块交互,因为这个例子,只需要一个模块即可。</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><br><span class="line">(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> ...</span><br><span class="line">})();</span><br></pre></td></tr></table></figure>
<blockquote>
<p>在普通的模块组织中,我们只是单纯的将许多js文件压缩成为一个js文件,因此此处的第一个分号则是为了防止上一个模块的结尾不用分号导致报错。必不可少。当然在通过require或者ES6模块等方式就不会出现这样的情况。</p>
</blockquote>
<p>在封装一个对象的时候,可以将属性与方法放置于构造函数或者原型中,而在增加了自执行函数之后,又可以将属性和方法放置于模块的内部作用域。</p>
<span class='p blue'>那么我们面临的挑战就在于,如何合理的处理属性与方法的位置</span> 。
<p>当然,每一个对象的情况都不一样,不能一概而论,我们需要清晰的知道这三种位置的特性才能做出最适合的决定。</p>
<ul>
<li>构造函数中: 属性与方法为当前实例单独拥有,只能被当前实例访问,并且每声明一个实例,其中的方法都会被重新创建一次。</li>
<li>原型中: 属性与方法为所有实例共同拥有,可以被所有实例访问,新声明实例不会重复创建方法。</li>
<li>模块作用域中:属性和方法不能被任何实例访问,但是能被内部方法访问,新声明的实例,不会重复创建相同的方法。</li>
</ul>
<p>对于方法的判断比较简单。</p>
<p>在构造函数中的方法总会在声明一个新的实例时被重复创建,因此我们声明的方法都尽量避免出现在构造函数中。</p>
<p>如果方法中需要用到构造函数中的变量,或者想要公开,就需要放在原型中。</p>
<p>如果方法需要私有不被外界访问,那么就放置在模块作用域中。</p>
<p>对于属性放置于什么位置有的时候很难做出正确的判断,因此我很难给出一个准确的定义告诉你什么属性一定要放在什么位置,这需要在实际开发中不断的总结经验。但是总的来说,仍然要结合这三个位置的特性来做出最合适的判断。</p>
<p>如果属性值只能被实例单独拥有,比如person对象的name,只能属于某一个person实例,又比如这里拖拽对象中,某一个元素的初始位置,也仅仅只是这个元素的当前位置,这个属性,则适合放在构造函数中。</p>
<p>而如果一个属性仅仅供内部方法访问,这个属性就适合放在模块作用域中。</p>
<p>根据这些思考,大家可以自己尝试封装一下。然后与我的做一些对比,看看我们的想法有什么不同,在下面例子的注释中,我将自己的想法表达出来。</p>
<p><a href="https://codepen.io/yangbo5207/pen/LWjWpe">点击查看已经封装好的demo</a></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// js 源码</span></span><br><span class="line">;</span><br><span class="line">(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="comment">// 这是一个私有属性,不需要被实例访问</span></span><br><span class="line"> <span class="keyword">var</span> transform = <span class="title function_">getTransform</span>();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">Drag</span>(<span class="params">selector</span>) {</span><br><span class="line"> <span class="comment">// 放在构造函数中的属性,都是属于每一个实例单独拥有</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">elem</span> = <span class="keyword">typeof</span> selector == <span class="string">'Object'</span> ? selector : <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(selector);</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">startX</span> = <span class="number">0</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">startY</span> = <span class="number">0</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">sourceX</span> = <span class="number">0</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">sourceY</span> = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">init</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 原型</span></span><br><span class="line"> <span class="title class_">Drag</span>.<span class="property"><span class="keyword">prototype</span></span> = {</span><br><span class="line"> <span class="attr">constructor</span>: <span class="title class_">Drag</span>,</span><br><span class="line"></span><br><span class="line"> <span class="attr">init</span>: <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="comment">// 初始时需要做些什么事情</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">setDrag</span>();</span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 稍作改造,仅用于获取当前元素的属性,类似于getName</span></span><br><span class="line"> <span class="attr">getStyle</span>: <span class="keyword">function</span> (<span class="params">property</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">document</span>.<span class="property">defaultView</span>.<span class="property">getComputedStyle</span> ? <span class="variable language_">document</span>.<span class="property">defaultView</span>.<span class="title function_">getComputedStyle</span>(<span class="variable language_">this</span>.<span class="property">elem</span>, <span class="literal">false</span>)[property] : <span class="variable language_">this</span>.<span class="property">elem</span>.<span class="property">currentStyle</span>[property];</span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 用来获取当前元素的位置信息,注意与之前的不同之处</span></span><br><span class="line"> <span class="attr">getPosition</span>: <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> pos = { <span class="attr">x</span>: <span class="number">0</span>, <span class="attr">y</span>: <span class="number">0</span> };</span><br><span class="line"> <span class="keyword">if</span> (transform) {</span><br><span class="line"> <span class="keyword">var</span> transformValue = <span class="variable language_">this</span>.<span class="title function_">getStyle</span>(transform);</span><br><span class="line"> <span class="keyword">if</span> (transformValue == <span class="string">'none'</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">elem</span>.<span class="property">style</span>[transform] = <span class="string">'translate(0, 0)'</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">var</span> temp = transformValue.<span class="title function_">match</span>(<span class="regexp">/-?\d+/g</span>);</span><br><span class="line"> pos = {</span><br><span class="line"> <span class="attr">x</span>: <span class="built_in">parseInt</span>(temp[<span class="number">4</span>].<span class="title function_">trim</span>()),</span><br><span class="line"> <span class="attr">y</span>: <span class="built_in">parseInt</span>(temp[<span class="number">5</span>].<span class="title function_">trim</span>())</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="title function_">getStyle</span>(<span class="string">'position'</span>) == <span class="string">'static'</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">elem</span>.<span class="property">style</span>.<span class="property">position</span> = <span class="string">'relative'</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> pos = {</span><br><span class="line"> <span class="attr">x</span>: <span class="built_in">parseInt</span>(<span class="variable language_">this</span>.<span class="title function_">getStyle</span>(<span class="string">'left'</span>) ? <span class="variable language_">this</span>.<span class="title function_">getStyle</span>(<span class="string">'left'</span>) : <span class="number">0</span>),</span><br><span class="line"> <span class="attr">y</span>: <span class="built_in">parseInt</span>(<span class="variable language_">this</span>.<span class="title function_">getStyle</span>(<span class="string">'top'</span>) ? <span class="variable language_">this</span>.<span class="title function_">getStyle</span>(<span class="string">'top'</span>) : <span class="number">0</span>)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> pos;</span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 用来设置当前元素的位置</span></span><br><span class="line"> <span class="attr">setPostion</span>: <span class="keyword">function</span> (<span class="params">pos</span>) {</span><br><span class="line"> <span class="keyword">if</span> (transform) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">elem</span>.<span class="property">style</span>[transform] = <span class="string">'translate('</span> + pos.<span class="property">x</span> + <span class="string">'px, '</span> + pos.<span class="property">y</span> + <span class="string">'px)'</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">elem</span>.<span class="property">style</span>.<span class="property">left</span> = pos.<span class="property">x</span> + <span class="string">'px'</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">elem</span>.<span class="property">style</span>.<span class="property">top</span> = pos.<span class="property">y</span> + <span class="string">'px'</span>;</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 该方法用来绑定事件</span></span><br><span class="line"> <span class="attr">setDrag</span>: <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> self = <span class="variable language_">this</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">elem</span>.<span class="title function_">addEventListener</span>(<span class="string">'mousedown'</span>, start, <span class="literal">false</span>);</span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">start</span>(<span class="params">event</span>) {</span><br><span class="line"> self.<span class="property">startX</span> = event.<span class="property">pageX</span>;</span><br><span class="line"> self.<span class="property">startY</span> = event.<span class="property">pageY</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> pos = self.<span class="title function_">getPosition</span>();</span><br><span class="line"></span><br><span class="line"> self.<span class="property">sourceX</span> = pos.<span class="property">x</span>;</span><br><span class="line"> self.<span class="property">sourceY</span> = pos.<span class="property">y</span>;</span><br><span class="line"></span><br><span class="line"> <span class="variable language_">document</span>.<span class="title function_">addEventListener</span>(<span class="string">'mousemove'</span>, move, <span class="literal">false</span>);</span><br><span class="line"> <span class="variable language_">document</span>.<span class="title function_">addEventListener</span>(<span class="string">'mouseup'</span>, end, <span class="literal">false</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">move</span>(<span class="params">event</span>) {</span><br><span class="line"> <span class="keyword">var</span> currentX = event.<span class="property">pageX</span>;</span><br><span class="line"> <span class="keyword">var</span> currentY = event.<span class="property">pageY</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> distanceX = currentX - self.<span class="property">startX</span>;</span><br><span class="line"> <span class="keyword">var</span> distanceY = currentY - self.<span class="property">startY</span>;</span><br><span class="line"></span><br><span class="line"> self.<span class="title function_">setPostion</span>({</span><br><span class="line"> <span class="attr">x</span>: (self.<span class="property">sourceX</span> + distanceX).<span class="title function_">toFixed</span>(),</span><br><span class="line"> <span class="attr">y</span>: (self.<span class="property">sourceY</span> + distanceY).<span class="title function_">toFixed</span>()</span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">end</span>(<span class="params">event</span>) {</span><br><span class="line"> <span class="variable language_">document</span>.<span class="title function_">removeEventListener</span>(<span class="string">'mousemove'</span>, move);</span><br><span class="line"> <span class="variable language_">document</span>.<span class="title function_">removeEventListener</span>(<span class="string">'mouseup'</span>, end);</span><br><span class="line"> <span class="comment">// do other things</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 私有方法,仅仅用来获取transform的兼容写法</span></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">getTransform</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> transform = <span class="string">''</span>,</span><br><span class="line"> divStyle = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">'div'</span>).<span class="property">style</span>,</span><br><span class="line"> transformArr = [<span class="string">'transform'</span>, <span class="string">'webkitTransform'</span>, <span class="string">'MozTransform'</span>, <span class="string">'msTransform'</span>, <span class="string">'OTransform'</span>],</span><br><span class="line"></span><br><span class="line"> i = <span class="number">0</span>,</span><br><span class="line"> len = transformArr.<span class="property">length</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (; i < len; i++) {</span><br><span class="line"> <span class="keyword">if</span> (transformArr[i] <span class="keyword">in</span> divStyle) {</span><br><span class="line"> <span class="keyword">return</span> transform = transformArr[i];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> transform;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 一种对外暴露的方式</span></span><br><span class="line"> <span class="variable language_">window</span>.<span class="property">Drag</span> = <span class="title class_">Drag</span>;</span><br><span class="line">})();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用:声明2个拖拽实例</span></span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Drag</span>(<span class="string">'target'</span>);</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Drag</span>(<span class="string">'target2'</span>);</span><br></pre></td></tr></table></figure>
<p>这样一个拖拽对象就封装完毕了。</p>
<div class="note no-icon flat"><p>原文链接:<a
前端基础进阶(十一):详解面向对象、构造函数、原型与原型链
https://fe32.top/articles/jsnb9538/
2022-06-22T13:32:24.000Z
2023-05-17T10:01:12.000Z
<h2 id="对象的定义"><a href="#对象的定义" class="headerlink" title="对象的定义"></a>对象的定义</h2><p>在ECMAScript-262中,对象被定义为“ <span class='p blue'>无序属性的集合,其属性可以包含基本值,对象或者函数</span> ”。</p>
<p>也就是说,在JavaScript中,对象无非就是由一些列无序的<code>key-value</code>对组成。其中value可以是基本值,对象或者函数。</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">// 这里的person就是一个对象</span></span><br><span class="line"><span class="keyword">var</span> person = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'Tom'</span>,</span><br><span class="line"> <span class="attr">age</span>: <span class="number">18</span>,</span><br><span class="line"> <span class="attr">getName</span>: <span class="keyword">function</span>(<span class="params"></span>) {},</span><br><span class="line"> <span class="attr">parent</span>: {}</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="创建对象"><a href="#创建对象" class="headerlink" title="创建对象"></a>创建对象</h3><p>可以通过new的方式创建一个对象。</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">var</span> obj = <span class="keyword">new</span> <span class="title class_">Object</span>();</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = {};</span><br></pre></td></tr></table></figure>
<p>想要给简单对象添加方法时,可以这样表示,</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 可以这样</span></span><br><span class="line"><span class="keyword">var</span> person = {};</span><br><span class="line">person.<span class="property">name</span> = <span class="string">"TOM"</span>;</span><br><span class="line">person.<span class="property">getName</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">name</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 也可以这样</span></span><br><span class="line"><span class="keyword">var</span> person = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">"TOM"</span>,</span><br><span class="line"> <span class="attr">getName</span>: <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">name</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="访问对象的属性和方法"><a href="#访问对象的属性和方法" class="headerlink" title="访问对象的属性和方法"></a>访问对象的属性和方法</h3><p>假如有一个简单的对象如下:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> person = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'TOM'</span>,</span><br><span class="line"> <span class="attr">age</span>: <span class="string">'20'</span>,</span><br><span class="line"> <span class="attr">getName</span>: <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">name</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>想要访问它的name属性时,可以用如下两种方式访问。</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">person.<span class="property">name</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 或者</span></span><br><span class="line">person[<span class="string">'name'</span>]</span><br></pre></td></tr></table></figure>
<p>如果想要访问的属性名是一个变量时,常常会使用第二种方式。例如我们要同时访问person的name与age,可以这样写:</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="string">'name'</span>, <span class="string">'age'</span>].<span class="title function_">forEach</span>(<span class="keyword">function</span>(<span class="params">item</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(person[item]);</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<h2 id="工厂模式"><a href="#工厂模式" class="headerlink" title="工厂模式"></a>工厂模式</h2><p>使用上面的方式创建对象虽然简单,但是在很多时候并不能满足需求。以person对象为例,假如在实际开发中,不仅仅需要一个名字叫做TOM的person对象,同时还需要另外一个名为Jake的person对象,虽然它们有很多相似之处,但是不得不重复写两次。</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="keyword">var</span> perTom = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'TOM'</span>,</span><br><span class="line"> <span class="attr">age</span>: <span class="number">20</span>,</span><br><span class="line"> <span class="attr">getName</span>: <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">name</span></span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> perJake = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'Jake'</span>,</span><br><span class="line"> <span class="attr">age</span>: <span class="number">22</span>,</span><br><span class="line"> <span class="attr">getName</span>: <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">name</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>很显然这并不是合理的方式,当相似对象太多时,大家都会崩溃掉。</p>
<p>可以使用工厂模式解决这个问题。顾名思义,工厂模式就是我们提供一个模子,然后通过这个模子复制出需要的对象。需要多少个,就复制多少个。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> createPerson = <span class="keyword">function</span>(<span class="params">name, age</span>) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 声明一个中间对象,该对象就是工厂模式的模子</span></span><br><span class="line"> <span class="keyword">var</span> o = <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 依次添加我们需要的属性与方法</span></span><br><span class="line"> o.<span class="property">name</span> = name;</span><br><span class="line"> o.<span class="property">age</span> = age;</span><br><span class="line"> o.<span class="property">getName</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">name</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> o;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建两个实例</span></span><br><span class="line"><span class="keyword">var</span> perTom = <span class="title function_">createPerson</span>(<span class="string">'TOM'</span>, <span class="number">20</span>);</span><br><span class="line"><span class="keyword">var</span> <span class="title class_">PerJake</span> = <span class="title function_">createPerson</span>(<span class="string">'Jake'</span>, <span class="number">22</span>);</span><br></pre></td></tr></table></figure>
<p>工厂模式帮助我们解决了重复代码上的麻烦,让我们可以写很少的代码,就能够创建很多个person对象。但是这里还有两个麻烦,需要我们注意。</p>
<p>第一个麻烦就是:没有办法识别对象实例的类型。我们可以使用 instanceof 识别对象的类型,如下例子:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = {};</span><br><span class="line"><span class="keyword">var</span> foo = <span class="keyword">function</span>(<span class="params"></span>) {}</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(obj <span class="keyword">instanceof</span> <span class="title class_">Object</span>); <span class="comment">// true</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(foo <span class="keyword">instanceof</span> <span class="title class_">Function</span>); <span class="comment">// true</span></span><br></pre></td></tr></table></figure>
<p>因此在工厂模式的基础上,我们需要使用 <span class='p blue'>构造函数</span> 的方式来解决这个麻烦。</p>
<h2 id="构造函数"><a href="#构造函数" class="headerlink" title="构造函数"></a>构造函数</h2><p>在JavaScript中,<span class='p blue'>new关键字</span> 可以让一个函数变得与众不同。通过下面的例子,我们来一探 <span class='p blue'>new关键字</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="keyword">function</span> <span class="title function_">demo</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">demo</span>(); <span class="comment">// window</span></span><br><span class="line"><span class="keyword">new</span> <span class="title function_">demo</span>(); <span class="comment">// demo</span></span><br></pre></td></tr></table></figure>
<p>为了能够直观地感受它们不同,建议大家动手实践观察一下。很显然,使用new之后,函数内部发生了事情,让this指向改变。new关键字到底做了什么?</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 先一本正经的创建一个构造函数,其实该函数与普通函数并无区别</span></span><br><span class="line"><span class="keyword">var</span> <span class="title class_">Person</span> = <span class="keyword">function</span>(<span class="params">name, age</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">age</span> = age;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">getName</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">name</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 将构造函数以参数形式传入</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">New</span>(<span class="params">func</span>) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 声明一个中间对象,该对象为最终返回的实例</span></span><br><span class="line"> <span class="keyword">var</span> res = {};</span><br><span class="line"> <span class="keyword">if</span> (func.<span class="property"><span class="keyword">prototype</span></span> !== <span class="literal">null</span>) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 将实例的原型指向构造函数的原型</span></span><br><span class="line"> res.<span class="property">__proto__</span> = func.<span class="property"><span class="keyword">prototype</span></span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// ret为构造函数执行的结果,这里通过apply,将构造函数内部的this指向修改为指向res,即为实例对象</span></span><br><span class="line"> <span class="keyword">var</span> ret = func.<span class="title function_">apply</span>(res, <span class="title class_">Array</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">slice</span>.<span class="title function_">call</span>(<span class="variable language_">arguments</span>, <span class="number">1</span>));</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 当我们在构造函数中明确指定了返回对象时,那么new的执行结果就是该返回对象</span></span><br><span class="line"> <span class="keyword">if</span> ((<span class="keyword">typeof</span> ret === <span class="string">"object"</span> || <span class="keyword">typeof</span> ret === <span class="string">"function"</span>) && ret !== <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">return</span> ret;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 如果没有明确指定返回对象,则默认返回res,这个res就是实例对象</span></span><br><span class="line"> <span class="keyword">return</span> res;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 通过new声明创建实例,这里的p1,实际接收的正是new中返回的res</span></span><br><span class="line"><span class="keyword">var</span> p1 = <span class="title class_">New</span>(<span class="title class_">Person</span>, <span class="string">'tom'</span>, <span class="number">20</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(p1.<span class="title function_">getName</span>());</span><br><span class="line"></span><br><span class="line"><span class="comment">// 当然,这里也可以判断出实例的类型了</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(p1 <span class="keyword">instanceof</span> <span class="title class_">Person</span>); <span class="comment">// true</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>JavaScript内部再通过其他的一些特殊处理,将<code>var p1 = New(Person, 'tom', 20)</code> 等效于<code>var p1 = new Person('tom', 20)</code>。就是我们认识的new关键字了。具体怎么处理的,我也不知道,别刨根问底了,一直回答下去我太难了 !</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><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> <span class="title class_">Person</span> = <span class="keyword">function</span>(<span class="params">name, age</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">age</span> = age;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">getName</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">name</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> p1 = <span class="keyword">new</span> <span class="title class_">Person</span>(<span class="string">'Ness'</span>, <span class="number">20</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(p1.<span class="title function_">getName</span>()); <span class="comment">// Ness</span></span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(p1 <span class="keyword">instanceof</span> <span class="title class_">Person</span>); <span class="comment">// true</span></span><br></pre></td></tr></table></figure>
<p>关于构造函数,如果你暂时不能够理解new的具体实现,就先记住下面这几个结论:</p>
<ul>
<li>与普通函数相比,构造函数并没有任何特别的地方,首字母大写只是我们约定的小规定,用于区分普通函数。</li>
<li>new关键字让构造函数具有了与普通函数不同的许多特点,而new的过程中,执行了如下过程:<ol>
<li>声明一个中间对象;</li>
<li>将该中间对象的原型指向构造函数的原型;</li>
<li>将构造函数的this,指向该中间对象;</li>
<li>返回该中间对象,即返回实例对象。</li>
</ol>
</li>
</ul>
<h2 id="原型"><a href="#原型" class="headerlink" title="原型"></a>原型</h2><p>构造函数虽然解决了判断实例类型的问题,但终究是一个对象复制的过程。跟工厂模式颇有相似之处。也就是说,当我们声明了100个person对象,就有100个getName方法被重新生成。</p>
<p>每一个getName方法实现的功能其实是一模一样的,但由于分别属于不同的实例,就不得不一直不停的为getName分配空间。这就是工厂模式存在的第二个麻烦。</p>
<p>显然这是不合理的。我们期望的是,既然都是实现同一个功能,那么能不能就让每一个实例对象都访问同一个方法?当然能,这就是原型对象要帮我们解决的问题了。</p>
<p>每创建一个函数,都有一个prototype属性,该属性指向一个对象。这个对象,就是我们这里说的原型对象。</p>
<p>创建对象时,可以根据自己的需求,选择性的将一些属性和方法通过prototype属性,挂载在原型对象上。而每一个new出来的实例,都有一个__proto__属性,该属性指向构造函数的原型对象,通过这个属性,让实例对象也能够访问原型对象上的方法。因此,当所有的实例都能够通过__proto__访问到原型对象时,原型对象的方法与属性就变成了共有方法与属性。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 声明构造函数</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Person</span>(<span class="params">name, age</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">age</span> = age;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 通过prototye属性,将方法挂载到原型对象上</span></span><br><span class="line"><span class="title class_">Person</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">getName</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">name</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> p1 = <span class="keyword">new</span> <span class="title class_">Person</span>(<span class="string">'tim'</span>, <span class="number">10</span>);</span><br><span class="line"><span class="keyword">var</span> p2 = <span class="keyword">new</span> <span class="title class_">Person</span>(<span class="string">'jak'</span>, <span class="number">22</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(p1.<span class="property">getName</span> === p2.<span class="property">getName</span>); <span class="comment">// true</span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/23/62b4709b17dc2.webp"/></div></div>
<p>通过上图可以看出,构造函数的prototype与所有实例对象的__proto__都指向原型对象。而原型对象的constructor指向构造函数。</p>
<p>除此之外,还可以从图中看出,实例对象实际上对前面所说的中间对象的复制,而中间对象中的属性与方法都在构造函数中添加。于是根据构造函数与原型的特性,可以在构造函数中,通过this声明的属性与方法称为私有变量与方法,它们被当前被某一个实例对象所独有。而通过原型声明的属性与方法,我们可以称之为共有属性与方法,它们可以被所有的实例对象访问。</p>
<p class='p blue'>当我们访问实例对象中的属性或者方法时,会优先访问实例对象自身的属性和方法。</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="keyword">function</span> <span class="title function_">Person</span>(<span class="params">name, age</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">age</span> = age;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">getName</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'this is constructor.'</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title class_">Person</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">getName</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">name</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> p1 = <span class="keyword">new</span> <span class="title class_">Person</span>(<span class="string">'tim'</span>, <span class="number">10</span>);</span><br><span class="line"></span><br><span class="line">p1.<span class="title function_">getName</span>(); <span class="comment">// this is constructor.</span></span><br></pre></td></tr></table></figure>
<p>在这个例子中,我们同时在原型与构造函数中都声明了一个getName函数,运行代码的结果表示原型中的访问并没有被访问。</p>
<p>我们还可以通过in来判断,一个对象是否拥有某一个属性/方法,无论是该属性/方法存在于实例对象还是原型对象。</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">function</span> <span class="title function_">Person</span>(<span class="params">name, age</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">age</span> = age;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title class_">Person</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">getName</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">name</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> p1 = <span class="keyword">new</span> <span class="title class_">Person</span>(<span class="string">'tim'</span>, <span class="number">10</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'name'</span> <span class="keyword">in</span> p1); <span class="comment">// true</span></span><br></pre></td></tr></table></figure>
<h3 id="更简单的原型写法"><a href="#更简单的原型写法" class="headerlink" title="更简单的原型写法"></a>更简单的原型写法</h3><p>根据前面例子的写法,如果要在原型上添加更多的方法,可以这样写:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Person</span>(<span class="params"></span>) {}</span><br><span class="line"></span><br><span class="line"><span class="title class_">Person</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">getName</span> = <span class="keyword">function</span>(<span class="params"></span>) {}</span><br><span class="line"><span class="title class_">Person</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">getAge</span> = <span class="keyword">function</span>(<span class="params"></span>) {}</span><br><span class="line"><span class="title class_">Person</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">sayHello</span> = <span class="keyword">function</span>(<span class="params"></span>) {}</span><br><span class="line">... ...</span><br></pre></td></tr></table></figure>
<p>除此之外,还可以使用更为简单的写法。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Person</span>(<span class="params"></span>) {}</span><br><span class="line"></span><br><span class="line"><span class="title class_">Person</span>.<span class="property"><span class="keyword">prototype</span></span> = {</span><br><span class="line"> <span class="attr">constructor</span>: <span class="title class_">Person</span>,</span><br><span class="line"> <span class="attr">getName</span>: <span class="keyword">function</span>(<span class="params"></span>) {},</span><br><span class="line"> <span class="attr">getAge</span>: <span class="keyword">function</span>(<span class="params"></span>) {},</span><br><span class="line"> <span class="attr">sayHello</span>: <span class="keyword">function</span>(<span class="params"></span>) {}</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>这种字面量的写法看上去简单很多,但是有一个需要特别注意的地方。<code>Person.prototype = {}</code>实际上是重新创建了一个<code>{}</code>对象并赋值给<code>Person.prototype</code>,这里的<code>{}</code>并不是最初的那个原型对象。因此它里面并不包含<code>constructor</code>属性。为了保证正确性,我们必须在新创建的<code>{}</code>对象中显示的设置<code>constructor</code>的指向。即上面的<code>constructor: Person</code>。</p>
<h2 id="原型链"><a href="#原型链" class="headerlink" title="原型链"></a>原型链</h2><p>原型对象其实也是普通的对象。几乎所有的对象都可能是原型对象,也可能是实例对象,而且还可以同时是原型对象与实例对象。这样的一个对象,正是构成原型链的一个节点。因此理解了原型,原型链并不是一个多么复杂的概念。</p>
<p>我们知道所有的函数都有一个叫做toString的方法。那么这个方法到底是在哪里的呢?</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="keyword">function</span> <span class="title function_">add</span>(<span class="params"></span>) {}</span><br></pre></td></tr></table></figure>
<p>用如下的图来表示这个函数的原型链:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/23/62b47cadc4087.webp" alt="原型链"/></div><span class="image-caption">原型链</span></div>
<p>其中add是Function对象的实例。而Function的原型对象同时又是Object的实例。这样就构成了一条原型链。原型链的访问,其实跟作用域链有很大的相似之处,它们都是一次单向的查找过程。因此实例对象能够通过原型链,访问到处于原型链上对象的所有属性与方法。这也是foo最终能够访问到处于Object原型对象上的toString方法的原因。</p>
<p>基于原型链的特性,我们可以很轻松的实现 <span class='p blue'>继承</span> 。</p>
<h2 id="继承"><a href="#继承" class="headerlink" title="继承"></a>继承</h2><p>我们常常结合构造函数与原型来创建一个对象。因为构造函数与原型的不同特性,因此当我们想要实现继承时,就必须得根据构造函数与原型的不同而采取不同的策略。</p>
<p>声明一个Person对象,该对象将作为父级,而子级cPerson将要继承Person的所有属性与方法。</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">function</span> <span class="title function_">Person</span>(<span class="params">name, age</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">age</span> = age;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title class_">Person</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">getName</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">name</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>首先来看构造函数的继承。在上面我们已经理解了构造函数的本质,它其实是在new内部实现的一个复制过程。而我们在继承时想要的,就是想父级构造函数中的操作在子级的构造函数中重现一遍即可。我们可以通过call方法来达到目的。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 构造函数的继承</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">cPerson</span>(<span class="params">name, age, job</span>) {</span><br><span class="line"> <span class="title class_">Person</span>.<span class="title function_">call</span>(<span class="variable language_">this</span>, name, age);</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">job</span> = job;</span><br><span class="line">}</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 继承原型</span></span><br><span class="line">cPerson.<span class="property"><span class="keyword">prototype</span></span> = <span class="keyword">new</span> <span class="title class_">Person</span>(name, age);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 添加更多方法</span></span><br><span class="line">cPerson.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">getLive</span> = <span class="keyword">function</span>(<span class="params"></span>) {}</span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/23/62b4814c51a07.webp" alt="原型链"/></div><span class="image-caption">原型链</span></div>
<h2 id="更好的继承"><a href="#更好的继承" class="headerlink" title="更好的继承"></a>更好的继承</h2><p>假设原型链的终点<code>Object.prototype</code>为原型链的E(end)端,原型链的起点为S(start)端。</p>
<p>通过前面对原型链的学习,我们知道,处于S端的对象,可以通过S -> E的单向查找,访问到原型链上的所有方法与属性。因此这给继承提供了理论基础。我们只需要在S端添加新的对象,那么新对象就能够通过原型链访问到父级的方法与属性。因此想要实现继承,是一件非常简单的事情。</p>
<p>因为封装一个对象由构造函数与原型共同组成,因此继承也会分别有构造函数的继承与原型的继承。</p>
<p>假设我们已经封装好了一个父类对象Person。如下:</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">var</span> <span class="title class_">Person</span> = <span class="keyword">function</span>(<span class="params">name, age</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">age</span> = age;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title class_">Person</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">getName</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">name</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title class_">Person</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">getAge</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">age</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>构造函数的继承比较简单,可以借助call/apply来实现。假设我们要通过继承封装一个Student的子类对象。那么构造函数可以如下实现。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> <span class="title class_">Student</span> = <span class="keyword">function</span>(<span class="params">name, age, grade</span>) {</span><br><span class="line"> <span class="comment">// 通过call方法还原Person构造函数中的所有处理逻辑</span></span><br><span class="line"> <span class="title class_">Person</span>.<span class="title function_">call</span>(<span class="variable language_">this</span>, name, age);</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">grade</span> = grade;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 等价于</span></span><br><span class="line"><span class="keyword">var</span> <span class="title class_">Student</span> = <span class="keyword">function</span>(<span class="params">name, age, grade</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">age</span> = age;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">grade</span> = grade;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>原型的继承则只需要让子类对象的原型,成为父类对象的一个实例,然后通过__proto__就可以访问父类对象的原型。这样就继承了父类原型中的方法与属性了。</p>
<p>我们可以先封装一个方法,该方法根据父类对象的原型创建一个实例,该实例将会作为子类对象的原型。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">create</span>(<span class="params">proto, options</span>) {</span><br><span class="line"> <span class="comment">// 创建一个空对象</span></span><br><span class="line"> <span class="keyword">var</span> tmp = {};</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 让这个新的空对象成为父类对象的实例</span></span><br><span class="line"> tmp.<span class="property">__proto__</span> = proto;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 传入的方法都挂载到新对象上,新的对象将作为子类对象的原型</span></span><br><span class="line"> <span class="title class_">Object</span>.<span class="title function_">defineProperties</span>(tmp, options);</span><br><span class="line"> <span class="keyword">return</span> tmp;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>简单封装<code>create</code>对象之后就可以使用该方法来实现原型的继承了。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Student</span>.<span class="property"><span class="keyword">prototype</span></span> = <span class="title function_">create</span>(<span class="title class_">Person</span>.<span class="property"><span class="keyword">prototype</span></span>, {</span><br><span class="line"> <span class="comment">// 不要忘了重新指定构造函数</span></span><br><span class="line"> <span class="attr">constructor</span>: {</span><br><span class="line"> <span class="attr">value</span>: <span class="title class_">Student</span></span><br><span class="line"> }</span><br><span class="line"> <span class="attr">getGrade</span>: {</span><br><span class="line"> <span class="attr">value</span>: <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">grade</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">})</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> s1 = <span class="keyword">new</span> <span class="title class_">Student</span>(<span class="string">'ming'</span>, <span class="number">22</span>, <span class="number">5</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(s1.<span class="title function_">getName</span>()); <span class="comment">// ming</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(s1.<span class="title function_">getAge</span>()); <span class="comment">// 22</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(s1.<span class="title function_">getGrade</span>()); <span class="comment">// 5</span></span><br></pre></td></tr></table></figure>
<p>全部都能正常访问,没问题。在ECMAScript5中直接提供了一个<code>Object.create</code>方法来完成我们上面自己封装的<code>create</code>的功能。因此我们可以直接使用<code>Object.create</code>。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Student</span>.<span class="property"><span class="keyword">prototype</span></span> = <span class="title function_">create</span>(<span class="title class_">Person</span>.<span class="property"><span class="keyword">prototype</span></span>, {</span><br><span class="line"> <span class="comment">// 不要忘了重新指定构造函数</span></span><br><span class="line"> <span class="attr">constructor</span>: {</span><br><span class="line"> <span class="attr">value</span>: <span class="title class_">Student</span></span><br><span class="line"> }</span><br><span class="line"> <span class="attr">getGrade</span>: {</span><br><span class="line"> <span class="attr">value</span>: <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">grade</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<p>完整代码如下:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Person</span>(<span class="params">name, age</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">age</span> = age;</span><br><span class="line">}</span><br><span class="line"><span class="title class_">Person</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">getName</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">name</span></span><br><span class="line">}</span><br><span class="line"><span class="title class_">Person</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">getAge</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">age</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Student</span>(<span class="params">name, age, grade</span>) {</span><br><span class="line"> <span class="comment">// 构造函数继承</span></span><br><span class="line"> <span class="title class_">Person</span>.<span class="title function_">call</span>(<span class="variable language_">this</span>, name, age);</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">grade</span> = grade;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 原型继承</span></span><br><span class="line"><span class="title class_">Student</span>.<span class="property"><span class="keyword">prototype</span></span> = <span class="title class_">Object</span>.<span class="title function_">create</span>(<span class="title class_">Person</span>.<span class="property"><span class="keyword">prototype</span></span>, {</span><br><span class="line"> <span class="comment">// 不要忘了重新指定构造函数</span></span><br><span class="line"> <span class="attr">constructor</span>: {</span><br><span class="line"> <span class="attr">value</span>: <span class="title class_">Student</span></span><br><span class="line"> }</span><br><span class="line"> <span class="attr">getGrade</span>: {</span><br><span class="line"> <span class="attr">value</span>: <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">grade</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> s1 = <span class="keyword">new</span> <span class="title class_">Student</span>(<span class="string">'ming'</span>, <span class="number">22</span>, <span class="number">5</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(s1.<span class="title function_">getName</span>()); <span class="comment">// ming</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(s1.<span class="title function_">getAge</span>()); <span class="comment">// 22</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(s1.<span class="title function_">getGrade</span>()); <span class="comment">// 5</span></span><br></pre></td></tr></table></figure>
<h2 id="属性类型"><a href="#属性类型" class="headerlink" title="属性类型"></a>属性类型</h2><p>在上面的继承实现中,使用了一个大家可能不太熟悉的方法<code>defineProperties</code>。并且在定义<code>getGrade</code>时使用了一个很奇怪的方式。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">getGrade</span>: {</span><br><span class="line"> <span class="attr">value</span>: <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">grade</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>这其实是对象中的属性类型。在我们平常的使用中,给对象添加一个属性时,直接使用<code>object.param</code>的方式就可以了,或者直接在对象中挂载。</p>
<p>在ECMAScript5中,对每个属性都添加了几个属性类型,来描述这些属性的特点。它们分别是:</p>
<ul>
<li>configurable: 表示该属性是否能被delete删除。当其值为false时,其他的特性也不能被改变。默认值为true</li>
<li>enumerable: 是否能枚举。也就是是否能被for-in遍历。默认值为true</li>
<li>writable: 是否能修改值。默认为true</li>
<li>value: 该属性的具体值是多少。默认为undefined</li>
<li>get: 当我们通过<code>person.name</code>访问name的值时,get将被调用。该方法可以自定义返回的具体值是多少。get默认值为undefined</li>
<li>set: 当我们通过<code>person.name = 'Jake'</code>设置name的值时,set方法将被调用。该方法可以自定义设置值的具体方式。set默认值为undefined</li>
</ul>
<blockquote>
<p>需要注意的是,不能同时设置value、writable 与 get、set的值。</p>
</blockquote>
<p>我们可以通过<code>Object.defineProperty</code>方法来修改这些属性类型。下面用一些简单的例子来演示一下这些属性类型的具体表现。</p>
<h3 id="configurable"><a href="#configurable" class="headerlink" title="configurable"></a>configurable</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 用普通的方式给person对象添加一个name属性,值为TOM</span></span><br><span class="line"><span class="keyword">var</span> person = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'TOM'</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用delete删除该属性</span></span><br><span class="line"><span class="keyword">delete</span> person.<span class="property">name</span>; <span class="comment">// 返回true 表示删除成功</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 通过Object.defineProperty重新添加name属性</span></span><br><span class="line"><span class="comment">// 并设置name的属性类型的configurable为false,表示不能再用delete删除</span></span><br><span class="line"><span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(person, <span class="string">'name'</span>, {</span><br><span class="line"> <span class="attr">configurable</span>: <span class="literal">false</span>,</span><br><span class="line"> <span class="attr">value</span>: <span class="string">'Jake'</span> <span class="comment">// 设置name属性的值</span></span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="comment">// 再次delete,已经不能删除了</span></span><br><span class="line"><span class="keyword">delete</span> person.<span class="property">name</span> <span class="comment">// false</span></span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(person.<span class="property">name</span>) <span class="comment">// 值为Jake</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 试图改变value</span></span><br><span class="line">person.<span class="property">name</span> = <span class="string">"alex"</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(person.<span class="property">name</span>) <span class="comment">// Jake 改变失败</span></span><br></pre></td></tr></table></figure>
<h3 id="enumerable"><a href="#enumerable" class="headerlink" title="enumerable"></a>enumerable</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> person = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'TOM'</span>,</span><br><span class="line"> <span class="attr">age</span>: <span class="number">20</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用for-in枚举person的属性</span></span><br><span class="line"><span class="keyword">var</span> params = [];</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">var</span> key <span class="keyword">in</span> person) {</span><br><span class="line"> params.<span class="title function_">push</span>(key);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 查看枚举结果</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(params); <span class="comment">// ['name', 'age']</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 重新设置name属性的类型,让其不可被枚举</span></span><br><span class="line"><span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(person, <span class="string">'name'</span>, {</span><br><span class="line"> <span class="attr">enumerable</span>: <span class="literal">false</span></span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> params_ = [];</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">var</span> key <span class="keyword">in</span> person) {</span><br><span class="line"> params_.<span class="title function_">push</span>(key)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 再次查看枚举结果</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(params_); <span class="comment">// ['age']</span></span><br></pre></td></tr></table></figure>
<h3 id="writable"><a href="#writable" class="headerlink" title="writable"></a>writable</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> person = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'TOM'</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 修改name的值</span></span><br><span class="line">person.<span class="property">name</span> = <span class="string">'Jake'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 查看修改结果</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(person.<span class="property">name</span>); <span class="comment">// Jake 修改成功</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 设置name的值不能被修改</span></span><br><span class="line"><span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(person, <span class="string">'name'</span>, {</span><br><span class="line"> <span class="attr">writable</span>: <span class="literal">false</span></span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="comment">// 再次试图修改name的值</span></span><br><span class="line">person.<span class="property">name</span> = <span class="string">'alex'</span>;</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(person.<span class="property">name</span>); <span class="comment">// Jake 修改失败</span></span><br></pre></td></tr></table></figure>
<h3 id="value"><a href="#value" class="headerlink" title="value"></a>value</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> person = {}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 添加一个name属性</span></span><br><span class="line"><span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(person, <span class="string">'name'</span>, {</span><br><span class="line"> <span class="attr">value</span>: <span class="string">'TOM'</span></span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(person.<span class="property">name</span>) <span class="comment">// TOM</span></span><br></pre></td></tr></table></figure>
<h3 id="get-x2F-set"><a href="#get-x2F-set" class="headerlink" title="get/set"></a>get/set</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> person = {}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 通过get与set自定义访问与设置name属性的方式</span></span><br><span class="line"><span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(person, <span class="string">'name'</span>, {</span><br><span class="line"> <span class="attr">get</span>: <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="comment">// 一直返回TOM</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">'TOM'</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">set</span>: <span class="keyword">function</span>(<span class="params">value</span>) {</span><br><span class="line"> <span class="comment">// 设置name属性时,返回该字符串,value为新值</span></span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(value + <span class="string">' in set'</span>);</span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="comment">// 第一次访问name,调用get</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(person.<span class="property">name</span>) <span class="comment">// TOM</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 尝试修改name值,此时set方法被调用</span></span><br><span class="line">person.<span class="property">name</span> = <span class="string">'alex'</span> <span class="comment">// alex in set</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 第二次访问name,还是调用get</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(person.<span class="property">name</span>) <span class="comment">// TOM</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>请尽量同时设置get、set。如果仅仅只设置了get,那么将无法设置该属性值。如果仅仅只设置了set,也无法读取该属性的值。</p>
</blockquote>
<p><code>Object.defineProperty</code>只能设置一个属性的属性特性。当我们想要同时设置多个属性的特性时,需要使用之前提到过的<code>Object.defineProperties</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> person = {}</span><br><span class="line"></span><br><span class="line"><span class="title class_">Object</span>.<span class="title function_">defineProperties</span>(person, {</span><br><span class="line"> <span class="attr">name</span>: {</span><br><span class="line"> <span class="attr">value</span>: <span class="string">'Jake'</span>,</span><br><span class="line"> <span class="attr">configurable</span>: <span class="literal">true</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">age</span>: {</span><br><span class="line"> <span class="attr">get</span>: <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">value</span> || <span class="number">22</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">set</span>: <span class="keyword">function</span>(<span class="params">value</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">value</span> = value</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">person.<span class="property">name</span> <span class="comment">// Jake</span></span><br><span class="line">person.<span class="property">age</span> <span class="comment">// 22</span></span><br></pre></td></tr></table></figure>
<h3 id="读取属性的特性值"><a href="#读取属性的特性值" class="headerlink" title="读取属性的特性值"></a>读取属性的特性值</h3><p>我们可以使用<code>Object.getOwnPropertyDescriptor</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> person = {}</span><br><span class="line"></span><br><span class="line"><span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(person, <span class="string">'name'</span>, {</span><br><span class="line"> <span class="attr">value</span>: <span class="string">'alex'</span>,</span><br><span class="line"> <span class="attr">writable</span>: <span class="literal">false</span>,</span><br><span class="line"> <span class="attr">configurable</span>: <span class="literal">false</span></span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> descripter = <span class="title class_">Object</span>.<span class="title function_">getOwnPropertyDescriptor</span>(person, <span class="string">'name'</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(descripter); <span class="comment">// 返回结果如下</span></span><br><span class="line"></span><br><span class="line">descripter = {</span><br><span class="line"> <span class="attr">configurable</span>: <span class="literal">false</span>,</span><br><span class="line"> <span class="attr">enumerable</span>: <span class="literal">false</span>,</span><br><span class="line"> <span class="attr">value</span>: <span class="string">'alex'</span>,</span><br><span class="line"> <span class="attr">writable</span>: <span class="literal">false</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<div class="note no-icon flat"><p>原文链接:<a
前端基础进阶(十):深入详解函数的柯里化
https://fe32.top/articles/jsnb9537/
2022-06-22T13:32:15.000Z
2023-05-17T10:01:12.000Z
<p>柯里化是函数的一个高级应用,通过上一个章节的学习我们知道,接收函数作为参数的函数,都可以叫做高阶函数。这一章我们要学习的柯里化,其实就是高阶函数的一种特殊用法。</p>
<p class='p blue'>柯里化是指这样一个函数(假设叫做createCurry),他接收函数A作为参数,运行后能够返回一个新的函数。并且这个新的函数能够处理函数A的剩余参数。</p>
<p>这样的定义可能不太好理解,我们可以通过下面的例子配合解释。</p>
<p>有一个接收三个参数的函数A。</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">function</span> <span class="title function_">A</span>(<span class="params">a, b, c</span>) {</span><br><span class="line"> <span class="comment">// do something</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>假如有一个已经封装好了的柯里化通用函数createCurry。它接收bar作为参数,能够将A转化为柯里化函数,返回结果就是这个被转化之后的函数。</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">var</span> _A = <span class="title function_">createCurry</span>(A);</span><br></pre></td></tr></table></figure>
<p>那么_A作为createCurry运行的返回函数,它能够处理A的剩余参数。因此下面的运行结果都是等价的。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">_A</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>);</span><br><span class="line"><span class="title function_">_A</span>(<span class="number">1</span>, <span class="number">2</span>)(<span class="number">3</span>);</span><br><span class="line"><span class="title function_">_A</span>(<span class="number">1</span>)(<span class="number">2</span>, <span class="number">3</span>);</span><br><span class="line"><span class="title function_">_A</span>(<span class="number">1</span>)(<span class="number">2</span>)(<span class="number">3</span>);</span><br><span class="line"><span class="title function_">A</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>);</span><br></pre></td></tr></table></figure>
<p>函数A被createCurry转化之后得到柯里化函数_A,_A能够处理A的所有剩余参数。因此柯里化也被称为部分求值。</p>
<p>在简单的场景下,可以不用借助柯里化通用式来转化得到柯里化函数,我们凭借眼力自己封装。</p>
<p>例如有一个简单的加法函数,它能够将自身的三个参数加起来并返回计算结果。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">add</span>(<span class="params">a, b, c</span>) {</span><br><span class="line"> <span class="keyword">return</span> a + b + c;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>那么add函数的柯里化函数_add则可以如下:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">_add</span>(<span class="params">a</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">function</span>(<span class="params">b</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">function</span>(<span class="params">c</span>) {</span><br><span class="line"> <span class="keyword">return</span> a + b + c;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>当然,靠眼力封装的柯里化函数自由度偏低,柯里化通用式具备更加强大的能力。需要知道如何去封装这样一个柯里化的通用式。</p>
<p>首先通过_add可以看出,柯里化函数的运行过程其实是一个参数的收集过程,将每一次传入的参数收集起来,并在最里层里面处理。在实现createCurry时,可以借助这个思路来进行封装。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 简单实现,参数只能从右到左传递</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">createCurry</span>(<span class="params">func, args</span>) {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> arity = func.<span class="property">length</span>;</span><br><span class="line"> <span class="keyword">var</span> args = args || [];</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> _args = [].<span class="property">slice</span>.<span class="title function_">call</span>(<span class="variable language_">arguments</span>);</span><br><span class="line"> [].<span class="property">push</span>.<span class="title function_">apply</span>(_args, args);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 如果参数个数小于最初的func.length,则递归调用,继续收集参数</span></span><br><span class="line"> <span class="keyword">if</span> (_args.<span class="property">length</span> < arity) {</span><br><span class="line"> <span class="keyword">return</span> createCurry.<span class="title function_">call</span>(<span class="variable language_">this</span>, func, _args);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 参数收集完毕,则执行func</span></span><br><span class="line"> <span class="keyword">return</span> func.<span class="title function_">apply</span>(<span class="variable language_">this</span>, _args);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>尽管已经做了足够详细的注解,但是理解起来可能并不是那么容易,因此建议大家用点耐心多阅读几遍。这个createCurry函数的封装借助闭包与递归,实现了一个参数收集,并在收集完毕之后执行所有参数的一个过程。</p>
<p>聪明的读者可能已经发现,把函数经过createCurry转化为一个柯里化函数,最后执行的结果,不是正好相当于执行函数自身吗?柯里化是不是把简单的问题复杂化了?</p>
<p>如果你能够提出这样的问题,那么说明你确实已经对柯里化有了一定的了解。柯里化确实是把简答的问题复杂化了,但是复杂化的同时,我们使用函数拥有了更加多的自由度。而这里对于函数参数的自由处理,正是柯里化的核心所在。</p>
<p>举一个非常常见的例子。</p>
<p>如果我们想要验证一串数字是否是正确的手机号,按照普通的思路来做,大家可能是这样封装,如下:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">checkPhone</span>(<span class="params">phoneNumber</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="regexp">/^1[34578]\d{9}$/</span>.<span class="title function_">test</span>(phoneNumber);</span><br><span class="line">}</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">function</span> <span class="title function_">checkEmail</span>(<span class="params">email</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="regexp">/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/</span>.<span class="title function_">test</span>(email);</span><br><span class="line">}</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">function</span> <span class="title function_">check</span>(<span class="params">targetString, reg</span>) {</span><br><span class="line"> <span class="keyword">return</span> reg.<span class="title function_">test</span>(targetString);</span><br><span class="line">}</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></pre></td><td class="code"><pre><span class="line"><span class="title function_">check</span>(<span class="regexp">/^1[34578]\d{9}$/</span>, <span class="string">'14900000088'</span>);</span><br><span class="line"><span class="title function_">check</span>(<span class="regexp">/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/</span>, <span class="string">'test@163.com'</span>);</span><br></pre></td></tr></table></figure>
<p>这个时候,我们可以借助柯里化,在check的基础上再做一层封装,以简化使用。</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">var</span> _check = <span class="title function_">createCurry</span>(check);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> checkPhone = <span class="title function_">_check</span>(<span class="regexp">/^1[34578]\d{9}$/</span>);</span><br><span class="line"><span class="keyword">var</span> checkEmail = <span class="title function_">_check</span>(<span class="regexp">/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/</span>);</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></pre></td><td class="code"><pre><span class="line"><span class="title function_">checkPhone</span>(<span class="string">'183888888'</span>);</span><br><span class="line"><span class="title function_">checkEmail</span>(<span class="string">'xxxxx@test.com'</span>);</span><br></pre></td></tr></table></figure>
<p>经过这个过程我们发现,柯里化能够应对更加复杂的逻辑封装。当情况变得多变,柯里化依然能够应付自如。虽然柯里化在一定程度上将问题复杂化了,也让代码更加不容易理解,但是柯里化在面对复杂情况下的灵活性却让我们不得不爱。</p>
<p>这个案例本身情况还算简单,不能够特别明显的凸显柯里化的优势,当然主要目的在于借助这个案例帮助大家了解柯里化在实践中的用途。</p>
<p>继续来思考一个例子。这个例子与map有关。在高阶函数的章节中,我们分析了封装map方法的思考过程。由于我们没有办法确认一个数组在遍历时会执行什么操作,因此只能将调用for循环的这个统一逻辑封装起来,而具体的操作则通过参数传入的形式让使用者自定义。这就是map函数。</p>
<p>实践中我们常常会发现,在某个项目中,针对于某一个数组的操作其实是固定的,同样的操作,可能会在项目的不同地方调用很多次。</p>
<p>这个时候,我们就可以在map函数的基础上,进行二次封装,以简化使用。假如这个在项目中会调用多次的操作是将数组的每一项都转化为百分比 1 –> 100%。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">getNewArray</span>(<span class="params">array</span>) {</span><br><span class="line"> <span class="keyword">return</span> array.<span class="title function_">map</span>(<span class="keyword">function</span>(<span class="params">item</span>) {</span><br><span class="line"> <span class="keyword">return</span> item * <span class="number">100</span> + <span class="string">'%'</span></span><br><span class="line"> })</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">getNewArray</span>([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">0.12</span>]); <span class="comment">// ['100%', '200%', '300%', '12%'];</span></span><br></pre></td></tr></table></figure>
<p>而如果借助柯里化来二次封装这样的逻辑,则会如下实现:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">_map</span>(<span class="params">func, array</span>) {</span><br><span class="line"> <span class="keyword">return</span> array.<span class="title function_">map</span>(func);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> _getNewArray = <span class="title function_">createCurry</span>(_map);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> getNewArray = <span class="title function_">_getNewArray</span>(<span class="keyword">function</span>(<span class="params">item</span>) {</span><br><span class="line"> <span class="keyword">return</span> item * <span class="number">100</span> + <span class="string">'%'</span></span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="title function_">getNewArray</span>([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">0.12</span>]); <span class="comment">// ['100%', '200%', '300%', '12%'];</span></span><br><span class="line"><span class="title function_">getNewArray</span>([<span class="number">0.01</span>, <span class="number">1</span>]); <span class="comment">// ['1%', '100%']</span></span><br></pre></td></tr></table></figure>
<p>如果项目中的固定操作是希望对数组进行一个过滤,找出数组中的所有Number类型的数据。借助柯里化思维我们可以这样做:</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">function</span> <span class="title function_">_filter</span>(<span class="params">func, array</span>) {</span><br><span class="line"> <span class="keyword">return</span> array.<span class="title function_">filter</span>(func);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> _find = <span class="title function_">createCurry</span>(_filter);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> findNumber = <span class="title function_">_find</span>(<span class="keyword">function</span>(<span class="params">item</span>) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> item == <span class="string">'number'</span>) {</span><br><span class="line"> <span class="keyword">return</span> item;</span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="title function_">findNumber</span>([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="string">'2'</span>, <span class="string">'3'</span>, <span class="number">4</span>]); <span class="comment">// [1, 2, 3, 4]</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 当我们继续封装另外的过滤操作时就会变得非常简单</span></span><br><span class="line"><span class="comment">// 找出数字为20的子项</span></span><br><span class="line"><span class="keyword">var</span> find20 = <span class="title function_">_find</span>(<span class="keyword">function</span>(<span class="params">item, i</span>) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> item === <span class="number">20</span>) {</span><br><span class="line"> <span class="keyword">return</span> i;</span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"><span class="title function_">find20</span>([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">30</span>, <span class="number">20</span>, <span class="number">100</span>]); <span class="comment">// 4</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 找出数组中大于100的所有数据</span></span><br><span class="line"><span class="keyword">var</span> findGreater100 = <span class="title function_">_find</span>(<span class="keyword">function</span>(<span class="params">item</span>) {</span><br><span class="line"> <span class="keyword">if</span> (item > <span class="number">100</span>) {</span><br><span class="line"> <span class="keyword">return</span> item;</span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"><span class="title function_">findGreater100</span>([<span class="number">1</span>, <span class="number">2</span>, <span class="number">101</span>, <span class="number">300</span>, <span class="number">2</span>, <span class="number">122</span>]); <span class="comment">// [101, 300, 122]</span></span><br></pre></td></tr></table></figure>
<p>我采用了与check例子不一样的思维方向来想大家展示我们在使用柯里化时的想法。目的是想告诉大家,柯里化能够帮助我们应对更多更复杂的场景。</p>
<p>不得不承认,这些例子都太简单了,简单到使用柯里化的思维来处理他们显得有一点多此一举,而且变得难以理解。在未来你的实践中,如果你发现用普通的思维封装一些逻辑慢慢变得困难,不妨想一想在这里学到的柯里化思维,应用起来,柯里化足够强大的自由度一定能给你一个惊喜。</p>
<p>不建议在任何情况下以炫技为目的的去使用柯里化,在柯里化的实现中,柯里化虽然具有了更多的自由度,但同时柯里化通用式里调用了arguments对象,使用了递归与闭包,因此柯里化的自由度是以牺牲了一定的性能为代价换来的。只有在情况变得复杂时,才是柯里化大显身手的时候。</p>
<blockquote>
<p>额外知识补充:</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 实现一个add方法,使计算结果能够满足如下预期:</span></span><br><span class="line"><span class="title function_">add</span>(<span class="number">1</span>)(<span class="number">2</span>)(<span class="number">3</span>) = <span class="number">6</span>;</span><br><span class="line"><span class="title function_">add</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)(<span class="number">4</span>) = <span class="number">10</span>;</span><br><span class="line"><span class="title function_">add</span>(<span class="number">1</span>)(<span class="number">2</span>)(<span class="number">3</span>)(<span class="number">4</span>)(<span class="number">5</span>) = <span class="number">15</span>;</span><br></pre></td></tr></table></figure>
<p>这个题目的目的是想让add执行之后返回一个函数能够继续执行,最终运算的结果是所有出现过的参数之和。而这个题目的难点则在于参数的不固定。我们不知道函数会执行几次。因此不能使用上面封装的createCurry的通用公式来转换一个柯里化函数。只能自己封装,那么怎么办呢?在此之前,补充2个非常重要的知识点。</p>
<p>第一个要补充的知识点是ES6函数的不定参数。假如有一个数组,希望把这个数组中所有的子项展开传递给一个函数作为参数。那么我们应该怎么做?</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 大家可以思考一下,如果将args数组的子项展开作为add的参数传入</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">add</span>(<span class="params">a, b, c, d</span>) {</span><br><span class="line"> <span class="keyword">return</span> a + b + c + d;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> args = [<span class="number">1</span>, <span class="number">3</span>, <span class="number">100</span>, <span class="number">1</span>];</span><br></pre></td></tr></table></figure>
<p>在ES5中,我们可以借助之前学过的apply来达到我们的目的。</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">add.<span class="title function_">apply</span>(<span class="literal">null</span>, args); <span class="comment">// 105</span></span><br></pre></td></tr></table></figure>
<p>而在ES6中,提供了一种新的语法来解决这个问题,那就是不定参。写法如下:</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="title function_">add</span>(...args); <span class="comment">// 105</span></span><br></pre></td></tr></table></figure>
<p>第二个要补充的知识点是函数的隐式转换。当我们直接将函数参与其他的计算时,函数会默认调用toString方法,直接将函数体转换为字符串参与计算。</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">function</span> <span class="title function_">fn</span>(<span class="params"></span>) { <span class="keyword">return</span> <span class="number">20</span> }</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(fn + <span class="number">10</span>); <span class="comment">// 输出结果 function fn() { return 20 }10</span></span><br></pre></td></tr></table></figure>
<p>我们可以重写函数的toString方法,让函数参与计算时,输出想要的结果。</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">function</span> <span class="title function_">fn</span>(<span class="params"></span>) { <span class="keyword">return</span> <span class="number">20</span>; }</span><br><span class="line">fn.<span class="property">toString</span> = <span class="keyword">function</span>(<span class="params"></span>) { <span class="keyword">return</span> <span class="number">30</span> }</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(fn + <span class="number">10</span>); <span class="comment">// 40</span></span><br></pre></td></tr></table></figure>
<p>除此之外,重写函数的valueOf方法也能够改变函数的隐式转换结果。</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">function</span> <span class="title function_">fn</span>(<span class="params"></span>) { <span class="keyword">return</span> <span class="number">20</span>; }</span><br><span class="line">fn.<span class="property">valueOf</span> = <span class="keyword">function</span>(<span class="params"></span>) { <span class="keyword">return</span> <span class="number">60</span> }</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(fn + <span class="number">10</span>); <span class="comment">// 70</span></span><br></pre></td></tr></table></figure>
<p>当同时重写函数的toString方法与valueOf方法时,最终的结果会取valueOf方法的返回结果。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">fn</span>(<span class="params"></span>) { <span class="keyword">return</span> <span class="number">20</span>; }</span><br><span class="line">fn.<span class="property">valueOf</span> = <span class="keyword">function</span>(<span class="params"></span>) { <span class="keyword">return</span> <span class="number">50</span> }</span><br><span class="line">fn.<span class="property">toString</span> = <span class="keyword">function</span>(<span class="params"></span>) { <span class="keyword">return</span> <span class="number">30</span> }</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(fn + <span class="number">10</span>); <span class="comment">// 60</span></span><br></pre></td></tr></table></figure>
<p>补充了这两个知识点之后,我们可以尝试完成之前的题目了。add方法的实现仍然会是一个参数的收集过程。当add函数执行到最后时,仍然返回的是一个函数,我们可以通过定义toString/valueOf的方式,让这个函数可以直接参与计算,并且转换的结果是我们想要的。而且它本身也仍然可以继续执行接收新的参数。实现方式如下。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">add</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="comment">// 第一次执行时,定义一个数组专门用来存储所有的参数</span></span><br><span class="line"> <span class="keyword">var</span> _args = [].<span class="property">slice</span>.<span class="title function_">call</span>(<span class="variable language_">arguments</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值</span></span><br><span class="line"> <span class="keyword">var</span> adder = <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> _adder = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="comment">// [].push.apply(_args, [].slice.call(arguments));</span></span><br><span class="line"> _args.<span class="title function_">push</span>(...<span class="variable language_">arguments</span>);</span><br><span class="line"> <span class="keyword">return</span> _adder;</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 利用隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回</span></span><br><span class="line"> _adder.<span class="property">toString</span> = <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> _args.<span class="title function_">reduce</span>(<span class="keyword">function</span> (<span class="params">a, b</span>) {</span><br><span class="line"> <span class="keyword">return</span> a + b;</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> _adder;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// return adder.apply(null, _args);</span></span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">adder</span>(..._args);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> a = <span class="title function_">add</span>(<span class="number">1</span>)(<span class="number">2</span>)(<span class="number">3</span>)(<span class="number">4</span>); <span class="comment">// f 10</span></span><br><span class="line"><span class="keyword">var</span> b = <span class="title function_">add</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>); <span class="comment">// f 10</span></span><br><span class="line"><span class="keyword">var</span> c = <span class="title function_">add</span>(<span class="number">1</span>, <span class="number">2</span>)(<span class="number">3</span>, <span class="number">4</span>); <span class="comment">// f 10</span></span><br><span class="line"><span class="keyword">var</span> d = <span class="title function_">add</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)(<span class="number">4</span>); <span class="comment">// f 10</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 可以利用隐式转换的特性参与计算</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(a + <span class="number">10</span>); <span class="comment">// 20</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(b + <span class="number">20</span>); <span class="comment">// 30</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(c + <span class="number">30</span>); <span class="comment">// 40</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(d + <span class="number">40</span>); <span class="comment">// 50</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 也可以继续传入参数,得到的结果再次利用隐式转换参与计算</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">a</span>(<span class="number">10</span>) + <span class="number">100</span>); <span class="comment">// 120</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">b</span>(<span class="number">10</span>) + <span class="number">100</span>); <span class="comment">// 120</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">c</span>(<span class="number">10</span>) + <span class="number">100</span>); <span class="comment">// 120</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">d</span>(<span class="number">10</span>) + <span class="number">100</span>); <span class="comment">// 120</span></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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 其实上栗中的add方法,就是下面这个函数的柯里化函数,只不过我们并没有使用通用式来转化,而是自己封装</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">add</span>(<span class="params">...args</span>) {</span><br><span class="line"> <span class="keyword">return</span> args.<span class="title function_">reduce</span>(<span class="function">(<span class="params">a, b</span>) =></span> a + b);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<div class="note no-icon flat"><p>原文链接:<a
前端基础进阶(九):函数与函数式编程
https://fe32.top/articles/jsnb9536/
2022-06-19T10:57:43.000Z
2023-05-17T10:01:12.000Z
<h2 id="函数声明、函数表达式、匿名函数与自执行函数"><a href="#函数声明、函数表达式、匿名函数与自执行函数" class="headerlink" title="函数声明、函数表达式、匿名函数与自执行函数"></a>函数声明、函数表达式、匿名函数与自执行函数</h2><h3 id="函数声明"><a href="#函数声明" class="headerlink" title="函数声明"></a>函数声明</h3><p>JavaScript中,有两种声明方式,一个是使用<code>var/let/const</code>的变量声明,另一个是使用<code>function</code>的函数声明。</p>
<p>在 <a href="https://fe32.top/articles/jsnb9530/">前端基础进阶(三):变量对象详解</a> 一文中有提到过,变量对象的创建过程中,函数声明比变量声明具有更为优先的执行顺序,即常常提到的函数声明提前。因此在执行上下文中,无论在什么位置声明了函数,都可以在同一个执行上下文中直接使用该函数。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">fn</span>(); <span class="comment">// function</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">fn</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'function'</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="函数表达式"><a href="#函数表达式" class="headerlink" title="函数表达式"></a>函数表达式</h3><p>与函数声明不同,函数表达式使用了<code>var/let/const</code>进行声明,在确认它是否可以正确使用的时候就必须依照<code>var/let/const</code>的规则进行判断,即变量声明。使用var进行变量声明,其实是进行了两步操作。</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">// 变量声明</span></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">20</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 实际执行顺序</span></span><br><span class="line"><span class="keyword">var</span> a = <span class="literal">undefined</span>; <span class="comment">// 变量声明,初始值undefined,变量提升,提升顺序次于function声明</span></span><br><span class="line">a = <span class="number">20</span>; <span class="comment">// 变量赋值,该操作不会提升</span></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="title function_">fn</span>(); <span class="comment">// 报错</span></span><br><span class="line"><span class="keyword">var</span> fn = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'function'</span>);</span><br><span class="line">}</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> fn = <span class="literal">undefined</span>; <span class="comment">// 变量声明提升</span></span><br><span class="line"><span class="title function_">fn</span>(); <span class="comment">// 执行报错</span></span><br><span class="line">fn = <span class="keyword">function</span>(<span class="params"></span>) { <span class="comment">// 赋值操作,此时将后边函数的引用赋值给fn</span></span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'function'</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<blockquote>
<p>由于声明方式的不同,导致了函数声明与函数表达式在使用上的一些差异需要我们注意,除此之外,这两种形式的函数在使用上并无不同。</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><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在构造函数中添加方法</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Person</span>(<span class="params">name</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">age</span> = age;</span><br><span class="line"> <span class="comment">// 在构造函数内部中添加方法</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">getAge</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">age</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="variable language_">this</span>.</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 给原型添加方法</span></span><br><span class="line"><span class="title class_">Person</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">getName</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">name</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在对象中添加方法</span></span><br><span class="line"><span class="keyword">var</span> a = {</span><br><span class="line"> <span class="attr">m</span>: <span class="number">20</span>,</span><br><span class="line"> <span class="attr">getM</span>: <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">m</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="匿名函数"><a href="#匿名函数" class="headerlink" title="匿名函数"></a>匿名函数</h3><p>匿名函数,顾名思义,就是指的没有被显示进行赋值操作的函数。它的使用场景,多作为一个参数传入另一个函数中。</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">var</span> a = <span class="number">10</span>;</span><br><span class="line"><span class="keyword">var</span> fn = <span class="keyword">function</span>(<span class="params">bar, num</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">bar</span>() + num;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">fn</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> a;</span><br><span class="line">}, <span class="number">20</span>)</span><br></pre></td></tr></table></figure>
<p>在上面的例子中,fn的第一个参数传入了一个匿名函数。虽然该匿名函数没有显示的进行赋值操作,我们没有办法在外部执行上下文中引用到它,但是在fn函数内部,我们将该匿名函数赋值给了变量bar,保存在了fn变量对象的arguments对象中。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 变量对象在fn上下文执行过程中的创建阶段</span></span><br><span class="line"><span class="title function_">VO</span>(fn) = {</span><br><span class="line"> <span class="attr">arguments</span>: {</span><br><span class="line"> <span class="attr">bar</span>: <span class="literal">undefined</span>,</span><br><span class="line"> <span class="attr">num</span>: <span class="literal">undefined</span>,</span><br><span class="line"> <span class="attr">length</span>: <span class="number">2</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 变量对象在fn上下文执行过程中的执行阶段</span></span><br><span class="line"><span class="comment">// 变量对象变为活动对象,并完成赋值操作与执行可执行代码</span></span><br><span class="line"><span class="variable constant_">VO</span> -> <span class="variable constant_">AO</span></span><br><span class="line"></span><br><span class="line"><span class="title function_">AO</span>(fn) = {</span><br><span class="line"> <span class="attr">arguments</span>: {</span><br><span class="line"> <span class="attr">bar</span>: <span class="keyword">function</span>(<span class="params"></span>) { <span class="keyword">return</span> a },</span><br><span class="line"> <span class="attr">num</span>: <span class="number">20</span>,</span><br><span class="line"> <span class="attr">length</span>: <span class="number">2</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>由于匿名函数传入另一个函数之后,最终会在另一个函数中执行,因此我们也常常称这个匿名函数为 <span class='p blue'>回调函数</span> 。关于匿名函数更多的内容,将会在下一篇深入探讨柯里化的文章中进行更加详细讲解。</p>
<span class='p blue'>匿名函数的这个应用场景几乎承担了函数的所有难以理解的知识点</span> ,一定要对它的这些细节了解的足够清楚,如果对于变量对象的演变过程你还看不太明白,一定要回过头去看这篇文章:[前端基础进阶(三):变量对象详解](https://fe32.top/articles/jsnb9530/)
<h3 id="函数自执行与块级作用域"><a href="#函数自执行与块级作用域" class="headerlink" title="函数自执行与块级作用域"></a>函数自执行与块级作用域</h3><p>在ES5中,没有块级作用域,因此我们常常使用函数自执行的方式来模仿块级作用域,这样就提供了一个独立的执行上下文,结合闭包,就为模块化提供了基础。而函数自执行,其实是匿名函数的一种应用。</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">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">})();</span><br></pre></td></tr></table></figure>
<p>一个模块往往可以包括:私有变量、私有方法、公有变量、公有方法。</p>
<p>根据作用域链的单向访问,外面可能很容易知道在这个独立的模块中,外部执行环境是无法访问内部的任何变量与方法的,因此我们可以很容易的创建属于这个模块的私有变量与私有方法。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="comment">// 私有变量</span></span><br><span class="line"> <span class="keyword">var</span> age = <span class="number">20</span>;</span><br><span class="line"> <span class="keyword">var</span> name = <span class="string">'Tom'</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 私有方法</span></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">getName</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">`your name is `</span> + name;</span><br><span class="line"> }</span><br><span class="line">})();</span><br></pre></td></tr></table></figure>
<p>但是共有方法和变量应该怎么办?大家还记得前面讲到过的 <span class='p blue'>闭包</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><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="comment">// 私有变量</span></span><br><span class="line"> <span class="keyword">var</span> age = <span class="number">20</span>;</span><br><span class="line"> <span class="keyword">var</span> name = <span class="string">'Tom'</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 私有方法</span></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">getName</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">`your name is `</span> + name;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 共有方法</span></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">getAge</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> age;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 将引用保存在外部执行环境的变量中,形成闭包,防止该执行环境被垃圾回收</span></span><br><span class="line"> <span class="variable language_">window</span>.<span class="property">getAge</span> = getAge;</span><br><span class="line">})();</span><br></pre></td></tr></table></figure>
<p>为了帮助大家进一步理解闭包,我们来看看jQuery中,是如何利用模块与闭包的。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用函数自执行的方式创建模块</span></span><br><span class="line">(<span class="keyword">function</span>(<span class="params"><span class="variable language_">window</span>, <span class="literal">undefined</span></span>) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 声明jQuery构造函数</span></span><br><span class="line"> <span class="keyword">var</span> jQuery = <span class="keyword">function</span>(<span class="params">name</span>) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 主动在构造函数中,返回一个jQuery实例</span></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> jQuery.<span class="property">fn</span>.<span class="title function_">init</span>(name);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 添加原型方法</span></span><br><span class="line"> jQuery.<span class="property"><span class="keyword">prototype</span></span> = jQuery.<span class="property">fn</span> = {</span><br><span class="line"> <span class="attr">constructor</span>: jQuery,</span><br><span class="line"> <span class="attr">init</span>:<span class="keyword">function</span>(<span class="params"></span>) { ... },</span><br><span class="line"> <span class="attr">css</span>: <span class="keyword">function</span>(<span class="params"></span>) { ... }</span><br><span class="line"> }</span><br><span class="line"> jQuery.<span class="property">fn</span>.<span class="property">init</span>.<span class="property"><span class="keyword">prototype</span></span> = jQuery.<span class="property">fn</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 将jQuery改名为$,并将引用保存在window上,形成闭包,对外开放jQuery构造函数,就可以访问所有挂载在jQuery原型上的方法了</span></span><br><span class="line"> <span class="variable language_">window</span>.<span class="property">jQuery</span> = <span class="variable language_">window</span>.<span class="property">$</span> = jQuery;</span><br><span class="line"> })(<span class="variable language_">window</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在使用时,直接执行了构造函数,因为在jQuery的构造函数中通过一些手段,返回的是jQuery的实例,所以就不用再每次用的时候自己new一个实例</span></span><br><span class="line">$(<span class="string">'#div1'</span>);</span><br></pre></td></tr></table></figure>
<p>在这里,只需看懂闭包与模块的部分就行,至于内部的原型链是如何绕的,为什么会这样写,在讲面向对象的时候会为大家慢慢分析。举这个例子的目的所在,就是希望大家能够重视函数,在实际开发中,它无处不在。</p>
<p>接下来我要分享一个高级的,非常有用的模块的应用。当项目越来越大,那么需要保存的数据与状态就越来越多,因此,我们需要一个专门的模块来维护这些数据,这个时候,一个叫做状态管理器的东西就应运而生。对于状态管理器,最出名的,我想非redux莫属了。虽然对于还在学习中的大家来说,redux是一个有点高深莫测的东西,但是在我们学习之前,可以先通过简单的方式,让大家大致了解状态管理器的实现原理,为我们未来的学习奠定坚实的基础。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 自执行创建模块</span></span><br><span class="line">(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="comment">// states 结构预览</span></span><br><span class="line"> <span class="comment">// states = {</span></span><br><span class="line"> <span class="comment">// a: 1,</span></span><br><span class="line"> <span class="comment">// b: 2,</span></span><br><span class="line"> <span class="comment">// m: 30, </span></span><br><span class="line"> <span class="comment">// o: {}</span></span><br><span class="line"> <span class="comment">// }</span></span><br><span class="line"> <span class="keyword">var</span> states = {}; <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">function</span> <span class="title function_">type</span>(<span class="params">elem</span>) {</span><br><span class="line"> <span class="keyword">if</span>(elem == <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">return</span> elem + <span class="string">''</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> toString.<span class="title function_">call</span>(elem).<span class="title function_">replace</span>(<span class="regexp">/[\[\]]/g</span>, <span class="string">''</span>).<span class="title function_">split</span>(<span class="string">' '</span>)[<span class="number">1</span>].<span class="title function_">toLowerCase</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Param</span> name 属性名</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span> 通过属性名获取保存在states中的值</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">get</span>(<span class="params">name</span>) {</span><br><span class="line"> <span class="keyword">return</span> states[name] ? states[name] : <span class="string">''</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">getStates</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> states;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * @param options {object} 键值对</span></span><br><span class="line"><span class="comment"> * @param target {object} 属性值为对象的属性,只在函数实现时递归中传入</span></span><br><span class="line"><span class="comment"> * @desc 通过传入键值对的方式修改state树,使用方式与小程序的data或者react中的setStates类似</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">set</span>(<span class="params">options, target</span>) {</span><br><span class="line"> <span class="keyword">var</span> keys = <span class="title class_">Object</span>.<span class="title function_">keys</span>(options);</span><br><span class="line"> <span class="keyword">var</span> o = target ? target : states;</span><br><span class="line"></span><br><span class="line"> keys.<span class="title function_">map</span>(<span class="keyword">function</span>(<span class="params">item</span>) {</span><br><span class="line"> <span class="keyword">if</span>(<span class="keyword">typeof</span> o[item] == <span class="string">'undefined'</span>) {</span><br><span class="line"> o[item] = options[item];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> <span class="title function_">type</span>(o[item]) == <span class="string">'object'</span> ? <span class="title function_">set</span>(options[item], o[item]) : o[item] = options[item];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> item;</span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 对外提供接口</span></span><br><span class="line"> <span class="variable language_">window</span>.<span class="property">get</span> = get;</span><br><span class="line"> <span class="variable language_">window</span>.<span class="property">set</span> = set;</span><br><span class="line"> <span class="variable language_">window</span>.<span class="property">getStates</span> = getStates;</span><br><span class="line">})()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 具体使用如下</span></span><br><span class="line"></span><br><span class="line"><span class="title function_">set</span>({ <span class="attr">a</span>: <span class="number">20</span> }); <span class="comment">// 保存 属性a</span></span><br><span class="line"><span class="title function_">set</span>({ <span class="attr">b</span>: <span class="number">100</span> }); <span class="comment">// 保存属性b</span></span><br><span class="line"><span class="title function_">set</span>({ <span class="attr">c</span>: <span class="number">10</span> }); <span class="comment">// 保存属性c</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 保存属性o, 它的值为一个对象</span></span><br><span class="line"><span class="title function_">set</span>({</span><br><span class="line"> <span class="attr">o</span>: {</span><br><span class="line"> <span class="attr">m</span>: <span class="number">10</span>,</span><br><span class="line"> <span class="attr">n</span>: <span class="number">20</span></span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="comment">// 修改对象o 的m值</span></span><br><span class="line"><span class="title function_">set</span>({</span><br><span class="line"> <span class="attr">o</span>: {</span><br><span class="line"> <span class="attr">m</span>: <span class="number">1000</span></span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="comment">// 给对象o中增加一个c属性</span></span><br><span class="line"><span class="title function_">set</span>({</span><br><span class="line"> <span class="attr">o</span>: {</span><br><span class="line"> <span class="attr">c</span>: <span class="number">100</span></span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">getStates</span>())</span><br></pre></td></tr></table></figure>
<p>在单页应用中,我们很可能会用到这样的思路。</p>
<blockquote>
<p>函数自执行的方式另外还有其他几种写法,诸如!function(){}(),+function(){}()</p>
</blockquote>
<h2 id="函数参数传递方式:按值传递"><a href="#函数参数传递方式:按值传递" class="headerlink" title="函数参数传递方式:按值传递"></a>函数参数传递方式:按值传递</h2><p>还记得基本数据类型与引用数据类型在复制上的差异吗?基本数据类型复制,是值直接发生了复制,因此改变后,各自相互不影响。但是引用数据类型的复制,是保存在变量对象中的引用发生了复制,因此复制之后的这两个引用实际访问的实际是同一个堆内存中的值。当改变其中一个时,另外一个自然也被改变。如下例。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">20</span>;</span><br><span class="line"><span class="keyword">var</span> b = a;</span><br><span class="line">b = <span class="number">10</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(a); <span class="comment">// 20</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> m = { <span class="attr">a</span>: <span class="number">1</span>, <span class="attr">b</span>: <span class="number">2</span> }</span><br><span class="line"><span class="keyword">var</span> n = m;</span><br><span class="line">n.<span class="property">a</span> = <span class="number">5</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(m.<span class="property">a</span>) <span class="comment">// 5</span></span><br></pre></td></tr></table></figure>
<p>当值作为函数的参数传递进入函数内部时,也有同样的差异。函数的参数在进入函数后,实际是被保存在了函数的变量对象中,因此,这个时候相当于发生了一次复制。如下例。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">20</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">fn</span>(<span class="params">a</span>) {</span><br><span class="line"> a = a + <span class="number">10</span>;</span><br><span class="line"> <span class="keyword">return</span> a;</span><br><span class="line">}</span><br><span class="line"><span class="title function_">fn</span>(a);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(a); <span class="comment">// 20</span></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = { <span class="attr">m</span>: <span class="number">10</span>, <span class="attr">n</span>: <span class="number">20</span> }</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">fn</span>(<span class="params">a</span>) {</span><br><span class="line"> a.<span class="property">m</span> = <span class="number">20</span>;</span><br><span class="line"> <span class="keyword">return</span> a;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">fn</span>(a);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(a); <span class="comment">// { m: 20, n: 20 }</span></span><br></pre></td></tr></table></figure>
<p>正是由于这样的不同,导致了许多人在理解函数参数的传递方式时,就有许多困惑。到底是按值传递还是按引用传递?实际上结论仍然是按值传递,只不过当我们期望传递一个引用类型时,真正传递的,只是这个引用类型保存在变量对象中的引用而已。为了说明这个问题,看看下面这个例子。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> person = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'Nicholas'</span>,</span><br><span class="line"> <span class="attr">age</span>: <span class="number">20</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">setName</span>(<span class="params">obj</span>) { <span class="comment">// 传入一个引用</span></span><br><span class="line"> obj = {}; <span class="comment">// 将传入的引用指向另外的值</span></span><br><span class="line"> obj.<span class="property">name</span> = <span class="string">'Greg'</span>; <span class="comment">// 修改引用的name属性</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">setName</span>(person);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(person.<span class="property">name</span>); <span class="comment">// Nicholas 未被改变</span></span><br></pre></td></tr></table></figure>
<p>在上面的例子中,如果person是按引用传递,那么person就会自动被修改为指向其name属性值为Gerg的新对象。从结果中看到,person对象并未发生任何改变,因此只是在函数内部引用被修改而已。</p>
<h2 id="函数式编程"><a href="#函数式编程" class="headerlink" title="函数式编程"></a>函数式编程</h2><p>虽然JavaScript并不是一门纯函数式编程的语言,但是它使用了许多函数式编程的特性。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">add</span>(<span class="params">a, b, c</span>) {</span><br><span class="line"> <span class="keyword">return</span> a + b + c;</span><br><span class="line">}</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></pre></td><td class="code"><pre><span class="line"><span class="title function_">add</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>); <span class="comment">// 6</span></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">mergeArr</span>(<span class="params">arr</span>) {</span><br><span class="line"> <span class="keyword">var</span> result = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">var</span> i = <span class="number">0</span>; i < arr.<span class="property">length</span>; i++) { result += arr[i] }</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>如果不通过函数封装的方式,那么再每次想要实现这个功能时,就不得不重新使用一次for循环,这样的后果导致代码中充斥着越来越多的重复代码。而封装之后,想要再次做这件事情的时候,只需要一句话就可以了。</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="title function_">mergeArr</span>([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>]);</span><br></pre></td></tr></table></figure>
<p>我们在初学时,往往会不由自主的使用命令式编程的风格来完成我们想要干的事情。因为命令式编程更加的简单,直白。例如我们现在有一个数组,<code>array = [1, 3, 'h', 5, 'm', '4']</code>,现在想要找出这个数组中的所有类型为number的子项。当我们使用命令式编程思维时,可能就会直接这样做。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> array = [<span class="number">1</span>, <span class="number">3</span>, <span class="string">'h'</span>, <span class="number">5</span>, <span class="string">'m'</span>, <span class="string">'4'</span>];</span><br><span class="line"><span class="keyword">var</span> res = [];</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">var</span> i = <span class="number">0</span>; i < array.<span class="property">length</span>; i ++) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> array[i] === <span class="string">'number'</span>) {</span><br><span class="line"> res.<span class="title function_">push</span>(array[i]);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>这种方式中平铺直叙的实现了我们的目的。这样做的问题在于,当我们在另外的时刻,想要找出另外一个数组中所有的子项时,不得不把同样的逻辑再写一次。当出现次数变多时,我们的代码也变得更加糟糕且难以维护。</p>
<p>函数式编程的思维则建议我们将这种会多次出现的功能封装起来以备调用。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">getNumbers</span>(<span class="params">array</span>) {</span><br><span class="line"> <span class="keyword">var</span> res = [];</span><br><span class="line"> array.<span class="title function_">forEach</span>(<span class="keyword">function</span>(<span class="params">item</span>) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> item === <span class="string">'number'</span>) {</span><br><span class="line"> res.<span class="title function_">push</span>(item);</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">return</span> res;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 以上是我们的封装,以下是功能实现</span></span><br><span class="line"><span class="keyword">var</span> array = [<span class="number">1</span>, <span class="number">3</span>, <span class="string">'h'</span>, <span class="number">5</span>, <span class="string">'m'</span>, <span class="string">'4'</span>];</span><br><span class="line"><span class="keyword">var</span> res = <span class="title function_">getNumbers</span>(array);</span><br></pre></td></tr></table></figure>
<p>将功能封装之后,我们实现同样的功能时,只需要写一行代码。而如果未来需求变动,或者稍作修改,我们只需要对getNumbers方法进行调整就可以了。而且在使用时,只需要关心这个方法能做什么,而不用关心具体是如何实现的。这也是函数式编程思维与命令式不同的地方之一。</p>
<p>函数式编程思维还具有以下几个特征:</p>
<p class='p blue'>1. 函数是第一等公民</p>
<p>所谓”第一等公民”(first class),指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) {} <span class="comment">// 赋值</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">fn</span>(<span class="params"><span class="keyword">function</span>() {}, num</span>) {} <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">function</span> <span class="title function_">var</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> ... ...</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>当然,这都是JavaScript的基本概念。但是我想很多人,甚至包括正在阅读的你自己都可能会无视这些概念。可以用一个简单的例子来验证一下。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">delay</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'5000ms之后执行该方法.'</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>现在要做的是,如果要求你结合setTimeout方法,让delay方法延迟5000ms执行,应该怎么做?</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> timer = <span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="title function_">delay</span>();</span><br><span class="line">}, <span class="number">5000</span>);</span><br></pre></td></tr></table></figure>
<p>那么现在问题来了,如果你对函数是一等公民有一个深刻的认知,我想你会发现上面这种写法其实是有一些问题的。所以思考一下,问题出在哪里?</p>
<p>函数既然能够作为一个参数传入另外一个函数,那么我们是不是可以直接将delay作为setTimeout的第一个参数,而不用额外的多加一层匿名函数呢?</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="keyword">var</span> timer = <span class="built_in">setTimeout</span>(delay, <span class="number">5000</span>);</span><br></pre></td></tr></table></figure>
<p class='p blue'>2. 只用"表达式",不用"语句"</p>
<p>“表达式”(expression)是一个单纯的运算过程,总是有返回值;”语句”(statement)是执行某种操作,没有返回值。函数式编程要求,只使用表达式,不使用语句。也就是说,每一步都是单纯的运算,而且都有返回值。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> ele = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">'.test'</span>);</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">setBackgroundColor</span>(<span class="params">color</span>) {</span><br><span class="line"> ele.<span class="property">style</span>.<span class="property">backgroundColor</span> = color;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 多处使用</span></span><br><span class="line"><span class="title function_">setBackgroundColor</span>(<span class="string">'red'</span>);</span><br><span class="line"><span class="title function_">setBackgroundColor</span>(<span class="string">'#ccc'</span>);</span><br></pre></td></tr></table></figure>
<p>我们可以很明显的感受到,setBackgroundColor封装的仅仅只是一条语句。这并不是理想的效果。函数式编程期望一个函数有输入,也有输出。因此良好的习惯应该如下做:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">setBackgroundColor</span>(<span class="params">ele, color</span>) {</span><br><span class="line"> ele.<span class="property">style</span>.<span class="property">backgroundColor</span> = color;</span><br><span class="line"> <span class="keyword">return</span> color;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 多处使用</span></span><br><span class="line"><span class="keyword">var</span> ele = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">'.test'</span>);</span><br><span class="line"><span class="title function_">setBackgroundColor</span>(ele, <span class="string">'red'</span>);</span><br><span class="line"><span class="title function_">setBackgroundColor</span>(ele, <span class="string">'#ccc'</span>);</span><br></pre></td></tr></table></figure>
<p>了解这一点,我们自己在封装函数的时候也要养成良好的习惯。</p>
<p class='p blue'>3. 纯函数</p>
<p>相同的输入总会得到相同的输出,并且不会产生副作用的函数,就是纯函数。</p>
<p>所谓”副作用”(side effect),指的是函数内部与外部互动(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果。</p>
<p>函数式编程强调没有”副作用”,意味着函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。</p>
<p>即:<em><strong>要是同样的参数传入,返回的结果一定是相等的。</strong></em></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">getLast</span>(<span class="params">arr</span>) {</span><br><span class="line"> <span class="keyword">return</span> arr[arr.<span class="property">length</span> - <span class="number">1</span>];</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">_getLast</span>(<span class="params">arr</span>) {</span><br><span class="line"> <span class="keyword">return</span> arr.<span class="title function_">pop</span>();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> source = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> last = <span class="title function_">getLast</span>(source); <span class="comment">// 返回结果4 原数组不变</span></span><br><span class="line"><span class="keyword">var</span> _last = <span class="title function_">_getLast</span>(source); <span class="comment">// 返回结果4 原数据最后一项被删除</span></span><br></pre></td></tr></table></figure>
<p>getLast与_getLast虽然同样能够获得数组的最后一项值,但是_getLast改变了原数组。而当原始数组被改变,那么当我们再次调用该方法时,得到的结果就会变得不一样。这样不可预测的封装方式,在我们看来是非常糟糕的。它会把我们的数据搞得非常混乱。在JavaScript原生支持的数据方法中,也有许多不纯的方法,我们在使用时需要非常警惕,我们要清晰的知道原始数据的改变是否会留下隐患。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> source = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>];</span><br><span class="line"></span><br><span class="line">source.<span class="title function_">slice</span>(<span class="number">1</span>, <span class="number">3</span>); <span class="comment">// 纯函数 返回[2, 3] source不变</span></span><br><span class="line">source.<span class="title function_">splice</span>(<span class="number">1</span>, <span class="number">3</span>); <span class="comment">// 不纯的 返回[2, 3, 4] source被改变</span></span><br><span class="line"></span><br><span class="line">source.<span class="title function_">pop</span>(); <span class="comment">// 不纯的</span></span><br><span class="line">source.<span class="title function_">push</span>(<span class="number">6</span>); <span class="comment">// 不纯的</span></span><br><span class="line">source.<span class="title function_">shift</span>(); <span class="comment">// 不纯的</span></span><br><span class="line">source.<span class="title function_">unshift</span>(<span class="number">1</span>); <span class="comment">// 不纯的</span></span><br><span class="line">source.<span class="title function_">reverse</span>(); <span class="comment">// 不纯的</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 我也不能短时间知道现在source被改变成了什么样子,干脆重新约定一下</span></span><br><span class="line">source = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>];</span><br><span class="line"></span><br><span class="line">source.<span class="title function_">concat</span>([<span class="number">6</span>, <span class="number">7</span>]); <span class="comment">// 纯函数 返回[1, 2, 3, 4, 5, 6, 7] source不变</span></span><br><span class="line">source.<span class="title function_">join</span>(<span class="string">'-'</span>); <span class="comment">// 纯函数 返回1-2-3-4-5 source不变</span></span><br></pre></td></tr></table></figure>
<div class="note no-icon flat"><p>原文链接:<a
前端基础进阶(八):在chrome开发者工具中观察函数调用栈、作用域链与闭包
https://fe32.top/articles/jsnb9535/
2022-06-19T10:57:16.000Z
2023-05-17T10:01:12.000Z
<p>前端开发中,有一个重要的技能,叫做 <span class='p blue'>断点调试</span> 。</p>
<p>利用chrome开发者工具中的断点调试,能够一步步观察JavaScript的执行过程,直观感知函数调用栈、作用域链、变量对象、闭包、this等关键信息的变化。因此,断点调试对于快速定位代码错误,以及快速了解代码的执行过程有着非常重要的作用,这也是前端开发必不可少的一个高级技能。</p>
<p>这篇文章的另一个目的在于借助对于断点调试的学习,进一步加深对闭包的理解。</p>
<h2 id="基础概念回顾"><a href="#基础概念回顾" class="headerlink" title="基础概念回顾"></a>基础概念回顾</h2><p>函数在被调用执行时,会创建一个当前函数的执行上下文。在该执行上下文的创建阶段,变量对象、作用域链、闭包、this指向会分别被确定。一个JavaScript程序中一般来说会有多个函数,JavaScript引擎使用函数调用栈来管理这些函数的调用顺序。函数调用栈的调用顺序与栈数据结构一致。</p>
<h2 id="认识断点调试工具"><a href="#认识断点调试工具" class="headerlink" title="认识断点调试工具"></a>认识断点调试工具</h2><p>在尽量新版本的chrome浏览器中(不确定你用的版本与我的一致),调出chrome浏览器的开发者工具。</p>
<blockquote>
<p>浏览器右上角竖着的三点 -> 更多工具 -> 开发者工具 -> Sources</p>
</blockquote>
<p>界面如图:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/19/62af2a8c47bb1.webp" alt="断点调试界面"/></div><span class="image-caption">断点调试界面</span></div>
<p>在demo中,我们把代码放在app.js,并在index.html中引入。只需要关注截图中红色箭头的地方。在最右侧上方,有一排图标。可以通过使用他们来控制函数的执行顺序。从左到右他们依次是:</p>
<p><strong>resume/pause script execution</strong><br>恢复/暂停脚本执行</p>
<p><strong>step over next function call</strong><br>跨过,实际表现是不遇到函数时,执行下一步。遇到函数时,不进入函数直接执行下一步。</p>
<p><strong>step into next function call</strong><br>跨入,实际表现是不遇到函数时,执行下一步。遇到到函数时,进入函数执行上下文。</p>
<p><strong>step out of current function</strong><br>跳出当前函数</p>
<p><strong>deactivate breakpoints</strong><br>停用断点</p>
<p><strong>don‘t pause on exceptions</strong><br>不暂停异常捕获</p>
<p>其中跨过,跨入,跳出是使用最多的三个操作。</p>
<p>上图右侧第二个红色箭头指向的是函数调用栈(call Stack),这里会显示代码执行过程中,调用栈的变化。</p>
<p>右侧第三个红色箭头指向的是作用域链(Scope),这里会显示当前函数的作用域链。其中Local表示当前的局部变量对象,Closure表示当前作用域链中的闭包。借助此处的作用域链展示,可以很直观的判断出一个例子中,到底谁是闭包,对于闭包的深入了解具有非常重要的帮助作用。</p>
<h2 id="断点设置"><a href="#断点设置" class="headerlink" title="断点设置"></a>断点设置</h2><p>在显示代码行数的地方点击,即可设置一个断点。断点设置有以下几个特点:</p>
<ul>
<li>在单独的变量声明(如果没有赋值),函数声明的那一行,无法设置断点。</li>
<li>设置断点后刷新页面,JavaScript代码会执行到断点位置处暂停执行,然后就可以使用上边介绍过的几个操作开始调试。</li>
<li>当你设置多个断点时,chrome工具会自动判断从最早执行的那个断点开始执行,因此一般设置一个断点就行了。</li>
</ul>
<h2 id="实例"><a href="#实例" class="headerlink" title="实例"></a>实例</h2><p>接下来,借助一些实例,来使用断点调试工具,看一看demo函数,在执行过程中的具体表现。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// demo01</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> fn;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">baz</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(a);</span><br><span class="line"> }</span><br><span class="line"> fn = baz;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">bar</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="title function_">fn</span>();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">foo</span>();</span><br><span class="line"><span class="title function_">bar</span>(); <span class="comment">// 2</span></span><br></pre></td></tr></table></figure>
<p>在向下阅读之前,可以停下来思考一下,这个例子中,谁是闭包?</p>
<p>这是来自《你不知道的js》中的一个例子。由于在使用断点调试过程中,发现chrome浏览器理解的闭包与该例子中所理解的闭包不太一致,因此专门挑出来,供大家参考。个人更加倾向于chrome中的理解。</p>
<p>第一步:设置断点,然后刷新页面。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/19/62af31c68c935.webp" alt="设置断点"/></div><span class="image-caption">设置断点</span></div>
<p>第二步:点击上图红色箭头指向的按钮(step into),该按钮的作用会根据代码执行顺序,一步一步向下执行。在点击的过程中,要注意观察下方call stack 与 scope的变化,以及函数执行位置的变化。</p>
<p>一步一步执行,当函数执行到上例子中</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/19/62af320cddb67.webp" alt="baz函数被调用执行,foo形成了闭包"/></div><span class="image-caption">baz函数被调用执行,foo形成了闭包</span></div>
<p>可以看到,在chrome工具的理解中,由于在foo内部声明的baz函数在调用时访问了它的变量a,因此foo成为了闭包。来看看在《你不知道的js》这本书中的例子中的理解。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/19/62af32506e3f5.webp" alt="你不知道的js中的例子"/></div><span class="image-caption">你不知道的js中的例子</span></div>
<p>书中的注释可以明显的看出,作者认为fn为闭包。即baz,这和chrome工具中明显是不一样的。</p>
<p>而在备受大家推崇的《JavaScript高级编程》一书中,是这样定义闭包。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/19/62af32917f2ad.webp" alt="JavaScript高级编程中闭包的定义"/></div><span class="image-caption">JavaScript高级编程中闭包的定义</span></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/19/62af32c629a29.webp" alt="书中作者将自己理解的闭包与包含函数所区分"/></div><span class="image-caption">书中作者将自己理解的闭包与包含函数所区分</span></div>
<p>这里chrome中理解的闭包,与我所阅读的这几本书中的理解的闭包不一样。详情请参考 <a href="https://fe32.top/articles/jsnb9532/">前端基础进阶(五):闭包</a></p>
<p>闭包是一个特殊对象,它由执行上下文(代号A)与在该执行上下文中创建的函数(代号B)共同组成。当B执行时,如果访问了A中变量对象中的值,那么闭包就会产生。</p>
<p>在大多数理解中,包括许多著名的书籍,文章里都以函数B的名字代指这里生成的闭包。而在chrome中,则以执行上下文A的函数名代指闭包。</p>
<p>修改一下demo01中的例子,来看看一个非常有意思的变化。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// demo02</span></span><br><span class="line"><span class="keyword">var</span> fn;</span><br><span class="line"><span class="keyword">var</span> m = <span class="number">20</span>;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">baz</span>(<span class="params">a</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(a);</span><br><span class="line"> }</span><br><span class="line"> fn = baz;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">bar</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="title function_">fn</span>(m);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">foo</span>();</span><br><span class="line"><span class="title function_">bar</span>(); <span class="comment">// 20</span></span><br></pre></td></tr></table></figure>
<p>这个例子在demo01的基础上,在baz函数中传入一个参数,并打印出来。在调用时,将全局的变量m传入。输出结果变为20。在使用断点调试看看作用域链。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/19/62af337f70e73.webp" alt="闭包没了,作用域链中没有包含foo了"/></div><span class="image-caption">闭包没了,作用域链中没有包含foo了</span></div>
<p>是不是结果有点意外,闭包没了,作用域链中没有包含foo了。所以通过这个对比,可以确定闭包的形成需要两个条件。</p>
<ul>
<li>在函数内部创建新的函数;</li>
<li>新的函数在执行时,访问了函数的变量对象;</li>
</ul>
<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></pre></td><td class="code"><pre><span class="line"><span class="comment">// demo03</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">function</span> <span class="title function_">bar</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> b = <span class="number">9</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">function</span> <span class="title function_">fn</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(a);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> bar = <span class="title function_">foo</span>();</span><br><span class="line"><span class="keyword">var</span> fn = <span class="title function_">bar</span>();</span><br><span class="line"><span class="title function_">fn</span>();</span><br></pre></td></tr></table></figure>
<p>在这个例子中,fn只访问了foo中的a变量,因此它的闭包只有foo。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/19/62af341f1903d.webp" alt="闭包只有foo"/></div><span class="image-caption">闭包只有foo</span></div>
<p>修改一下demo03,在fn中也访问bar中b变量试试看。</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"><span class="comment">// demo04</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">function</span> <span class="title function_">bar</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> b = <span class="number">9</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">function</span> <span class="title function_">fn</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(a, b);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> bar = <span class="title function_">foo</span>();</span><br><span class="line"><span class="keyword">var</span> fn = <span class="title function_">bar</span>();</span><br><span class="line"><span class="title function_">fn</span>();</span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/19/62af3498414a5.webp" alt="这个时候闭包变成了两个"/></div><span class="image-caption">这个时候闭包变成了两个</span></div>
<p>这个时候,闭包变成了两个。分别是bar,foo。闭包在模块中的应用非常重要。因此,我们来一个模块的例子,也用断点工具来观察一下。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// demo05</span></span><br><span class="line">(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">10</span>;</span><br><span class="line"> <span class="keyword">var</span> b = <span class="number">20</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> test = {</span><br><span class="line"> <span class="attr">m</span>: <span class="number">20</span>,</span><br><span class="line"> <span class="attr">add</span>: <span class="keyword">function</span> (<span class="params">x</span>) {</span><br><span class="line"> <span class="keyword">return</span> a + x;</span><br><span class="line"> },</span><br><span class="line"> <span class="attr">sum</span>: <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> a + b + <span class="variable language_">this</span>.<span class="property">m</span>;</span><br><span class="line"> },</span><br><span class="line"> <span class="attr">mark</span>: <span class="keyword">function</span> (<span class="params">k, j</span>) {</span><br><span class="line"> <span class="keyword">return</span> k + j;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="variable language_">window</span>.<span class="property">test</span> = test;</span><br><span class="line"></span><br><span class="line">})();</span><br><span class="line"></span><br><span class="line">test.<span class="title function_">add</span>(<span class="number">100</span>);</span><br><span class="line">test.<span class="title function_">sum</span>();</span><br><span class="line">test.<span class="title function_">mark</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> _mark = test.<span class="property">mark</span>;</span><br><span class="line"><span class="title function_">_mark</span>();</span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/19/62af35f7ecfb8.webp" alt="add执行时,闭包为外层的自执行函数,this指向test"/></div><span class="image-caption">add执行时,闭包为外层的自执行函数,this指向test</span></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/19/62af366338770.webp" alt="sum执行时,同上"/></div><span class="image-caption">sum执行时,同上</span></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/19/62af36786ab36.webp" alt="mark执行时,闭包为外层的自执行函数,this指向test"/></div><span class="image-caption">mark执行时,闭包为外层的自执行函数,this指向test</span></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/19/62af3704cc2ec.webp" alt="_mark执行时,闭包为外层的自执行函数,this指向window"/></div><span class="image-caption">_mark执行时,闭包为外层的自执行函数,this指向window</span></div>
<blockquote>
<p>注意:这里的this指向显示为Object或者Window,大写开头,它们表示的是实例的构造函数,实际上this是指向的具体实例。</p>
</blockquote>
<blockquote>
<p>test.mark能形成闭包,跟下面的补充例子(demo07)情况是一样的。</p>
</blockquote>
<p>结合断点调试的方式,来理解那些困扰很久的this指向。随时观察this的指向,在实际开发调试中非常有用。</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="comment">// demo06</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">10</span>;</span><br><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> <span class="attr">a</span>: <span class="number">20</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">fn</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>.<span class="property">a</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">fn.<span class="title function_">call</span>(obj); <span class="comment">// 20</span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/19/62af37e84292b.webp" alt="this指向obj"/></div><span class="image-caption">this指向obj</span></div>
<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></pre></td><td class="code"><pre><span class="line"><span class="comment">// demo07</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">10</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">fn1</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> a;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">fn2</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">10</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="title function_">fn2</span>();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">foo</span>();</span><br></pre></td></tr></table></figure>
<p>这个例子,和其他例子不太一样。虽然fn2并没有访问到foo的变量,但是foo执行时仍然变成了闭包。而将fn1的声明去掉时,闭包便不会出现了。</p>
<p>那么结合这个特殊的例子,我们可以这样这样定义闭包。</p>
<span class='p blue'>闭包是指这样的作用域(foo),它包含有一个函数(fn1),这个函数(fn1)可以调用被这个作用域所封闭的变量(a)、函数、或者闭包等内容。通常我们通过闭包所对应的函数来获得对闭包的访问</span> 。
<p>最后,根据以上的摸索情况,再次总结一下闭包:</p>
<ul>
<li>闭包是在函数被调用执行的时候才被确认创建的。</li>
<li>闭包的形成,与作用域链的访问顺序有直接关系。</li>
<li>只有内部函数访问了上层作用域链中的变量对象时,才会形成闭包,因此,我们可以利用闭包来访问函数内部的变量。</li>
</ul>
<div class="note no-icon flat"><p>原文链接:<a
前端基础进阶(七):全方位解读this
https://fe32.top/articles/jsnb9534/
2022-06-16T14:40:19.000Z
2023-05-17T10:01:12.000Z
<p>在这之前,先回顾一下执行上下文的生命周期。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/16/62ab45dd10499.webp" alt="执行上下文生命周期"/></div><span class="image-caption">执行上下文生命周期</span></div>
<p>执行上下文的创建阶段,会分别生成变量对象,建立作用域链,确定this指向。本文的关键,就是确定this指向。<span class='p blue'>this的指向,是在函数被调用的时候确定的。也就是执行上下文被创建时确定的</span> 。</p>
<p>一个函数中的this指向,可以非常灵活。比如下面的例子中,同一个函数由于调用方式的不同,this指向了不一样的对象。</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="keyword">var</span> a = <span class="number">10</span>;</span><br><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> <span class="attr">a</span>: <span class="number">20</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">fn</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>.<span class="property">a</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">fn</span>(); <span class="comment">// 10</span></span><br><span class="line">fn.<span class="title function_">call</span>(obj); <span class="comment">// 20</span></span><br></pre></td></tr></table></figure>
<p>除此之外,<span class='p blue'>在函数执行过程中,this一旦被确定,就不可更改了</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">10</span>;</span><br><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> <span class="attr">a</span>: <span class="number">20</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">fn</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">this</span> = obj; <span class="comment">// 这句话试图修改this,运行后会报错</span></span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>.<span class="property">a</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">fn</span>();</span><br></pre></td></tr></table></figure>
<h2 id="全局对象中的this"><a href="#全局对象中的this" class="headerlink" title="全局对象中的this"></a>全局对象中的this</h2><p>关于全局对象的this,之前在总结变量对象的时候提到过,它是一个比较特殊的存在。全局环境中的this,指向它本身。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 通过this绑定到全局对象</span></span><br><span class="line"><span class="variable language_">this</span>.<span class="property">a2</span> = <span class="number">20</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 通过声明绑定到变量对象,但在全局环境中,变量对象就是它自身</span></span><br><span class="line"><span class="keyword">var</span> a1 = <span class="number">10</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 仅仅只有赋值操作,标识符会隐式绑定到全局对象</span></span><br><span class="line">a3 = <span class="number">30</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果会全部符合预期</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(a1);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(a2);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(a3);</span><br></pre></td></tr></table></figure>
<h2 id="函数中的this"><a href="#函数中的this" class="headerlink" title="函数中的this"></a>函数中的this</h2><p>在总结函数中this指向之前,有必要通过一些奇怪的例子,来感受一下函数中this的内涵。</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">// demo01</span></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">20</span>;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">fn</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>.<span class="property">a</span>);</span><br><span class="line">}</span><br><span class="line"><span class="title function_">fn</span>();</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// demo02</span></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">20</span>;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">fn</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>.<span class="property">a</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="title function_">foo</span>();</span><br><span class="line">}</span><br><span class="line"><span class="title function_">fn</span>();</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// demo03</span></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">20</span>;</span><br><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> <span class="attr">a</span>: <span class="number">10</span>,</span><br><span class="line"> <span class="attr">c</span>: <span class="variable language_">this</span>.<span class="property">a</span> + <span class="number">20</span>,</span><br><span class="line"> <span class="attr">fn</span>: <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">a</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(obj.<span class="property">c</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(obj.<span class="title function_">fn</span>());</span><br></pre></td></tr></table></figure>
<p>在一个函数上下文中,this由调用者提供,由调用函数的方式来决定。<span class='p blue'>如果调用者函数,被某一个对象所拥有,那么该函数在调用时,内部的this指向该对象。如果函数独立调用,那么该函数内部的this,则指向undefined</span> 。但是在非严格模式中,当this指向undefined时,它会被自动指向全局对象。</p>
<p>想要准确确定this指向,找到函数的调用者以及区分他是否是独立调用十分关键。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 为了能够准确判断,在函数内部使用严格模式,因为非严格模式会自动指向全局</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">fn</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="string">'use strict'</span>;</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">fn</span>(); <span class="comment">// fn是调用者,独立调用</span></span><br><span class="line"><span class="variable language_">window</span>.<span class="title function_">fn</span>(); <span class="comment">// fn是调用者,被window所拥有</span></span><br></pre></td></tr></table></figure>
<p>在上面的简单例子中,<code>fn()</code>作为独立调用者,按照定义的理解,它内部的this指向就为undefined。而<code>window.fn()</code>则因为fn被window所拥有,内部的this就指向了window对象。</p>
<p>掌握了这个规则,现在回过头去看看上面的三个例子,通过添加/去除严格模式,你就会发现,原来this已经变得有迹可循了。</p>
<p>需要特别注意的是demo03。在demo03中,对象obj中的c属性使用<code>this.a + 20</code>来计算。这里需要明确的一点是,单独的<code>{}</code>不会形成新的作用域,因此这里的<code>this.a</code>,由于并没有作用域的限制,它仍然处于全局作用域之中。所以这里的this其实是指向的window对象。</p>
<p>修改一下demo03的代码,大家可以思考一下会发生什么变化。</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="meta">'use strict'</span>;</span><br><span class="line"><span class="keyword">var</span> a = <span class="number">20</span>;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">var</span> obj = {</span><br><span class="line"> <span class="attr">a</span>: <span class="number">10</span>,</span><br><span class="line"> <span class="attr">c</span>: <span class="variable language_">this</span>.<span class="property">a</span> + <span class="number">20</span>,</span><br><span class="line"> <span class="attr">fn</span>: <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">a</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> obj.<span class="property">c</span>;</span><br><span class="line">}</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">foo</span>()); <span class="comment">// ?</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">window</span>.<span class="title function_">foo</span>()); <span class="comment">// ?</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>实际开发中,并不推荐这样使用this;上面多次提到的严格模式,需要大家认真对待,因为在实际开发中,现在基本已经全部采用严格模式了,而最新的ES6,也是默认支持严格模式。</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="keyword">var</span> a = <span class="number">20</span>;</span><br><span class="line"><span class="keyword">var</span> foo = {</span><br><span class="line"> <span class="attr">a</span>: <span class="number">10</span>,</span><br><span class="line"> <span class="attr">getA</span>: <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">a</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(foo.<span class="title function_">getA</span>()); <span class="comment">// 10</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> test = foo.<span class="property">getA</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">test</span>()); <span class="comment">// 20</span></span><br></pre></td></tr></table></figure>
<p><code>foo.getA()</code>中,getA是调用者,他不是独立调用,被对象foo所拥有,因此它的this指向了foo。而<code>test()</code>作为调用者,尽管他与foo.getA的引用相同,但是它是独立调用的,因此this指向undefined,在非严格模式,自动转向全局window。</p>
<p>稍微修改一下代码,大家自行理解。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">20</span>;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">getA</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">a</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> foo = {</span><br><span class="line"> <span class="attr">a</span>: <span class="number">10</span>,</span><br><span class="line"> <span class="attr">getA</span>: getA</span><br><span class="line">}</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(foo.<span class="title function_">getA</span>()); <span class="comment">// 10</span></span><br></pre></td></tr></table></figure>
<p>再来一个例子。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>.<span class="property">a</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">active</span>(<span class="params">fn</span>) {</span><br><span class="line"> <span class="title function_">fn</span>(); <span class="comment">// 真实调用者,为独立调用</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">20</span>;</span><br><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> <span class="attr">a</span>: <span class="number">10</span>,</span><br><span class="line"> <span class="attr">getA</span>: foo</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">active</span>(obj.<span class="property">getA</span>);</span><br></pre></td></tr></table></figure>
<h2 id="call,apply指定this"><a href="#call,apply指定this" class="headerlink" title="call,apply指定this"></a>call,apply指定this</h2><p>JavaScript内部提供了一种机制,让我们可以自行手动设置this的指向。它们就是 <span class='p blue'>call</span> 与 <span class='p blue'>apply</span> 。所有的函数都具有这两个方法。它们除了参数略有不同之外,其功能完全一样。它们的第一个参数都为this将要指向的对象。</p>
<p>如下例子所示。fn 并非属于 对象obj 的方法,但是通过call,将fn内部的this绑定为obj,因此就可以使用this.a访问obj的a属性了。这就是call/apply的用法。</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">function</span> <span class="title function_">fn</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>.<span class="property">a</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> <span class="attr">a</span>: <span class="number">20</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">fn.<span class="title function_">call</span>(obj);</span><br></pre></td></tr></table></figure>
<p>call与applay后面的参数,都是向将要执行的函数传递参数。其中call以一个一个的形式传递,apply以数组的形式传递。这是他们唯一的不同。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">fn</span>(<span class="params">num1, num2</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>.<span class="property">a</span> + num1 + num2);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> <span class="attr">a</span>: <span class="number">20</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">fn.<span class="title function_">call</span>(obj, <span class="number">100</span>, <span class="number">10</span>); <span class="comment">// 130</span></span><br><span class="line">fn.<span class="title function_">apply</span>(obj, [<span class="number">20</span>, <span class="number">10</span>]); <span class="comment">// 50</span></span><br></pre></td></tr></table></figure>
<p>因为call/apply的存在,JavaScript变得更加灵活。也因此他们的使用场景就多种多样。简单总结几点,也欢迎大家补充。</p>
<h3 id="将类数组对象转换为数组"><a href="#将类数组对象转换为数组" class="headerlink" title="将类数组对象转换为数组"></a>将类数组对象转换为数组</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">exam</span>(<span class="params">a, b, c, d, e</span>) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 先看看函数的自带属性 arguments 什么是样子的</span></span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">arguments</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 使用call/apply将arguments转换为数组, 返回结果为数组,arguments自身不会改变</span></span><br><span class="line"> <span class="keyword">var</span> arg = [].<span class="property">slice</span>.<span class="title function_">call</span>(<span class="variable language_">arguments</span>);</span><br><span class="line"></span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(arg);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">exam</span>(<span class="number">2</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">3</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// result:</span></span><br><span class="line"><span class="comment">// { '0': 2, '1': 8, '2': 9, '3': 10, '4': 3 }</span></span><br><span class="line"><span class="comment">// [ 2, 8, 9, 10, 3 ]</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 也常常使用该方法将DOM中的nodelist转换为数组</span></span><br><span class="line"><span class="comment">// [].slice.call( document.getElementsByTagName('li') );</span></span><br></pre></td></tr></table></figure>
<h3 id="根据需求灵活修改this指向"><a href="#根据需求灵活修改this指向" class="headerlink" title="根据需求灵活修改this指向"></a>根据需求灵活修改this指向</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> foo = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'joker'</span>,</span><br><span class="line"> <span class="attr">showName</span>: <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>.<span class="property">name</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> bar = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'rose'</span></span><br><span class="line">}</span><br><span class="line">foo.<span class="property">showName</span>.<span class="title function_">call</span>(bar);</span><br></pre></td></tr></table></figure>
<h3 id="实现继承"><a href="#实现继承" class="headerlink" title="实现继承"></a>实现继承</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义父级的构造函数</span></span><br><span class="line"><span class="keyword">var</span> <span class="title class_">Person</span> = <span class="keyword">function</span> (<span class="params">name, age</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">age</span> = age;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">gender</span> = [<span class="string">'man'</span>, <span class="string">'woman'</span>];</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义子类的构造函数</span></span><br><span class="line"><span class="keyword">var</span> <span class="title class_">Student</span> = <span class="keyword">function</span> (<span class="params">name, age, high</span>) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// use call</span></span><br><span class="line"> <span class="title class_">Person</span>.<span class="title function_">call</span>(<span class="variable language_">this</span>, name, age);</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">high</span> = high;</span><br><span class="line">}</span><br><span class="line"><span class="title class_">Student</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">message</span> = <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'name:'</span> + <span class="variable language_">this</span>.<span class="property">name</span> + <span class="string">', age:'</span> + <span class="variable language_">this</span>.<span class="property">age</span> + <span class="string">', high:'</span> + <span class="variable language_">this</span>.<span class="property">high</span> + <span class="string">', gender:'</span> + <span class="variable language_">this</span>.<span class="property">gender</span>[<span class="number">0</span>] + <span class="string">';'</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Student</span>(<span class="string">'xiaom'</span>, <span class="number">12</span>, <span class="string">'150cm'</span>).<span class="title function_">message</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// result</span></span><br><span class="line"><span class="comment">// ----------</span></span><br><span class="line"><span class="comment">// name:xiaom, age:12, high:150cm, gender:man;</span></span><br></pre></td></tr></table></figure>
<p>简单给有面向对象基础的朋友解释一下。在Student的构造函数中,借助call方法,将父级的构造函数执行了一次,相当于将Person中的代码,在Sudent中复制了一份,其中的this指向为从Student中new出来的实例对象。call方法保证了this的指向正确,因此就相当于实现了继承。Student的构造函数等同于下。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> <span class="title class_">Student</span> = <span class="keyword">function</span> (<span class="params">name, age, high</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">age</span> = age;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">gender</span> = [<span class="string">'man'</span>, <span class="string">'woman'</span>];</span><br><span class="line"> <span class="comment">// Person.call(this, name, age); 这一句话,相当于上面三句话,因此实现了继承</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">high</span> = high;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="在向其他执行上下文的传递中,确保this的指向保持不变"><a href="#在向其他执行上下文的传递中,确保this的指向保持不变" class="headerlink" title="在向其他执行上下文的传递中,确保this的指向保持不变"></a>在向其他执行上下文的传递中,确保this的指向保持不变</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> <span class="attr">a</span>: <span class="number">20</span>,</span><br><span class="line"> <span class="attr">getA</span>: <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>.<span class="property">a</span>)</span><br><span class="line"> }, <span class="number">1000</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">obj.<span class="title function_">getA</span>();</span><br></pre></td></tr></table></figure>
<p>如上例中,我们期待的是getA被obj调用时,this指向obj,但是由于匿名函数的存在导致了this指向的丢失,在这个匿名函数中this指向了全局,因此需要想些办法找回正确的this指向。</p>
<p>常规的解决办法很简单,就是使用一个变量,将this的引用保存起来。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> <span class="attr">a</span>: <span class="number">20</span>,</span><br><span class="line"> <span class="attr">getA</span>: <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> self = <span class="variable language_">this</span>;</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(self.<span class="property">a</span>)</span><br><span class="line"> }, <span class="number">1000</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>另外就是借助闭包与apply方法,封装一个bind方法。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">bind</span>(<span class="params">fn, obj</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> fn.<span class="title function_">apply</span>(obj, <span class="variable language_">arguments</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> <span class="attr">a</span>: <span class="number">20</span>,</span><br><span class="line"> <span class="attr">getA</span>: <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="title function_">bind</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>.<span class="property">a</span>)</span><br><span class="line"> }, <span class="variable language_">this</span>), <span class="number">1000</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">obj.<span class="title function_">getA</span>();</span><br></pre></td></tr></table></figure>
<p>当然,也可以使用ES5中已经自带的bind方法。它与我上面封装的bind方法是一样的效果。</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">var</span> obj = {</span><br><span class="line"> <span class="attr">a</span>: <span class="number">20</span>,</span><br><span class="line"> <span class="attr">getA</span>: <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>.<span class="property">a</span>)</span><br><span class="line"> }.<span class="title function_">bind</span>(<span class="variable language_">this</span>), <span class="number">1000</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="构造函数与原型方法上的this"><a href="#构造函数与原型方法上的this" class="headerlink" title="构造函数与原型方法上的this"></a>构造函数与原型方法上的this</h2><p>在封装对象的时候,大家几乎都会用到this,只有少数人搞明白了在这个过程中的this指向,就算理解了原型,也不一定理解到了this。所以这一部分,我认为将会为这篇文章最重要最核心的部分。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Person</span>(<span class="params">name, age</span>) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 这里的this指向了谁?</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">age</span> = age; </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title class_">Person</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">getName</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 这里的this又指向了谁?</span></span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">name</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 上面的2个this,是同一个吗,他们是否指向了原型对象?</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> p1 = <span class="keyword">new</span> <span class="title class_">Person</span>(<span class="string">'Nick'</span>, <span class="number">20</span>);</span><br><span class="line">p1.<span class="title function_">getName</span>();</span><br></pre></td></tr></table></figure>
<p>我们已经知道this是在函数调用过程中确定,因此,搞明白new的过程中到底发生了什么就变得十分重要。</p>
<p>通过new操作符调用构造函数,会经历以下4个阶段。</p>
<ul>
<li>创建一个新的对象;</li>
<li>将构造函数的this指向这个新对象;</li>
<li>指向构造函数的代码,为这个对象添加属性,方法等;</li>
<li>返回新对象。</li>
</ul>
<p>当new操作符调用构造函数时,this其实指向的是这个新创建的对象,最后又将新的对象返回出来,被实例对象p1接收。这个时候,构造函数的this,指向了新的实例对象p1。</p>
<p>而原型方法上的this就好理解多了,根据上边对函数中this的定义,p1.getName()中的getName为调用者,它被p1所拥有,因此getName中的this,也是指向了p1。</p>
<div class="note no-icon flat"><p>原文链接:<a
前端基础进阶(六):setTimeout与循环闭包面试题详解
https://fe32.top/articles/jsnb9533/
2022-06-14T15:43:39.000Z
2023-05-17T10:01:12.000Z
<p>在上文 <a href="https://fe32.top/articles/jsnb9532/">前端基础进阶(五):闭包</a> 中的结尾留下了一个关于setTimeout与循环闭包的思考题。</p>
<blockquote>
<p>利用闭包,修改下面的代码,让循环输出的结果依次为1, 2, 3, 4, 5</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">1</span>; i <= <span class="number">5</span>; i++) {</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="keyword">function</span> <span class="title function_">timer</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(i);</span><br><span class="line"> }, i * <span class="number">1000</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<blockquote>
<p>setTimeout 方法会设置一个计时器,一旦计时器到期,该计时器就会执行一个函数或指定的一段代码。<br>它的返回timeoutID的是一个正整数值,用于标识调用创建的计时器setTimeout()。<br>该函数详细介绍请移步: <a href="https://developer.mozilla.org/en-US/docs/Web/API/setTimeout">setTimeout - MDN</a></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></pre></td><td class="code"><pre><span class="line"><span class="built_in">setTimeout</span>(code)</span><br><span class="line"><span class="built_in">setTimeout</span>(code, delay)</span><br><span class="line"></span><br><span class="line"><span class="built_in">setTimeout</span>(functionRef)</span><br><span class="line"><span class="built_in">setTimeout</span>(functionRef, delay)</span><br><span class="line"><span class="built_in">setTimeout</span>(functionRef, delay, param1)</span><br><span class="line"><span class="built_in">setTimeout</span>(functionRef, delay, param1, param2)</span><br><span class="line"><span class="built_in">setTimeout</span>(functionRef, delay, param1, param2, <span class="comment">/* ... ,*/</span> paramN)</span><br></pre></td></tr></table></figure>
<table>
<thead>
<tr>
<th align="left">参数</th>
<th align="left">说明</th>
</tr>
</thead>
<tbody><tr>
<td align="left">functionRef</td>
<td align="left">Afunction在计时器到期后执行。</td>
</tr>
<tr>
<td align="left">code</td>
<td align="left">允许您包含字符串而不是函数的替代语法,该语法在计时器到期时编译并执行。由于与使用安全风险,不建议使用eval()语法。</td>
</tr>
<tr>
<td align="left">delay 可选</td>
<td align="left">在执行指定的函数或代码之前计时器应等待的时间(以毫秒为单位)。如果省略此参数,则使用值 0,表示“立即”执行,或者更准确地说,执行下一个事件循环。</td>
</tr>
<tr>
<td align="left">param1, …,paramN 可选</td>
<td align="left">传递给指定函数的附加参数 function。</td>
</tr>
</tbody></table>
<p>执行结果如图:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/15/62aa0009bb7d4.webp"/></div></div>
<p>上图中的数字7,就是这个唯一的timeoutID。在使用时,常常会使用一个变量将这个唯一的timeoutID保存起来,用以传入clearTimeout,清除定时器。</p>
<p>接下来,我们还需要考虑另外一个重要的问题,那就是setTimeout中定义的操作,在什么时候执行?为了引起大家的重视,我们来看看下面的例子。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> timer = <span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'setTimeout actions.'</span>);</span><br><span class="line">}, <span class="number">0</span>);</span><br><span class="line"> </span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'other actions.'</span>);</span><br></pre></td></tr></table></figure>
<p>思考一下,当我将setTimeout的延迟时间设置为0时,上面的执行顺序会是什么?</p>
<p>在浏览器中的console中运行试试看,很容易就能够知道答案。</p>
<p>在对于 <a href="https://fe32.top/articles/jsnb9529/">执行上下文</a> 的介绍中,与大家分享了 <span class='p blue'>函数调用栈</span> 这种特殊数据结构的调用特性。在这里,将会介绍另外一个特殊的队列结构,页面中所有由setTimeout定义的操作,都将放在同一个队列中依次执行。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/16/62aa0393b10bf.webp" alt="队列数据结构的特点:先进先出"/></div><span class="image-caption">队列数据结构的特点:先进先出</span></div>
<blockquote>
<p>而这个队列执行的时间,需要等待到函数调用栈清空之后才开始执行。即所有可执行代码执行完毕之后,才会开始执行由setTimeout定义的操作。而这些操作进入队列的顺序,则由设定的延迟时间来决定。</p>
</blockquote>
<p>因此在上面这个例子中,即使我们将延迟时间设置为0,它定义的操作仍然需要等待所有代码执行完毕之后才开始执行。这里的延迟时间,并非相对于setTimeout执行这一刻,而是相对于其他代码执行完毕这一刻。</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="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(a);</span><br><span class="line">}, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">10</span>;</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(b);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(fn);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> b = <span class="number">20</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">fn</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'setTImeout 10ms.'</span>);</span><br><span class="line"> }, <span class="number">10</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">fn.<span class="property">toString</span> = <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">30</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(fn);</span><br><span class="line"></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'setTimeout 20ms.'</span>);</span><br><span class="line">}, <span class="number">20</span>);</span><br><span class="line"></span><br><span class="line"><span class="title function_">fn</span>();</span><br></pre></td></tr></table></figure>
<p>执行结果如图所示:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/16/62aa041ec1de3.webp"/></div></div>
<p>到这一步,关于setTimeout就暂时先介绍到这里,我们回过头来看看循环闭包的思考题。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">1</span>; i <= <span class="number">5</span>; i++) {</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="keyword">function</span> <span class="title function_">timer</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(i);</span><br><span class="line"> }, i * <span class="number">1000</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>如果直接这样写,根据setTimeout定义的操作在函数调用栈清空之后才会执行的特点,for循环里定义了5个setTimeout操作。而当这些操作开始执行时,for循环的i值,已经先一步变成了6。因此输出结果总为6。想要让输出结果依次执行,就必须借助闭包的特性,每次循环时,将i值保存在一个闭包中,当setTimeout中定义的操作执行时,则访问对应闭包保存的i值即可。</p>
<p>如果知道在函数中闭包判定的准则,即执行时是否在内部定义的函数中访问了上层作用域的变量。我们则需要包裹一层自执行函数为闭包的形成提供条件。</p>
<p>因此,只需要2个操作就可以完成题目需求,一是使用自执行函数提供闭包条件,二是传入i值并保存在闭包中。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">1</span>; i <= <span class="number">5</span>; i++) {</span><br><span class="line"> (<span class="keyword">function</span> (<span class="params">i</span>) {</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="keyword">function</span> <span class="title function_">timer</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(i);</span><br><span class="line"> }, i * <span class="number">1000</span>);</span><br><span class="line"> })(i)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/16/62aa041ec1de3.webp" alt="利用断点调试,在chrome中查看执行顺序与每一个闭包中不同的i值"/></div><span class="image-caption">利用断点调试,在chrome中查看执行顺序与每一个闭包中不同的i值</span></div>
<p>当然,也可以在setTimeout的第一个参数处利用闭包。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">1</span>; i <= <span class="number">5</span>; i++) {</span><br><span class="line"> <span class="built_in">setTimeout</span>((<span class="keyword">function</span> (<span class="params">i</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(i);</span><br><span class="line"> }</span><br><span class="line"> })(i), i * <span class="number">1000</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>闭包之外的方法:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// setTimeout第三个参数</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i=<span class="number">1</span>; i<=<span class="number">5</span>; i++) {</span><br><span class="line"> <span class="built_in">setTimeout</span>( <span class="keyword">function</span> <span class="title function_">timer</span>(<span class="params">i</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(i);</span><br><span class="line"> }, i*<span class="number">1000</span>,i );</span><br><span class="line">}</span><br><span class="line"><span class="comment">// bind</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i=<span class="number">1</span>; i<=<span class="number">5</span>; i++) {</span><br><span class="line"> <span class="built_in">setTimeout</span>( <span class="keyword">function</span> <span class="title function_">timer</span>(<span class="params">i</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(i);</span><br><span class="line"> }.<span class="title function_">bind</span>(<span class="literal">null</span>,i), i*<span class="number">1000</span> );</span><br><span class="line">}</span><br><span class="line"><span class="comment">// let</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> i=<span class="number">1</span>; i<=<span class="number">5</span>; i++) {</span><br><span class="line"> <span class="built_in">setTimeout</span>( <span class="keyword">function</span> <span class="title function_">timer</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(i);</span><br><span class="line"> }, i*<span class="number">1000</span> );</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<div class="note no-icon flat"><p>原文链接:<a
前端基础进阶(五):闭包
https://fe32.top/articles/jsnb9532/
2022-06-13T13:54:22.000Z
2023-05-17T10:01:12.000Z
<h2 id="闭包"><a href="#闭包" class="headerlink" title="闭包"></a>闭包</h2><p class='p blue'>闭包是一种特殊的对象。它由两部分组成。执行上下文(代号A),以及在该执行上下文中创建的函数(代号B)。</p>
<p class='p blue'>当B执行时,如果访问了A中变量对象中的值,那么闭包就会产生。</p>
<p class='p blue'>在大多数理解中,包括许多著名的书籍,文章里都以函数B的名字代指这里生成的闭包。而在chrome中,则以执行上下文A的函数名代指闭包 。</p>
<blockquote>
<p>一个闭包对象,由A、B共同组成,在以后的篇幅中,我将以chrome的标准来称呼。</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// demo01</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">20</span>;</span><br><span class="line"> <span class="keyword">var</span> b = <span class="number">30</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">bar</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> a + b;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> bar;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> bar = <span class="title function_">foo</span>();</span><br><span class="line"><span class="title function_">bar</span>();</span><br></pre></td></tr></table></figure>
<p>上面的例子,首先有执行上下文foo,在foo中定义了函数bar,而通过对外返回bar的方式让bar得以执行。当bar执行时,访问了foo内部的变量a,b。因此这个时候闭包产生。</p>
<p>在 <a href="https://fe32.top/articles/jsnb9528/">前端基础进阶(一):内存空间详细图解</a> 中,我总结了JavaScript的垃圾回收机制。JavaScript拥有自动的垃圾回收机制,关于垃圾回收机制,有一个重要的行为,那就是,当一个值,在内存中失去引用时,垃圾回收机制会根据特殊的算法找到它,并将其回收,释放内存。</p>
<p>而我们知道,函数的执行上下文,在执行完毕之后,生命周期结束,那么该函数的执行上下文就会失去引用。其占用的内存空间很快就会被垃圾回收器释放。可是闭包的存在,会阻止这一过程。</p>
<p>先来一个简单的例子。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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">var</span> fn = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">innnerFoo</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(a);</span><br><span class="line"> }</span><br><span class="line"> fn = innnerFoo; <span class="comment">// 将 innnerFoo的引用,赋值给全局变量中的fn</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">bar</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="title function_">fn</span>(); <span class="comment">// 此处的保留的innerFoo的引用</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">foo</span>();</span><br><span class="line"><span class="title function_">bar</span>(); <span class="comment">// 2</span></span><br></pre></td></tr></table></figure>
<p>在上面的例子中,<code>foo()</code>执行完毕之后,按照常理,其执行环境生命周期会结束,所占内存被垃圾收集器释放。但是通过<code>fn = innerFoo</code>,函数innerFoo的引用被保留了下来,复制给了全局变量fn。这个行为,导致了foo的变量对象,也被保留了下来。于是,函数fn在函数bar内部执行时,依然可以访问这个被保留下来的变量对象。所以此刻仍然能够访问到变量a的值。</p>
<p>这样,我们就可以称foo为闭包。下图展示了闭包foo的作用域链:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/14/62a77ccd9390b.webp" alt="闭包foo的作用域链"/></div><span class="image-caption">闭包foo的作用域链</span></div>
<p>我们可以在chrome浏览器的开发者工具中查看这段代码运行时产生的函数调用栈与作用域链的生成情况。如下图:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/14/62a77d6f9cf32.webp" alt="从图中可以看出,chrome浏览器认为闭包是foo,而不是通常我们认为的innerFoo"/></div><span class="image-caption">从图中可以看出,chrome浏览器认为闭包是foo,而不是通常我们认为的innerFoo</span></div>
<p>在上面的图中,红色箭头所指的正是闭包。其中Call Stack为当前的函数调用栈,Scope为当前正在被执行的函数的作用域链,Local为当前的局部变量。</p>
<p>所以,通过闭包,我们可以在其他的执行上下文中,访问到函数的内部变量。比如在上面的例子中,我们在函数bar的执行环境中访问到了函数foo的a变量。个人认为,从应用层面,这是闭包最重要的特性。利用这个特性,我们可以实现很多有意思的东西。</p>
<p>不过需要注意的是,虽然例子中的闭包被保存在了全局变量中,但是闭包的作用域链并不会发生任何改变。在闭包中,能访问到的变量,仍然是作用域链上能够查询到的变量。</p>
<p>对上面的例子稍作修改,如果我们在函数bar中声明一个变量c,并在闭包fn中试图访问该变量,运行结果会抛出错误。</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"><span class="keyword">var</span> fn = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">innnerFoo</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(c); <span class="comment">// 在这里,试图访问函数bar中的c变量,会抛出错误</span></span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(a);</span><br><span class="line"> }</span><br><span class="line"> fn = innnerFoo; <span class="comment">// 将 innnerFoo的引用,赋值给全局变量中的fn</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">bar</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> c = <span class="number">100</span>;</span><br><span class="line"> <span class="title function_">fn</span>(); <span class="comment">// 此处的保留的innerFoo的引用</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">foo</span>();</span><br><span class="line"><span class="title function_">bar</span>();</span><br></pre></td></tr></table></figure>
<h2 id="闭包的应用场景"><a href="#闭包的应用场景" class="headerlink" title="闭包的应用场景"></a>闭包的应用场景</h2><h3 id="柯里化"><a href="#柯里化" class="headerlink" title="柯里化"></a>柯里化</h3><p>在函数式编程中,利用闭包能够实现很多炫酷的功能,柯里化便是其中很重要的一种。</p>
<h3 id="模块化"><a href="#模块化" class="headerlink" title="模块化"></a>模块化</h3><p>模块化是闭包最强大的一个应用场景。如果你是初学者,对于模块的了解可以暂时不用放在心上,因为理解模块需要更多的基础知识。但是如果你已经有了很多JavaScript的使用经验,在彻底了解了闭包之后,不妨借助本文介绍的作用域链与闭包的思路,重新理一理关于模块的知识。这对于我们理解各种各样的设计模式具有莫大的帮助。</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="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">10</span>;</span><br><span class="line"> <span class="keyword">var</span> b = <span class="number">20</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">add</span>(<span class="params">num1, num2</span>) {</span><br><span class="line"> <span class="keyword">var</span> num1 = !!num1 ? num1 : a;</span><br><span class="line"> <span class="keyword">var</span> num2 = !!num2 ? num2 : b;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> num1 + num2;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="variable language_">window</span>.<span class="property">add</span> = add;</span><br><span class="line">})();</span><br><span class="line"></span><br><span class="line"><span class="title function_">add</span>(<span class="number">10</span>, <span class="number">20</span>);</span><br></pre></td></tr></table></figure>
<p>在上面的例子中,我使用函数自执行的方式,创建了一个模块。add是模块对外暴露的一个公共方法。而变量a,b被作为私有变量。在面向对象的开发中,我们常常需要考虑是将变量作为私有变量,还是放在构造函数中的this中,因此理解闭包,以及原型链是一个非常重要的事情。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/14/62a77da17b1b4.webp" alt="此图中可以观看到当代码执行到add方法时的调用栈与作用域链,此刻的闭包为外层的自执行函数"/></div><span class="image-caption">此图中可以观看到当代码执行到add方法时的调用栈与作用域链,此刻的闭包为外层的自执行函数</span></div>
<p>为了验证自己有没有搞懂作用域链与闭包,这里留下一个经典的思考题,常常也会在面试中被问到。</p>
<p>利用闭包,修改下面的代码,让循环输出的结果依次为1, 2, 3, 4, 5</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">1</span>; i <= <span class="number">5</span>; i++) {</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="keyword">function</span> <span class="title function_">timer</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(i);</span><br><span class="line"> }, i * <span class="number">1000</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<div class="note no-icon flat"><p>原文链接:<a
前端基础进阶(四):作用域与作用域链
https://fe32.top/articles/jsnb9531/
2022-06-12T14:45:47.000Z
2023-05-17T10:01:12.000Z
<h2 id="词法环境(Lexical-Environments)"><a href="#词法环境(Lexical-Environments)" class="headerlink" title="词法环境(Lexical Environments)"></a>词法环境(Lexical Environments)</h2><p>官方规范对词法环境的说明是:词法环境(Lexical Environments)是 <span class='p blue'>一种规范类型,用于根据ECMAScript代码的词法嵌套结构来定义标识符与特定变量和函数的关联</span> 。</p>
<p>通俗来说,词法环境就是一套约定好的规则。我们写代码,应该按照这个规则来。JS引擎对JS源码进行词法分析,也是按照这个规则。</p>
<blockquote>
<p>词法环境,其实就是作用域。</p>
</blockquote>
<p>得出结论:<br>一、在JavaScript中,我们可以将作用域定义为一套规则,这套规则用来管理JS引擎如何在当前作用域以及嵌套的子作用域中根据标识符(变量或者函数)名称进行变量查找。<br>二、JavaScript中有全局作用域与函数作用域(因为eval我们平时开发中几乎不会用到它,这里不讨论)。</p>
<h2 id="作用域链"><a href="#作用域链" class="headerlink" title="作用域链"></a>作用域链</h2><p>作用域是一套规则;而作用域链,则是作用域的具体实现。</p>
<span class='p blue'>作用域链,是由当前环境与上层环境的一系列变量对象组成,它保证了当前执行环境对符合访问权限的变量和函数的有序访问</span> 。
<p>作用域链,在函数声明阶段确认。如果要结合 JavaScript 引擎来理解的话,作用域链,就是在代码解析阶段确认的。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">20</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">test</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> b = a + <span class="number">10</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">innerTest</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> c = <span class="number">10</span>;</span><br><span class="line"> <span class="keyword">return</span> b + c;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">innerTest</span>();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">test</span>();</span><br></pre></td></tr></table></figure>
<p>在上面的例子中,全局,函数test,函数innerTest的执行上下文先后创建。我们设定他们的变量对象分别为VO(global),VO(test), VO(innerTest)。而innerTest的作用域链,则同时包含了这三个变量对象,所以innerTest的执行上下文可如下表示。</p>
<p>很多人会误解为当前作用域与上层作用域为包含关系,但其实并不是。以最前端为起点,最末端为终点的单方向通道我认为是更加贴切的形容。如图:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/13/62a741da874d2.webp" alt="作用域链图示"/></div><span class="image-caption">作用域链图示</span></div>
<blockquote>
<p>注意:因为变量对象在执行上下文进入执行阶段时,就变成了活动对象,这一点在上一篇文章中已经讲过,因此图中使用了AO来表示Active Object。</p>
</blockquote>
<p>作用域链是由一系列变量对象组成,我们可以在这个单向通道中,查询变量对象中的标识符,这样,就能访问到上一层作用域中的变量。</p>
<p>最后,用一个问题,验证一下大家对作用域和作用域链以及变量对象的理解:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">20</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">if</span> (!a) {</span><br><span class="line"> a = <span class="number">100</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">10</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> a;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">foo</span>());</span><br></pre></td></tr></table></figure>
<div class="note no-icon flat"><p>原文链接:<a
前端基础进阶(三):变量对象详解
https://fe32.top/articles/jsnb9530/
2022-06-12T12:35:46.000Z
2023-05-17T10:01:12.000Z
<blockquote>
<p>变量对象,新版本中,准确的说法应该是<span class='p blue'>环境记录对象</span>。而环境记录对象,又区分词法环境对象与变量环境对象,词法环境对象用于解析当前上下文中,由 const 声明的标识符引用,变量环境对象,用于解析由 var 声明的标识符引用。执行上下文内部的实现逻辑过于复杂,并不利于理解,因此此处为了理解方便,仍然统一采用变量对象的说法.</p>
</blockquote>
<p>在JavaScript中,肯定不可避免的需要声明变量和函数,JS编译器是如何找到这些变量的呢?</p>
<p>我们还得对执行上下文有一个进一步的了解。</p>
<p>在上一篇文章中已经知道,当调用一个函数时(激活),一个新的执行上下文就会被创建。一个执行上下文的生命周期可以分为以下几个阶段:</p>
<ul>
<li><p>创建阶段<br>在这个阶段中,执行上下文会分别创建变量对象,确定this指向,以及其他需要的状态。</p>
</li>
<li><p>代码执行阶段<br>创建完成之后,就会开始执行代码,会完成变量赋值,以及执行其他代码。</p>
</li>
<li><p>销毁阶段<br>可执行代码执行完毕之后,执行上下文出栈,对应的内存空间失去引用,等待被回收</p>
</li>
</ul>
<h2 id="变量对象(Variable-Object)"><a href="#变量对象(Variable-Object)" class="headerlink" title="变量对象(Variable Object)"></a>变量对象(Variable Object)</h2><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">// 这里a为属性名,20是属性值</span></span><br><span class="line">{</span><br><span class="line"> <span class="attr">a</span>: <span class="number">20</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>一、建立arguments对象:检查当前上下文中的参数,建立该对象下的属性与属性值。<br>二、检查当前上下文的函数声明,也就是使用function关键字声明的函数。在变量对象中以函数名建立一个属性,属性值为指向该函数所在内存地址的引用。<br>三、检查当前上下文中的变量声明,每找到一个变量声明,就在变量对象中以变量名建立一个属性,属性值为undefined,const/let 声明的变量没有赋值,不能提前使用。</p>
<blockquote>
<p>如果 var 变量与函数同名,则在这个阶段,以函数值为准,在下一个阶段,函数值会被变量值覆盖。</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></pre></td><td class="code"><pre><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(foo); <span class="comment">// function foo</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) { <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'function foo'</span>) }</span><br><span class="line"><span class="keyword">var</span> foo = <span class="number">20</span>;</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></pre></td><td class="code"><pre><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">function</span> <span class="title function_">foo</span>(<span class="params"></span>) { <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'function foo'</span>) }</span><br><span class="line"></span><br><span class="line"><span class="comment">// 其次将所有变量声明放入变量对象中,但是因为foo已经存在同名函数,此时以函数值为准,而不会被undefined覆盖</span></span><br><span class="line"><span class="comment">// var foo = undefined;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 然后开始执行阶段代码的执行</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(foo); <span class="comment">// function foo</span></span><br><span class="line">foo = <span class="number">20</span>;</span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/12/62a5ec81c9f94.webp"/></div></div>
<p>根据这个规则,理解变量提升就变得十分简单了。在很多文章中虽然提到了变量提升,但是具体是怎么回事还真的很多人都说不出来,以后在面试中用变量对象的创建过程跟面试官解释变量提升,简直逼格满满。</p>
<p>在上面的规则中我们看出,function声明会比var声明优先级更高一点。为了帮助大家更好的理解变量对象,我们结合一些简单的例子来进行探讨。</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="comment">// demo01</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">test</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(a);</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">foo</span>());</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">2</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">test</span>(); </span><br></pre></td></tr></table></figure>
<p>在上例中,我们直接从<code>test()</code>的执行上下文开始理解。全局作用域中运行<code>test()</code>时,<code>test()</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">// 创建过程</span></span><br><span class="line">testEC = {</span><br><span class="line"> <span class="comment">// 变量对象</span></span><br><span class="line"> <span class="attr">VO</span>: {},</span><br><span class="line"> <span class="attr">scopeChain</span>: {}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 因为本文暂时不详细解释作用域链,所以把变量对象专门提出来说明</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// VO 为 Variable Object的缩写,即变量对象</span></span><br><span class="line"><span class="variable constant_">VO</span> = {</span><br><span class="line"> <span class="attr">arguments</span>: {...}, <span class="comment">//注:在浏览器的展示中,函数的参数可能并不是放在arguments对象中,这里为了方便理解,我做了这样的处理</span></span><br><span class="line"> <span class="attr">foo</span>: <span class="language-xml"><span class="tag"><<span class="name">foo</span> <span class="attr">reference</span>></span> // 表示foo的地址引用</span></span><br><span class="line"><span class="language-xml"> a: undefined</span></span><br><span class="line"><span class="language-xml">}</span></span><br></pre></td></tr></table></figure>
<p>未进入执行阶段之前,变量对象中的属性都不能访问!但是进入执行阶段之后,变量对象转变为了活动对象,里面的属性都能被访问了,然后开始进行执行阶段的操作。</p>
<blockquote>
<p>变量对象和活动对象其实都是同一个对象,只是处于执行上下文的不同生命周期。不过只有处于函数调用栈栈顶的执行上下文中的变量对象,才会变成活动对象。</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 执行阶段</span></span><br><span class="line"><span class="variable constant_">VO</span> -> <span class="variable constant_">AO</span> <span class="comment">// Active Object</span></span><br><span class="line"><span class="variable constant_">AO</span> = {</span><br><span class="line"> <span class="attr">arguments</span>: {...},</span><br><span class="line"> <span class="attr">foo</span>: <span class="language-xml"><span class="tag"><<span class="name">foo</span> <span class="attr">reference</span>></span>,</span></span><br><span class="line"><span class="language-xml"> a: 1,</span></span><br><span class="line"><span class="language-xml"> this: Window</span></span><br><span class="line"><span class="language-xml">}</span></span><br></pre></td></tr></table></figure>
<p>因此,上面的例子demo1,执行顺序就变成了这样。</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="keyword">function</span> <span class="title function_">test</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">2</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">var</span> a;</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(a);</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">foo</span>());</span><br><span class="line"> a = <span class="number">1</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">test</span>(); </span><br></pre></td></tr></table></figure>
<p>再来一个例子,巩固一下我们的理解。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// demo2</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">test</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(foo);</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(bar);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> foo = <span class="string">'Hello'</span>;</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(foo);</span><br><span class="line"> <span class="keyword">var</span> bar = <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">'world'</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">'hello'</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">test</span>();</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 创建阶段</span></span><br><span class="line"><span class="variable constant_">VO</span> = {</span><br><span class="line"> <span class="attr">arguments</span>: {...},</span><br><span class="line"> <span class="attr">foo</span>: <span class="language-xml"><span class="tag"><<span class="name">foo</span> <span class="attr">reference</span>></span>,</span></span><br><span class="line"><span class="language-xml"> bar: undefined</span></span><br><span class="line"><span class="language-xml">}</span></span><br><span class="line"><span class="language-xml">// 这里有一个需要注意的地方,var声明的变量与函数同名,以函数为准</span></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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 执行阶段</span></span><br><span class="line"><span class="variable constant_">VO</span> -> <span class="variable constant_">AO</span></span><br><span class="line"><span class="variable constant_">VO</span> = {</span><br><span class="line"> <span class="attr">arguments</span>: {...},</span><br><span class="line"> <span class="attr">foo</span>: <span class="string">'Hello'</span>,</span><br><span class="line"> <span class="attr">bar</span>: <span class="language-xml"><span class="tag"><<span class="name">bar</span> <span class="attr">reference</span>></span>,</span></span><br><span class="line"><span class="language-xml"> this: Window</span></span><br><span class="line"><span class="language-xml">}</span></span><br></pre></td></tr></table></figure>
<h2 id="全局上下文的变量对象"><a href="#全局上下文的变量对象" class="headerlink" title="全局上下文的变量对象"></a>全局上下文的变量对象</h2><p>以浏览器中为例,全局对象为<code>window</code>。全局上下文有一个特殊的地方,它的变量对象,就是<code>window对象</code>。而这个特殊,在<code>this</code>指向上也同样适用,this也是指向window。</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">// 以浏览器中为例,全局对象为window</span></span><br><span class="line"><span class="comment">// 全局上下文</span></span><br><span class="line">windowEC = {</span><br><span class="line"> <span class="attr">VO</span>: <span class="title class_">Window</span>,</span><br><span class="line"> <span class="attr">scopeChain</span>: {},</span><br><span class="line"> <span class="attr">this</span>: <span class="title class_">Window</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>除此之外,全局上下文的生命周期,与程序的生命周期一致,只要程序运行不结束,比如关掉浏览器窗口,全局上下文就会一直存在。其他所有的上下文环境,都能直接访问全局上下文的属性。</p>
<h2 id="let-x2F-const"><a href="#let-x2F-const" class="headerlink" title="let/const"></a>let/const</h2><p>let/const声明的变量,是否还会变量提升?</p>
<p>我们来做个试验,验证一下这个问题:</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="variable language_">console</span>.<span class="title function_">log</span>(a);</span><br></pre></td></tr></table></figure>
<p>报错信息如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/12/62a5f4938e194.webp" alt="not defined"/></div><span class="image-caption">not defined</span></div>
<p>第二步,我们在let之前调用变量</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="variable language_">console</span>.<span class="title function_">log</span>(a);</span><br><span class="line"><span class="keyword">let</span> a = <span class="number">10</span>;</span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/12/62a5f4caa1a60.webp"/></div></div>
<p><strong>不能在初始化之前访问a。</strong></p>
<p>这个报错说明了什么问题呢?变量定义了,但是没有初始化。</p>
<p>所以在这里我们就可以得出结论:let/const声明的变量,仍然会提前被收集到变量对象中,但和var不同的是,let/const定义的变量,不会在这个时候给他赋值undefined。</p>
<p>因为完全没有赋值,即使变量提升了,也不能在赋值之前调用它。这就是我们常说的 <span class='p blue'>暂时性死区</span>。</p>
<p>最后,变量提升的现象确实会对我们的代码造成一些负面影响,因此,开发中的好习惯,尽量将变量声明放在最前面来写。</p>
<div class="note no-icon flat"><p>原文链接:<a
前端基础进阶(二):执行上下文详细图解
https://fe32.top/articles/jsnb9529/
2022-06-12T11:15:19.000Z
2023-05-17T10:01:12.000Z
<p>先引入一个JavaScript中最基础,但同时也是最重要的概念: <span class='p blue'>执行上下文(Execution Context)</span>。</p>
<p>每次当控制器转到可执行代码的时候,就会进入一个执行上下文 。执行上下文可以理解为当前代码的执行环境,它会形成一个作用域。</p>
<p>JavaScript中的运行环境大概包括三种情况:</p>
<ul>
<li>全局环境:JavaScript代码运行起来会首先进入该环境</li>
<li>函数环境:当函数被调用执行时,会进入当前函数中执行代码</li>
<li>eval(不建议使用,可忽略)</li>
</ul>
<p>因此在一个JavaScript程序中,必定会产生多个执行上下文,在上一篇文章中也有提到,JavaScript引擎会以栈的方式来处理它们,这个栈,我们称其为<span class='p blue'>函数调用栈(call stack)</span>。栈底永远都是全局上下文,而栈顶就是当前正在执行的上下文。</p>
<p>当代码在执行过程中,遇到以上三种情况,都会生成一个执行上下文,放入栈中,而处于栈顶的上下文执行完毕之后,就会自动出栈。为了更加清晰的理解这个过程,根据下面的例子,结合图示给大家展示。</p>
<blockquote>
<p>执行上下文可以理解为函数执行的环境,每一个函数执行时,都会给对应的函数创建这样一个执行环境。</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> color = <span class="string">'blue'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">changeColor</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> anotherColor = <span class="string">'red'</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">swapColors</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> tempColor = anotherColor;</span><br><span class="line"> anotherColor = color;</span><br><span class="line"> color = tempColor;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="title function_">swapColors</span>();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">changeColor</span>();</span><br></pre></td></tr></table></figure>
<p>我们用<span class='p blue'>ECStack</span> 来表示处理执行上下文组的堆栈。我们很容易知道,第一步,首先是全局上下文入栈。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/12/62a5d6b13370b.webp" alt="第一步:全局上下文入栈"/></div><span class="image-caption">第一步:全局上下文入栈</span></div>
<p>全局上下文入栈之后,其中的可执行代码开始执行,直到遇到了<code>changeColor()</code>,这一句激活函数<code>changeColor</code>创建它自己的执行上下文,因此第二步就是<code>changeColor</code>的执行上下文入栈。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/12/62a5d70bb4b17.webp" alt="第二步:changeColor的执行上下文入栈"/></div><span class="image-caption">第二步:changeColor的执行上下文入栈</span></div>
<p>changeColor的上下文入栈之后,控制器开始执行其中的可执行代码,遇到<code>swapColors()</code>之后又激活了一个执行上下文。因此第三步是swapColors的执行上下文入栈。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/12/62a5d83cbba9f.webp" alt="第三步:swapColors的执行上下文入栈"/></div><span class="image-caption">第三步:swapColors的执行上下文入栈</span></div>
<p>在swapColors的可执行代码中,再没有遇到其他能生成执行上下文的情况,因此这段代码顺利执行完毕,swapColors的上下文从栈中弹出。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/12/62a5d70bb4b17.webp" alt="第四步:swapColors的执行上下文出栈"/></div><span class="image-caption">第四步:swapColors的执行上下文出栈</span></div>
<p>swapColors的执行上下文弹出之后,继续执行changeColor的可执行代码,也没有再遇到其他执行上下文,顺利执行完毕之后弹出。这样,ECStack中就只剩下全局上下文了。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/12/62a5d6b13370b.webp" alt="第五步:changeColor的执行上下文出栈"/></div><span class="image-caption">第五步:changeColor的执行上下文出栈</span></div>
<p>全局上下文在浏览器窗口关闭后出栈。</p>
<blockquote>
<p>注意:函数中,遇到return能直接终止可执行代码的执行,因此会直接将当前上下文弹出栈。</p>
</blockquote>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/12/62a5d98300ac3.webp" alt="整个过程"/></div><span class="image-caption">整个过程</span></div>
<p>详细了解了这个过程之后,我们就可以对执行上下文总结一些结论了。</p>
<ol>
<li>单线程。</li>
<li>同步执行,只有栈顶的上下文处于执行中,其他上下文需要等待。</li>
<li>全局上下文只有唯一的一个,它在浏览器关闭时出栈。</li>
<li>函数的执行上下文的个数没有限制。</li>
<li>每次某个函数被调用,就会有个新的执行上下文为其创建,即使是调用的自身函数,也是如此。</li>
</ol>
<p>为了巩固一下执行上下文的理解,我们再来绘制一个例子的演变过程,这是一个简单的闭包例子。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">f1</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> n = <span class="number">999</span>;</span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">f2</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="title function_">alert</span>(n);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> f2;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> result = <span class="title function_">f1</span>();</span><br><span class="line"><span class="title function_">result</span>(); <span class="comment">// 999</span></span><br></pre></td></tr></table></figure>
<p>因为f1中的函数f2在f1的可执行代码中,并没有被调用执行,因此执行f1时,f2不会创建新的上下文,而直到result执行时,才创建了一个新的。具体演变过程如下。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/12/62a5d985f3d3a.webp" alt="上例演变过程"/></div><span class="image-caption">上例演变过程</span></div>
<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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> name = <span class="string">"window"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> p = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'Perter'</span>,</span><br><span class="line"> <span class="attr">getName</span>: <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 利用变量保存的方式保证其访问的是p对象</span></span><br><span class="line"> <span class="keyword">var</span> self = <span class="variable language_">this</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> self.<span class="property">name</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> getName = p.<span class="title function_">getName</span>();</span><br><span class="line"><span class="keyword">var</span> _name = <span class="title function_">getName</span>();</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(_name);</span><br></pre></td></tr></table></figure>
<div class="note no-icon flat"><p>原文链接:<a
前端基础进阶(一):内存空间详细图解
https://fe32.top/articles/jsnb9528/
2022-06-09T16:57:34.000Z
2023-05-17T10:01:12.000Z
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/10/62a22e76dd651.webp"/></div></div>
<blockquote>
<p>在学习内存空间之前,我们需要对三种数据结构有一个清晰的理解。他们分别是 <strong>堆(heap)</strong> ,<strong>栈(stack)</strong> 与 <strong>队列(queue)</strong> 。</p>
</blockquote>
<h2 id="栈数据结构"><a href="#栈数据结构" class="headerlink" title="栈数据结构"></a>栈数据结构</h2><p>与 C/C++ 不同,JavaScript中并没有严格意义上区分栈内存与堆内存。</p>
<blockquote>
<p>一般情况是基础数据类型,在栈内存中维护,引用数据类型,在堆内存中维护,栈内存和堆内存没有本质差别,但是栈内存是从地址高位开始分配,堆内存从地址低位开始分配,这里要结合函数调用栈来一起理解。</p>
</blockquote>
<p>但是在某些场景,我们仍然需要基于 <strong>栈数据结构</strong> 的思维来实现一些功能,比如 JavaScript 的 执行上下文(关于执行上下文会在下一篇文章中总结)。执行上下文的执行顺序借用了 <strong>栈数据结构</strong> 的存取方式(也就是后面我们会经常提到的 <strong>函数调用栈</strong>)。</p>
<p>要简单理解栈的存取方式,我们可以通过类比乒乓球盒子来分析。如下图左侧。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/10/62a22e77d8d9b.webp"/></div></div>
<p>这种乒乓球的存放方式与栈中存取数据的方式如出一辙。处于盒子中最顶层的乒乓球5,它一定是最后被放进去,但可以最先被使用。而我们想要使用底层的乒乓球1,就必须将上面的4个乒乓球取出来,让乒乓球1处于盒子顶层。这就是栈空间 <span class='p blue'>先进后出,后进先出</span> 的特点。图中已经详细的表明了栈空间的存储原理。</p>
<h2 id="堆数据结构"><a href="#堆数据结构" class="headerlink" title="堆数据结构"></a>堆数据结构</h2><p><strong>堆数据结构</strong>是一种<strong>树状结构</strong>。它的存取数据的方式,则与书架与书非常相似。</p>
<p>书虽然也整齐的存放在书架上,但是我们只要知道书的名字,就可以很方便的取出我们想要的书,而不用像从乒乓球盒子里取乒乓一样,非得将上面的所有乒乓球拿出来才能取到中间的某一个乒乓球。好比在JSON格式的数据中,我们存储的<code>key-value</code>是可以无序的,因为顺序的不同并不影响我们的使用,我们只需要关心书的名字。</p>
<h2 id="队列"><a href="#队列" class="headerlink" title="队列"></a>队列</h2><p>在 JavaScript 中,理解队列数据结构的目的主要是为了清晰的明白 <span class='p blue'>事件循环(Event Loop)</span> 的机制到底是怎么回事。在后续的章节中会详细分析事件循环机制。</p>
<p>队列是一种 <span class='p blue'>先进先出(FIFO)</span> 的数据结构。正如排队过安检一样,排在队伍前面的人一定是最先过检的人。用以下的图示可以清楚的理解队列的原理。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/10/62a22e803f547.webp"/></div></div>
<h2 id="变量对象与基础数据类型"><a href="#变量对象与基础数据类型" class="headerlink" title="变量对象与基础数据类型"></a>变量对象与基础数据类型</h2><blockquote>
<p>在新的 ECMAScript 版本中,此处的变量对象,应该叫做环境记录对象更为标准,但变量对象更有利于简化理解内部机制,所以本系列文章不做更改。</p>
</blockquote>
<p>JavaScript 的 <strong>执行上下文</strong> 生成之后,会创建一个叫做 <strong>变量对象</strong> 的特殊对象(具体会在下一篇文章与执行上下文一起总结),JavaScript 的基础数据类型往往都会保存在变量对象中。</p>
<p>基础数据类型都是一些简单的数据段,JavaScript 中有5种基础数据类型,分别是<code>Undefined</code>、<code>Null</code>、<code>Boolean</code>、<code>Number</code>、<code>String</code>。基础数据类型都是按值访问,我们可以直接操作保存在变量中的实际值。</p>
<blockquote>
<p>ES6 中新加了一种基础数据类型 Symbol,可以先不用考虑他。</p>
</blockquote>
<h2 id="引用数据类型与堆内存"><a href="#引用数据类型与堆内存" class="headerlink" title="引用数据类型与堆内存"></a>引用数据类型与堆内存</h2><p>与其他语言不同,JS的引用数据类型,比如数组Array,它们值的大小是不固定的。引用数据类型的值是保存在堆内存中的对象。JavaScript不允许直接访问堆内存中的数据,因此我们不能直接操作对象的堆内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。因此,引用类型的值都是按引用访问的。这里的引用,我们可以理解为保存在变量对象中的一个地址,该地址与堆内存的实际值相关联。</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="keyword">var</span> a1 = <span class="number">0</span>; <span class="comment">// 变量对象</span></span><br><span class="line"><span class="keyword">var</span> a2 = <span class="string">'this is string'</span>; <span class="comment">// 变量对象</span></span><br><span class="line"><span class="keyword">var</span> a3 = <span class="literal">null</span>; <span class="comment">// 变量对象</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> b = { <span class="attr">m</span>: <span class="number">20</span> }; <span class="comment">// 变量b存在于变量对象中,{m: 20} 作为对象存在于堆内存中</span></span><br><span class="line"><span class="keyword">var</span> c = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]; <span class="comment">// 变量c存在于变量对象中,[1, 2, 3] 作为对象存在于堆内存中</span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/10/62a22e83b47e3.webp"/></div></div>
<p>当我们要访问堆内存中的引用数据类型时,实际上我们首先是从变量对象中获取了该对象的地址引用(或者地址指针),然后再从堆内存中取得我们需要的数据。</p>
<p>理解了JS的内存空间,我们就可以借助内存空间的特性来验证一下引用类型的一些特点。</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">// demo01.js</span></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">20</span>;</span><br><span class="line"><span class="keyword">var</span> b = a;</span><br><span class="line">b = <span class="number">30</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 这时a的值是多少?</span></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></pre></td><td class="code"><pre><span class="line"><span class="comment">// demo02.js</span></span><br><span class="line"><span class="keyword">var</span> m = { <span class="attr">a</span>: <span class="number">10</span>, <span class="attr">b</span>: <span class="number">20</span> }</span><br><span class="line"><span class="keyword">var</span> n = m;</span><br><span class="line">n.<span class="property">a</span> = <span class="number">15</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 这时m.a的值是多少</span></span><br></pre></td></tr></table></figure>
<p>在变量对象中的数据发生复制行为时,系统会自动为新的变量分配一个新值。<code>var b = a</code>执行之后,a与b虽然值都等于20,但是他们其实已经是相互独立互不影响的值了。具体如图。所以我们修改了b的值以后,a的值并不会发生变化。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/10/62a22e8594f0f.webp" alt="demo01图解"/></div><span class="image-caption">demo01图解</span></div>
<p>在demo02中,我们通过<code>var n = m</code>执行一次复制引用类型的操作。引用类型的复制同样也会为新的变量自动分配一个新的值保存在变量对象中,但不同的是,这个新的值,仅仅只是引用类型的一个地址指针。当地址指针相同时,尽管他们相互独立,但是在变量对象中访问到的具体对象实际上是同一个。如图所示。</p>
<p>因此当我改变n时,m也发生了变化。这就是引用类型的特性。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/10/62a22e872e3f7.webp" alt="demo02图解"/></div><span class="image-caption">demo02图解</span></div>
<h2 id="内存空间管理"><a href="#内存空间管理" class="headerlink" title="内存空间管理"></a>内存空间管理</h2><p>因为JavaScript具有自动垃圾收集机制,所以我们在开发时好像不用关心内存的使用问题,内存的分配与回收都完全实现了自动管理。</p>
<p>JavaScript的内存生命周期:</p>
<ol>
<li>分配你所需要的内存</li>
<li>使用分配到的内存(读、写)</li>
<li>不需要时将其释放、归还</li>
</ol>
<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">var</span> a = <span class="number">20</span>; <span class="comment">// 在内存中给数值变量分配空间</span></span><br><span class="line"><span class="title function_">alert</span>(a + <span class="number">100</span>); <span class="comment">// 使用内存</span></span><br><span class="line">a = <span class="literal">null</span>; <span class="comment">// 使用完毕之后,释放内存空间</span></span><br></pre></td></tr></table></figure>
<p>第一步和第二步我们都很好理解,JavaScript在定义变量时就完成了内存分配。第三步释放内存空间则是我们需要重点理解的一个点。</p>
<p>JavaScript有自动垃圾收集机制,那么这个自动垃圾收集机制的原理是什么呢?其实很简单,就是 <span class='p blue'>找出那些不再继续使用的值,然后释放其占用的内存。垃圾收集器会每隔固定的时间段就执行一次释放操作</span> 。</p>
<p>在JavaScript中,最常用的是通过 <span class='p blue'>标记清除</span> 的算法来找到哪些对象是不再继续使用的,因此<code>a = null</code>其实仅仅只是做了一个释放引用的操作,<span class='p blue'>让 a 原本对应的值失去引用,脱离执行环境,这个值会在下一次垃圾收集器执行操作时被找到并释放</span> 。而在适当的时候解除引用,是为页面获得更好性能的一个重要方式。</p>
<p>需要注意的是,在局部作用域中,当函数执行完毕,局部变量也就没有存在的必要了,因此垃圾收集器很容易做出判断并回收。但是全局变量什么时候需要释放内存空间则很难判断,因此在我们的开发中,原则上应该避免使用全局变量。</p>
<blockquote>
<p>要详细了解垃圾收集机制,建议阅读《JavaScript高级编程》中的4.3节。</p>
</blockquote>
<div class="note no-icon flat"><p>原文链接:<a
JavaScript 核心进阶
https://fe32.top/articles/jsnb9527/
2022-06-08T17:57:53.000Z
2023-05-17T10:01:12.000Z
本系列文章旨在带给读者地道的前端基础知识体系,深入底层原理,了解最权威的前端基础,循序渐进,带领读者领略正确学习方法的独特魅力。
基于 Hexo 从零开始搭建个人博客系列
https://fe32.top/articles/hexo1600/
2022-06-05T06:52:37.000Z
2024-02-23T15:35:21.199Z
Hexo + Butterfly 系列教程,手把手教你从零搭建属于自己的个人网站!
基于 Hexo 从零开始搭建个人博客(六)
https://fe32.top/articles/hexo1606/
2022-06-05T05:50:17.000Z
2024-02-23T15:35:21.199Z
<blockquote>
<p>阅读本篇前,请先阅读前几篇文章:<br><a href="https://fe32.top/articles/hexo1601/">基于 Hexo 从零开始搭建个人博客(一)</a><br><a href="https://fe32.top/articles/hexo1602/">基于 Hexo 从零开始搭建个人博客(二)</a><br><a href="https://fe32.top/articles/hexo1603/">基于 Hexo 从零开始搭建个人博客(三)</a><br><a href="https://fe32.top/articles/hexo1604/">基于 Hexo 从零开始搭建个人博客(四)</a><br><a href="https://fe32.top/articles/hexo1605/">基于 Hexo 从零开始搭建个人博客(五)</a></p>
</blockquote>
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><!-- > 本站基于`Hexo`搭建,用的 🦋 [hexo-theme-butterfly](https://github.com/jerryc127/hexo-theme-butterfly.git) 主题 [v3.7.1](https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1),请注意最新的🦋 [hexo-theme-butterfly](https://github.com/jerryc127/hexo-theme-butterfly.git) 版本已经更新到 [v4.8.1](https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.8.1) 。<br>
如果你是 [v3.7.1](https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1) 之外的版本,可能有些地方会有出入,请留意。 -->
<blockquote>
<p>本站基于<code>Hexo</code>搭建,用的 🦋 <a href="https://github.com/jerryc127/hexo-theme-butterfly.git">hexo-theme-butterfly</a> 主题,已经升级到 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.7.0">v4.7.0</a> 。 请注意最新的🦋 <a href="https://github.com/jerryc127/hexo-theme-butterfly.git">hexo-theme-butterfly</a> 版本已经更新到 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.8.1">v4.8.1</a> 。<br><br>如果你是 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1">v3.7.1</a> 的版本,请移步 <a href="https://old.fe32.top/" target="_blank">v3.7.1</a> 站点进行浏览。</p>
</blockquote>
<blockquote>
<p>注意:我的博客根目录路径为 【G:/hexo-blog/blog-demo】,下文所说的根目录都是此路径,将用<code>[BlogRoot]</code>代替。如果不清楚根目录路径,请回到教程 <a href="https://fe32.top/articles/hexo1602/">基于 Hexo 从零开始搭建个人博客(二)</a>,查看你执行<code>hexo init xxx</code>这条命令时所选择的路径,例如我选择的路径是【G:/hexo-blog】,我的博客根目录即为【G:/hexo-blog/xxx】。<br><br>修改站点配置文件<code>_config.yml</code>,路径为【BlogRoot/_config.yml】。<br><br>修改主题配置文件<code>_config.butterfly.yml</code>,路径为【BlogRoot/_config.butterfly.yml】。</p>
</blockquote>
<div class="timeline purple"><div class='timeline-item headline'><div class='timeline-item-title'><div class='item-circle'><h2 id="更新记录"><a href="#更新记录" class="headerlink" title="更新记录"></a>更新记录</h2></div></div></div><div class='timeline-item'><div class='timeline-item-title'><div class='item-circle'><p>2023-02-14</p>
</div></div><div class='timeline-item-content'><ol>
<li><a href="https://fe32.top/articles/hexo1606/#添加豆瓣插件" target="_top">更新【豆瓣插件】</a></li>
</ol>
</div></div><div class='timeline-item'><div class='timeline-item-title'><div class='item-circle'><p>2023-01-13</p>
</div></div><div class='timeline-item-content'><ol>
<li><a href="https://fe32.top/articles/hexo1606/#自定义页脚" target="_top">新增【自定义页脚】原版教程</a></li>
<li><a href="https://fe32.top/articles/hexo1606/#侧边栏公众号" target="_top">新增【侧边栏公众号】原版教程</a></li>
<li><a href="https://fe32.top/articles/hexo1606/#文章加密插件" target="_top">新增【文章加密插件】</a></li>
<li><a href="https://fe32.top/articles/hexo1606/#友链朋友圈" target="_top">新增【友链朋友圈】</a></li>
</ol>
</div></div><div class='timeline-item'><div class='timeline-item-title'><div class='item-circle'><p>2022-11-12</p>
</div></div><div class='timeline-item-content'><ol>
<li><a href="https://fe32.top/articles/hexo1606/#哔哩哔哩番剧页面插件" target="_top">更新关于【哔哩哔哩番剧页面插件】跟主题懒加载冲突的问题</a></li>
<li><a href="https://fe32.top/articles/hexo1606/#公告栏两个小人" target="_top">更新【公告栏两个小人】CDN(fastly.jsdelivr.net -> elemecdn)</a></li>
</ol>
</div></div><div class='timeline-item'><div class='timeline-item-title'><div class='item-circle'><p>2022-11-12</p>
</div></div><div class='timeline-item-content'><ol>
<li><a href="https://fe32.top/articles/hexo1606/#侧边栏电子时钟" target="_top">更新【侧边栏电子时钟】教程</a></li>
</ol>
</div></div><div class='timeline-item'><div class='timeline-item-title'><div class='item-circle'><p>2022-11-10</p>
</div></div><div class='timeline-item-content'><ol>
<li><a href="https://fe32.top/articles/hexo1606/#首页分类磁铁" target="_top">新增【分类磁铁】原版教程</a></li>
<li><a href="https://fe32.top/articles/hexo1606/#自定义右键菜单" target="_top">新增【自定义右键菜单】教程</a></li>
<li><a href="https://fe32.top/articles/hexo1606/#加载动画" target="_top">新增【加载动画】教程</a></li>
<li><a href="https://fe32.top/articles/hexo1606/#评论弹幕" target="_top">新增【评论弹幕】教程</a></li>
<li><a href="https://fe32.top/articles/hexo1606/#更换字体" target="_top">修改【字体CDN】</a></li>
<li><a href="https://fe32.top/articles/hexo1606/#禁止右键及F12等事件" target="_top">更新【禁止右键及F12等事件】</a></li>
</ol>
</div></div><div class='timeline-item'><div class='timeline-item-title'><div class='item-circle'><p>2022-06-05 持续优化</p>
</div></div><div class='timeline-item-content'><ol>
<li><a href="https://fe32.top/articles/hexo1606/#公告栏两个小人" target="_top">公告栏两个小人</a></li>
<li><a href="https://fe32.top/articles/hexo1606/#星空背景和流星特效" target="_top">星空背景和流星特效</a></li>
</ol>
</div></div><div class='timeline-item'><div class='timeline-item-title'><div class='item-circle'><p>2022-05-29 CDN替换方案</p>
</div></div><div class='timeline-item-content'><ol>
<li>CDN解决方案:请参考<a href="https://fe32.top/articles/hexo1605/#CDN">基于 Hexo 从零开始搭建个人博客(五)</a></li>
</ol>
</div></div><div class='timeline-item'><div class='timeline-item-title'><div class='item-circle'><p>2021-11-27 valine获取评论失败</p>
</div></div><div class='timeline-item-content'><ol>
<li><a href="https://fe32.top/articles/hexo1606/#valine获取评论失败" target="_top">新增【valine获取评论失败说明】</a></li>
</ol>
</div></div><div class='timeline-item'><div class='timeline-item-title'><div class='item-circle'><p>2021-11-22 持续优化</p>
</div></div><div class='timeline-item-content'><ol>
<li><a href="https://fe32.top/articles/hexo1606/#禁止右键及F12等事件" target="_top">新增【禁止右键及F12等事件】说明</a></li>
<li><a href="https://fe32.top/articles/hexo1606/#部分动效说明" target="_top">新增【部分动效说明】</a></li>
<li><a href="https://fe32.top/articles/hexo1606/#部分页面插入视频" target="_top">新增【部分页面插入视频】</a></li>
</ol>
</div></div><div class='timeline-item'><div class='timeline-item-title'><div class='item-circle'><p>2021-08-26 持续优化</p>
</div></div><div class='timeline-item-content'><ol>
<li><a href="https://fe32.top/articles/hexo1606/#首页分类磁铁" target="_top">新增【首页分类磁铁】</a></li>
<li><a href="https://fe32.top/articles/hexo1606/#侧边栏电子时钟" target="_top">新增【侧边栏电子时钟】</a></li>
<li><a href="https://fe32.top/articles/hexo1606/#更换字体" target="_top">更换字体</a></li>
<li><a href="https://fe32.top/articles/hexo1606/#局部css优化" target="_top">对局部css进行了优化</a></li>
<li><a href="https://fe32.top/articles/hexo1606/#灯笼特效" target="_top">灯笼特效</a></li>
</ol>
</div></div><div class='timeline-item'><div class='timeline-item-title'><div class='item-circle'><p>2021-04-27 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1">butterfly3.4.0 -> butterfly3.7.1</a></p>
</div></div><div class='timeline-item-content'><ol>
<li>更新v3.7.1适配方案。</li>
<li>注意一点的是,butterfly_v3.6.0取消了缓存配置,转为完全默认,需要将<code>{cache:theme.fragment_cache}</code> 改为 <code>{ cache:true }</code>。如果安装的版本较新,可直接忽略这一步。</li>
</ol>
</div></div></div>
<h2 id="Butterfly主题美化"><a href="#Butterfly主题美化" class="headerlink" title="Butterfly主题美化"></a>Butterfly主题美化</h2><h3 id="生成文章唯一链接"><a href="#生成文章唯一链接" class="headerlink" title="生成文章唯一链接"></a>生成文章唯一链接</h3><p>Hexo的默认文章链接格式是年,月,日,标题这种格式来生成的。如果你的标题是中文的话,那你的URL链接就会包含中文。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">permalink:</span> <span class="string">:year/:month/:day/:title</span></span><br></pre></td></tr></table></figure>
<ol>
<li>前往你的Hexo博客根目录,打开cmd命令窗口执行<code>npm install hexo-abbrlink --save</code>。<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 hexo-abbrlink --save</span><br></pre></td></tr></table></figure></li>
<li>修改站点配置文件<code>_config.yml</code>中<code>permalink</code>属性。<figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="deletion">- permalink: :year/:month/:day/:title/</span></span><br><span class="line">#修改为</span><br><span class="line">permalink: post/:abbrlink.html # post为自定义前缀</span><br><span class="line">abbrlink:</span><br><span class="line"> alg: crc32 #算法: crc16(default) and crc32</span><br><span class="line"> rep: hex #进制: dec(default) and hex</span><br></pre></td></tr></table></figure></li>
</ol>
<h3 id="页面底部-footer-跳动的心"><a href="#页面底部-footer-跳动的心" class="headerlink" title="页面底部 footer 跳动的心"></a>页面底部 footer 跳动的心</h3><details class="folding-tag" cyan><summary> 效果预览 </summary>
<div class='content'>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/1f4f6f6e7a7fb.jpg"/></div></div>
</div>
</details>
<details class="folding-tag" yellow><summary> 查看步骤 </summary>
<div class='content'>
<p>编辑<code>BlogRoot/themes/butterfly/layout/includes/footer.pug</code>文件,</p><p>将以下内容</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&copy;${theme.footer.owner.since} - ${nowYear} By ${config.author}</span><br></pre></td></tr></table></figure><p>改为</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&copy;${theme.footer.owner.since} - ${nowYear + ' '} <i id="heartbeat" class="fa fas fa-heartbeat"></i> ${config.author}</span><br></pre></td></tr></table></figure><p>将以下内容</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&copy;${nowYear} By ${config.author} </span><br></pre></td></tr></table></figure><p>改为</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&copy;${nowYear + ' '} <i id="heartbeat" class="fa fas fa-heartbeat"></i> ${config.author}</span><br></pre></td></tr></table></figure><p>将以下内容添加到 <code><head></head></code>标签内:</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string"><link</span> <span class="string">rel="stylesheet"</span> <span class="string">href="https://fastly.jsdelivr.net/gh/HCLonely/images@master/others/heartbeat.min.css"></span></span><br></pre></td></tr></table></figure><p>具体放置位置,可以参考下图:</p><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/05/629c7d29468ce.jpg"/></div></div>
</div>
</details>
<h3 id="修改标题样式"><a href="#修改标题样式" class="headerlink" title="修改标题样式"></a>修改标题样式</h3><details class="folding-tag" cyan><summary> 效果预览 </summary>
<div class='content'>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/19/62d6b17d2c3c5.jpg" alt="回形针"/></div><span class="image-caption">回形针</span></div><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/19/62d6b7fda1799.jpg" alt="旋转风车"/></div><span class="image-caption">旋转风车</span></div>
</div>
</details>
<details class="folding-tag" yellow><summary> 查看步骤 </summary>
<div class='content'>
<p>在<code>BlogRoot/themes/butterfly/source/css</code>文件下新建 css 文件,并命名为 <code>custom.css</code>(命名按照自己喜好去命名,只需在主题配置文件<code>_config.butterfly.yml</code>中引入对应的<code>css</code>文件即可),将以下代码复制到新建的<code>custom.css</code>中。</p><p>如果想自行修改标题样式的话,将<code>content: '\f0c1';</code>中的内容换成自己想要的即可,如要添加动画,参考<code>animation: avatar_turn_around 1s linear infinite;</code>。</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h1</span><span class="selector-pseudo">:before</span>,</span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h2</span><span class="selector-pseudo">:before</span>,</span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h3</span><span class="selector-pseudo">:before</span>,</span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h4</span><span class="selector-pseudo">:before</span>,</span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h5</span><span class="selector-pseudo">:before</span>,</span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h6</span><span class="selector-pseudo">:before</span>,</span><br><span class="line"><span class="selector-id">#post</span> <span class="selector-class">.post-outdate-notice</span><span class="selector-pseudo">:before</span>,</span><br><span class="line"><span class="selector-class">.fontawesomeIcon</span>,</span><br><span class="line"><span class="selector-class">.note</span><span class="selector-pseudo">:not</span>(<span class="selector-class">.no-icon</span>)<span class="selector-pseudo">::before</span>,</span><br><span class="line">hr<span class="selector-pseudo">:before</span> {</span><br><span class="line"> <span class="attribute">display</span>: inline-block;</span><br><span class="line"> <span class="attribute">font-weight</span>: <span class="number">600</span>;</span><br><span class="line"> <span class="attribute">font-style</span>: normal;</span><br><span class="line"> <span class="attribute">font-variant</span>: normal;</span><br><span class="line"> <span class="attribute">font-family</span>: <span class="string">'Font Awesome 5 Free'</span>;</span><br><span class="line"> <span class="attribute">text-rendering</span>: auto;</span><br><span class="line"> -webkit-<span class="attribute">font-smoothing</span>: antialiased</span><br><span class="line">}</span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h1</span><span class="selector-pseudo">:before</span>,</span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h2</span><span class="selector-pseudo">:before</span>,</span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h3</span><span class="selector-pseudo">:before</span>,</span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h4</span><span class="selector-pseudo">:before</span>,</span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h5</span><span class="selector-pseudo">:before</span>,</span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h6</span><span class="selector-pseudo">:before</span> {</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#f47466</span>;</span><br><span class="line"> <span class="comment">/* 回形针 */</span></span><br><span class="line"> <span class="attribute">content</span>: <span class="string">'\f0c1'</span>; </span><br><span class="line"> <span class="attribute">line-height</span>: <span class="number">1</span>;</span><br><span class="line"> -webkit-<span class="attribute">transition</span>: all .<span class="number">2s</span> ease-out;</span><br><span class="line"> -moz-<span class="attribute">transition</span>: all .<span class="number">2s</span> ease-out;</span><br><span class="line"> -o-<span class="attribute">transition</span>: all .<span class="number">2s</span> ease-out;</span><br><span class="line"> -ms-<span class="attribute">transition</span>: all .<span class="number">2s</span> ease-out;</span><br><span class="line"> <span class="attribute">transition</span>: all .<span class="number">2s</span> ease-out;</span><br><span class="line"> <span class="comment">/* 若要使用风车效果,请去掉下面的注释 */</span></span><br><span class="line"> <span class="comment">/* content: '\f863'; </span></span><br><span class="line"><span class="comment"> animation: avatar_turn_around 1s linear infinite; */</span></span><br><span class="line">}</span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h1</span> {</span><br><span class="line"> <span class="attribute">padding-left</span>: <span class="number">1.4rem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h1</span> <span class="selector-tag">code</span> {</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">1rem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h1</span><span class="selector-pseudo">:before</span> {</span><br><span class="line"> <span class="attribute">margin-left</span>: -<span class="number">1.3rem</span>;</span><br><span class="line"> <span class="attribute">top</span>: <span class="built_in">calc</span>(<span class="number">50%</span> - .<span class="number">5rem</span>);</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">1rem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h1</span><span class="selector-pseudo">:hover</span> {</span><br><span class="line"> <span class="attribute">padding-left</span>: <span class="number">1.6rem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h2</span> {</span><br><span class="line"> <span class="attribute">padding-left</span>: <span class="number">1.3rem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h2</span> <span class="selector-tag">code</span> {</span><br><span class="line"> <span class="attribute">font-size</span>: .<span class="number">9rem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h2</span><span class="selector-pseudo">:before</span> {</span><br><span class="line"> <span class="attribute">margin-left</span>: -<span class="number">1.4rem</span>;</span><br><span class="line"> <span class="attribute">top</span>: <span class="built_in">calc</span>(<span class="number">50%</span> - .<span class="number">45rem</span>);</span><br><span class="line"> <span class="attribute">font-size</span>: .<span class="number">9rem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h2</span><span class="selector-pseudo">:hover</span> {</span><br><span class="line"> <span class="attribute">padding-left</span>: <span class="number">1.5rem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h3</span> {</span><br><span class="line"> <span class="attribute">padding-left</span>: <span class="number">1.2rem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h3</span> <span class="selector-tag">code</span> {</span><br><span class="line"> <span class="attribute">font-size</span>: .<span class="number">8rem</span>;</span><br><span class="line"> <span class="attribute">top</span>: <span class="built_in">calc</span>(<span class="number">50%</span> - .<span class="number">4rem</span>);</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h3</span><span class="selector-pseudo">:before</span> {</span><br><span class="line"> <span class="attribute">margin-left</span>: -<span class="number">1.2rem</span>;</span><br><span class="line"> <span class="attribute">top</span>: <span class="built_in">calc</span>(<span class="number">50%</span> - .<span class="number">4rem</span>);</span><br><span class="line"> <span class="attribute">font-size</span>: .<span class="number">8rem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h3</span><span class="selector-pseudo">:hover</span> {</span><br><span class="line"> <span class="attribute">padding-left</span>: <span class="number">1.4rem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h4</span> {</span><br><span class="line"> <span class="attribute">padding-left</span>: <span class="number">1.1rem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h4</span> <span class="selector-tag">code</span> {</span><br><span class="line"> <span class="attribute">font-size</span>: .<span class="number">7rem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h4</span><span class="selector-pseudo">:before</span> {</span><br><span class="line"> <span class="attribute">margin-left</span>: -<span class="number">1rem</span>;</span><br><span class="line"> <span class="attribute">top</span>: <span class="built_in">calc</span>(<span class="number">50%</span> - .<span class="number">35rem</span>);</span><br><span class="line"> <span class="attribute">font-size</span>: .<span class="number">7rem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h4</span><span class="selector-pseudo">:hover</span> {</span><br><span class="line"> <span class="attribute">padding-left</span>: <span class="number">1.3rem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h5</span> {</span><br><span class="line"> <span class="attribute">padding-left</span>: <span class="number">1rem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h5</span> <span class="selector-tag">code</span> {</span><br><span class="line"> <span class="attribute">font-size</span>: .<span class="number">6rem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h5</span><span class="selector-pseudo">:before</span> {</span><br><span class="line"> <span class="attribute">margin-left</span>: -.<span class="number">8rem</span>;</span><br><span class="line"> <span class="attribute">top</span>: <span class="built_in">calc</span>(<span class="number">50%</span> - .<span class="number">3rem</span>);</span><br><span class="line"> <span class="attribute">font-size</span>: .<span class="number">6rem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h5</span><span class="selector-pseudo">:hover</span> {</span><br><span class="line"> <span class="attribute">padding-left</span>: <span class="number">1.2rem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h6</span> {</span><br><span class="line"> <span class="attribute">padding-left</span>: <span class="number">1rem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h6</span> <span class="selector-tag">code</span> {</span><br><span class="line"> <span class="attribute">font-size</span>: .<span class="number">6rem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h6</span><span class="selector-pseudo">:before</span> {</span><br><span class="line"> <span class="attribute">margin-left</span>: -.<span class="number">8rem</span>;</span><br><span class="line"> <span class="attribute">top</span>: <span class="built_in">calc</span>(<span class="number">50%</span> - .<span class="number">3rem</span>);</span><br><span class="line"> <span class="attribute">font-size</span>: .<span class="number">6rem</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#article-container</span> <span class="selector-tag">h6</span><span class="selector-pseudo">:hover</span> {</span><br><span class="line"> <span class="attribute">padding-left</span>: <span class="number">1.2rem</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</div>
</details>
<h3 id="添加豆瓣插件"><a href="#添加豆瓣插件" class="headerlink" title="添加豆瓣插件"></a>添加豆瓣插件</h3><!-- 详情请看[传送门](https://github.com/mythsman/hexo-douban) -->
<blockquote>
<del>此方法可能不再适用:node 版本过高导致,可以下个 nvm 来控制 node 版本,建议安装较低的 node 版本(推荐v12.18.0)</del>
</blockquote>
<div class="tabs" id=""><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#-1">1.安装豆瓣插件</button></li><li class="tab"><button type="button" data-href="#-2">2.注入豆瓣内容</button></li><li class="tab"><button type="button" data-href="#-3">3.主题中注入豆瓣</button></li><li class="tab"><button type="button" data-href="#-4">4.执行Docs命令</button></li><li class="tab"><button type="button" data-href="#-5">5.添加相关导航</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="-1"><p>前往博客根目录,打开cmd命令窗口执行<code>npm install hexo-douban --save</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">npm install hexo-douban --save</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="-2"><p>修改站点配置文件<code>_config.yml</code>,添加如下代码:</p>
<!-- douban:
# user: ethan_tzy
# builtin: false
# book:
# title: ''
# quote: 'One who is filled with knowledge always behaves with elegance.'
# movie:
# title: '梦想与现实的碰撞'
# quote: 'The collision between dream and reality.'
# game:
# title: '诅咒中闪烁泪光,倾诉霜之哀伤。'
# quote: 'Tears flicker in the curse and pour out the sorrow of frost.'
# timeout: 10000 -->
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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="attr">douban:</span></span><br><span class="line"> <span class="attr">id:</span> <span class="number">228172215</span></span><br><span class="line"> <span class="attr">builtin:</span> <span class="literal">false</span></span><br><span class="line"> <span class="attr">item_per_page:</span> <span class="number">10</span></span><br><span class="line"> <span class="attr">book:</span></span><br><span class="line"> <span class="attr">path:</span> <span class="string">books/index.html</span></span><br><span class="line"> <span class="attr">title:</span> <span class="string">'This is my book title'</span></span><br><span class="line"> <span class="attr">quote:</span> <span class="string">'This is my book quote'</span></span><br><span class="line"> <span class="attr">option:</span></span><br><span class="line"> <span class="attr">movie:</span></span><br><span class="line"> <span class="attr">path:</span> <span class="string">movies/index.html</span></span><br><span class="line"> <span class="attr">title:</span> <span class="string">'This is my movie title'</span></span><br><span class="line"> <span class="attr">quote:</span> <span class="string">'This is my movie quote'</span></span><br><span class="line"> <span class="attr">option:</span></span><br><span class="line"> <span class="attr">game:</span></span><br><span class="line"> <span class="attr">path:</span> <span class="string">games/index.html</span></span><br><span class="line"> <span class="attr">title:</span> <span class="string">'This is my game title'</span></span><br><span class="line"> <span class="attr">quote:</span> <span class="string">'This is my game quote'</span></span><br><span class="line"> <span class="attr">option:</span></span><br><span class="line"> <span class="attr">song:</span></span><br><span class="line"> <span class="attr">path:</span> <span class="string">songs/index.html</span></span><br><span class="line"> <span class="attr">title:</span> <span class="string">'This is my song title'</span></span><br><span class="line"> <span class="attr">quote:</span> <span class="string">'This is my song quote'</span></span><br><span class="line"> <span class="attr">option:</span></span><br><span class="line"> <span class="attr">timeout:</span> <span class="number">10000</span> </span><br></pre></td></tr></table></figure>
<ul>
<li>id: 你的豆瓣ID(纯数字格式,不是自定义的域名)。获取方法可以参考 <a href="https://www.zhihu.com/question/19634899">怎样获取豆瓣的数字 ID</a> ?</li>
<li>builtin: 是否将 hexo douban 命令默认嵌入进 hexo g、hexo s,使其自动执行 hexo douban 命令。默认关闭。当你的豆瓣条目较多时,也建议关闭。</li>
<li>item_per_page: 每页展示的条目数,默认 10 。</li>
<li>path: 生成页面后的路径,默认生成在 //my-blog/books/index.html 等下面。如需自定义路径,则可以修改这里。</li>
<li>title: 该页面的标题。</li>
<li>quote: 写在页面开头的一段话,支持 html 语法。</li>
<li>option: 该页面额外的 Front-matter 配置,参考 <a href="https://hexo.io/docs/front-matter.html">Hexo 文档</a>。无特别需要,留空即可。</li>
<li>timeout: 爬取数据的超时时间,默认是 10000ms ,如果在使用时发现报了超时的错(ETIMEOUT)可以把这个数据设置的大一点。</li>
</ul>
<!-- - user: 你的豆瓣ID。打开豆瓣,登入账户,然后在右上角点击 ”个人主页“,这时候地址栏的URL大概是这样:https://www.douban.com/people/xxxxxx/ ,其中的”xxxxxx”就是你的个人ID了。
- builtin: 是否将生成页面的功能嵌入 hexo s 和 hexo g 中,默认是 false ,另一可选项为 true 。
- title: 该页面的标题。
- quote: 写在页面开头的一段话,支持html语法。
- timeout: 爬取数据的超时时间,默认是 10000ms,如果在使用时发现报了超时的错(ETIMEOUT)可以把这个数据设置的大一点。
- 如果只想显示某一个页面(比如movie),那就把其他的配置项注释掉即可。 --><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="-3"><p>在主题配置文件<code>_config.butterfly.yml</code>中配置以下内容:</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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"># 如果你有使用hexo-douban,可配置這個</span></span><br><span class="line"><span class="attr">douban:</span></span><br><span class="line"> <span class="attr">meta:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">movies_img:</span> <span class="string">https://fastly.jsdelivr.net/gh/jerryc127/butterfly_cdn@2.1.0/top_img/movie.jpg</span></span><br><span class="line"> <span class="attr">books_img:</span> <span class="string">https://fastly.jsdelivr.net/npm/blog-gallery@1.0.0/1/20200206161657.webp</span></span><br><span class="line"><span class="comment"># games_img:</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="-4"><p>前往博客根目录,打开cmd命令窗口执行<code>hexo douban</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">hexo douban</span><br></pre></td></tr></table></figure>
<p>等待命令执行完毕,出现下图所示内容即为成功。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/d579a68e2fd94.jpg"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="-5"><div class="note red icon-padding modern"><i class="note-icon fas fa-fan"></i><p>请注意,我的butterfly主题版本不是最新的,导航菜单栏格式请按照最新的格式写。</p>
</div>
<p>如下图:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/badbd5bb7f80f.jpg"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div>
<h3 id="添加访客地图"><a href="#添加访客地图" class="headerlink" title="添加访客地图"></a>添加访客地图</h3><details class="folding-tag" cyan><summary> 效果预览 </summary>
<div class='content'>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/e391380d7db28.jpg"/></div></div>
</div>
</details>
<details class="folding-tag" yellow><summary> 查看步骤 </summary>
<div class='content'>
<p>前往 <a href="https://clustrmaps.com/">clustrmaps</a> 网站注册一个帐号。</p><p>找到<code>Free Tools</code>下面的<code>Website Widget</code>, 点击<code>Get Map Widget</code>。</p><p>输入你的博客网址,点击<code>Next</code>。</p><p>根据你自己的喜好选择样式<code>Map widget</code>或<code>Globe Widget</code>(本人使用后者)。</p><p>找到如下代码,记住 src (****** 的部分):</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"><script type=<span class="string">"text/javascript"</span> id=<span class="string">"clstr_globe"</span> src=<span class="string">"**********************"</span>></span><br></pre></td></tr></table></figure><p>在 <code>BlogRoot/themes/butterfly/layout/includes/widget</code>文件夹新建<code>card_map.pug</code>文件,文件内容如下:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">.card-widget.card-map</span><br><span class="line"> .card-content</span><br><span class="line"> .item-headline</span><br><span class="line"> i.fa.fa-globe-asia(aria-hidden="true")</span><br><span class="line"> span= _p('aside.card_map')</span><br><span class="line"> script#clstr_globe(type="text/javascript" defer="defer" src="此处填入你自己的代码")</span><br></pre></td></tr></table></figure><p>编辑 <code>BlogRoot/themes/butterfly/layout/includes/widget/index.pug</code>文件,在你想要显示的位置插入以下代码(注意格式):</p><figure class="highlight plaintext"><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">if theme.aside.card_map</span><br><span class="line"> !=partial('includes/widget/card_map', {}, {cache:true})</span><br></pre></td></tr></table></figure><p>具体放置位置,可以参考下图:</p><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/05/629c845643925.jpg"/></div></div><p>编辑主题配置文件<code>_config.butterfly.yml</code>,在<code>card_webinfo</code>下面添加一行<code>card_map: true</code>。</p><p>编辑<code>BlogRoot/themes/butterfly/languages/zh-CN.yml</code>文件 (请根据你的网站语言选择),找到<code>card_announcement: 公告</code> , 在下面添加一行<code>card_map: 访客地图</code> (后面的文本可自定义),繁体字方法一样,修改<code>BlogRoot/themes/butterfly/languages/zh-TW.yml</code>文件就行。</p><p>如果不想显示,直接把主题配置文件<code>_config.butterfly.yml</code>中的<code>card_map: true</code> 改为 <code>card_map: false</code> 即可。</p>
</div>
</details>
<h3 id="添加Pixiv日榜"><a href="#添加Pixiv日榜" class="headerlink" title="添加Pixiv日榜"></a>添加Pixiv日榜</h3><details class="folding-tag" cyan><summary> 效果预览 </summary>
<div class='content'>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/b17aa37e6baa5.jpg"/></div></div>
</div>
</details>
<details class="folding-tag" yellow><summary> 查看步骤 </summary>
<div class='content'>
<ol><li><p>在<code>BlogRoot/themes/butterfly/layout/includes/widget</code>文件夹新建 <code>card_pixiv.pug</code>文件,文件内容如下:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">.card-widget.card-pixiv</span><br><span class="line"> .card-content</span><br><span class="line"> .item-headline</span><br><span class="line"> i.fa.fa-image(aria-hidden="true")</span><br><span class="line"> span= _p('aside.card_pixiv')</span><br><span class="line"> iframe(src="https://cloud.mokeyjay.com/pixiv" frameborder="0" style="width:99%;height:380px;margin:0;")</span><br></pre></td></tr></table></figure><p><a href="https://cloud.mokeyjay.com/pixiv">https://cloud.mokeyjay.com/pixiv</a> 使用的是 <a href="https://www.mokeyjay.com/">超能小紫</a> 提供的服务,也可以自行搭建,搭建方式请看<a href="https://www.mokeyjay.com/archives/1063">这里</a>。</p></li><li><p>编辑<code>BlogRoot/themes/butterfly/layout/includes/widget/index.pug</code> 文件,在你想要显示的位置插入以下代码:</p><figure class="highlight plaintext"><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">if theme.aside.card_pixiv</span><br><span class="line"> !=partial('includes/widget/card_pixiv', {}, {cache:true})</span><br></pre></td></tr></table></figure><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/05/629c85346ca23.jpg"/></div></div></li><li><p>编辑主题配置文件<code>_config.butterfly.yml</code>,在 <code>card_webinfo</code> 下面添加一行 <code>card_pixiv: true</code>,可以通过搜索关键词 <code>aside</code>,找到对应位置添加即可。不想显示,直接把文件中的 <code>card_pixiv: true</code> 改为 <code>card_pixiv: false</code> 即可。</p></li><li><p>编辑 <code>BlogRoot/themes/butterfly/languages/zh-CN.yml</code>文件 (请根据你的网站语言选择),找到<code>card_announcement: 公告</code>,在下面添加一行<code>card_pixiv: Pixiv日榜Top50</code>(后面的文本可自定义)。</p></li></ol>
</div>
</details>
<h3 id="哔哩哔哩番剧页面插件"><a href="#哔哩哔哩番剧页面插件" class="headerlink" title="哔哩哔哩番剧页面插件"></a>哔哩哔哩番剧页面插件</h3><details class="folding-tag" cyan><summary> 效果预览 </summary>
<div class='content'>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/f09072fc46848.jpg"/></div></div>
</div>
</details>
<details class="folding-tag" yellow><summary> 查看步骤 </summary>
<div class='content'>
<details class="folding-tag" red><summary> 关于图片加载相关问题 </summary> <div class='content'> <p>懒加载问题,此插件懒加载可能与你主题的懒加载存在冲突,以下为几种解决方法:</p><p>1.全站关闭懒加载,此插件启用/不启用懒加载均可正常运行;<br>2.如果主题提供单独页面的懒加载配置参数,可在插件配置的extra_option中配置为关。<br>3.[建议]关闭此插件的懒加载,并按照主题的懒加载图片格式配置srcValue和lazyloadAttrName,例butterfly主题:</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">bangumi:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="string">...</span></span><br><span class="line"> <span class="attr">lazyload:</span> <span class="literal">false</span></span><br><span class="line"> <span class="attr">srcValue:</span> <span class="string">'__image__'</span></span><br><span class="line"> <span class="string">...</span></span><br></pre></td></tr></table></figure><p>更多详情可见:<a href="https://github.com/HCLonely/hexo-bilibili-bangumi/issues/134">关于图片加载相关问题</a></p> </div> </details><ol><li><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 hexo-bilibili-bangumi --save</span><br></pre></td></tr></table></figure></li><li><p>更新依赖库</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install hexo-bilibili-bangumi --update --save</span><br></pre></td></tr></table></figure></li><li><p>注入哔哩哔哩番剧<br>修改站点配置文件<code>_config.yml</code>,添加如下代码:</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">bangumi:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span> </span><br><span class="line"> <span class="attr">vmid:</span> <span class="number">321638084</span></span><br><span class="line"> <span class="attr">title:</span> <span class="string">'生命不息,追番不止。'</span></span><br><span class="line"> <span class="attr">quote:</span> <span class="string">'Where there is life, there is life.'</span></span><br><span class="line"> <span class="attr">show:</span> <span class="number">1</span></span><br><span class="line"> <span class="attr">loading:</span> <span class="string">'/img/bangumi-loading.gif'</span></span><br></pre></td></tr></table></figure><p> <a href="https://github.com/HCLonely/hexo-bilibili-bangumi#%E9%85%8D%E7%BD%AE">配置说明</a>:</p><ul><li>enable: 是否启用</li><li>vmid: 哔哩哔哩番剧页面的 vmid(uid), <a href="https://github.com/HCLonely/hexo-bilibili-bangumi#%E8%8E%B7%E5%8F%96-bilibili-uid">如何获取</a>?</li><li>title: 该页面的标题</li><li>quote: 写在页面开头的一段话,支持 html 语法</li><li>show: 初始显示页面:0: 想看 , 1: 在看 , 2: 看过,默认为 1</li><li>loading: 图片加载完成前的 loading 图片</li></ul></li><li><p>执行Docs命令<br>前往博客根目录,打开cmd命令窗口执行<code>hexo new page bangumis</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">hexo new page bangumis</span><br></pre></td></tr></table></figure><p>找到 <code>BlogRoot/source/bangumis/index.md</code> 这个文件,修改这个文件,添加 <code>type: "bangumis"</code>。</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">title: bangumis</span><br><span class="line">date: 2020-12-14 14:43:39</span><br><span class="line"><span class="section">type: "bangumis"</span></span><br><span class="line"><span class="section">---</span></span><br></pre></td></tr></table></figure><p>防止请求次数过多插件不再自动获取番剧数据,所以请根据自己的需要在<code>hexo generate</code>或<code>hexo deploy</code>之前使用<code>hexo bangumi -u</code>命令更新番剧数据。<br>删除数据命令: <code>hexo bangumi -d</code></p></li><li><p>获取 uid<br>登录哔哩哔哩后前往 <a href="https://space.bilibili.com/">https://space.bilibili.com/xxx</a>,网址最后的一串数字就是 uid。</p></li></ol>
</div>
</details>
<h3 id="添加卡通人物(看板娘)"><a href="#添加卡通人物(看板娘)" class="headerlink" title="添加卡通人物(看板娘)"></a>添加卡通人物(看板娘)</h3><details class="folding-tag" cyan><summary> 效果预览 </summary>
<div class='content'>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/05/629cbe6848d9a.jpg"/></div></div>
</div>
</details>
<details class="folding-tag" yellow><summary> 查看步骤 </summary>
<div class='content'>
<p>前往博客根目录,打开cmd命令窗口执行<code>npm install --save hexo-helper-live2d</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">npm install --save hexo-helper-live2d</span><br></pre></td></tr></table></figure><p>输入以下命令,下载相应的模型, 替换成你想要的模型名称即可,更多模型选择<a href="https://github.com/xiazeyu/live2d-widget-models">请点击此处</a>,各个模型的预览请访问<a href="https://huaji8.top/post/live2d-plugin-2.0/">原作者</a></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 live2d-widget-model-shizuku</span><br></pre></td></tr></table></figure><p>修改站点配置文件<code>_config.yml</code>,添加如下代码:</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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="attr">live2d:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">scriptFrom:</span> <span class="string">local</span> <span class="comment"># 默认</span></span><br><span class="line"> <span class="attr">tagMode:</span> <span class="literal">false</span> <span class="comment"># 标签模式, 是否仅替换 live2d tag标签而非插入到所有页面中</span></span><br><span class="line"> <span class="attr">debug:</span> <span class="literal">false</span> <span class="comment"># 调试, 是否在控制台输出日志</span></span><br><span class="line"> <span class="attr">model:</span></span><br><span class="line"> <span class="attr">use:</span> <span class="string">live2d-widget-model-shizuku</span> <span class="comment">#模型选择</span></span><br><span class="line"> <span class="attr">display:</span></span><br><span class="line"> <span class="attr">position:</span> <span class="string">right</span> <span class="comment">#模型位置</span></span><br><span class="line"> <span class="attr">width:</span> <span class="number">150</span> <span class="comment">#模型宽度</span></span><br><span class="line"> <span class="attr">height:</span> <span class="number">300</span> <span class="comment">#模型高度</span></span><br><span class="line"> <span class="attr">hOffset:</span> <span class="number">20</span></span><br><span class="line"> <span class="attr">vOffset:</span> <span class="number">-20</span></span><br><span class="line"> <span class="attr">mobile:</span></span><br><span class="line"> <span class="attr">show:</span> <span class="literal">false</span> <span class="comment">#是否在手机端显示</span></span><br></pre></td></tr></table></figure><p>重新编译运行,即可看到效果。</p>
</div>
</details>
<h3 id="添加全局吸底APlayer"><a href="#添加全局吸底APlayer" class="headerlink" title="添加全局吸底APlayer"></a>添加全局吸底APlayer</h3><blockquote>
<p>参考链接:<a href="https://butterfly.js.org/posts/507c070f/">Butterfly添加全局吸底Aplayer教程</a><br>此步骤适用于安装了<code>hexo-tag-aplayer</code>插件的人</p>
</blockquote>
<details class="folding-tag" cyan><summary> 效果预览 </summary>
<div class='content'>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/886d62deceb24.jpg"/></div></div>
</div>
</details>
<details class="folding-tag" yellow><summary> 查看步骤 </summary>
<div class='content'>
<ol><li><p>前往博客根目录,打开cmd命令窗口执行以下命令:</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 i hexo-tag-aplayer --save</span><br></pre></td></tr></table></figure></li><li><p>关闭<code>asset_inject</code><br>由于需要全局都插入 aplayer 和 meting 资源,为了防止插入重复的资源,需要把 asset_inject 设为<code>false</code>。<br>修改站点配置文件<code>_config.yml</code>,配置以下内容:</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">aplayer:</span></span><br><span class="line"> <span class="attr">meting:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">asset_inject:</span> <span class="literal">false</span></span><br></pre></td></tr></table></figure></li><li><p>开启主题的aplayerInject<br>修改主题配置文件<code>_config.butterfly.yml</code>,将<code>enable</code>设为<code>true</code>和<code>per_page</code>设为<code>true</code>。</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Inject the css and script (aplayer/meting)</span></span><br><span class="line"><span class="attr">aplayerInject:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">per_page:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure></li><li><p>插入Aplayer html<br>为了适配<code>hexo-tag-aplayer</code>,主题内置的<code>Meting js</code>仍为1.2版本,并非最新的2.x版本。<br>Aplayer html 例子(示例中没有显示歌词):</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"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"aplayer no-destroy"</span> <span class="attr">data-id</span>=<span class="string">"7427714271"</span> <span class="attr">data-server</span>=<span class="string">"netease"</span> <span class="attr">data-type</span>=<span class="string">"playlist"</span> <span class="attr">data-fixed</span>=<span class="string">"true"</span> <span class="attr">data-mini</span>=<span class="string">"true"</span> <span class="attr">data-listFolded</span>=<span class="string">"false"</span> <span class="attr">data-order</span>=<span class="string">"random"</span> <span class="attr">data-lrctype</span>=<span class="string">"1"</span> <span class="attr">data-preload</span>=<span class="string">"none"</span> <span class="attr">data-autoplay</span>=<span class="string">"true"</span> <span class="attr">muted</span>></span><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure><p>参数解释如下表:</p><table><thead><tr><th align="left">option</th><th align="left">default</th><th align="left">description</th></tr></thead><tbody><tr><td align="left">data-id</td><td align="left">require</td><td align="left">song id / playlist id / album id / search keyword</td></tr><tr><td align="left">data-server</td><td align="left">require</td><td align="left">music platform: netease, tencent, kugou, xiami, baidu</td></tr><tr><td align="left">data-type</td><td align="left">require</td><td align="left">song, playlist, album, search, artist</td></tr><tr><td align="left">data-fixed</td><td align="left">false</td><td align="left">enable fixed mode</td></tr><tr><td align="left">data-mini</td><td align="left">false</td><td align="left">enable mini mode</td></tr><tr><td align="left">data-autoplay</td><td align="left">false</td><td align="left">audio autoplay</td></tr><tr><td align="left">data-theme</td><td align="left">#2980b9</td><td align="left">main color</td></tr><tr><td align="left">data-loop</td><td align="left">all</td><td align="left">player loop play, values: ‘all’, ‘one’, ‘none’</td></tr><tr><td align="left">data-order</td><td align="left">list</td><td align="left">player play order, values: ‘list’, ‘random’</td></tr><tr><td align="left">data-preload</td><td align="left">auto</td><td align="left">values: ‘none’, ‘metadata’, ‘auto’</td></tr><tr><td align="left">data-volume</td><td align="left">0.7</td><td align="left">default volume, notice that player will remember user setting, default volume will not work after user set volume themselves</td></tr><tr><td align="left">data-mutex</td><td align="left">true</td><td align="left">prevent to play multiple player at the same time, pause other players when this player start play</td></tr><tr><td align="left">data-lrctype</td><td align="left">0</td><td align="left">lyric type</td></tr><tr><td align="left">data-listfolded</td><td align="left">false</td><td align="left">indicate whether list should folded at first</td></tr><tr><td align="left">data-listmaxheight</td><td align="left">340px</td><td align="left">list max height</td></tr><tr><td align="left">data-storagename</td><td align="left">metingjs</td><td align="left">localStorage key that store player setting</td></tr></tbody></table><div class="note info flat"><p><code>require</code>代表着这些参数是必须要使用的,其它的参数则可以根据自己需要配置。<br>配置全局吸底,<code>data-fixed</code>和<code>data-mini</code>也必须配置,配置为<code>true</code>。<br>如果使用Pjax,则在class里需添加<code>no-destroy</code>,这样防止切换页面时Aplayer被销毁。</p></div>将以下代码插入到主题配置文件(_config.butterfly.yml)的 “inject.bottom” 中<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">inject:</span></span><br><span class="line"> <span class="attr">head:</span></span><br><span class="line"> <span class="attr">bottom:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string"><div</span> <span class="string">class="aplayer</span> <span class="literal">no</span><span class="string">-destroy"</span> <span class="string">data-id="7427714271"</span> <span class="string">data-server="netease"</span> <span class="string">data-type="playlist"</span> <span class="string">data-fixed="true"</span> <span class="string">data-mini="true"</span> <span class="string">data-listFolded="false"</span> <span class="string">data-order="random"</span> <span class="string">data-lrctype="1"</span> <span class="string">data-preload="none"</span> <span class="string">data-autoplay="true"</span> <span class="string">muted></div></span></span><br></pre></td></tr></table></figure>运行Hexo就可以看到网页左下角出现了Aplayer。最后,如果你想切换页面时,音乐不会中断。请把主题配置文件的 “pjax” 设为 “true” 。<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">pjax:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">exclude:</span></span><br></pre></td></tr></table></figure></li><li><p>UI 调整</p><ul><li>向上调整<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></pre></td><td class="code"><pre><span class="line"><span class="selector-id">#toggle-sidebar</span> {</span><br><span class="line"> <span class="attribute">bottom</span>: <span class="number">80px</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/26dde2aeaa502.gif"/></div></div>修改主题配置文件`_config.butterfly.yml`,将代码添加到 “inject.head” 中。<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">inject:</span></span><br><span class="line"> <span class="attr">head:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">'<style type="text/css">#toggle-sidebar {bottom: 80px}</style>'</span></span><br></pre></td></tr></table></figure></li><li>向右调整<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></pre></td><td class="code"><pre><span class="line"><span class="selector-id">#toggle-sidebar</span> {</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">100px</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/5ef1e1245fc5a.gif"/></div></div>修改主题配置文件(_config.butterfly.yml),将代码添加到 “inject.head” 中。<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">inject:</span></span><br><span class="line"> <span class="attr">head:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">'<style type="text/css">#toggle-sidebar {left:100px}</style>'</span></span><br></pre></td></tr></table></figure></li></ul></li></ol>
</div>
</details>
<h3 id="添加贡献日历"><a href="#添加贡献日历" class="headerlink" title="添加贡献日历"></a>添加贡献日历</h3><details class="folding-tag" cyan><summary> 效果预览 </summary>
<div class='content'>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/4c1e9d34afcb9.jpg"/></div></div>
</div>
</details>
<details class="folding-tag" yellow><summary> 查看步骤 </summary>
<div class='content'>
<p>我的版本可能比较旧,贡献日历已经更新迭代了,所以直接参此教程:<a href="https://akilar.top/posts/1f9c68c9/">Gitcalendar</a></p>
</div>
</details>
<h3 id="主页冒泡特效"><a href="#主页冒泡特效" class="headerlink" title="主页冒泡特效"></a>主页冒泡特效</h3><details class="folding-tag" cyan><summary> 效果预览 </summary>
<div class='content'>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/38a0bd6cef409.jpg"/></div></div>
</div>
</details>
<details class="folding-tag" yellow><summary> 查看步骤 </summary>
<div class='content'>
<p>在<code>BlogRoot/themes/butterfly/source/js</code>目录下创建一个<code>chocolate.js</code>文件。</p><p>具体位置,请看下图所示:</p><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/03/05/13410b8cd419d.jpg"/></div></div><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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * @Author: tzy1997</span></span><br><span class="line"><span class="comment"> * @Date: 2020-12-15 20:55:25</span></span><br><span class="line"><span class="comment"> * @LastEditors: tzy1997</span></span><br><span class="line"><span class="comment"> * @LastEditTime: 2021-01-12 19:02:25</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line">$(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="comment">// 气泡</span></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">bubble</span>(<span class="params"></span>) {</span><br><span class="line"> $(<span class="string">'#page-header'</span>).<span class="title function_">circleMagic</span>({</span><br><span class="line"> <span class="attr">radius</span>: <span class="number">10</span>,</span><br><span class="line"> <span class="attr">density</span>: <span class="number">.2</span>,</span><br><span class="line"> <span class="attr">color</span>: <span class="string">'rgba(255,255,255,.4)'</span>,</span><br><span class="line"> <span class="attr">clearOffset</span>: <span class="number">0.99</span></span><br><span class="line"> });</span><br><span class="line"> }! <span class="keyword">function</span>(<span class="params">p</span>) {</span><br><span class="line"> p.<span class="property">fn</span>.<span class="property">circleMagic</span> = <span class="keyword">function</span>(<span class="params">t</span>) {</span><br><span class="line"> <span class="keyword">var</span> o, a, n, r, e = !<span class="number">0</span>,</span><br><span class="line"> i = [],</span><br><span class="line"> d = p.<span class="title function_">extend</span>({ <span class="attr">color</span>: <span class="string">"rgba(255,0,0,.5)"</span>, <span class="attr">radius</span>: <span class="number">10</span>, <span class="attr">density</span>: <span class="number">.3</span>, <span class="attr">clearOffset</span>: <span class="number">.2</span> }, t),</span><br><span class="line"> l = <span class="variable language_">this</span>[<span class="number">0</span>];</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">c</span>(<span class="params"></span>) { e = !(<span class="variable language_">document</span>.<span class="property">body</span>.<span class="property">scrollTop</span> > a) }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">s</span>(<span class="params"></span>) { o = l.<span class="property">clientWidth</span>, a = l.<span class="property">clientHeight</span>, l.<span class="property">height</span> = a <span class="string">"px"</span>, n.<span class="property">width</span> = o, n.<span class="property">height</span> = a }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">h</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">if</span> (e)</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> t <span class="keyword">in</span> r.<span class="title function_">clearRect</span>(<span class="number">0</span>, <span class="number">0</span>, o, a), i) i[t].<span class="title function_">draw</span>();</span><br><span class="line"> <span class="title function_">requestAnimationFrame</span>(h)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">f</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> t = <span class="variable language_">this</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">e</span>(<span class="params"></span>) { t.<span class="property">pos</span>.<span class="property">x</span> = <span class="title class_">Math</span>.<span class="title function_">random</span>() * o, t.<span class="property">pos</span>.<span class="property">y</span> = a <span class="number">100</span> * <span class="title class_">Math</span>.<span class="title function_">random</span>(), t.<span class="property">alpha</span> = <span class="number">.1</span> <span class="title class_">Math</span>.<span class="title function_">random</span>() * d.<span class="property">clearOffset</span>, t.<span class="property">scale</span> = <span class="number">.1</span> <span class="number">.3</span> * <span class="title class_">Math</span>.<span class="title function_">random</span>(), t.<span class="property">speed</span> = <span class="title class_">Math</span>.<span class="title function_">random</span>(), <span class="string">"random"</span> === d.<span class="property">color</span> ? t.<span class="property">color</span> = <span class="string">"rgba("</span> <span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="number">255</span> * <span class="title class_">Math</span>.<span class="title function_">random</span>()) <span class="string">", "</span> <span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="number">0</span> * <span class="title class_">Math</span>.<span class="title function_">random</span>()) <span class="string">", "</span> <span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="number">0</span> * <span class="title class_">Math</span>.<span class="title function_">random</span>()) <span class="string">", "</span> <span class="title class_">Math</span>.<span class="title function_">random</span>().<span class="title function_">toPrecision</span>(<span class="number">2</span>) <span class="string">")"</span> : t.<span class="property">color</span> = d.<span class="property">color</span> }</span><br><span class="line"> t.<span class="property">pos</span> = {}, <span class="title function_">e</span>(), <span class="variable language_">this</span>.<span class="property">draw</span> = <span class="keyword">function</span>(<span class="params"></span>) { t.<span class="property">alpha</span> <= <span class="number">0</span> && <span class="title function_">e</span>(), t.<span class="property">pos</span>.<span class="property">y</span> -= t.<span class="property">speed</span>, t.<span class="property">alpha</span> -= <span class="number">5e-4</span>, r.<span class="title function_">beginPath</span>(), r.<span class="title function_">arc</span>(t.<span class="property">pos</span>.<span class="property">x</span>, t.<span class="property">pos</span>.<span class="property">y</span>, t.<span class="property">scale</span> * d.<span class="property">radius</span>, <span class="number">0</span>, <span class="number">2</span> * <span class="title class_">Math</span>.<span class="property">PI</span>, !<span class="number">1</span>), r.<span class="property">fillStyle</span> = t.<span class="property">color</span>, r.<span class="title function_">fill</span>(), r.<span class="title function_">closePath</span>() }</span><br><span class="line"> }! <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> o = l.<span class="property">offsetWidth</span>, a = l.<span class="property">offsetHeight</span>,</span><br><span class="line"> <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> t = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">"canvas"</span>);</span><br><span class="line"> t.<span class="property">id</span> = <span class="string">"canvas"</span>, t.<span class="property">style</span>.<span class="property">top</span> = <span class="number">0</span>, t.<span class="property">style</span>.<span class="property">zIndex</span> = <span class="number">0</span>, t.<span class="property">style</span>.<span class="property">position</span> = <span class="string">"absolute"</span>, l.<span class="title function_">appendChild</span>(t), t.<span class="property">parentElement</span>.<span class="property">style</span>.<span class="property">overflow</span> = <span class="string">"hidden"</span></span><br><span class="line"> }(), (n = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">"canvas"</span>)).<span class="property">width</span> = o, n.<span class="property">height</span> = a, r = n.<span class="title function_">getContext</span>(<span class="string">"2d"</span>);</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> t = <span class="number">0</span>; t < o * d.<span class="property">density</span>; t++) {</span><br><span class="line"> <span class="keyword">var</span> e = <span class="keyword">new</span> f;</span><br><span class="line"> i.<span class="title function_">push</span>(e)</span><br><span class="line"> }</span><br><span class="line"> <span class="title function_">h</span>()</span><br><span class="line"> }(), <span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">"scroll"</span>, c, !<span class="number">1</span>), <span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">"resize"</span>, s, !<span class="number">1</span>)</span><br><span class="line"> }</span><br><span class="line"> }(jQuery);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 调用气泡方法</span></span><br><span class="line"> <span class="title function_">bubble</span>();</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p>最后,在主题配置文件<code>_config.butterfly.yml</code>中,引入<code>jquery.min.js</code>和<code>chocolate.js</code>。</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">inject:</span></span><br><span class="line"> <span class="attr">head:</span></span><br><span class="line"> <span class="comment"># - <link rel="stylesheet" href="/xxx.css"></span></span><br><span class="line"> <span class="attr">bottom:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string"><script</span> <span class="string">defer</span> <span class="string">src="https://npm.elemecdn.com/jquery@latest/dist/jquery.min.js"></script></span></span><br><span class="line"> <span class="bullet">-</span> <span class="string"><script</span> <span class="string">data-pjax</span> <span class="string">defer</span> <span class="string">src="https://npm.elemecdn.com/tzy-blog/lib/js/theme/chocolate.js"></script></span></span><br></pre></td></tr></table></figure><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/05/629cc712293d1.jpg"/></div></div>
</div>
</details>
<h3 id="鼠标样式"><a href="#鼠标样式" class="headerlink" title="鼠标样式"></a>鼠标样式</h3><details class="folding-tag" cyan><summary> 效果预览 </summary>
<div class='content'>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/17/6283c365d20dd.png"/></div></div>
</div>
</details>
<details class="folding-tag" yellow><summary> 查看步骤 </summary>
<div class='content'>
<p>将以下代码复制到<code>custom.css</code>即可。</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">body</span> {</span><br><span class="line"> <span class="attribute">cursor</span>: <span class="built_in">url</span>(<span class="string">https://bu.dusays.com/2022/05/17/6283c365d20dd.png</span>), auto;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.hide-block</span>><span class="selector-class">.hide-button</span><span class="selector-class">.open</span>,</span><br><span class="line"><span class="selector-class">.hide-inline</span>><span class="selector-class">.hide-button</span><span class="selector-class">.open</span> {</span><br><span class="line"> <span class="attribute">display</span>: block</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-tag">a</span>,</span><br><span class="line"><span class="selector-tag">button</span>,</span><br><span class="line"><span class="selector-tag">img</span> {</span><br><span class="line"> <span class="attribute">cursor</span>: <span class="built_in">url</span>(<span class="string">https://bu.dusays.com/2022/05/17/6283c376afcfc.png</span>), auto</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</div>
</details>
<h3 id="鼠标-字跟随掉落效果"><a href="#鼠标-字跟随掉落效果" class="headerlink" title="鼠标 * 字跟随掉落效果"></a>鼠标 * 字跟随掉落效果</h3><details class="folding-tag" cyan><summary> 效果预览 </summary>
<div class='content'>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/03/05/f9fc72341622b.png"/></div></div>
</div>
</details>
<details class="folding-tag" yellow><summary> 查看步骤 </summary>
<div class='content'>
<p>在<code>BlogRoot/themes/butterfly/source/js</code>目录下创建一个<code>cursor.js</code>文件。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/*!</span></span><br><span class="line"><span class="comment"> * Fairy Dust Cursor.js</span></span><br><span class="line"><span class="comment"> * - 90's cursors collection</span></span><br><span class="line"><span class="comment"> * -- https://github.com/tholman/90s-cursor-effects</span></span><br><span class="line"><span class="comment"> * -- http://codepen.io/tholman/full/jWmZxZ/</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line">(<span class="keyword">function</span> <span class="title function_">fairyDustCursor</span>(<span class="params"></span>) {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> possibleColors = [<span class="string">"#D61C59"</span>, <span class="string">"#E7D84B"</span>, <span class="string">"#1B8798"</span>]</span><br><span class="line"> <span class="keyword">var</span> width = <span class="variable language_">window</span>.<span class="property">innerWidth</span>;</span><br><span class="line"> <span class="keyword">var</span> height = <span class="variable language_">window</span>.<span class="property">innerHeight</span>;</span><br><span class="line"> <span class="keyword">var</span> cursor = { <span class="attr">x</span>: width / <span class="number">2</span>, <span class="attr">y</span>: width / <span class="number">2</span> };</span><br><span class="line"> <span class="keyword">var</span> particles = [];</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">init</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="title function_">bindEvents</span>();</span><br><span class="line"> <span class="title function_">loop</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Bind events that are needed</span></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">bindEvents</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">document</span>.<span class="title function_">addEventListener</span>(<span class="string">'mousemove'</span>, onMouseMove);</span><br><span class="line"> <span class="variable language_">document</span>.<span class="title function_">addEventListener</span>(<span class="string">'touchmove'</span>, onTouchMove);</span><br><span class="line"> <span class="variable language_">document</span>.<span class="title function_">addEventListener</span>(<span class="string">'touchstart'</span>, onTouchMove);</span><br><span class="line"></span><br><span class="line"> <span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">'resize'</span>, onWindowResize);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">onWindowResize</span>(<span class="params">e</span>) {</span><br><span class="line"> width = <span class="variable language_">window</span>.<span class="property">innerWidth</span>;</span><br><span class="line"> height = <span class="variable language_">window</span>.<span class="property">innerHeight</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">onTouchMove</span>(<span class="params">e</span>) {</span><br><span class="line"> <span class="keyword">if</span> (e.<span class="property">touches</span>.<span class="property">length</span> > <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < e.<span class="property">touches</span>.<span class="property">length</span>; i++) {</span><br><span class="line"> <span class="title function_">addParticle</span>(e.<span class="property">touches</span>[i].<span class="property">clientX</span>, e.<span class="property">touches</span>[i].<span class="property">clientY</span>, possibleColors[<span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="title class_">Math</span>.<span class="title function_">random</span>() * possibleColors.<span class="property">length</span>)]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">onMouseMove</span>(<span class="params">e</span>) {</span><br><span class="line"> cursor.<span class="property">x</span> = e.<span class="property">clientX</span>;</span><br><span class="line"> cursor.<span class="property">y</span> = e.<span class="property">clientY</span>;</span><br><span class="line"></span><br><span class="line"> <span class="title function_">addParticle</span>(cursor.<span class="property">x</span>, cursor.<span class="property">y</span>, possibleColors[<span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="title class_">Math</span>.<span class="title function_">random</span>() * possibleColors.<span class="property">length</span>)]);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">addParticle</span>(<span class="params">x, y, color</span>) {</span><br><span class="line"> <span class="keyword">var</span> particle = <span class="keyword">new</span> <span class="title class_">Particle</span>();</span><br><span class="line"> particle.<span class="title function_">init</span>(x, y, color);</span><br><span class="line"> particles.<span class="title function_">push</span>(particle);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">updateParticles</span>(<span class="params"></span>) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Updated</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < particles.<span class="property">length</span>; i++) {</span><br><span class="line"> particles[i].<span class="title function_">update</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Remove dead particles</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = particles.<span class="property">length</span> - <span class="number">1</span>; i >= <span class="number">0</span>; i--) {</span><br><span class="line"> <span class="keyword">if</span> (particles[i].<span class="property">lifeSpan</span> < <span class="number">0</span>) {</span><br><span class="line"> particles[i].<span class="title function_">die</span>();</span><br><span class="line"> particles.<span class="title function_">splice</span>(i, <span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">loop</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="title function_">requestAnimationFrame</span>(loop);</span><br><span class="line"> <span class="title function_">updateParticles</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Particles</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">Particle</span>(<span class="params"></span>) {</span><br><span class="line"></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">character</span> = <span class="string">"*"</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">lifeSpan</span> = <span class="number">120</span>; <span class="comment">//ms</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">initialStyles</span> = {</span><br><span class="line"> <span class="string">"position"</span>: <span class="string">"fixed"</span>,</span><br><span class="line"> <span class="string">"top"</span>: <span class="string">"0"</span>, <span class="comment">//必须加</span></span><br><span class="line"> <span class="string">"display"</span>: <span class="string">"block"</span>,</span><br><span class="line"> <span class="string">"pointerEvents"</span>: <span class="string">"none"</span>,</span><br><span class="line"> <span class="string">"z-index"</span>: <span class="string">"10000000"</span>,</span><br><span class="line"> <span class="string">"fontSize"</span>: <span class="string">"20px"</span>,</span><br><span class="line"> <span class="string">"will-change"</span>: <span class="string">"transform"</span></span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Init, and set properties</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">init</span> = <span class="keyword">function</span>(<span class="params">x, y, color</span>) {</span><br><span class="line"></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">velocity</span> = {</span><br><span class="line"> <span class="attr">x</span>: (<span class="title class_">Math</span>.<span class="title function_">random</span>() < <span class="number">0.5</span> ? -<span class="number">1</span> : <span class="number">1</span>) * (<span class="title class_">Math</span>.<span class="title function_">random</span>() / <span class="number">2</span>),</span><br><span class="line"> <span class="attr">y</span>: <span class="number">1</span></span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">position</span> = { <span class="attr">x</span>: x - <span class="number">10</span>, <span class="attr">y</span>: y - <span class="number">20</span> };</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">initialStyles</span>.<span class="property">color</span> = color;</span><br><span class="line"></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">element</span> = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">'span'</span>);</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">element</span>.<span class="property">innerHTML</span> = <span class="variable language_">this</span>.<span class="property">character</span>;</span><br><span class="line"> <span class="title function_">applyProperties</span>(<span class="variable language_">this</span>.<span class="property">element</span>, <span class="variable language_">this</span>.<span class="property">initialStyles</span>);</span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">update</span>();</span><br><span class="line"></span><br><span class="line"> <span class="variable language_">document</span>.<span class="property">body</span>.<span class="title function_">appendChild</span>(<span class="variable language_">this</span>.<span class="property">element</span>);</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">update</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">position</span>.<span class="property">x</span> += <span class="variable language_">this</span>.<span class="property">velocity</span>.<span class="property">x</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">position</span>.<span class="property">y</span> += <span class="variable language_">this</span>.<span class="property">velocity</span>.<span class="property">y</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">lifeSpan</span>--;</span><br><span class="line"></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">element</span>.<span class="property">style</span>.<span class="property">transform</span> = <span class="string">"translate3d("</span> + <span class="variable language_">this</span>.<span class="property">position</span>.<span class="property">x</span> + <span class="string">"px,"</span> + <span class="variable language_">this</span>.<span class="property">position</span>.<span class="property">y</span> + <span class="string">"px,0) scale("</span> + (<span class="variable language_">this</span>.<span class="property">lifeSpan</span> / <span class="number">120</span>) + <span class="string">")"</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">die</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">element</span>.<span class="property">parentNode</span>.<span class="title function_">removeChild</span>(<span class="variable language_">this</span>.<span class="property">element</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Utils</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// Applies css `properties` to an element.</span></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">applyProperties</span>(<span class="params">target, properties</span>) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> key <span class="keyword">in</span> properties) {</span><br><span class="line"> target.<span class="property">style</span>[key] = properties[key];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> ($(<span class="variable language_">window</span>).<span class="title function_">innerWidth</span>() > <span class="number">576</span>) {</span><br><span class="line"> <span class="title function_">init</span>();</span><br><span class="line"> }</span><br><span class="line">})();</span><br></pre></td></tr></table></figure><p>然后,在主题配置文件<code>_config.butterfly.yml</code>中,引入<code>cursor.js</code>文件。</p><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/05/629ccb16bb10b.jpg"/></div></div>
</div>
</details>
<h3 id="滚动条"><a href="#滚动条" class="headerlink" title="滚动条"></a>滚动条</h3><details class="folding-tag" yellow><summary> 查看步骤 </summary>
<div class='content'>
<p>将以下代码复制到<code>custom.css</code>即可。</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">::-webkit-scrollbar {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">8px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">8px</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">::-webkit-scrollbar-track {</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">2em</span>;</span><br><span class="line"> <span class="comment">/* background-color: rgba(73, 177, 245, .2); */</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">::-webkit-scrollbar-thumb {</span><br><span class="line"> <span class="attribute">background-color</span>: <span class="built_in">rgb</span>(<span class="number">255</span> <span class="number">255</span> <span class="number">255</span> / .<span class="number">3</span>);</span><br><span class="line"> <span class="attribute">background-image</span>: <span class="built_in">-webkit-linear-gradient</span>(<span class="number">45deg</span>, <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.1</span>) <span class="number">25%</span>, transparent <span class="number">25%</span>, transparent <span class="number">50%</span>, <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.1</span>) <span class="number">50%</span>, <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0.1</span>) <span class="number">75%</span>, transparent <span class="number">75%</span>, transparent);</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">2em</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">::-webkit-scrollbar-corner {</span><br><span class="line"> <span class="attribute">background-color</span>: transparent</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</div>
</details>
<h3 id="手机侧边栏默认不展开"><a href="#手机侧边栏默认不展开" class="headerlink" title="手机侧边栏默认不展开"></a>手机侧边栏默认不展开</h3><p>若主题版本大于 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.0.0">v4.0.0</a>,可直接在子目录里添加 <code>hide</code> :</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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="attr">menu:</span></span><br><span class="line"> <span class="attr">Home:</span> <span class="string">/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-home</span></span><br><span class="line"> <span class="attr">Archives:</span> <span class="string">/archives/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-archive</span></span><br><span class="line"> <span class="attr">Tags:</span> <span class="string">/tags/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-tags</span></span><br><span class="line"> <span class="attr">Categories:</span> <span class="string">/categories/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-folder-open</span></span><br><span class="line"> <span class="string">List||fas</span> <span class="string">fa-list||hide:</span></span><br><span class="line"> <span class="attr">Music:</span> <span class="string">/music/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-music</span></span><br><span class="line"> <span class="attr">Movie:</span> <span class="string">/movies/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-video</span></span><br><span class="line"> <span class="attr">Link:</span> <span class="string">/link/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-link</span></span><br><span class="line"> <span class="attr">About:</span> <span class="string">/about/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-heart</span></span><br></pre></td></tr></table></figure>
<p><a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.0.0">v3.7.1</a> 版本中直接默认子目录是展开的,如果你想要隐藏,按下面步骤操作即可。</p>
<details class="folding-tag" cyan><summary> 效果预览 </summary>
<div class='content'>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/03/05/5bf3d54f04dff.jpg"/></div></div>
</div>
</details>
<details class="folding-tag" yellow><summary> 查看步骤 </summary>
<div class='content'>
<p>在<code>BlogRoot/themes/butterfly/source/js</code>文件下新建 js 文件,并命名为 <code>custom.js</code>(命名按照自己喜好去命名,只需在主题配置文件<code>_config.butterfly.yml</code>中引入对应的<code>js</code>文件即可),将以下代码复制到新建的<code>custom.js</code>中。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 手机侧边栏默认不展开 */</span></span><br><span class="line"><span class="keyword">var</span> mobile_sidebar_menus = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">"sidebar-menus"</span>);</span><br><span class="line"><span class="keyword">if</span> (mobile_sidebar_menus) {</span><br><span class="line"> <span class="keyword">var</span> menus_item_child = mobile_sidebar_menus.<span class="title function_">getElementsByClassName</span>(</span><br><span class="line"> <span class="string">"menus_item_child"</span></span><br><span class="line"> );</span><br><span class="line"> <span class="keyword">var</span> menus_expand = mobile_sidebar_menus.<span class="title function_">getElementsByClassName</span>(<span class="string">"expand"</span>);</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < menus_item_child.<span class="property">length</span>; i++) {</span><br><span class="line"> menus_item_child[i].<span class="property">style</span>.<span class="property">display</span> = <span class="string">"none"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</div>
</details>
<h3 id="图库"><a href="#图库" class="headerlink" title="图库"></a>图库</h3><p>图库页面只是普通的页面,你只需要hexo new page xxxxx 创建你的页面就行。</p>
<p>然后使用标签外挂 <a href="https://butterfly.js.org/posts/4aa8abbe/#Gallery%E7%9B%B8%E5%86%8A%E5%9C%96%E5%BA%AB">galleryGroup</a>,具体用法请查看对应的内容。</p>
<figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"gallery-group-main"</span>></span></span></span><br><span class="line">{% galleryGroup 'ACG' '那些二次元的故事' '/gallery/ACG' https://bu.dusays.com/2022/11/26/638228a86935a.webp %}</span><br><span class="line">{% galleryGroup '岳阳' '2017年5月岳阳' '/gallery/YY' https://bu.dusays.com/2022/11/27/63831b681e9f8.jpg %}</span><br><span class="line">{% galleryGroup 'OH MY GIRL' '关于OH MY GIRL的图片' '/Gallery/ohmygirl' https://i.loli.net/2019/12/25/hOqbQ3BIwa6KWpo.jpg %}</span><br><span class="line"><span class="language-xml"><span class="tag"></<span class="name">div</span>></span></span></span><br></pre></td></tr></table></figure>
<div class="gallery-group-main">
<figure class="gallery-group">
<img class="gallery-group-img no-lightbox" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src='https://bu.dusays.com/2022/11/26/638228a86935a.webp' alt="Group Image Gallery">
<figcaption>
<div class="gallery-group-name">ACG</div>
<p>那些二次元的故事</p>
<a href='/gallery/ACG' target='_blank'></a>
</figcaption>
</figure>
<figure class="gallery-group">
<img class="gallery-group-img no-lightbox" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src='https://bu.dusays.com/2022/11/27/63831b681e9f8.jpg' alt="Group Image Gallery">
<figcaption>
<div class="gallery-group-name">岳阳</div>
<p>2017年5月岳阳</p>
<a href='/gallery/YY' target='_blank'></a>
</figcaption>
</figure>
<figure class="gallery-group">
<img class="gallery-group-img no-lightbox" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src='https://i.loli.net/2019/12/25/hOqbQ3BIwa6KWpo.jpg' alt="Group Image Gallery">
<figcaption>
<div class="gallery-group-name">OH MY GIRL</div>
<p>关于OH MY GIRL的图片</p>
<a href='/Gallery/ohmygirl' target='_blank'></a>
</figcaption>
</figure>
</div>
<h4 id="子页面"><a href="#子页面" class="headerlink" title="子页面"></a>子页面</h4><p>子页面也是普通的页面,你只需要hexo new page xxxxx 创建你的页面就行。</p>
<p>然后使用标签外挂 <a href="https://butterfly.js.org/posts/4aa8abbe/#Gallery%E7%9B%B8%E5%86%8A">gallery</a>,具体用法请查看对应的内容。</p>
<figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">{% gallery %}</span><br><span class="line">![](<span class="link">https://i.loli.net/2019/12/25/Fze9jchtnyJXMHN.jpg</span>)</span><br><span class="line">![](<span class="link">https://i.loli.net/2019/12/25/ryLVePaqkYm4TEK.jpg</span>)</span><br><span class="line">![](<span class="link">https://i.loli.net/2019/12/25/gEy5Zc1Ai6VuO4N.jpg</span>)</span><br><span class="line">![](<span class="link">https://i.loli.net/2019/12/25/d6QHbytlSYO4FBG.jpg</span>)</span><br><span class="line">![](<span class="link">https://i.loli.net/2019/12/25/6nepIJ1xTgufatZ.jpg</span>)</span><br><span class="line">![](<span class="link">https://i.loli.net/2019/12/25/E7Jvr4eIPwUNmzq.jpg</span>)</span><br><span class="line">![](<span class="link">https://i.loli.net/2019/12/25/mh19anwBSWIkGlH.jpg</span>)</span><br><span class="line">![](<span class="link">https://i.loli.net/2019/12/25/2tu9JC8ewpBFagv.jpg</span>)</span><br><span class="line">{% endgallery %}</span><br></pre></td></tr></table></figure>
<div class="gallery">
<div class="fj-gallery data" data-rowHeight="220" data-limit="10">
<span class="gallery-data">[{"url":"https://i.loli.net/2019/12/25/Fze9jchtnyJXMHN.jpg","alt":""},{"url":"https://i.loli.net/2019/12/25/ryLVePaqkYm4TEK.jpg","alt":""},{"url":"https://i.loli.net/2019/12/25/gEy5Zc1Ai6VuO4N.jpg","alt":""},{"url":"https://i.loli.net/2019/12/25/d6QHbytlSYO4FBG.jpg","alt":""},{"url":"https://i.loli.net/2019/12/25/6nepIJ1xTgufatZ.jpg","alt":""},{"url":"https://i.loli.net/2019/12/25/E7Jvr4eIPwUNmzq.jpg","alt":""},{"url":"https://i.loli.net/2019/12/25/mh19anwBSWIkGlH.jpg","alt":""},{"url":"https://i.loli.net/2019/12/25/2tu9JC8ewpBFagv.jpg","alt":""}]</span>
</div>
<button class="gallery-load-more"><span>加载更多</span><i class="fa-solid fa-arrow-down"></i></button>
</div>
<div class="note pink icon-padding flat"><i class="note-icon fas fa-fan"></i><p>如果你想要使用<code>/photo/ohmygirl</code>这样的链接显示你的图片内容</p>
<p>你可以把创建好的<code>ohmygirl</code>整个文件夹移到<code>photo</code>文件夹里去</p>
</div>
<h3 id="Valine评论邮件回复提醒"><a href="#Valine评论邮件回复提醒" class="headerlink" title="Valine评论邮件回复提醒"></a>Valine评论邮件回复提醒</h3><blockquote>
<p>由于 Valine 的国际版共享域名将于 2022 年 8 月 1 日起不再向中国大陆的最终用户提供服务,国际版共享域名仅服务于海外用户。本站已弃用 Valine ,改为 Twikoo。如果你更喜欢 Valine 的风格,你可以使用它的国区版。</p>
</blockquote>
<h4 id="参考教程"><a href="#参考教程" class="headerlink" title="参考教程"></a>参考教程</h4><p><a href="http://www.zhaojun.im/hexo-valine-admin/">Hexo 优化 — Valine 扩展之邮件通知</a><br><a href="https://blog.hclonely.com/posts/409d3090/">Valine 添加验证码、博主标签及评论微信、QQ 通知</a></p>
<h4 id="我的表情数据包"><a href="#我的表情数据包" class="headerlink" title="我的表情数据包"></a>我的表情数据包</h4><p>这里放上我的valine表情数据,展示如下图:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/8cad934e780d0.jpg"/></div></div>
<p>下载请点击下面按钮:</p>
<div class="btns rounded grid5">
<a class="button" href='https://wwd.lanzoul.com/iMvtz05xlche' title='下载表情文件'><i class='fas fa-download'></i>下载表情文件</a>
</div>
<h4 id="改写-menhera-表情样式"><a href="#改写-menhera-表情样式" class="headerlink" title="改写 menhera 表情样式"></a>改写 menhera 表情样式</h4><p>去除了dark模式下评论者头像的 border 和 padding(我是默认dark,如果不喜欢,则将css中的第一段去掉)。</p>
<p>因为 valine的默认宽度是 25px , 对于 menhera-chan 表情根本无法看清,所以将它pc端评论后的表情加载设置成300px,设屏宽小于768px的,让它继承继承父元素的100%宽 - 30px ,30px是为了与右侧有一段细微间隔,下图可以看到手机端的时候,表情和盒子最右侧是有一定距离的。</p>
<p>对于浏览选择 menhera-chan 表情时,宽度太小,看不清图片,所以这里调整为设备宽的 21%-22% 。QQ和B站表情保持不变,只对 menhera-chan 表情做出改善。</p>
<p>将以下代码复制到<code>custom.css</code>即可。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* valine 评论 */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 去除了dark模式下头像border和padding */</span></span><br><span class="line"><span class="selector-attr">[data-theme=<span class="string">'dark'</span>]</span> <span class="selector-class">.v</span><span class="selector-attr">[data-class=v]</span> <span class="selector-class">.vcards</span> <span class="selector-class">.vcard</span> <span class="selector-class">.vimg</span> {</span><br><span class="line"> <span class="attribute">padding</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">border</span>: none;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-attr">[data-theme=<span class="string">'dark'</span>]</span> <span class="selector-id">#post</span> <span class="selector-class">.v</span><span class="selector-attr">[data-class=v]</span> <span class="selector-class">.vcontent</span> <span class="selector-tag">img</span> {</span><br><span class="line"> <span class="attribute">display</span>: inline-block;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.v</span><span class="selector-attr">[data-class=v]</span> <span class="selector-class">.emoji</span>,</span><br><span class="line"><span class="selector-class">.v</span><span class="selector-attr">[data-class=v]</span> <span class="selector-class">.vemoji</span> {</span><br><span class="line"> <span class="attribute">vertical-align</span>: text-bottom;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.v</span><span class="selector-attr">[data-class=v]</span> <span class="selector-class">.vwrap</span> <span class="selector-class">.vemojis</span> {</span><br><span class="line"> <span class="attribute">max-height</span>: <span class="number">260px</span> <span class="meta">!important</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.v</span><span class="selector-attr">[data-class=v]</span> <span class="selector-class">.vwrap</span> <span class="selector-class">.vemojis</span> <span class="selector-tag">i</span><span class="selector-attr">[title|=menhera]</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">22%</span> <span class="meta">!important</span>;</span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">5px</span> <span class="number">10px</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.v</span><span class="selector-attr">[data-class=v]</span> <span class="selector-class">.vwrap</span> <span class="selector-class">.vemojis</span> <span class="selector-tag">img</span><span class="selector-attr">[alt|=menhera]</span> {</span><br><span class="line"> <span class="attribute">max-width</span>: <span class="number">100%</span> <span class="meta">!important</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.v</span><span class="selector-attr">[data-class=v]</span> <span class="selector-class">.vcontent</span> <span class="selector-class">.emoji</span><span class="selector-attr">[alt|=menhera]</span>,</span><br><span class="line"><span class="selector-class">.v</span><span class="selector-attr">[data-class=v]</span> <span class="selector-class">.vcontent</span> <span class="selector-class">.vemoji</span><span class="selector-attr">[alt|=menhera]</span> {</span><br><span class="line"> <span class="attribute">max-width</span>: <span class="number">300px</span> <span class="meta">!important</span>;</span><br><span class="line"> <span class="comment">/* vertical-align: middle; */</span></span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">8px</span> <span class="number">1px</span>;</span><br><span class="line"> <span class="attribute">display</span>: block <span class="meta">!important</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">@media</span> screen <span class="keyword">and</span> (<span class="attribute">max-width</span>: <span class="number">1200px</span>) {</span><br><span class="line"> <span class="selector-class">.v</span><span class="selector-attr">[data-class=v]</span> <span class="selector-class">.vwrap</span> <span class="selector-class">.vemojis</span> <span class="selector-tag">i</span><span class="selector-attr">[title|=menhera]</span> {</span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">5px</span> <span class="number">8px</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">@media</span> screen <span class="keyword">and</span> (<span class="attribute">max-width</span>: <span class="number">768px</span>) {</span><br><span class="line"> <span class="selector-class">.v</span><span class="selector-attr">[data-class=v]</span> <span class="selector-class">.vcontent</span> <span class="selector-class">.emoji</span><span class="selector-attr">[alt|=menhera]</span>,</span><br><span class="line"> <span class="selector-class">.v</span><span class="selector-attr">[data-class=v]</span> <span class="selector-class">.vcontent</span> <span class="selector-class">.vemoji</span><span class="selector-attr">[alt|=menhera]</span> {</span><br><span class="line"> <span class="attribute">max-width</span>: <span class="built_in">calc</span>(<span class="number">100%</span> - <span class="number">30px</span>) <span class="meta">!important</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">@media</span> screen <span class="keyword">and</span> (<span class="attribute">max-width</span>: <span class="number">576px</span>) {</span><br><span class="line"> <span class="selector-class">.v</span><span class="selector-attr">[data-class=v]</span> <span class="selector-class">.vwrap</span> <span class="selector-class">.vemojis</span> {</span><br><span class="line"> <span class="attribute">max-height</span>: <span class="number">200px</span> <span class="meta">!important</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="selector-class">.v</span><span class="selector-attr">[data-class=v]</span> <span class="selector-class">.vwrap</span> <span class="selector-class">.vemojis</span> <span class="selector-tag">i</span><span class="selector-attr">[title|=menhera]</span> {</span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">5px</span> <span class="number">5px</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">@media</span> screen <span class="keyword">and</span> (<span class="attribute">max-width</span>: <span class="number">400px</span>) {</span><br><span class="line"> <span class="selector-class">.v</span><span class="selector-attr">[data-class=v]</span> <span class="selector-class">.vwrap</span> <span class="selector-class">.vemojis</span> <span class="selector-tag">i</span><span class="selector-attr">[title|=menhera]</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">21%</span> <span class="meta">!important</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>效果如下:</p>
<div class="gallery">
<div class="fj-gallery data" data-rowHeight="220" data-limit="10">
<span class="gallery-data">[{"url":"https://bu.dusays.com/2021/08/26/84d3e2911b8ec.jpg","alt":""},{"url":"https://bu.dusays.com/2021/08/26/24353a0e2da4f.jpg","alt":""},{"url":"https://bu.dusays.com/2021/03/05/987c04fdd05f6.jpg","alt":""},{"url":"https://bu.dusays.com/2021/03/05/396f32b981353.jpg","alt":""},{"url":"https://bu.dusays.com/2021/03/05/72321b1f63e2c.jpg","alt":""},{"url":"https://bu.dusays.com/2021/03/05/564b64b674298.jpg","alt":""}]</span>
</div>
<button class="gallery-load-more"><span>加载更多</span><i class="fa-solid fa-arrow-down"></i></button>
</div>
<h4 id="添加-博主-小伙伴-访客-标签"><a href="#添加-博主-小伙伴-访客-标签" class="headerlink" title="添加[博主,小伙伴,访客]标签"></a>添加[博主,小伙伴,访客]标签</h4><ol>
<li>打开<code>BlogRoot/themes/butterfly/layout/includes/third-party/comments/valine.pug</code>,按指示添加如下字段。<figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">function initValine () {</span><br><span class="line"> const valine = new Valine(Object.assign({</span><br><span class="line"> el: '#vcomment',</span><br><span class="line"> appId: '#{theme.valine.appId}',</span><br><span class="line"> appKey: '#{theme.valine.appKey}',</span><br><span class="line"> placeholder: '#{theme.valine.placeholder}',</span><br><span class="line"> avatar: '#{theme.valine.avatar}',</span><br><span class="line"> meta: '#{theme.valine.guest_info }'.split(','),</span><br><span class="line"> pageSize: '#{theme.valine.pageSize}',</span><br><span class="line"> lang: '#{theme.valine.lang}',</span><br><span class="line"> recordIP: #{theme.valine.recordIP},</span><br><span class="line"> serverURLs: '#{theme.valine.serverURLs}',</span><br><span class="line"> emojiCDN: '#{theme.valine.emojiCDN}',</span><br><span class="line"> emojiMaps: !{emojiMaps},</span><br><span class="line"> enableQQ: #{theme.valine.enableQQ},</span><br><span class="line"> path: window.location.pathname,</span><br><span class="line"> requiredFields: [!{theme.valine.requiredFields ? JSON.stringify(theme.valine.requiredFields).split(',') : ''}],</span><br><span class="line"> master: '#{theme.valine.master}'.split(','),</span><br><span class="line"> friends: '#{theme.valine.friends}'.split(','),</span><br><span class="line"> tagMeta: '#{theme.valine.tagMeta || "博主,小伙伴,访客"}'.split(','),</span><br><span class="line"> metaPlaceholder: !{JSON.stringify(theme.valine.metaPlaceholder || {})},</span><br><span class="line"> visitor: #{theme.valine.visitor}</span><br><span class="line"> }, !{JSON.stringify(theme.valine.option)}))</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
如图所示:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/04/29/09fee55119095.jpg"/></div></div></li>
<li>修改主题配置文件<code>_config.butterfly.yml</code>的<code>valine</code>属性。<ul>
<li><p>valine配置项添加 md5加密的博主邮箱,小伙伴邮箱</p>
<figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"># valine</span><br><span class="line"># https://valine.js.org</span><br><span class="line">valine:</span><br><span class="line"> appId: # leancloud application app id</span><br><span class="line"> appKey: # leancloud application app key</span><br><span class="line"> pageSize: 10 # comment list page size</span><br><span class="line"> avatar: monsterid # gravatar style https://valine.js.org/#/avatar</span><br><span class="line"> lang: zh-CN # i18n: zh-CN/zh-TW/en/ja</span><br><span class="line"> placeholder: # valine comment input placeholder (like: Please leave your footprints)</span><br><span class="line"> guest_info: nick,mail,link # valine comment header info (nick/mail/link)</span><br><span class="line"> recordIP: false # Record reviewer IP</span><br><span class="line"> serverURLs: # This configuration is suitable for domestic custom domain name users, overseas version will be automatically detected (no need to manually fill in)</span><br><span class="line"> bg: # valine background</span><br><span class="line"> emojiCDN: # emoji CDN</span><br><span class="line"> enableQQ: true # enable the Nickname box to automatically get QQ Nickname and QQ Avatar</span><br><span class="line"> requiredFields: nick,mail # required fields (nick/mail)</span><br><span class="line">master: # md5加密后的博主邮箱</span><br><span class="line"> - d4e7????????????44a14e9a94 #可添加多个</span><br><span class="line">friends: # md5加密后的小伙伴邮箱</span><br><span class="line"> - 5c?????????????e268ad3819c #可添加多个</span><br><span class="line"> - 7c?????????????e2????3919c</span><br><span class="line">tagMeta: '博主,小伙伴,访客' # 标签要显示的文字,默认'博主,小伙伴,访客'</span><br><span class="line">metaPlaceholder:</span><br><span class="line"> nick: 昵称/QQ号(必填)</span><br><span class="line"> mail: 邮箱(必填)</span><br><span class="line"> link: 网址(https://)</span><br></pre></td></tr></table></figure>
<p>如图所示:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/04/27/15d1c1b458da1.jpg"/></div></div>
</li>
<li><p>在主题配置文件<code>_config.butterfly.yml</code>的CDN配置项添加如下内容。将<code>Valine.min.js</code>替换成魔改版本。</p>
</li>
</ul>
</li>
</ol>
<figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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"> CDN:</span><br><span class="line"> # comments</span><br><span class="line"> gitalk: https://fastly.jsdelivr.net/npm/gitalk@latest/dist/gitalk.min.js</span><br><span class="line"> gitalk_css: https://fastly.jsdelivr.net/npm/gitalk/dist/gitalk.min.css</span><br><span class="line"><span class="deletion">- valine: https://fastly.jsdelivr.net/npm/valine/dist/Valine.min.js</span></span><br><span class="line"> valine: https://fastly.jsdelivr.net/gh/tzy13755126023/BLOG_SOURCE/valine_f/valine.min.js</span><br></pre></td></tr></table></figure>
<h4 id="valine获取评论失败"><a href="#valine获取评论失败" class="headerlink" title="valine获取评论失败"></a>valine获取评论失败</h4><p>如下图:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/1d3723a5bf858.jpg"/></div></div>
<p>这里并没有加载和渲染 valine 评论数据 , 但在 LeanCloud 数据存储中 是存在评论数据的。</p>
<div class="note red icon-padding modern"><i class="note-icon fas fa-bullhorn card-announcement-animation"></i><p>原因 :LeanCloud 国际版通用域名失效</p>
</div>
<p>打开控制台发现<code>https://us.avoscloud.com/1.1/classes/Comment?xxxxx</code>已经请求失败(net::ERR_NAME_NOT_RESOLVED),域名也无法ping通。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/1f20328863e46.jpg"/></div></div>
<p>解决办法:</p>
<p>将 Valine 指定 自定义服务器URL。在主题配置里的 Valine 的 serverURLs 填入<code>https://xxxxxxxx.api.lncldglobal.com</code>。自定义服务器的URL需要到 LeanCloud后台 查看。打开后台之后进入 Settings - App Keys ,Request domain 里面的<code>xxxxxxxx.api.lncldglobal.com</code>就是你需要指定的服务器URL。xxxxxxxx 就是 AppID的前8位字符。</p>
<p>如下图:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/7a37bcaf7b976.jpg"/></div></div>
<p>经 postman 测试 ,<code>https://xxxxxxxx.api.lncldglobal.com/1.1/classes/Comment?x=x&xx=xx</code>该接口会正常返回评论数据。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/a3545407baae3.jpg"/></div></div>
<blockquote>
<p>关于valine评论,这里会抽空写一篇详细的教程,敬请期待。</p>
</blockquote>
<h3 id="首页分类磁铁"><a href="#首页分类磁铁" class="headerlink" title="首页分类磁铁"></a>首页分类磁铁</h3><blockquote>
<p>原教程:<a href="https://akilar.top/posts/a9131002/">Categories Magnet</a></p>
</blockquote>
<details class="folding-tag" cyan><summary> 效果预览 </summary>
<div class='content'>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/08/26/5f6c8366b5a94.jpg"/></div></div>
</div>
</details>
<details class="folding-tag" red><summary> 插件版教程(推荐👍) </summary>
<div class='content'>
<ol><li>安装依赖,前往博客根目录,打开cmd命令窗口执行如下命令:<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 hexo-butterfly-categories-card --save</span><br></pre></td></tr></table></figure></li><li>添加配置信息,在站点配置文件<code>_config.yml</code>或者主题配置文件<code>_config.butterfly.yml</code>中添加<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">categoryBar:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span> <span class="comment"># 开关</span></span><br><span class="line"> <span class="attr">priority:</span> <span class="number">5</span> <span class="comment">#过滤器优先权</span></span><br><span class="line"> <span class="attr">enable_page:</span> <span class="string">/</span> <span class="comment"># 应用页面</span></span><br><span class="line"> <span class="attr">layout:</span> <span class="comment"># 挂载容器类型</span></span><br><span class="line"> <span class="attr">type:</span> <span class="string">id</span></span><br><span class="line"> <span class="attr">name:</span> <span class="string">recent-posts</span></span><br><span class="line"> <span class="attr">index:</span> <span class="number">0</span></span><br><span class="line"> <span class="attr">column:</span> <span class="string">odd</span> <span class="comment"># odd:3列 | even:4列</span></span><br><span class="line"> <span class="attr">row:</span> <span class="number">1</span> <span class="comment">#显示行数,默认两行,超过行数切换为滚动显示</span></span><br><span class="line"> <span class="attr">message:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">descr:</span> <span class="string">Ubuntu指南</span></span><br><span class="line"> <span class="attr">cover:</span> <span class="string">https://assets.akilar.top/image/cover1.webp</span></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">descr:</span> <span class="string">玩转Win10</span></span><br><span class="line"> <span class="attr">cover:</span> <span class="string">https://assets.akilar.top/image/cover2.webp</span></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">descr:</span> <span class="string">长篇小说连载</span></span><br><span class="line"> <span class="attr">cover:</span> <span class="string">https://assets.akilar.top/image/cover3.webp</span></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">descr:</span> <span class="string">个人日记</span></span><br><span class="line"> <span class="attr">cover:</span> <span class="string">https://assets.akilar.top/image/cover4.webp</span></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">descr:</span> <span class="string">诗词歌赋</span></span><br><span class="line"> <span class="attr">cover:</span> <span class="string">https://assets.akilar.top/image/cover5.webp</span></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">descr:</span> <span class="string">杂谈教程</span></span><br><span class="line"> <span class="attr">cover:</span> <span class="string">https://assets.akilar.top/image/cover6.webp</span></span><br><span class="line"> <span class="attr">custom_css:</span> <span class="string">https://npm.elemecdn.com/hexo-butterfly-categories-card@1.0.0/lib/categorybar.css</span></span><br></pre></td></tr></table></figure></li><li>参数意义<table><thead><tr><th align="center">参数</th><th align="center">备选值/类型</th><th align="center">释义</th></tr></thead><tbody><tr><td align="center">priority</td><td align="center">number</td><td align="center">【可选】过滤器优先级,数值越小,执行越早,默认为10,选填</td></tr><tr><td align="center">enable</td><td align="center">true/false</td><td align="center">【必选】控制开关</td></tr><tr><td align="center">enable_page</td><td align="center">path/all</td><td align="center">【可选】填写想要应用的页面的相对路径(即路由地址),如根目录就填’/‘,分类页面就填’/categories/‘。若要应用于所有页面,就填’all’,默认为’/‘</td></tr><tr><td align="center">layout.type</td><td align="center">id/class</td><td align="center">【可选】挂载容器类型,填写id或class,不填则默认为id</td></tr><tr><td align="center">layout.name</td><td align="center">text</td><td align="center">【必选】挂载容器名称</td></tr><tr><td align="center">layout.index</td><td align="center">0和正整数</td><td align="center">【可选】前提是layout.type为class,因为同一页面可能有多个class,此项用来确认究竟排在第几个顺位</td></tr><tr><td align="center">column</td><td align="center">odd/even</td><td align="center">【可选】显示列数,考虑到比例问题,只提供3列和4列,odd为3列, even为4列</td></tr><tr><td align="center">row</td><td align="center">number</td><td align="center">【可选】显示行数,默认两行,超过行数切换为滚动显示</td></tr></tbody></table></li></ol><p>| message.descr | text | 分类描述,需要和你自己的文章分类一一对应。 |<br>| message.cover | url | 分类背景,需要和你自己的文章分类一一对应。 |<br>| custom_css | url | 【可选】自定义样式,会替换默认的css链接,可以下载文档给出的cdn链接后自主修改 |</p>
</div>
</details>
<details class="folding-tag" yellow><summary> 旧版教程(不推荐) </summary>
<div class='content'>
<ol><li>修改<code>BlogRoot/themes/butterfly/layout/index.pug</code><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">extends includes/layout.pug</span><br><span class="line">block content</span><br><span class="line"> include ./includes/mixins/post-ui.pug</span><br><span class="line"> #recent-posts.recent-posts</span><br><span class="line"> if theme.categoryBar.enable</span><br><span class="line"> .recent-post-item(style='height:auto;width:100%;padding:0px;')</span><br><span class="line"> #categoryBar!= list_categories(site.categories,{class: 'categoryBar',depth: 1})</span><br><span class="line"> +postUI</span><br><span class="line"> include includes/pagination.pug</span><br></pre></td></tr></table></figure></li><li>新建<code>BlogRoot/themes/butterfly/source/css/_layout/categoryBar.styl</code><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><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></pre></td><td class="code"><pre><span class="line">if hexo-config('categoryBar<span class="selector-class">.enable</span>')</span><br><span class="line"> <span class="selector-id">#categoryBar</span></span><br><span class="line"> <span class="attribute">width</span> <span class="number">100%</span>!important</span><br><span class="line"> <span class="selector-tag">ul</span></span><br><span class="line"> &<span class="selector-class">.categoryBar-list</span></span><br><span class="line"> <span class="attribute">margin</span> <span class="number">5px</span> <span class="number">5px</span> <span class="number">0</span> <span class="number">5px</span>!important</span><br><span class="line"> <span class="attribute">padding</span> <span class="number">0</span>!important</span><br><span class="line"></span><br><span class="line"> <span class="selector-tag">li</span></span><br><span class="line"> &<span class="selector-class">.categoryBar-list-item</span></span><br><span class="line"> <span class="attribute">font-weight</span> bold</span><br><span class="line"> <span class="attribute">display</span> inline-block</span><br><span class="line"> <span class="attribute">height</span> <span class="number">180px</span>!important</span><br><span class="line"> <span class="attribute">margin</span> <span class="number">5px</span> .<span class="number">5%</span> <span class="number">0</span> .<span class="number">5%</span>!important</span><br><span class="line"> <span class="attribute">background-image</span> linear-gradient(rgba(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0.4</span>) <span class="number">25%</span>, rgba(<span class="number">16</span>, <span class="number">16</span>, <span class="number">16</span>, <span class="number">0</span>) <span class="number">100%</span>)</span><br><span class="line"> <span class="attribute">border-radius</span> <span class="number">10px</span></span><br><span class="line"> <span class="attribute">padding</span> <span class="number">25px</span> <span class="number">0</span> <span class="number">25px</span> <span class="number">25px</span>!important</span><br><span class="line"> <span class="attribute">box-shadow</span> rgba(<span class="number">50</span>, <span class="number">50</span>, <span class="number">50</span>, <span class="number">0.3</span>) <span class="number">50px</span> <span class="number">50px</span> <span class="number">50px</span> <span class="number">50px</span> inset</span><br><span class="line"> <span class="attribute">overflow</span> hidden</span><br><span class="line"> <span class="attribute">background-size</span> <span class="number">100%</span>!important</span><br><span class="line"> <span class="attribute">background-position</span> center!important</span><br><span class="line"> &<span class="selector-pseudo">:hover</span></span><br><span class="line"> <span class="attribute">background-size</span> <span class="number">110%</span>!important</span><br><span class="line"> <span class="attribute">box-shadow</span> inset <span class="number">500px</span> <span class="number">50px</span> <span class="number">50px</span> <span class="number">50px</span> rgba(<span class="number">50</span>,<span class="number">50</span>,<span class="number">50</span>, <span class="number">0.6</span>)</span><br><span class="line"> <span class="selector-tag">span</span></span><br><span class="line"> &<span class="selector-class">.categoryBar-list-count</span></span><br><span class="line"> &<span class="selector-pseudo">::after</span></span><br><span class="line"> <span class="attribute">transition</span> <span class="attribute">all</span> .<span class="number">5s</span></span><br><span class="line"> <span class="attribute">transform</span> translate(-<span class="number">100%</span>, <span class="number">0</span>)</span><br><span class="line"> <span class="selector-tag">a</span></span><br><span class="line"> &<span class="selector-class">.categoryBar-list-link</span></span><br><span class="line"> <span class="attribute">color</span> white!important</span><br><span class="line"> <span class="attribute">font-size</span> <span class="number">20px</span>!important</span><br><span class="line"> &<span class="selector-pseudo">::before</span></span><br><span class="line"> <span class="attribute">content</span> '|'!important</span><br><span class="line"> <span class="attribute">color</span> white!important</span><br><span class="line"> <span class="attribute">font-size</span> <span class="number">20px</span>!important</span><br><span class="line"> &<span class="selector-pseudo">:after</span></span><br><span class="line"> <span class="attribute">content</span> ''</span><br><span class="line"> <span class="attribute">position</span> relative</span><br><span class="line"> <span class="attribute">width</span> <span class="number">0</span></span><br><span class="line"> <span class="attribute">bottom</span> <span class="number">0</span></span><br><span class="line"> <span class="attribute">display</span> block</span><br><span class="line"> <span class="attribute">height</span> <span class="number">3px</span></span><br><span class="line"> <span class="attribute">border-radius</span> <span class="number">3px</span></span><br><span class="line"> <span class="attribute">background-color</span> white</span><br><span class="line"> &<span class="selector-pseudo">:hover</span></span><br><span class="line"> &<span class="selector-pseudo">:after</span></span><br><span class="line"> <span class="attribute">width</span> <span class="number">90%</span></span><br><span class="line"> <span class="attribute">left</span> <span class="number">1%</span></span><br><span class="line"> <span class="attribute">transition</span> <span class="attribute">all</span> <span class="number">0.5s</span></span><br><span class="line"></span><br><span class="line"> <span class="selector-tag">span</span></span><br><span class="line"> &<span class="selector-class">.categoryBar-list-count</span></span><br><span class="line"> <span class="attribute">display</span> block!important</span><br><span class="line"> <span class="attribute">color</span> white!important</span><br><span class="line"> <span class="attribute">font-size</span> <span class="number">20px</span>!important</span><br><span class="line"> &<span class="selector-pseudo">::before</span></span><br><span class="line"> <span class="attribute">content</span> '\f02d'!important</span><br><span class="line"> <span class="attribute">padding-right</span> <span class="number">15px</span>!important</span><br><span class="line"> <span class="keyword">@extend</span> .fontawesomeIcon</span><br><span class="line"> &::after</span><br><span class="line"> padding <span class="number">5px</span></span><br><span class="line"> display block!important</span><br><span class="line"> <span class="attribute">color</span> white!important</span><br><span class="line"> font-size <span class="number">20px</span>!important</span><br><span class="line"> position relative</span><br><span class="line"> right -<span class="number">100%</span></span><br><span class="line"> covers = hexo-config(<span class="string">'categoryBar.cover'</span>)</span><br><span class="line"> for cover,i in covers</span><br><span class="line"> li.<span class="attribute">categoryBar-list-item</span>:nth-child({<span class="selector-tag">i</span>+<span class="number">1</span>})</span><br><span class="line"> <span class="attribute">background</span> unquote(cover)</span><br><span class="line"> descrs = hexo-config('categoryBar<span class="selector-class">.descr</span>')</span><br><span class="line"> for descr,<span class="selector-tag">i</span> in descrs</span><br><span class="line"> <span class="selector-tag">li</span><span class="selector-class">.categoryBar-list-item</span><span class="selector-pseudo">:nth-child</span>({<span class="selector-tag">i</span>+<span class="number">1</span>})><span class="selector-tag">span</span><span class="selector-pseudo">::after</span></span><br><span class="line"> <span class="attribute">content</span> descr!important</span><br><span class="line"> if hexo-config('categoryBar<span class="selector-class">.column</span>') == 'odd'</span><br><span class="line"> <span class="selector-tag">li</span></span><br><span class="line"> &<span class="selector-class">.categoryBar-list-item</span></span><br><span class="line"> <span class="attribute">width</span> <span class="number">32.3%</span>!important</span><br><span class="line"> else if hexo-config('categoryBar<span class="selector-class">.column</span>') == 'even'</span><br><span class="line"> <span class="selector-tag">li</span></span><br><span class="line"> &<span class="selector-class">.categoryBar-list-item</span></span><br><span class="line"> <span class="attribute">width</span> <span class="number">24%</span>!important</span><br><span class="line"> <span class="keyword">@media</span> screen <span class="keyword">and</span> (<span class="attribute">max-width</span>: <span class="number">650px</span>)</span><br><span class="line"> li</span><br><span class="line"> &.categoryBar-list-item</span><br><span class="line"> <span class="attribute">width</span> <span class="number">48%</span>!important</span><br><span class="line"> <span class="attribute">height</span> <span class="number">150px</span>!important</span><br><span class="line"> margin <span class="number">5px</span> <span class="number">1%</span> <span class="number">0</span> <span class="number">1%</span>!important</span><br><span class="line"></span><br><span class="line"> $caterow = hexo-config(<span class="string">'categoryBar.row'</span>)?hexo-config(<span class="string">'categoryBar.row'</span>):<span class="number">2</span></span><br><span class="line"> .categoryBar-list</span><br><span class="line"> <span class="attribute">max-height</span> <span class="number">190px</span> * $caterow</span><br><span class="line"> overflow auto</span><br><span class="line"> &::-webkit-scrollbar</span><br><span class="line"> <span class="attribute">width</span> <span class="number">0</span>!important</span><br><span class="line"> @media screen <span class="keyword">and</span> (<span class="attribute">max-width</span>: <span class="number">650px</span>)</span><br><span class="line"> .categoryBar-list</span><br><span class="line"> <span class="attribute">max-height</span> <span class="number">160px</span> * $caterow</span><br></pre></td></tr></table></figure></li><li>在主题配置文件<code>_config.butterfly.yml</code>中添加配置项:<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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="attr">categoryBar:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">column:</span> <span class="string">odd</span> <span class="comment"># 显示列数,odd:3列 | even:4列</span></span><br><span class="line"> <span class="attr">row:</span> <span class="number">2</span> <span class="comment">#显示行数,默认两行,超过行数切换为滚动显示</span></span><br><span class="line"> <span class="attr">descr:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">Good</span> <span class="string">things</span> <span class="string">to</span> <span class="string">share</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">Back</span> <span class="string">end</span> <span class="string">talk</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">personal</span> <span class="string">diary</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">Front</span> <span class="string">end</span> <span class="string">serialization</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">Butterfly</span> <span class="string">theme</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">Other</span> <span class="string">items</span></span><br><span class="line"> <span class="attr">cover:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">url('https://fastly.jsdelivr.net/npm/akilar-candyassets/image/cover1.webp')</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">'#abcdef'</span> <span class="comment"># HEX格式色值需要用''包裹,不然会被识别成注释</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">rgba(45,67,89,0.7)</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">linear-gradient(rgba(0,</span> <span class="number">0</span><span class="string">,</span> <span class="number">0</span><span class="string">,</span> <span class="number">0.4</span><span class="string">)</span> <span class="number">25</span><span class="string">%,</span> <span class="string">rgba(200,16</span> <span class="string">,</span> <span class="number">16</span><span class="string">,</span> <span class="number">0</span><span class="string">)</span> <span class="number">100</span><span class="string">%)</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">url('https://fastly.jsdelivr.net/npm/akilar-candyassets/image/cover5.webp')</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">url('https://fastly.jsdelivr.net/npm/akilar-candyassets/image/cover6.webp')</span></span><br></pre></td></tr></table></figure></li></ol>
</div>
</details>
<h3 id="侧边栏电子时钟"><a href="#侧边栏电子时钟" class="headerlink" title="侧边栏电子时钟"></a>侧边栏电子时钟</h3><details class="folding-tag" cyan><summary> 效果预览 </summary>
<div class='content'>
<div class="tabs" id="text-"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#text--1">新版效果</button></li><li class="tab"><button type="button" data-href="#text--2">旧版效果</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="text--1"><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/12/636fa453ebfa1.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="text--2"><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/08/26/84a75c812010f.jpg"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div>
</div>
</details>
<details class="folding-tag" red><summary> 查看步骤 (新版,推荐👍) </summary>
<div class='content'>
<p>✨可参考教程✨:<a href="https://anzhiy.cn/posts/fc18.html">给butterfly添加侧边栏电子钟</a></p><ol><li>如果跑过下面旧版的教程,前往博客根目录,卸载原版电子钟插件,打开cmd命令窗口执行如下命令:<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 uninstall hexo-butterfly-clock</span><br></pre></td></tr></table></figure></li><li>安装新的插件,前往博客根目录,打开cmd命令窗口执行如下命令:<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install hexo-butterfly-clock-anzhiyu --save</span><br></pre></td></tr></table></figure></li><li>在站点配置文件_config.yml 或 主题配置文件_config.butterfly.yml 中添加配置信息<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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="attr">electric_clock:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span> <span class="comment"># 开关</span></span><br><span class="line"> <span class="attr">priority:</span> <span class="number">5</span> <span class="comment">#过滤器优先权</span></span><br><span class="line"> <span class="attr">enable_page:</span> <span class="string">all</span> <span class="comment"># 应用页面</span></span><br><span class="line"> <span class="attr">exclude:</span></span><br><span class="line"> <span class="comment"># - /posts/</span></span><br><span class="line"> <span class="comment"># - /about/</span></span><br><span class="line"> <span class="attr">layout:</span> <span class="comment"># 挂载容器类型</span></span><br><span class="line"> <span class="attr">type:</span> <span class="string">class</span></span><br><span class="line"> <span class="attr">name:</span> <span class="string">sticky_layout</span></span><br><span class="line"> <span class="attr">index:</span> <span class="number">0</span></span><br><span class="line"> <span class="attr">loading:</span> <span class="string">https://cdn.cbd.int/hexo-butterfly-clock-anzhiyu/lib/loading.gif</span> <span class="comment">#加载动画自定义</span></span><br><span class="line"> <span class="attr">clock_css:</span> <span class="string">https://cdn.cbd.int/hexo-butterfly-clock-anzhiyu/lib/clock.min.css</span></span><br><span class="line"> <span class="attr">clock_js:</span> <span class="string">https://cdn.cbd.int/hexo-butterfly-clock-anzhiyu/lib/clock.min.js</span></span><br><span class="line"> <span class="attr">ip_api:</span> <span class="string">https://widget.qweather.net/simple/static/js/he-simple-common.js?v=2.0</span></span><br><span class="line"> <span class="comment"># 和风天气key 默认是 b16a1fa0e63c46a4b8f28abfb06ae3fe</span></span><br><span class="line"> <span class="attr">qweather_key:</span> <span class="comment"># 你的key</span></span><br><span class="line"> <span class="comment"># 高得地图web服务key 默认为 e2b04289e870b005374ee030148d64fd&s=rsv3</span></span><br><span class="line"> <span class="attr">gaud_map_key:</span> <span class="comment"># 你的key</span></span><br><span class="line"> <span class="attr">default_rectangle:</span> <span class="literal">false</span> <span class="comment"># 开启后将一直显示rectangle位置的天气,否则将获取访问者的地理位置与天气</span></span><br><span class="line"> <span class="attr">rectangle:</span> <span class="number">112.982279</span><span class="string">,28.19409</span> <span class="comment"># 获取访问者位置失败时会显示该位置的天气,同时该位置为开启default_rectangle后的位置</span></span><br></pre></td></tr></table></figure></li><li>具体参数含义<details class="folding-tag" yellow><summary> 点击查看具体参数 </summary> <div class='content'> <table><thead><tr><th align="center">参数</th><th align="center">备选值/类型</th><th align="center">释义</th></tr></thead><tbody><tr><td align="center">priority</td><td align="center">number</td><td align="center">【可选】过滤器优先级,数值越小,执行越早,默认为 10,选填</td></tr><tr><td align="center">enable</td><td align="center">true/false</td><td align="center">【必选】控制开关</td></tr><tr><td align="center">enable_page</td><td align="center">path/all</td><td align="center">【可选】填写想要应用的页面的相对路径(即路由地址),如根目录就填’/‘,分类页面就填’/categories/‘。若要应用于所有页面,就填’all’,默认为 all</td></tr><tr><td align="center">exclude</td><td align="center">path</td><td align="center">【可选】填写想要屏蔽的页面,可以多个。写法见示例。原理是将屏蔽项的内容逐个放到当前路径去匹配,若当前路径包含任一屏蔽项,则不会挂载。</td></tr><tr><td align="center">layout.type</td><td align="center">id/class</td><td align="center">【可选】挂载容器类型,填写 id 或 class,不填则默认为 id</td></tr><tr><td align="center">layout.name</td><td align="center">text</td><td align="center">【必选】挂载容器名称</td></tr><tr><td align="center">layout.index</td><td align="center">0 和正整数</td><td align="center">【可选】前提是 layout.type 为 class,因为同一页面可能有多个 class,此项用来确认究竟排在第几个顺位</td></tr><tr><td align="center">loading</td><td align="center">URL</td><td align="center">【可选】电子钟加载动画的图片</td></tr><tr><td align="center">clock_css</td><td align="center">URL</td><td align="center">【可选】电子钟样式 CDN 资源</td></tr><tr><td align="center">clock_js</td><td align="center">URL</td><td align="center">【可选】电子钟执行脚本 CDN 资源</td></tr><tr><td align="center">ip_api</td><td align="center">URL</td><td align="center">【可选】获取时钟 IP 的 API</td></tr><tr><td align="center">qweather_key</td><td align="center">text</td><td align="center">【可选】和风天气 key</td></tr><tr><td align="center">gaud_map_key</td><td align="center">text</td><td align="center">【可选】高得地图 web 服务 key</td></tr><tr><td align="center">default_rectangle</td><td align="center">text</td><td align="center">【可选】开启后将一直显示 rectangle 位置的天气,否则将获取访问者的地理位置与天气</td></tr><tr><td align="center">rectangle</td><td align="center">text</td><td align="center">【可选】获取访问者位置失败时会显示该位置的天气,同时该位置为开启 default_rectangle 后的位置</td></tr></tbody></table> </div> </details></li></ol><div class="note red icon-padding modern"><i class="note-icon fas fa-bullhorn"></i><p>【qweather_key】和【gaud_map_key】建议自己申请。</p></div><details class="folding-tag" cyan><summary> qweather_key 申请流程 </summary> <div class='content'> <ol><li><a href="https://id.qweather.com/#/login">qweather_key 传送门</a></li><li>登录后进入控制台(可能要绑定邮箱和手机)<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/12/636fa9d74658e.png"/></div></div></li><li>创建项目,项目名称(名字随意)、勾选【免费订阅】、适用平台【 Web API】、KEY的名称(随意填),最后点击【创建】<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/12/636fad5a00726.png"/></div></div></li><li>可在项目管理中,到对应的【key】<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/12/636fb12960217.png"/></div></div></li></ol> </div> </details><details class="folding-tag" cyan><summary> gaud_map_key 申请流程 </summary> <div class='content'> <ol><li><a href="https://lbs.amap.com/">gaud_map_key 传送门</a></li><li>登录后完成一些认证(个人开发),进入控制台</li><li>创建应用,应用名称随意,类型选其他<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/12/636fae8a47c02.png"/></div></div></li><li>点击【添加】,【key名称】随便填,【服务平台】选择 Web服务,点击提交,就得到【key】了<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/12/636faf588e615.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/12/636faf7daca3d.png"/></div></div></li></ol> </div> </details>
</div>
</details>
<details class="folding-tag" yellow><summary> 查看步骤(旧版,不建议) </summary>
<div class='content'>
<p>✨参考教程✨: <a href="https://akilar.top/posts/4e39cf4a/">Sidebar Card Clock</a></p><ol><li>安装依赖,前往博客根目录,打开cmd命令窗口执行如下命令:<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 hexo-butterfly-clock --save </span><br></pre></td></tr></table></figure></li><li>在站点配置文件<code>_config.yml</code>添加配置项:<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># electric_clock</span></span><br><span class="line"><span class="attr">electric_clock:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span> <span class="comment"># 开关</span></span><br><span class="line"> <span class="attr">priority:</span> <span class="number">5</span> <span class="comment">#过滤器优先权</span></span><br><span class="line"> <span class="attr">enable_page:</span> <span class="string">all</span> <span class="comment"># 应用页面</span></span><br><span class="line"> <span class="attr">exclude:</span></span><br><span class="line"> <span class="comment"># - /posts/</span></span><br><span class="line"> <span class="comment"># - /about/</span></span><br><span class="line"> <span class="attr">layout:</span> <span class="comment"># 挂载容器类型</span></span><br><span class="line"> <span class="attr">type:</span> <span class="string">class</span></span><br><span class="line"> <span class="attr">name:</span> <span class="string">sticky_layout</span></span><br><span class="line"> <span class="attr">index:</span> <span class="number">0</span></span><br><span class="line"> <span class="attr">loading:</span> <span class="string">https://fastly.jsdelivr.net/gh/tzy13755126023/BLOG_SOURCE/theme_f/loading.gif</span> <span class="comment">#加载动画自定义 </span></span><br></pre></td></tr></table></figure></li></ol>
</div>
</details>
<h3 id="更换字体"><a href="#更换字体" class="headerlink" title="更换字体"></a>更换字体</h3><details class="folding-tag" cyan><summary> 查看步骤 </summary>
<div class='content'>
<p>添加下面这段css即可。</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">@font-face</span> {</span><br><span class="line"> <span class="attribute">font-family</span>: <span class="string">'tzy'</span>;</span><br><span class="line"> <span class="comment">/* 字体名自定义即可 */</span></span><br><span class="line"> <span class="comment">/* src: url('https://cdn.jsdelivr.net/gh/tzy13755126023/BLOG_SOURCE/font/ZhuZiAWan.woff2'); */</span></span><br><span class="line"> <span class="attribute">src</span>: <span class="built_in">url</span>(<span class="string">'https://npm.elemecdn.com/ethan4116-blog/lib/font/ZhuZiAWan.ttf'</span>);</span><br><span class="line"> <span class="comment">/* 字体文件路径 */</span></span><br><span class="line"> <span class="attribute">font-display</span>: swap;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-tag">body</span>,</span><br><span class="line"><span class="selector-class">.gitcalendar</span> {</span><br><span class="line"> <span class="attribute">font-family</span>: tzy <span class="meta">!important</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</div>
</details>
<h3 id="局部css优化"><a href="#局部css优化" class="headerlink" title="局部css优化"></a>局部css优化</h3><details class="folding-tag" cyan><summary> 查看代码 </summary>
<div class='content'>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="selector-class">.categoryBar-list</span> {</span><br><span class="line"> <span class="attribute">max-height</span>: <span class="number">400px</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.clock-row</span> {</span><br><span class="line"> <span class="attribute">overflow</span>: hidden;</span><br><span class="line"> <span class="attribute">text-overflow</span>: ellipsis;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*3s为加载动画的时间,1为加载动画的次数,ease-in-out为动画效果*/</span></span><br><span class="line"></span><br><span class="line"><span class="selector-id">#page-header</span>,</span><br><span class="line"><span class="selector-id">#web_bg</span> {</span><br><span class="line"> -webkit-<span class="attribute">animation</span>: imgblur <span class="number">2s</span> <span class="number">1</span> ease-in-out;</span><br><span class="line"> <span class="attribute">animation</span>: imgblur <span class="number">2s</span> <span class="number">1</span> ease-in-out;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">@keyframes</span> imgblur {</span><br><span class="line"> <span class="number">0%</span> {</span><br><span class="line"> <span class="attribute">filter</span>: <span class="built_in">blur</span>(<span class="number">5px</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="number">100%</span> {</span><br><span class="line"> <span class="attribute">filter</span>: <span class="built_in">blur</span>(<span class="number">0px</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*适配使用-webkit内核的浏览器 */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">@-webkit-keyframes</span> imgblur {</span><br><span class="line"> <span class="number">0%</span> {</span><br><span class="line"> -webkit-<span class="attribute">filter</span>: <span class="built_in">blur</span>(<span class="number">5px</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="number">100%</span> {</span><br><span class="line"> -webkit-<span class="attribute">filter</span>: <span class="built_in">blur</span>(<span class="number">0px</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.table-wrap</span> <span class="selector-tag">img</span> {</span><br><span class="line"> <span class="attribute">margin</span>: .<span class="number">6rem</span> auto .<span class="number">1rem</span> <span class="meta">!important</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 标签外挂 网站卡片 start */</span></span><br><span class="line"></span><br><span class="line"><span class="selector-class">.site-card-group</span> <span class="selector-tag">img</span> {</span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">0</span> auto .<span class="number">1rem</span> <span class="meta">!important</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.site-card-group</span> <span class="selector-class">.info</span> <span class="selector-tag">a</span> <span class="selector-tag">img</span> {</span><br><span class="line"> <span class="attribute">margin-right</span>: <span class="number">10px</span> <span class="meta">!important</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-attr">[data-theme=<span class="string">'dark'</span>]</span> <span class="selector-class">.site-card-group</span> <span class="selector-class">.site-card</span> <span class="selector-class">.info</span> <span class="selector-class">.title</span> {</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#f0f0f0</span> <span class="meta">!important</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-attr">[data-theme=<span class="string">'dark'</span>]</span> <span class="selector-class">.site-card-group</span> <span class="selector-class">.site-card</span> <span class="selector-class">.info</span> <span class="selector-class">.desc</span> {</span><br><span class="line"> <span class="attribute">color</span>: <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, .<span class="number">7</span>) <span class="meta">!important</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.site-card-group</span> <span class="selector-class">.info</span> <span class="selector-class">.desc</span> {</span><br><span class="line"> <span class="attribute">margin-top</span>: <span class="number">4px</span> <span class="meta">!important</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="selector-tag">figure</span><span class="selector-class">.highlight</span> pre <span class="selector-class">.addition</span> {</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#00bf03</span> <span class="meta">!important</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</div>
</details>
<h3 id="禁止右键及F12等事件"><a href="#禁止右键及F12等事件" class="headerlink" title="禁止右键及F12等事件"></a>禁止右键及F12等事件</h3><details class="folding-tag" cyan><summary> 效果预览 </summary>
<div class='content'>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/e9a2228cf4291.jpg"/></div></div>
</div>
</details>
<details class="folding-tag" red><summary> 查看步骤 (新版,推荐👍) </summary>
<div class='content'>
<p><del>旧版教程(已弃用)</del>,对于高版本浏览器,可以先打开控制台再进入站点(亲测 Google Chrome v100),因此本站已弃用<code>forbidden_control()</code>,可直接删除。引入的 <code>function.min.css</code> 也可以删除。</p><p>下面开始新的教程,如果你不需要 <del>禁用控制台</del> 以及 <del>禁用一些特殊 keyCode 事件</del> ,可直接忽略。</p><ol><li>修改【BlogRoot/themes/butterfly/layout/includes/layout.pug】,根据图中位置添加以下 pug 代码(跟 <code>head</code>、<code>body</code>同级)。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/10/636d109016294.jpg"/></div></div><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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">script.</span><br><span class="line"> ((function() {var callbacks = [],timeLimit = 50,open = false;setInterval(loop, 1);return {addListener: function(fn) {callbacks.push(fn);},cancleListenr: function(fn) {callbacks = callbacks.filter(function(v) {return v !== fn;});}}</span><br><span class="line"> function loop() {var startTime = new Date();debugger;if (new Date() - startTime > timeLimit) {if (!open) {callbacks.forEach(function(fn) {fn.call(null);});}open = true;window.stop();alert('你真坏,请关闭控制台!');document.body.innerHTML = "";} else {open = false;}}})()).addListener(function() {window.location.reload();});</span><br><span class="line">script.</span><br><span class="line"> function toDevtools(){</span><br><span class="line"> let num = 0; </span><br><span class="line"> let devtools = new Date();</span><br><span class="line"> devtools.toString = function() {</span><br><span class="line"> num++;</span><br><span class="line"> if (num > 1) {</span><br><span class="line"> alert('你真坏,请关闭控制台!')</span><br><span class="line"> window.location.href = "about:blank"</span><br><span class="line"> blast();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> console.log('', devtools);</span><br><span class="line"> }</span><br><span class="line"> toDevtools();</span><br></pre></td></tr></table></figure></li><li>将以下代码复制到自定义的<code>custom.js</code><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="variable language_">document</span>.<span class="property">onkeydown</span> = <span class="keyword">function</span> (<span class="params">e</span>) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="number">123</span> == e.<span class="property">keyCode</span> || (e.<span class="property">ctrlKey</span> && e.<span class="property">shiftKey</span> && (<span class="number">74</span> === e.<span class="property">keyCode</span> || <span class="number">73</span> === e.<span class="property">keyCode</span> || <span class="number">67</span> === e.<span class="property">keyCode</span>)) || (e.<span class="property">ctrlKey</span> && <span class="number">85</span> === e.<span class="property">keyCode</span>)) <span class="keyword">return</span> btf.<span class="title function_">snackbarShow</span>(<span class="string">"你真坏,不能打开控制台喔!"</span>), event.<span class="property">keyCode</span> = <span class="number">0</span>, event.<span class="property">returnValue</span> = !<span class="number">1</span>, !<span class="number">1</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure></li><li>重新编译运行,即可看到效果。</li><li>注意: 如果自己调试阶段,可注释第一步和第二步中的代码,再进行编译,就可以打开控制台了。部署时放开注释,编译好再丢上去就OK了。</li></ol>
</div>
</details>
<details class="folding-tag" yellow><summary> 查看步骤 (旧版,已弃用) </summary>
<div class='content'>
<ol><li><p>在 主题配置 的 inject 的 head 里 引入 这个 css。</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string"><link</span> <span class="string">rel="stylesheet"</span> <span class="string">href="https://fastly.jsdelivr.net/gh/tzy13755126023/BLOG_SOURCE/css/function.min.css"></span></span><br></pre></td></tr></table></figure></li><li><p>将以下代码复制到自定义的<code>custom.js</code>, 执行 forbidden_control() 执行即可看到效果。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">forbidden_control</span>(<span class="params"></span>) {</span><br><span class="line"> $.<span class="title function_">extend</span>({</span><br><span class="line"> <span class="attr">message</span>: <span class="keyword">function</span>(<span class="params">a</span>) {</span><br><span class="line"> <span class="keyword">var</span> b = {</span><br><span class="line"> <span class="attr">title</span>: <span class="string">""</span>,</span><br><span class="line"> <span class="attr">message</span>: <span class="string">"操作成功"</span>,</span><br><span class="line"> <span class="attr">time</span>: <span class="string">"3000"</span>,</span><br><span class="line"> <span class="attr">type</span>: <span class="string">"success"</span>,</span><br><span class="line"> <span class="attr">showClose</span>: !<span class="number">0</span>,</span><br><span class="line"> <span class="attr">autoClose</span>: !<span class="number">0</span>,</span><br><span class="line"> <span class="attr">onClose</span>: <span class="keyword">function</span>(<span class="params"></span>) {}</span><br><span class="line"> };</span><br><span class="line"> <span class="string">"string"</span> == <span class="keyword">typeof</span> a && (b.<span class="property">message</span> = a), <span class="string">"object"</span> == <span class="keyword">typeof</span> a && (b = $.<span class="title function_">extend</span>({}, b, a));</span><br><span class="line"> <span class="keyword">var</span> c, d, e, f = b.<span class="property">showClose</span> ? <span class="string">'<div class="c-message--close">×</div>'</span> : <span class="string">""</span>,</span><br><span class="line"> g = <span class="string">""</span> !== b.<span class="property">title</span> ? <span class="string">'<h2 class="c-message__title">'</span> b.<span class="property">title</span> <span class="string">"</h2>"</span> : <span class="string">""</span>,</span><br><span class="line"> h = <span class="string">'<div class="c-message animated animated-lento slideInRight"><i class=" c-message--icon c-message--'</span> b.<span class="property">type</span> <span class="string">'"></i><div class="el-notification__group">'</span> g <span class="string">'<div class="el-notification__content">'</span> b.<span class="property">message</span> <span class="string">"</div>"</span> f <span class="string">"</div></div>"</span>,</span><br><span class="line"> i = $(<span class="string">"body"</span>),</span><br><span class="line"> j = $(h);</span><br><span class="line"> d = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> j.<span class="title function_">addClass</span>(<span class="string">"slideOutRight"</span>), j.<span class="title function_">one</span>(<span class="string">"webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend"</span>, <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="title function_">e</span>()</span><br><span class="line"> })</span><br><span class="line"> }, e = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> j.<span class="title function_">remove</span>(), b.<span class="title function_">onClose</span>(b), <span class="built_in">clearTimeout</span>(c)</span><br><span class="line"> }, $(<span class="string">".c-message"</span>).<span class="title function_">remove</span>(), i.<span class="title function_">append</span>(j), j.<span class="title function_">one</span>(<span class="string">"webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend"</span>, <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> j.<span class="title function_">removeClass</span>(<span class="string">"messageFadeInDown"</span>)</span><br><span class="line"> }), i.<span class="title function_">on</span>(<span class="string">"click"</span>, <span class="string">".c-message--close"</span>, <span class="keyword">function</span>(<span class="params">a</span>) {</span><br><span class="line"> <span class="title function_">d</span>()</span><br><span class="line"> }), b.<span class="property">autoClose</span> && (c = <span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="title function_">d</span>()</span><br><span class="line"> }, b.<span class="property">time</span>))</span><br><span class="line"> }</span><br><span class="line"> }),</span><br><span class="line"> <span class="variable language_">document</span>.<span class="property">onkeydown</span> = <span class="keyword">function</span>(<span class="params">e</span>) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="number">123</span> == e.<span class="property">keyCode</span> || e.<span class="property">ctrlKey</span> && e.<span class="property">shiftKey</span> && (<span class="number">74</span> === e.<span class="property">keyCode</span> || <span class="number">73</span> === e.<span class="property">keyCode</span> || <span class="number">67</span> === e.<span class="property">keyCode</span>) || (e.<span class="property">ctrlKey</span> && <span class="number">85</span> === e.<span class="property">keyCode</span>)) <span class="keyword">return</span> $.<span class="title function_">message</span>({</span><br><span class="line"> <span class="attr">message</span>: <span class="string">"采用本站js及css请注明来源,禁止商业使用!"</span>,</span><br><span class="line"> <span class="attr">title</span>: <span class="string">"你真坏,不能打开控制台喔!"</span>,</span><br><span class="line"> <span class="attr">type</span>: <span class="string">"error"</span>,</span><br><span class="line"> <span class="attr">autoHide</span>: !<span class="number">1</span>,</span><br><span class="line"> <span class="attr">time</span>: <span class="string">"3000"</span></span><br><span class="line"> }), event.<span class="property">keyCode</span> = <span class="number">0</span>, event.<span class="property">returnValue</span> = !<span class="number">1</span>, !<span class="number">1</span></span><br><span class="line"> }, <span class="variable language_">document</span>.<span class="property">oncontextmenu</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> $.<span class="title function_">message</span>({</span><br><span class="line"> <span class="attr">message</span>: <span class="string">"采用本站js及css请注明来源,禁止商业使用!"</span>,</span><br><span class="line"> <span class="attr">title</span>: <span class="string">"不能右键/长按喔!"</span>,</span><br><span class="line"> <span class="attr">type</span>: <span class="string">"error"</span>,</span><br><span class="line"> <span class="attr">autoHide</span>: !<span class="number">1</span>,</span><br><span class="line"> <span class="attr">time</span>: <span class="string">"3000"</span></span><br><span class="line"> }), !<span class="number">1</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ol>
</div>
</details>
<h3 id="部分动效说明"><a href="#部分动效说明" class="headerlink" title="部分动效说明"></a>部分动效说明</h3><details class="folding-tag" yellow><summary> 查看步骤 </summary>
<div class='content'>
<p>请移步此博文 : <a href="https://fe32.top/articles/0xiipgum/#%E7%89%B9%E6%95%88%E6%A0%87%E7%AD%BE-wow">特效标签 wow.js</a></p>
</div>
</details>
<h3 id="部分页面插入视频"><a href="#部分页面插入视频" class="headerlink" title="部分页面插入视频"></a>部分页面插入视频</h3><details class="folding-tag" cyan><summary> 效果预览 </summary>
<div class='content'>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/735570394b43e.jpg"/></div></div><p>实际视频效果请移步: <a href="https://old.fe32.top/comments/">留言板</a> 、 <a href="https://old.fe32.top/link/">友链</a></p>
</div>
</details>
<details class="folding-tag" yellow><summary> 查看步骤 </summary>
<div class='content'>
<div class="btns rounded grid5"> <a class="button" href='https://wwe.lanzoui.com/iNoJiww30li' title='下载文件'><i class='fas fa-download'></i>下载文件</a> </div><ol><li>替换文件或修改文件。<br>如果你所使用的主题版本是 3.7.1 ,可直接下载文件,将<code>BlogRoot/node_modules/hexo-theme-butterfly/source/css/_layout/head.styl</code>替换成新下载的<code>head.styl</code>,将<code>BlogRoot/node_modules/hexo-theme-butterfly/layout/includes/header/index.pug</code>替换成新下载的<code>index.pug</code>。<br>如果你所使用的主题版本跟本站(v3.7.1)有所出入,请对比一下两个文件(下载的文件和你的主题文件)之间的差异,可能需要你对<code>BlogRoot/themes/butterfly/source/css/_layout/head.styl</code>该文件做出一些修改:<figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">&<span class="selector-class">.not-home-page</span></span><br><span class="line"> <span class="attribute">height</span>: <span class="number">20rem</span></span><br><span class="line"></span><br><span class="line"> +<span class="built_in">maxWidth768</span>()</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">14rem</span> <span class="meta">!important</span></span><br><span class="line"></span><br><span class="line"><span class="selector-id">#page-site-info</span></span><br><span class="line"> <span class="attribute">position</span>: absolute</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">10rem</span></span><br><span class="line"> <span class="attribute">padding</span>: <span class="number">0</span> .<span class="number">5rem</span></span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100%</span></span><br><span class="line"> <span class="attribute">z-index</span>: <span class="number">2</span></span><br><span class="line"></span><br><span class="line"> +<span class="built_in">maxWidth768</span>()</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">7rem</span> <span class="meta">!important</span></span><br><span class="line"></span><br><span class="line"><span class="selector-id">#post-info</span></span><br><span class="line"> <span class="attribute">position</span>: absolute</span><br><span class="line"> ... ...</span><br><span class="line"></span><br><span class="line">&<span class="selector-class">.has-video</span></span><br><span class="line"> <span class="attribute">position</span>: relative </span><br><span class="line"> <span class="attribute">height</span>: <span class="number">80vh</span> <span class="meta">!important</span></span><br><span class="line"> </span><br><span class="line"> <span class="selector-id">#page-site-info</span></span><br><span class="line"> <span class="attribute">top</span>: <span class="number">50%</span> <span class="meta">!important</span></span><br><span class="line"> <span class="attribute">margin-top</span>: -<span class="number">1.425em</span></span><br><span class="line"></span><br><span class="line"> +<span class="built_in">maxWidth768</span>()</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">7rem</span> <span class="meta">!important</span></span><br><span class="line"> <span class="attribute">margin-top</span>: <span class="number">0</span></span><br><span class="line"> </span><br><span class="line"> +<span class="built_in">maxWidth768</span>()</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">14rem</span> <span class="meta">!important</span></span><br><span class="line"> </span><br><span class="line">&<span class="selector-class">.not-top-img</span></span><br><span class="line"> <span class="attribute">margin-bottom</span>: .<span class="number">5rem</span></span><br><span class="line"> ... ...</span><br></pre></td></tr></table></figure>主要针对<code>not-home-page</code>、<code>#page-site-info</code>修改一些属性,并新增了一个名为<code>has-video</code>的类。</li><li>在自定义 css 中加入以下样式。也可以直接将这段换成<code>style</code>格式写进 <code>head.styl</code>。<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-id">#index-video</span> {</span><br><span class="line"> <span class="attribute">z-index</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">80vh</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line"> <span class="attribute">object-fit</span>: cover;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">@media</span> <span class="keyword">only</span> screen <span class="keyword">and</span> (<span class="attribute">max-width</span>: <span class="number">768px</span>) {</span><br><span class="line"> <span class="selector-id">#index-video</span> {</span><br><span class="line"> <span class="attribute">display</span>: none;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">@media</span> <span class="keyword">only</span> screen <span class="keyword">and</span> (<span class="attribute">min-width</span>: <span class="number">768px</span>) {</span><br><span class="line"> <span class="selector-class">.bg-cover</span> {</span><br><span class="line"> <span class="attribute">background-image</span>: none <span class="meta">!important</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li>在想插入视频的页面,一定要有某个属性 ,可自行配置 ,如果用 type , 则<code>BlogRoot/themes/butterfly/layout/includes/header/index.pug</code>中则根据对应的 type 类型去写逻辑即可。<br>比如我的 <code>BlogRoot/source/comment/index.md</code> 配置如下:<figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">title: 留言板</span><br><span class="line"><span class="section">type: 'comment' </span></span><br><span class="line"><span class="section">---</span></span><br></pre></td></tr></table></figure>所以在<code>BlogRoot/themes/butterfly/layout/includes/header/index.pug</code>中的第 23 行加入 <code>page.type == 'comment'</code><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">- var isHomeClass = is_home() ? 'full_page' : (page.type == 'comment' || page.type == 'link' ) ? 'not-home-page has-video bg-cover' : 'not-home-page'</span><br></pre></td></tr></table></figure>第 51 - 56 行 加入<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">if page.type == 'comment' </span><br><span class="line"> video#index-video(autoplay='' loop='' muted='')</span><br><span class="line"> source(src='你的视频地址')</span><br><span class="line">if page.type == 'link' </span><br><span class="line"> video#index-video(autoplay='' loop='' muted='')</span><br><span class="line"> source(src='你的视频地址')</span><br></pre></td></tr></table></figure></li><li>如果已经用了主页冒泡特效,请将下面这段代码替换之前的<code>chocolate.js</code>。没有使用即可忽略这步。<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></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * @Author: tzy1997</span></span><br><span class="line"><span class="comment"> * @Date: 2020-12-15 20:55:25</span></span><br><span class="line"><span class="comment"> * @LastEditors: tzy1997</span></span><br><span class="line"><span class="comment"> * @LastEditTime: 2021-11-25 18:15:47</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="comment">// TODO 获取窗口高度 11-19</span></span><br><span class="line"><span class="keyword">var</span> b_h = $(<span class="variable language_">window</span>).<span class="title function_">height</span>()</span><br><span class="line"><span class="keyword">var</span> b_w = $(<span class="variable language_">window</span>).<span class="title function_">width</span>()</span><br><span class="line"></span><br><span class="line">$(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 气泡</span></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">bubble</span>(<span class="params"></span>) {</span><br><span class="line"> $(<span class="string">'#page-header'</span>).<span class="title function_">circleMagic</span>({</span><br><span class="line"> <span class="attr">radius</span>: <span class="number">10</span>,</span><br><span class="line"> <span class="attr">density</span>: <span class="number">.2</span>,</span><br><span class="line"> <span class="attr">color</span>: <span class="string">'rgba(255,255,255,.4)'</span>,</span><br><span class="line"> <span class="attr">clearOffset</span>: <span class="number">0.99</span></span><br><span class="line"> });</span><br><span class="line"> }! <span class="keyword">function</span>(<span class="params">p</span>) {</span><br><span class="line"> p.<span class="property">fn</span>.<span class="property">circleMagic</span> = <span class="keyword">function</span>(<span class="params">t</span>) {</span><br><span class="line"> <span class="keyword">var</span> o, a, n, r, e = !<span class="number">0</span>,</span><br><span class="line"> i = [],</span><br><span class="line"> d = p.<span class="title function_">extend</span>({ <span class="attr">color</span>: <span class="string">"rgba(255,0,0,.5)"</span>, <span class="attr">radius</span>: <span class="number">10</span>, <span class="attr">density</span>: <span class="number">.3</span>, <span class="attr">clearOffset</span>: <span class="number">.2</span> }, t),</span><br><span class="line"> l = <span class="variable language_">this</span>[<span class="number">0</span>];</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">c</span>(<span class="params"></span>) { e = !(<span class="variable language_">document</span>.<span class="property">body</span>.<span class="property">scrollTop</span> > a) }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">s</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="comment">// TODO 获取窗口高度 ethan_tzy</span></span><br><span class="line"> <span class="keyword">var</span> a_c = l.<span class="property">clientHeight</span></span><br><span class="line"> <span class="keyword">if</span> ($(<span class="string">'#index-video'</span>).<span class="property">length</span> > <span class="number">0</span> && b_w > <span class="number">768</span>) {</span><br><span class="line"> a = b_h * <span class="number">0.8</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> a = a_c</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// o = l.clientWidth, a = l.clientHeight, l.height = a "px", n.width = o, n.height = a</span></span><br><span class="line"> o = l.<span class="property">clientWidth</span>, l.<span class="property">height</span> = a <span class="string">"px"</span>, n.<span class="property">width</span> = o, n.<span class="property">height</span> = a</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">h</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">if</span> (e)</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> t <span class="keyword">in</span> r.<span class="title function_">clearRect</span>(<span class="number">0</span>, <span class="number">0</span>, o, a), i) i[t].<span class="title function_">draw</span>();</span><br><span class="line"> <span class="title function_">requestAnimationFrame</span>(h)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">f</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> t = <span class="variable language_">this</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">e</span>(<span class="params"></span>) { t.<span class="property">pos</span>.<span class="property">x</span> = <span class="title class_">Math</span>.<span class="title function_">random</span>() * o, t.<span class="property">pos</span>.<span class="property">y</span> = a <span class="number">100</span> * <span class="title class_">Math</span>.<span class="title function_">random</span>(), t.<span class="property">alpha</span> = <span class="number">.1</span> <span class="title class_">Math</span>.<span class="title function_">random</span>() * d.<span class="property">clearOffset</span>, t.<span class="property">scale</span> = <span class="number">.1</span> <span class="number">.3</span> * <span class="title class_">Math</span>.<span class="title function_">random</span>(), t.<span class="property">speed</span> = <span class="title class_">Math</span>.<span class="title function_">random</span>(), <span class="string">"random"</span> === d.<span class="property">color</span> ? t.<span class="property">color</span> = <span class="string">"rgba("</span> <span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="number">255</span> * <span class="title class_">Math</span>.<span class="title function_">random</span>()) <span class="string">", "</span> <span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="number">0</span> * <span class="title class_">Math</span>.<span class="title function_">random</span>()) <span class="string">", "</span> <span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="number">0</span> * <span class="title class_">Math</span>.<span class="title function_">random</span>()) <span class="string">", "</span> <span class="title class_">Math</span>.<span class="title function_">random</span>().<span class="title function_">toPrecision</span>(<span class="number">2</span>) <span class="string">")"</span> : t.<span class="property">color</span> = d.<span class="property">color</span> }</span><br><span class="line"> t.<span class="property">pos</span> = {}, <span class="title function_">e</span>(), <span class="variable language_">this</span>.<span class="property">draw</span> = <span class="keyword">function</span>(<span class="params"></span>) { t.<span class="property">alpha</span> <= <span class="number">0</span> && <span class="title function_">e</span>(), t.<span class="property">pos</span>.<span class="property">y</span> -= t.<span class="property">speed</span>, t.<span class="property">alpha</span> -= <span class="number">5e-4</span>, r.<span class="title function_">beginPath</span>(), r.<span class="title function_">arc</span>(t.<span class="property">pos</span>.<span class="property">x</span>, t.<span class="property">pos</span>.<span class="property">y</span>, t.<span class="property">scale</span> * d.<span class="property">radius</span>, <span class="number">0</span>, <span class="number">2</span> * <span class="title class_">Math</span>.<span class="property">PI</span>, !<span class="number">1</span>), r.<span class="property">fillStyle</span> = t.<span class="property">color</span>, r.<span class="title function_">fill</span>(), r.<span class="title function_">closePath</span>() }</span><br><span class="line"> }! <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="comment">// TODO 气泡的高度 11-19</span></span><br><span class="line"> <span class="keyword">var</span> a_c = l.<span class="property">clientHeight</span></span><br><span class="line"> <span class="keyword">if</span> ($(<span class="string">'#index-video'</span>).<span class="property">length</span> > <span class="number">0</span> && b_w > <span class="number">768</span>) {</span><br><span class="line"> a = b_h * <span class="number">0.8</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> a = a_c</span><br><span class="line"> }</span><br><span class="line"> o = l.<span class="property">offsetWidth</span>,</span><br><span class="line"> <span class="comment">// o = l.offsetWidth, a = l.offsetHeight,</span></span><br><span class="line"> <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> t = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">"canvas"</span>);</span><br><span class="line"> t.<span class="property">id</span> = <span class="string">"canvas"</span>, t.<span class="property">style</span>.<span class="property">top</span> = <span class="number">0</span>, t.<span class="property">style</span>.<span class="property">zIndex</span> = <span class="number">0</span>, t.<span class="property">style</span>.<span class="property">position</span> = <span class="string">"absolute"</span>, l.<span class="title function_">appendChild</span>(t), t.<span class="property">parentElement</span>.<span class="property">style</span>.<span class="property">overflow</span> = <span class="string">"hidden"</span></span><br><span class="line"> }(), (n = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">"canvas"</span>)).<span class="property">width</span> = o, n.<span class="property">height</span> = a, r = n.<span class="title function_">getContext</span>(<span class="string">"2d"</span>);</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> t = <span class="number">0</span>; t < o * d.<span class="property">density</span>; t++) {</span><br><span class="line"> <span class="keyword">var</span> e = <span class="keyword">new</span> f;</span><br><span class="line"> i.<span class="title function_">push</span>(e)</span><br><span class="line"> }</span><br><span class="line"> <span class="title function_">h</span>()</span><br><span class="line"> }(), <span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">"scroll"</span>, c, !<span class="number">1</span>), <span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">"resize"</span>, s, !<span class="number">1</span>)</span><br><span class="line"> }</span><br><span class="line"> }(jQuery);</span><br><span class="line"> <span class="title function_">bubble</span>();</span><br><span class="line">})</span><br></pre></td></tr></table></figure></li><li>重新编译运行,即可看到效果。</li></ol>
</div>
</details>
<h3 id="公告栏两个小人"><a href="#公告栏两个小人" class="headerlink" title="公告栏两个小人"></a>公告栏两个小人</h3><details class="folding-tag" cyan><summary> 效果预览 </summary>
<div class='content'>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/06/629cdcc58af14.jpg"/></div></div>
</div>
</details>
<details class="folding-tag" yellow><summary> 查看步骤 </summary>
<div class='content'>
<p>在【BlogRoot/themes/butterfly/layout/includes/widget/card_announcement.pug】下添加如下部分代码。</p><p>注意: 将代码复制到【card_announcement.pug】文件以后,不难发现会有重复的一段代码。你要做的一步操作是,<span class='p blue'>删除重复的代码(优先保留你主题版本原有的代码),</span> 这里之所以没用 Diff 代码块,是因为怕删除【+】号的时候在格式上特别容易出错。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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">if theme.aside.card_announcement.enable</span><br><span class="line"> .card-widget.card-announcement</span><br><span class="line"> .item-headline</span><br><span class="line"> i.fas.fa-bullhorn.card-announcement-animation</span><br><span class="line"> span= _p('aside.card_announcement')</span><br><span class="line"> .announcement_content!= theme.aside.card_announcement.content</span><br><span class="line"> .xpand(style='height:200px;')</span><br><span class="line"> canvas.illo(width='800' height='800' style='max-width: 200px; max-height: 200px; touch-action: none; width: 640px; height: 640px;')</span><br><span class="line">script(src='https://npm.elemecdn.com/ethan4116-blog/lib/js/other/two-people/twopeople1.js')</span><br><span class="line">script(src='https://npm.elemecdn.com/ethan4116-blog/lib/js/other/two-people/zdog.dist.js')</span><br><span class="line">script#rendered-js(src='https://npm.elemecdn.com/ethan4116-blog/lib/js/other/two-people/twopeople.js')</span><br><span class="line">style.</span><br><span class="line"> .card-widget.card-announcement {</span><br><span class="line"> margin: 0;</span><br><span class="line"> align-items: center;</span><br><span class="line"> justify-content: center;</span><br><span class="line"> text-align: center;</span><br><span class="line"> }</span><br><span class="line"> canvas {</span><br><span class="line"> display: block;</span><br><span class="line"> margin: 0 auto;</span><br><span class="line"> cursor: move;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
</div>
</details>
<h3 id="星空背景和流星特效"><a href="#星空背景和流星特效" class="headerlink" title="星空背景和流星特效"></a>星空背景和流星特效</h3><details class="folding-tag" yellow><summary> 查看步骤 </summary>
<div class='content'>
<ol><li>在<code>BlogRoot/themes/butterfly/source/js</code>目录下新建<code>universe.js</code>,输入以下代码:<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">function</span> <span class="title function_">dark</span>(<span class="params"></span>) {<span class="variable language_">window</span>.<span class="property">requestAnimationFrame</span>=<span class="variable language_">window</span>.<span class="property">requestAnimationFrame</span>||<span class="variable language_">window</span>.<span class="property">mozRequestAnimationFrame</span>||<span class="variable language_">window</span>.<span class="property">webkitRequestAnimationFrame</span>||<span class="variable language_">window</span>.<span class="property">msRequestAnimationFrame</span>;<span class="keyword">var</span> n,e,i,h,t=<span class="number">.05</span>,s=<span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">"universe"</span>),o=!<span class="number">0</span>,a=<span class="string">"180,184,240"</span>,r=<span class="string">"226,225,142"</span>,d=<span class="string">"226,225,224"</span>,c=[];<span class="keyword">function</span> <span class="title function_">f</span>(<span class="params"></span>){n=<span class="variable language_">window</span>.<span class="property">innerWidth</span>,e=<span class="variable language_">window</span>.<span class="property">innerHeight</span>,i=<span class="number">.216</span>*n,s.<span class="title function_">setAttribute</span>(<span class="string">"width"</span>,n),s.<span class="title function_">setAttribute</span>(<span class="string">"height"</span>,e)}<span class="keyword">function</span> <span class="title function_">u</span>(<span class="params"></span>){h.<span class="title function_">clearRect</span>(<span class="number">0</span>,<span class="number">0</span>,n,e);<span class="keyword">for</span>(<span class="keyword">var</span> t=c.<span class="property">length</span>,i=<span class="number">0</span>;i<t;i++){<span class="keyword">var</span> s=c[i];s.<span class="title function_">move</span>(),s.<span class="title function_">fadeIn</span>(),s.<span class="title function_">fadeOut</span>(),s.<span class="title function_">draw</span>()}}<span class="keyword">function</span> <span class="title function_">y</span>(<span class="params"></span>){<span class="variable language_">this</span>.<span class="property">reset</span>=<span class="keyword">function</span>(<span class="params"></span>){<span class="variable language_">this</span>.<span class="property">giant</span>=<span class="title function_">m</span>(<span class="number">3</span>),<span class="variable language_">this</span>.<span class="property">comet</span>=!<span class="variable language_">this</span>.<span class="property">giant</span>&&!o&&<span class="title function_">m</span>(<span class="number">10</span>),<span class="variable language_">this</span>.<span class="property">x</span>=<span class="title function_">l</span>(<span class="number">0</span>,n-<span class="number">10</span>),<span class="variable language_">this</span>.<span class="property">y</span>=<span class="title function_">l</span>(<span class="number">0</span>,e),<span class="variable language_">this</span>.<span class="property">r</span>=<span class="title function_">l</span>(<span class="number">1.1</span>,<span class="number">2.6</span>),<span class="variable language_">this</span>.<span class="property">dx</span>=<span class="title function_">l</span>(t,<span class="number">6</span>*t)+(<span class="variable language_">this</span>.<span class="property">comet</span>+<span class="number">1</span>-<span class="number">1</span>)*t*<span class="title function_">l</span>(<span class="number">50</span>,<span class="number">120</span>)+<span class="number">2</span>*t,<span class="variable language_">this</span>.<span class="property">dy</span>=-<span class="title function_">l</span>(t,<span class="number">6</span>*t)-(<span class="variable language_">this</span>.<span class="property">comet</span>+<span class="number">1</span>-<span class="number">1</span>)*t*<span class="title function_">l</span>(<span class="number">50</span>,<span class="number">120</span>),<span class="variable language_">this</span>.<span class="property">fadingOut</span>=<span class="literal">null</span>,<span class="variable language_">this</span>.<span class="property">fadingIn</span>=!<span class="number">0</span>,<span class="variable language_">this</span>.<span class="property">opacity</span>=<span class="number">0</span>,<span class="variable language_">this</span>.<span class="property">opacityTresh</span>=<span class="title function_">l</span>(<span class="number">.2</span>,<span class="number">1</span>-<span class="number">.4</span>*(<span class="variable language_">this</span>.<span class="property">comet</span>+<span class="number">1</span>-<span class="number">1</span>)),<span class="variable language_">this</span>.<span class="property">do</span>=<span class="title function_">l</span>(<span class="number">5e-4</span>,<span class="number">.002</span>)+<span class="number">.001</span>*(<span class="variable language_">this</span>.<span class="property">comet</span>+<span class="number">1</span>-<span class="number">1</span>)},<span class="variable language_">this</span>.<span class="property">fadeIn</span>=<span class="keyword">function</span>(<span class="params"></span>){<span class="variable language_">this</span>.<span class="property">fadingIn</span>&&(<span class="variable language_">this</span>.<span class="property">fadingIn</span>=!(<span class="variable language_">this</span>.<span class="property">opacity</span>><span class="variable language_">this</span>.<span class="property">opacityTresh</span>),<span class="variable language_">this</span>.<span class="property">opacity</span>+=<span class="variable language_">this</span>.<span class="property">do</span>)},<span class="variable language_">this</span>.<span class="property">fadeOut</span>=<span class="keyword">function</span>(<span class="params"></span>){<span class="variable language_">this</span>.<span class="property">fadingOut</span>&&(<span class="variable language_">this</span>.<span class="property">fadingOut</span>=!(<span class="variable language_">this</span>.<span class="property">opacity</span><<span class="number">0</span>),<span class="variable language_">this</span>.<span class="property">opacity</span>-=<span class="variable language_">this</span>.<span class="property">do</span>/<span class="number">2</span>,(<span class="variable language_">this</span>.<span class="property">x</span>>n||<span class="variable language_">this</span>.<span class="property">y</span><<span class="number">0</span>)&&(<span class="variable language_">this</span>.<span class="property">fadingOut</span>=!<span class="number">1</span>,<span class="variable language_">this</span>.<span class="title function_">reset</span>()))},<span class="variable language_">this</span>.<span class="property">draw</span>=<span class="keyword">function</span>(<span class="params"></span>){<span class="keyword">if</span>(h.<span class="title function_">beginPath</span>(),<span class="variable language_">this</span>.<span class="property">giant</span>)h.<span class="property">fillStyle</span>=<span class="string">"rgba("</span>+a+<span class="string">","</span>+<span class="variable language_">this</span>.<span class="property">opacity</span>+<span class="string">")"</span>,h.<span class="title function_">arc</span>(<span class="variable language_">this</span>.<span class="property">x</span>,<span class="variable language_">this</span>.<span class="property">y</span>,<span class="number">2</span>,<span class="number">0</span>,<span class="number">2</span>*<span class="title class_">Math</span>.<span class="property">PI</span>,!<span class="number">1</span>);<span class="keyword">else</span> <span class="keyword">if</span>(<span class="variable language_">this</span>.<span class="property">comet</span>){h.<span class="property">fillStyle</span>=<span class="string">"rgba("</span>+d+<span class="string">","</span>+<span class="variable language_">this</span>.<span class="property">opacity</span>+<span class="string">")"</span>,h.<span class="title function_">arc</span>(<span class="variable language_">this</span>.<span class="property">x</span>,<span class="variable language_">this</span>.<span class="property">y</span>,<span class="number">1.5</span>,<span class="number">0</span>,<span class="number">2</span>*<span class="title class_">Math</span>.<span class="property">PI</span>,!<span class="number">1</span>);<span class="keyword">for</span>(<span class="keyword">var</span> t=<span class="number">0</span>;t<<span class="number">30</span>;t++)h.<span class="property">fillStyle</span>=<span class="string">"rgba("</span>+d+<span class="string">","</span>+(<span class="variable language_">this</span>.<span class="property">opacity</span>-<span class="variable language_">this</span>.<span class="property">opacity</span>/<span class="number">20</span>*t)+<span class="string">")"</span>,h.<span class="title function_">rect</span>(<span class="variable language_">this</span>.<span class="property">x</span>-<span class="variable language_">this</span>.<span class="property">dx</span>/<span class="number">4</span>*t,<span class="variable language_">this</span>.<span class="property">y</span>-<span class="variable language_">this</span>.<span class="property">dy</span>/<span class="number">4</span>*t-<span class="number">2</span>,<span class="number">2</span>,<span class="number">2</span>),h.<span class="title function_">fill</span>()}<span class="keyword">else</span> h.<span class="property">fillStyle</span>=<span class="string">"rgba("</span>+r+<span class="string">","</span>+<span class="variable language_">this</span>.<span class="property">opacity</span>+<span class="string">")"</span>,h.<span class="title function_">rect</span>(<span class="variable language_">this</span>.<span class="property">x</span>,<span class="variable language_">this</span>.<span class="property">y</span>,<span class="variable language_">this</span>.<span class="property">r</span>,<span class="variable language_">this</span>.<span class="property">r</span>);h.<span class="title function_">closePath</span>(),h.<span class="title function_">fill</span>()},<span class="variable language_">this</span>.<span class="property">move</span>=<span class="keyword">function</span>(<span class="params"></span>){<span class="variable language_">this</span>.<span class="property">x</span>+=<span class="variable language_">this</span>.<span class="property">dx</span>,<span class="variable language_">this</span>.<span class="property">y</span>+=<span class="variable language_">this</span>.<span class="property">dy</span>,!<span class="number">1</span>===<span class="variable language_">this</span>.<span class="property">fadingOut</span>&&<span class="variable language_">this</span>.<span class="title function_">reset</span>(),(<span class="variable language_">this</span>.<span class="property">x</span>>n-n/<span class="number">4</span>||<span class="variable language_">this</span>.<span class="property">y</span><<span class="number">0</span>)&&(<span class="variable language_">this</span>.<span class="property">fadingOut</span>=!<span class="number">0</span>)},<span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>){o=!<span class="number">1</span>},<span class="number">50</span>)}<span class="keyword">function</span> <span class="title function_">m</span>(<span class="params">t</span>){<span class="keyword">return</span> <span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="number">1e3</span>*<span class="title class_">Math</span>.<span class="title function_">random</span>())+<span class="number">1</span><<span class="number">10</span>*t}<span class="keyword">function</span> <span class="title function_">l</span>(<span class="params">t,i</span>){<span class="keyword">return</span> <span class="title class_">Math</span>.<span class="title function_">random</span>()*(i-t)+t}<span class="title function_">f</span>(),<span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">"resize"</span>,f,!<span class="number">1</span>),<span class="keyword">function</span>(<span class="params"></span>){h=s.<span class="title function_">getContext</span>(<span class="string">"2d"</span>);<span class="keyword">for</span>(<span class="keyword">var</span> t=<span class="number">0</span>;t<i;t++)c[t]=<span class="keyword">new</span> y,c[t].<span class="title function_">reset</span>();<span class="title function_">u</span>()}(),<span class="keyword">function</span> <span class="title function_">t</span>(<span class="params"></span>){<span class="variable language_">document</span>.<span class="title function_">getElementsByTagName</span>(<span class="string">'html'</span>)[<span class="number">0</span>].<span class="title function_">getAttribute</span>(<span class="string">'data-theme'</span>)==<span class="string">'dark'</span>&&<span class="title function_">u</span>(),<span class="variable language_">window</span>.<span class="title function_">requestAnimationFrame</span>(t)}()};</span><br><span class="line"><span class="title function_">dark</span>()</span><br></pre></td></tr></table></figure></li><li>在<code>BlogRoot/themes/butterfly/source/css</code>目录下新建<code>universe.css</code>,输入以下代码:<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 背景宇宙星光 */</span></span><br><span class="line"><span class="selector-id">#universe</span>{</span><br><span class="line"> <span class="attribute">display</span>: block;</span><br><span class="line"> <span class="attribute">position</span>: fixed;</span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">padding</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">border</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">outline</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">100%</span>;</span><br><span class="line"> <span class="attribute">pointer-events</span>: none;</span><br><span class="line"> <span class="attribute">z-index</span>: -<span class="number">1</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li>在主题配置文件<code>_config.butterfly.yml</code>的<code>inject</code>配置项中<code>bottom</code>下填入:<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">inject:</span></span><br><span class="line"> <span class="attr">bottom:</span></span><br><span class="line"> <span class="comment"># 星空背景</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string"><canvas</span> <span class="string">id="universe"></canvas></span></span><br><span class="line"> <span class="bullet">-</span> <span class="string"><script</span> <span class="string">defer</span> <span class="string">src="/js/universe.js"></script></span></span><br></pre></td></tr></table></figure></li><li>在主题配置文件<code>_config.butterfly.yml</code>的<code>inject</code>配置项中<code>head</code>下填入:<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">inject:</span></span><br><span class="line"> <span class="attr">head:</span></span><br><span class="line"> <span class="comment">## 星空背景</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string"><link</span> <span class="string">rel="stylesheet"</span> <span class="string">href="/css/universe.css"></span></span><br></pre></td></tr></table></figure></li><li>重新编译即可看到效果。</li></ol>
</div>
</details>
<h3 id="樱花飘落效果"><a href="#樱花飘落效果" class="headerlink" title="樱花飘落效果"></a>樱花飘落效果</h3><details class="folding-tag" cyan><summary> 效果预览 </summary>
<div class='content'>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/06/629ce3b40d3aa.jpg"/></div></div>
</div>
</details>
<details class="folding-tag" yellow><summary> 查看步骤 </summary>
<div class='content'>
<p>在主题配置文件<code>_config.butterfly.yml</code>的<code>inject</code>配置项中<code>bottom</code>下引入<code>sakura.js</code>即可。</p><figure class="highlight diff"><table><tr><td class="gutter"><pre><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">inject:</span><br><span class="line"> bottom:</span><br><span class="line"> # 樱花飘落效果</span><br><span class="line"> # - <script async src="https://npm.elemecdn.com/tzy-blog/lib/js/other/sakura.js"></script></span><br></pre></td></tr></table></figure>
</div>
</details>
<h3 id="灯笼特效"><a href="#灯笼特效" class="headerlink" title="灯笼特效"></a>灯笼特效</h3><div class="tag link"><a class="link-card" title="Hexo-悬挂灯笼" href="https://fe32.top/articles/Ha5487ng/"><div class="left"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/01/626e8a2063be4.png"/></div><div class="right"><p class="text">Hexo-悬挂灯笼</p><p class="url">https://fe32.top/articles/Ha5487ng/</p></div></a></div>
<h3 id="外挂标签"><a href="#外挂标签" class="headerlink" title="外挂标签"></a>外挂标签</h3><div class="tag link"><a class="link-card" title="基于Butterfly的外挂标签引入" href="https://fe32.top/articles/0xiipgum/"><div class="left"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/01/626e8d500f25f.png"/></div><div class="right"><p class="text">基于Butterfly的外挂标签引入</p><p class="url">https://fe32.top/articles/0xiipgum/</p></div></a></div>
<h3 id="添加github徽标"><a href="#添加github徽标" class="headerlink" title="添加github徽标"></a>添加github徽标</h3><div class="tag link"><a class="link-card" title="添加github徽标" href="https://fe32.top/articles/kfwr2gpa/"><div class="left"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/02/626eb40f87b02.png"/></div><div class="right"><p class="text">添加github徽标</p><p class="url">https://fe32.top/articles/kfwr2gpa/</p></div></a></div>
<h3 id="自定义右键菜单"><a href="#自定义右键菜单" class="headerlink" title="自定义右键菜单"></a>自定义右键菜单</h3><table>
<thead>
<tr>
<th align="center">参考方向</th>
<th align="center">教程原贴</th>
</tr>
</thead>
<tbody><tr>
<td align="center">TZY</td>
<td align="center"><a href="https://fe32.top/articles/hexo1608">Hexo Butterfly 自定义右键菜单(基础)</a></td>
</tr>
<tr>
<td align="center">ZHHEO</td>
<td align="center"><a href="https://blog.zhheo.com/p/5e931b65.html">Butterfly 魔改:自定义右键菜单</a></td>
</tr>
<tr>
<td align="center">LYX</td>
<td align="center"><a href="https://yisous.xyz/posts/11eb4aac/">博客自定义右键菜单升级版</a></td>
</tr>
</tbody></table>
<h3 id="加载动画"><a href="#加载动画" class="headerlink" title="加载动画"></a>加载动画</h3><p>可参考 <a href="https://akilar.top/posts/3d221bf2/">Loading Animation</a></p>
<h3 id="评论弹幕"><a href="#评论弹幕" class="headerlink" title="评论弹幕"></a>评论弹幕</h3><p>参考教程 <a href="https://yisous.xyz/posts/69707535/">Butterfly 主题的留言弹幕界面增强版(支持 Twikoo、Waline、Valine)</a></p>
<h3 id="自定义页脚"><a href="#自定义页脚" class="headerlink" title="自定义页脚"></a>自定义页脚</h3><details class="folding-tag" cyan><summary> 效果预览 </summary>
<div class='content'>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/12/11/6395f7970a3bb.jpg"/></div></div>
</div>
</details>
<details class="folding-tag" yellow><summary> 查看教程 </summary>
<div class='content'>
<div class="tag link"><a class="link-card" title="Hexo + Butterfly 自定义页脚" href="https://fe32.top/articles/hexo1617/"><div class="left"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/12/11/6395eec4c51ed.png"/></div><div class="right"><p class="text">Hexo + Butterfly 自定义页脚</p><p class="url">https://fe32.top/articles/hexo1617/</p></div></a></div>
</div>
</details>
<h3 id="侧边栏公众号"><a href="#侧边栏公众号" class="headerlink" title="侧边栏公众号"></a>侧边栏公众号</h3><details class="folding-tag" cyan><summary> 效果预览 </summary>
<div class='content'>
<center><img no-lazy class="inline" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2023/01/05/63b6d7e812c3f.jpg" style="height:120px;"/><span style="margin:0 2px"></span><img no-lazy class="inline" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2023/01/05/63b6d7e96e93c.jpg" style="height:120px;"/></center><center><img no-lazy class="inline" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2023/01/05/63b6da843dbae.jpg" style="height:120px;"/><span style="margin:0 2px"></span><img no-lazy class="inline" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2023/01/05/63b6da85c9ba4.jpg" style="height:120px;"/></center>
</div>
</details>
<details class="folding-tag" yellow><summary> 查看教程 </summary>
<div class='content'>
<div class="tag link"><a class="link-card" title="Hexo + Butterfly 侧边栏公众号" href="https://fe32.top/articles/hexo1618/"><div class="left"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2023/01/05/63b6cb1a2bab2.png"/></div><div class="right"><p class="text">Hexo + Butterfly 侧边栏公众号</p><p class="url">https://fe32.top/articles/hexo1618/</p></div></a></div>
</div>
</details>
<h3 id="文章加密插件"><a href="#文章加密插件" class="headerlink" title="文章加密插件"></a>文章加密插件</h3><details class="folding-tag" cyan><summary> 效果预览 </summary>
<div class='content'>
<p>点击 <a href="https://mhexo.github.io/">Demo Page</a> , 所有的密码都是 【hello】。</p>
</div>
</details>
<details class="folding-tag" yellow><summary> 查看教程 </summary>
<div class='content'>
<p>详见开源地址:<a href="https://github.com/D0n9X1n/hexo-blog-encrypt">hexo-blog-encrypt</a></p><ol><li>在根目录执行以下命令<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 hexo-blog-encrypt</span><br></pre></td></tr></table></figure></li><li>Front matter配置方法<figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">title: Hello World</span><br><span class="line">tags:</span><br><span class="line"><span class="bullet">-</span> 作为日记加密</span><br><span class="line">date: 2016-03-30 21:12:21</span><br><span class="line">password: mikemessi</span><br><span class="line">abstract: 有东西被加密了, 请输入密码查看.</span><br><span class="line">message: 您好, 这里需要密码.</span><br><span class="line">theme: xray</span><br><span class="line">wrong<span class="emphasis">_pass_</span>message: 抱歉, 这个密码看着不太对, 请再试试.</span><br><span class="line"><span class="section">wrong<span class="emphasis">_hash_</span>message: 抱歉, 这个文章不能被校验, 不过您还是能看看解密后的内容.</span></span><br><span class="line"><span class="section">---</span></span><br></pre></td></tr></table></figure></li><li>可以在线挑选你喜欢的主题,并应用到你的博客中: <a href="https://mhexo.github.io/2020/12/23/Theme-Test-Default/">default</a>、<a href="https://mhexo.github.io/2020/12/23/Theme-Test-Blink/">blink</a>、<a href="https://mhexo.github.io/2020/12/23/Theme-Test-Shrink/">shrink</a>、<a href="https://mhexo.github.io/2020/12/23/Theme-Test-Flip/">flip</a>、<a href="https://mhexo.github.io/2020/12/23/Theme-Test-Up/">up</a>、<a href="https://mhexo.github.io/2020/12/23/Theme-Test-Surge/">surge</a>、<a href="https://mhexo.github.io/2020/12/23/Theme-Test-Wave/">wave</a>、<a href="https://mhexo.github.io/2020/12/23/Theme-Test-Xray/">xray</a></li><li>重新编译项目并进入对应的文章页即可看到加密效果</li></ol>
</div>
</details>
<h3 id="友链朋友圈"><a href="#友链朋友圈" class="headerlink" title="友链朋友圈"></a>友链朋友圈</h3><details class="folding-tag" cyan><summary> 效果预览 </summary>
<div class='content'>
<p>效果请移步:<a href="https://fe32.top/frdcenter/">朋友圈</a></p>
</div>
</details>
<details class="folding-tag" yellow><summary> 查看教程 </summary>
<div class='content'>
<ul><li>部署教程:<a href="https://fcircle-doc.yyyzyyyz.cn/">文档</a> 、<a href="https://fcircle-doc.is-a.dev/">备用地址</a></li><li><a href="https://blog.zhheo.com/p/4e18a507.html">友链朋友圈5 - 我的部署历程与主题样式分享</a></li></ul>
</div>
</details>
<!-- ### 评论表情包放大 ###
请移步教程:[评论表情包放大功能,超实用](https://blog.leonus.cn/2022/owo-big.html)
### 评论输入提醒 ###
请移步教程:[给你的评论添加一个输入提醒吧](https://blog.leonus.cn/2022/inputAlert.html) -->
<!-- ## 遇到问题
<div class="tip faa-horizontal"><p>如果在搭建博客中遇到什么问题 ,请在 <span class='p yellow'>评论区</span> 留言 ,我会在第一时间内帮助您解决问题 。</p>
</div>
基于 Hexo 从零开始搭建个人博客(五)
https://fe32.top/articles/hexo1605/
2022-05-29T15:41:28.000Z
2024-03-13T07:48:48.471Z
<blockquote>
<p>阅读本篇前,请先阅读前几篇文章:<br><a href="https://fe32.top/articles/hexo1601/">基于 Hexo 从零开始搭建个人博客(一)</a><br><a href="https://fe32.top/articles/hexo1602/">基于 Hexo 从零开始搭建个人博客(二)</a><br><a href="https://fe32.top/articles/hexo1603/">基于 Hexo 从零开始搭建个人博客(三)</a><br><a href="https://fe32.top/articles/hexo1604/">基于 Hexo 从零开始搭建个人博客(四)</a></p>
</blockquote>
<p>更多配置请移步 <a href="https://butterfly.js.org/posts/4aa8abbe/">官方文档</a>。</p>
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><!-- > 本站基于`Hexo`搭建,用的 🦋 [hexo-theme-butterfly](https://github.com/jerryc127/hexo-theme-butterfly.git) 主题 [v3.7.1](https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1),请注意最新的🦋 [hexo-theme-butterfly](https://github.com/jerryc127/hexo-theme-butterfly.git) 版本已经更新到 [v4.8.1](https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.8.1) 。<br>
如果你是 [v3.7.1](https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1) 之外的版本,可能有些地方会有出入,请留意。 -->
<blockquote>
<p>本站基于<code>Hexo</code>搭建,用的 🦋 <a href="https://github.com/jerryc127/hexo-theme-butterfly.git">hexo-theme-butterfly</a> 主题,已经升级到 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.7.0">v4.7.0</a> 。 请注意最新的🦋 <a href="https://github.com/jerryc127/hexo-theme-butterfly.git">hexo-theme-butterfly</a> 版本已经更新到 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.8.1">v4.8.1</a> 。<br><br>如果你是 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1">v3.7.1</a> 的版本,请移步 <a href="https://old.fe32.top/" target="_blank">v3.7.1</a> 站点进行浏览。</p>
</blockquote>
<blockquote>
<p>注意:我的博客根目录路径为 【G:/hexo-blog/blog-demo】,下文所说的根目录都是此路径,将用<code>[BlogRoot]</code>代替。如果不清楚根目录路径,请回到教程 <a href="https://fe32.top/articles/hexo1602/">基于 Hexo 从零开始搭建个人博客(二)</a>,查看你执行<code>hexo init xxx</code>这条命令时所选择的路径,例如我选择的路径是【G:/hexo-blog】,我的博客根目录即为【G:/hexo-blog/xxx】。<br><br>修改站点配置文件<code>_config.yml</code>,路径为【BlogRoot/_config.yml】。<br><br>修改主题配置文件<code>_config.butterfly.yml</code>,路径为【BlogRoot/_config.butterfly.yml】。</p>
</blockquote>
<h2 id="语言"><a href="#语言" class="headerlink" title="语言"></a>语言</h2><p>修改站点配置文件<code>_config.yml</code>,默认语言是 en 。</p>
<p>主题支持三种语言:</p>
<ul>
<li>default(en)</li>
<li>zh-CN (简体中文)</li>
<li>zh-TW (繁体中文)</li>
</ul>
<h2 id="网站资料"><a href="#网站资料" class="headerlink" title="网站资料"></a>网站资料</h2><p>修改网站各种资料,例如标题、副标题和邮箱等个人资料,请修改站点配置文件<code>_config.yml</code>。部分参数如下,详细参数可参考官方的<a href="https://hexo.io/zh-cn/docs/configuration">配置描述</a>。</p>
<table>
<thead>
<tr>
<th align="left">参数</th>
<th align="left">描述</th>
</tr>
</thead>
<tbody><tr>
<td align="left">title</td>
<td align="left">网站标题</td>
</tr>
<tr>
<td align="left">subtitle</td>
<td align="left">描述</td>
</tr>
<tr>
<td align="left">description</td>
<td align="left">网站描述</td>
</tr>
<tr>
<td align="left">keywords</td>
<td align="left">网站的关键词。支持多个关键词。</td>
</tr>
<tr>
<td align="left">author</td>
<td align="left">您的名字</td>
</tr>
<tr>
<td align="left">language</td>
<td align="left">网站使用的语言。对于简体中文用户来说,使用不同的主题可能需要设置成不同的值,请参考你的主题的文档自行设置,常见的有 zh-Hans和 zh-CN。</td>
</tr>
<tr>
<td align="left">timezone</td>
<td align="left">网站时区。Hexo 默认使用您电脑的时区。请参考 时区列表 进行设置,如 America/New_York, Japan, 和 UTC 。一般的,对于中国大陆地区可以使用 Asia/Shanghai。</td>
</tr>
</tbody></table>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/31/629500bfaecbf.jpg"/></div></div>
<h2 id="导航菜单"><a href="#导航菜单" class="headerlink" title="导航菜单"></a>导航菜单</h2><p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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="attr">menu:</span></span><br><span class="line"> <span class="attr">Home:</span> <span class="string">/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-home</span></span><br><span class="line"> <span class="attr">Archives:</span> <span class="string">/archives/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-archive</span></span><br><span class="line"> <span class="attr">Tags:</span> <span class="string">/tags/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-tags</span></span><br><span class="line"> <span class="attr">Categories:</span> <span class="string">/categories/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-folder-open</span></span><br><span class="line"> <span class="string">List||fas</span> <span class="attr">fa-list:</span></span><br><span class="line"> <span class="attr">Music:</span> <span class="string">/music/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-music</span></span><br><span class="line"> <span class="attr">Movie:</span> <span class="string">/movies/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-video</span></span><br><span class="line"> <span class="attr">Link:</span> <span class="string">/link/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-link</span></span><br><span class="line"> <span class="attr">About:</span> <span class="string">/about/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-heart</span></span><br></pre></td></tr></table></figure>
<p>必须是 <code>/xxx/</code>,后面<code>||</code>分开,然后写图标名。</p>
<p>如果不希望显示图标,图标名可不写。</p>
<p><a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.0.0">v3.7.1</a> 版本中直接默认子目录是展开的,如果你想要隐藏,后续在魔改中会提到。<br>若主题版本大于 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.0.0">v4.0.0</a>,可以直接在子目录里添加 <code>hide</code> 。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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="attr">menu:</span></span><br><span class="line"> <span class="attr">Home:</span> <span class="string">/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-home</span></span><br><span class="line"> <span class="attr">Archives:</span> <span class="string">/archives/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-archive</span></span><br><span class="line"> <span class="attr">Tags:</span> <span class="string">/tags/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-tags</span></span><br><span class="line"> <span class="attr">Categories:</span> <span class="string">/categories/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-folder-open</span></span><br><span class="line"> <span class="string">List||fas</span> <span class="string">fa-list||hide:</span></span><br><span class="line"> <span class="attr">Music:</span> <span class="string">/music/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-music</span></span><br><span class="line"> <span class="attr">Movie:</span> <span class="string">/movies/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-video</span></span><br><span class="line"> <span class="attr">Link:</span> <span class="string">/link/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-link</span></span><br><span class="line"> <span class="attr">About:</span> <span class="string">/about/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-heart</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>注意: 导航的文字可自行更改</p>
</blockquote>
<p>例如:</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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="attr">menu:</span></span><br><span class="line"> <span class="string">首页:</span> <span class="string">/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-home</span></span><br><span class="line"> <span class="string">时间轴:</span> <span class="string">/archives/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-archive</span></span><br><span class="line"> <span class="string">标签:</span> <span class="string">/tags/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-tags</span></span><br><span class="line"> <span class="string">分类:</span> <span class="string">/categories/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-folder-open</span></span><br><span class="line"> <span class="string">清单||fa</span> <span class="attr">fa-heartbeat:</span></span><br><span class="line"> <span class="string">音乐:</span> <span class="string">/music/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-music</span></span><br><span class="line"> <span class="string">照片:</span> <span class="string">/Gallery/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-images</span></span><br><span class="line"> <span class="string">电影:</span> <span class="string">/movies/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-video</span></span><br><span class="line"> <span class="string">友链:</span> <span class="string">/link/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-link</span></span><br><span class="line"> <span class="string">关于:</span> <span class="string">/about/</span> <span class="string">||</span> <span class="string">fas</span> <span class="string">fa-heart</span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/31/629500c26a299.jpg"/></div></div>
<h2 id="导航栏设置"><a href="#导航栏设置" class="headerlink" title="导航栏设置"></a>导航栏设置</h2><p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">nav:</span></span><br><span class="line"> <span class="attr">logo:</span> <span class="comment">#image</span></span><br><span class="line"> <span class="attr">display_title:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">fixed:</span> <span class="literal">false</span> <span class="comment"># fixed navigation bar</span></span><br></pre></td></tr></table></figure>
<table>
<thead>
<tr>
<th align="left">参数</th>
<th align="left">解释</th>
</tr>
</thead>
<tbody><tr>
<td align="left">logo</td>
<td align="left">网站的 logo,支持图片,直接填入图片链接</td>
</tr>
<tr>
<td align="left">display_title</td>
<td align="left">是否显示网站标题,填写 true 或者 false</td>
</tr>
<tr>
<td align="left">fixed</td>
<td align="left">是否固定状态栏,填写 true 或者 false</td>
</tr>
</tbody></table>
<h2 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h2><blockquote>
<p>代码块中的所有功能只适用于 Hexo 自带的代码渲染。<br>如果使用第三方的渲染器,不一定会有效。</p>
</blockquote>
<h3 id="代码高亮主题"><a href="#代码高亮主题" class="headerlink" title="代码高亮主题"></a>代码高亮主题</h3><p><code>Butterfly</code>支持 6 种代码高亮样式:</p>
<ul>
<li>darker</li>
<li>pale night</li>
<li>light</li>
<li>ocean</li>
<li>mac</li>
<li>mac light</li>
</ul>
<p>修改主题配置文件<code>_config.butterfly.yml</code>。中的<code>highlight_theme</code>属性。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">highlight_theme:</span> <span class="string">light</span></span><br></pre></td></tr></table></figure>
<div class="tabs" id="span-"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#span--1">darker</button></li><li class="tab"><button type="button" data-href="#span--2">pale night</button></li><li class="tab"><button type="button" data-href="#span--3">light</button></li><li class="tab"><button type="button" data-href="#span--4">ocean</button></li><li class="tab"><button type="button" data-href="#span--5">mac</button></li><li class="tab"><button type="button" data-href="#span--6">mac light</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="span--1"><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/31/62962018a84e7.webp"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--2"><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/31/62961859b9e8c.webp"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--3"><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/31/629617b3de088.webp"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--4"><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/31/6296188b7c513.webp"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--5"><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/31/629618f4b4847.webp"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--6"><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/31/629618cb37ff7.webp"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div>
<h3 id="代码复制"><a href="#代码复制" class="headerlink" title="代码复制"></a>代码复制</h3><p>修改主题配置文件<code>_config.butterfly.yml</code>。中的<code>highlight_copy</code>属性。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">highlight_copy:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/31/629502e4c3233.jpg"/></div></div>
<h3 id="代码框展开-x2F-关闭"><a href="#代码框展开-x2F-关闭" class="headerlink" title="代码框展开/关闭"></a>代码框展开/关闭</h3><p>修改主题配置文件<code>_config.butterfly.yml</code>。中的<code>highlight_shrink</code>属性。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">highlight_shrink:</span> <span class="literal">true</span> <span class="comment">#代码框不展开,需点击 '>' 打开</span></span><br></pre></td></tr></table></figure>
<p>在默认情况下,代码框自动展开,可设置是否所有代码框都关闭状态,点击<code>></code>可展开代码。</p>
<ul>
<li>true 全部代码框不展开,需点击<code>></code>打开</li>
<li>false 代码框展开,有<code>></code>点击按钮</li>
<li>none 不显示<code>></code>按钮</li>
</ul>
<div class="tabs" id="span-"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#span--1">true</button></li><li class="tab"><button type="button" data-href="#span--2">false</button></li><li class="tab"><button type="button" data-href="#span--3">none</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="span--1"><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/31/629502e7751d9.jpg"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--2"><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/31/629502e9cfdf7.jpg"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--3"><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/31/629502ec0db67.jpg"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div>
<h3 id="代码换行"><a href="#代码换行" class="headerlink" title="代码换行"></a>代码换行</h3><p>在默认情况下,Hexo 在编译的时候不会实现代码自动换行。如果你不希望在代码块的区域里有横向滚动条的话,那么你可以考虑开启这个功能。</p>
<p>修改主题配置文件<code>_config.butterfly.yml</code>。中的<code>code_word_wrap</code>属性。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">code_word_wrap:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure>
<h3 id="代码高度限制"><a href="#代码高度限制" class="headerlink" title="代码高度限制"></a>代码高度限制</h3><blockquote>
<p><a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.0">v3.7.0</a> 及以上支持。</p>
</blockquote>
<p>可配置代码高度限制,超出的部分会隐藏,并显示展开按钮。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">highlight_height_limit:</span> <span class="literal">false</span> <span class="comment"># unit: px</span></span><br></pre></td></tr></table></figure>
<p>注意:</p>
<ol>
<li>单位是<code>px</code>,直接添加数字,如 200</li>
<li>实际限制高度为<code>highlight_height_limit + 30 px </code>,多增加 30px 限制,目的是避免代码高度只超出highlight_height_limit 一点时,出现展开按钮,展开没内容。</li>
<li>不适用于隐藏后的代码块( css 设置 <code>display: none</code>)。</li>
</ol>
<h2 id="社交图标"><a href="#社交图标" class="headerlink" title="社交图标"></a>社交图标</h2><p><code>Butterfly</code>支持 <a href="https://fontawesome.com/icons?from=io">font-awesome v6</a> 图标。</p>
<p>书写格式:<code>图标名:url || 描述性文字</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">social:</span></span><br><span class="line"> <span class="attr">fab fa-github:</span> <span class="string">https://github.com/xxxxx</span> <span class="string">||</span> <span class="string">Github</span></span><br><span class="line"> <span class="attr">fas fa-envelope:</span> <span class="string">mailto:xxxxxx@gmail.com</span> <span class="string">||</span> <span class="string">Email</span></span><br></pre></td></tr></table></figure>
<h2 id="主页文章节选-自动节选和文章页description"><a href="#主页文章节选-自动节选和文章页description" class="headerlink" title="主页文章节选(自动节选和文章页description)"></a>主页文章节选(自动节选和文章页description)</h2><p>因为主题UI的关係,<code>主页文章节选</code>只支持<code>自动节选</code>和<code>文章页description</code>。</p>
<p>在<code>butterfly</code>里,有四种可供选择</p>
<ol>
<li>description: 只显示description</li>
<li>both: 优先选择description,如果没有配置description,则显示自动节选的内容</li>
<li>auto_excerpt:只显示自动节选</li>
<li>false: 不显示文章内容</li>
</ol>
<p>修改主题配置文件<code>_config.butterfly.yml</code>。中的<code>index_post_content</code>属性。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">index_post_content:</span></span><br><span class="line"> <span class="attr">method:</span> <span class="number">3</span></span><br><span class="line"> <span class="attr">length:</span> <span class="number">500</span> <span class="comment"># if you set method to 2 or 3, the length need to config</span></span><br></pre></td></tr></table></figure>
<p><code>description</code>在front-matter里添加。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2023/04/09/64327947353af.png"/></div></div>
<h2 id="顶部图"><a href="#顶部图" class="headerlink" title="顶部图"></a>顶部图</h2><blockquote>
<p>如果不要显示顶部图,可直接配置 <code>disable_top_img: true</code>。</p>
</blockquote>
<p>顶部图的获取顺序,如果都没有配置,则不显示顶部图。</p>
<p>页面顶部图的获取顺序:<br><code>各自配置的 top_img</code> > <code>配置文件的 default_top_img</code></p>
<p>文章页顶部图的获取顺序:<br><code>各自配置的 top_img</code> > <code>cover</code> > <code>配置文件的 default_top_img</code></p>
<table>
<thead>
<tr>
<th align="left">配置</th>
<th align="left">解释</th>
</tr>
</thead>
<tbody><tr>
<td align="left">index_img</td>
<td align="left">主页的 top_img</td>
</tr>
<tr>
<td align="left">default_top_img</td>
<td align="left">默认的 top_img,当页面的 top_img 没有配置时,会显示 default_top_img</td>
</tr>
<tr>
<td align="left">archive_img</td>
<td align="left">归档页面的 top_img</td>
</tr>
<tr>
<td align="left">tag_img</td>
<td align="left">tag子页面 的 默认 top_img</td>
</tr>
<tr>
<td align="left">tag_per_img</td>
<td align="left">tag子页面的 top_img,可配置每个 tag 的 top_img</td>
</tr>
<tr>
<td align="left">category_img</td>
<td align="left">category 子页面 的 默认 top_img</td>
</tr>
<tr>
<td align="left">category_per_img</td>
<td align="left">category 子页面的 top_img,可配置每个 category 的 top_img</td>
</tr>
</tbody></table>
<p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">index_img:</span> <span class="string">xxx.png</span></span><br></pre></td></tr></table></figure>
<p>其它页面 (tags/categories/自建页面)和文章页的<code>top_img</code>,请到对应的 md 页面设置<code>front-matter</code>中的<code>top_img</code>。</p>
<div class="tabs" id="span-"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#span--1">3.2.0 以下版本</button></li><li class="tab"><button type="button" data-href="#span--2">3.2.0 以上版本</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="span--1"><p><a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.2.0">3.2.0</a> 以下版本的配置只支持</p>
<ul>
<li>留空,true 和 false - 显示默认的颜色</li>
<li>img链接 - 显示所配置的图片</li>
</ul>
<table>
<thead>
<tr>
<th align="left">配置的值</th>
<th align="left">效果</th>
</tr>
</thead>
<tbody><tr>
<td align="left">留空</td>
<td align="left">显示默认的top_img(如有),否则显示默认的颜色 文章页top_img留空的话,会显示 cover 的值)</td>
</tr>
<tr>
<td align="left">img链接</td>
<td align="left">图片的链接,显示所配置的图片</td>
</tr>
<tr>
<td align="left">顔色(<br>HEX值 - #0000FF<br>RGB值 - rgb(0,0,255)<br>顔色单词 - orange<br>渐变色 - linear-gradient( 135deg, #E2B0FF 10%, #9F44D3 100%)<br>)</td>
<td align="left">对应的颜色</td>
</tr>
<tr>
<td align="left">transparent</td>
<td align="left">透明</td>
</tr>
<tr>
<td align="left">false</td>
<td align="left">不显示 top_img</td>
</tr>
</tbody></table><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--2"><p><code>tag_per_img</code>和<code>category_per_img</code>是 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.2.0">v3.2.0</a> 新增的内容,可对 tag 和 category 进行单独的配置。</p>
<p>并不推荐为每个 tag 和每个 category 都配置不同的顶部图,因为配置太多会拖慢生成速度。</p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="string">tag_per_img:</span></span><br><span class="line"> <span class="attr">aplayer:</span> <span class="string">https://xxxxxx.png</span></span><br><span class="line"> <span class="attr">android:</span> <span class="string">ddddddd.png</span></span><br><span class="line"></span><br><span class="line"><span class="string">category_per_img:</span></span><br><span class="line"> <span class="string">随想:</span> <span class="string">hdhdh.png</span></span><br><span class="line"> <span class="string">推荐:</span> <span class="string">ddjdjdjd.png</span></span><br></pre></td></tr></table></figure>
<h2 id="文章置顶"><a href="#文章置顶" class="headerlink" title="文章置顶"></a>文章置顶</h2><p>【推荐】<code>hexo-generator-index</code>从 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/2.0.0">v2.0.0</a> 开始,已经支持文章置顶功能。你可以直接在文章的<code>front-matter</code>区域里添加<code>sticky: 1</code>属性来把这篇文章置顶。数值越大,置顶的优先级越大。</p>
<h2 id="文章封面"><a href="#文章封面" class="headerlink" title="文章封面"></a>文章封面</h2><p>文章的markdown文档上,在<code>Front-matter</code>添加<code>cover</code>,并填上要显示的图片地址。如果不配置<code>cover</code>,可以设置显示默认的<code>cover</code>。</p>
<p>如果不想在首页显示cover,可以设置为<code>false</code>。</p>
<p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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="attr">cover:</span></span><br><span class="line"> <span class="comment"># 是否显示文章封面</span></span><br><span class="line"> <span class="attr">index_enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">aside_enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">archives_enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="comment"># 封面显示的位置</span></span><br><span class="line"> <span class="comment"># 三个值可配置 left , right , both</span></span><br><span class="line"> <span class="attr">position:</span> <span class="string">both</span></span><br><span class="line"> <span class="comment"># 当没有设置cover时,默认的封面显示</span></span><br><span class="line"> <span class="attr">default_cover:</span> </span><br></pre></td></tr></table></figure>
<table>
<thead>
<tr>
<th align="left">参数</th>
<th align="left">解释</th>
</tr>
</thead>
<tbody><tr>
<td align="left">index_enable</td>
<td align="left">主页是否显示文章封面图</td>
</tr>
<tr>
<td align="left">aside_enable</td>
<td align="left">侧栏是否显示文章封面图</td>
</tr>
<tr>
<td align="left">archives_enable</td>
<td align="left">归档页面是否显示文章封面图</td>
</tr>
<tr>
<td align="left">position</td>
<td align="left">主页卡片文章封面的显示位置<br>- left:全部显示在左边<br>- right:全部显示在右边<br>- both:封面位置以左右左右轮流显示</td>
</tr>
<tr>
<td align="left">default_cover</td>
<td align="left">默认的 cover, 可配置图片链接/顔色/渐变色等</td>
</tr>
</tbody></table>
<p>当配置多张图片时,会随机选择一张作为cover,此时写法应为:</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">default_cover:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">https://bu.dusays.com/2022/06/05/629c5257753d1.png</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">https://bu.dusays.com/2022/05/29/62939662553c9.png</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">https://bu.dusays.com/2022/05/26/628fa0426213d.png</span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/31/62962526c257f.jpg"/></div></div>
<h2 id="文章页相关配置"><a href="#文章页相关配置" class="headerlink" title="文章页相关配置"></a>文章页相关配置</h2><h3 id="文章meta显示"><a href="#文章meta显示" class="headerlink" title="文章meta显示"></a>文章meta显示</h3><p><code>post_meta</code>这个属性用于显示文章的相关信息的。</p>
<p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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="attr">post_meta:</span></span><br><span class="line"> <span class="attr">page:</span></span><br><span class="line"> <span class="attr">date_type:</span> <span class="string">both</span> <span class="comment"># created or updated or both 主页文章日期是创建日或者更新日或都显示</span></span><br><span class="line"> <span class="attr">date_format:</span> <span class="string">relative</span> <span class="comment"># date/relative 显示日期还是相对日期</span></span><br><span class="line"> <span class="attr">categories:</span> <span class="literal">true</span> <span class="comment"># true or false 主页是否显示分类</span></span><br><span class="line"> <span class="attr">tags:</span> <span class="literal">true</span> <span class="comment"># true or false 主页是否显示标签</span></span><br><span class="line"> <span class="attr">label:</span> <span class="literal">true</span> <span class="comment"># true or false 显示描述性文字</span></span><br><span class="line"> <span class="attr">post:</span></span><br><span class="line"> <span class="attr">date_type:</span> <span class="string">both</span> <span class="comment"># created or updated or both 文章页日期是创建日或者更新日或都显示</span></span><br><span class="line"> <span class="attr">date_format:</span> <span class="string">relative</span> <span class="comment"># date/relative 显示日期还是相对日期</span></span><br><span class="line"> <span class="attr">categories:</span> <span class="literal">true</span> <span class="comment"># true or false 文章页是否显示分类</span></span><br><span class="line"> <span class="attr">tags:</span> <span class="literal">true</span> <span class="comment"># true or false 文章页是否显示标签</span></span><br><span class="line"> <span class="attr">label:</span> <span class="literal">true</span> <span class="comment"># true or false 显示描述性文字</span></span><br></pre></td></tr></table></figure>
<h3 id="文章版权和协议许可"><a href="#文章版权和协议许可" class="headerlink" title="文章版权和协议许可"></a>文章版权和协议许可</h3><p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">post_copyright:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">decode:</span> <span class="literal">false</span></span><br><span class="line"> <span class="attr">author_href:</span></span><br><span class="line"> <span class="attr">license:</span> <span class="string">CC</span> <span class="string">BY-NC-SA</span> <span class="number">4.0</span></span><br><span class="line"> <span class="attr">license_url:</span> <span class="string">https://creativecommons.org/licenses/by-nc-sa/4.0/</span></span><br></pre></td></tr></table></figure>
<p>由于<code>Hexo 4.1</code>开始,默认对网址进行解码,以至于如果是中文网址,会被解码,可设置<code>decode: true</code>来显示中文网址。</p>
<p>如果有文章(例如:转载文章)不需要显示版权,可以在文章页<code>Front-matter</code>中单独设置。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">copyright:</span> <span class="literal">false</span></span><br></pre></td></tr></table></figure>
<p>从<a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.0.0">v3.0.0</a> 开始,支持对单独文章设置版权信息,可以在文章Front-matter单独设置。</p>
<figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">post<span class="emphasis">_copyright:</span></span><br><span class="line"><span class="emphasis">copyright_</span>author: xxxx</span><br><span class="line">copyright<span class="emphasis">_author_</span>href: https://xxxxxx.com</span><br><span class="line">copyright<span class="emphasis">_url: https://xxxxxx.com</span></span><br><span class="line"><span class="emphasis">copyright_</span>info: 此文章版权归xxxxx所有,如有转载,请註明来自原作者</span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/31/629628278eee0.jpg"/></div></div>
<h3 id="文章打赏"><a href="#文章打赏" class="headerlink" title="文章打赏"></a>文章打赏</h3><p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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="attr">reward:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">QR_code:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">img:</span> <span class="string">/img/wechat.jpg</span></span><br><span class="line"> <span class="attr">link:</span></span><br><span class="line"> <span class="attr">text:</span> <span class="string">wechat</span></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">img:</span> <span class="string">/img/alipay.jpg</span></span><br><span class="line"> <span class="attr">link:</span></span><br><span class="line"> <span class="attr">text:</span> <span class="string">alipay</span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/31/62962b52a1419.webp"/></div></div>
<h3 id="TOC"><a href="#TOC" class="headerlink" title="TOC"></a>TOC</h3><p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">toc:</span></span><br><span class="line"> <span class="attr">post:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">page:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">number:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">expand:</span> <span class="literal">false</span></span><br><span class="line"> <span class="attr">style_simple:</span> <span class="literal">false</span> <span class="comment"># for post</span></span><br><span class="line"> <span class="attr">scroll_percent:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure>
<table>
<thead>
<tr>
<th align="left">属性</th>
<th align="left">解释</th>
</tr>
</thead>
<tbody><tr>
<td align="left">post</td>
<td align="left">文章页是否显示 TOC</td>
</tr>
<tr>
<td align="left">page</td>
<td align="left">普通页面是否显示 TOC</td>
</tr>
<tr>
<td align="left">number</td>
<td align="left">是否显示章节数</td>
</tr>
<tr>
<td align="left">expand</td>
<td align="left">是否展开 TOC</td>
</tr>
<tr>
<td align="left">style_simple</td>
<td align="left">简洁模式(侧边栏只显示 TOC, 只对文章页有效 )</td>
</tr>
<tr>
<td align="left">scroll_percent</td>
<td align="left">是否显示滚动进度百分比</td>
</tr>
</tbody></table>
<h4 id="为特定的文章配置"><a href="#为特定的文章配置" class="headerlink" title="为特定的文章配置"></a>为特定的文章配置</h4><p>在你的文章<code>md</code>文件的头部,加入<code>toc_number</code>和<code>toc</code>,并配置<code>true</code>或者<code>false</code>即可。</p>
<p>主题会优先判断文章 Markdown 的<code>Front-matter</code>是否有配置,如有,则以<code>Front-matter</code>的配置为准。否则,以<code>主题配置文件中</code>的配置为准。</p>
<h3 id="相关文章推荐"><a href="#相关文章推荐" class="headerlink" title="相关文章推荐"></a>相关文章推荐</h3><p>相关文章推荐的原理是根据文章tags的比重来推荐。</p>
<p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">related_post:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">limit:</span> <span class="number">6</span> <span class="comment"># 显示推荐文章数目</span></span><br><span class="line"> <span class="attr">date_type:</span> <span class="string">created</span> <span class="comment"># or created or updated 文章日期显示创建日或者更新日</span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/01/629648ad74ec2.jpg"/></div></div>
<h3 id="文章过期提醒"><a href="#文章过期提醒" class="headerlink" title="文章过期提醒"></a>文章过期提醒</h3><p>可设置是否显示文章过期提醒,以更新时间为基准。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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"># Displays outdated notice for a post (文章过期提醒)</span></span><br><span class="line"><span class="attr">noticeOutdate:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">style:</span> <span class="string">flat</span> <span class="comment"># style: simple/flat</span></span><br><span class="line"> <span class="attr">limit_day:</span> <span class="number">365</span> <span class="comment"># When will it be shown</span></span><br><span class="line"> <span class="attr">position:</span> <span class="string">top</span> <span class="comment"># position: top/bottom</span></span><br><span class="line"> <span class="attr">message_prev:</span> <span class="string">It</span> <span class="string">has</span> <span class="string">been</span></span><br><span class="line"> <span class="attr">message_next:</span> <span class="string">days</span> <span class="string">since</span> <span class="string">the</span> <span class="string">last</span> <span class="string">update,</span> <span class="string">the</span> <span class="string">content</span> <span class="string">of</span> <span class="string">the</span> <span class="string">article</span> <span class="string">may</span> <span class="string">be</span> <span class="string">outdated.</span></span><br></pre></td></tr></table></figure>
<p><code>limit_day</code>: 距离更新时间多少天才显示文章过期提醒。<br><code>message_prev</code> : 天数之前的文字。<br><code>message_next</code>:天数之后的文字。</p>
<h3 id="文章分页按钮"><a href="#文章分页按钮" class="headerlink" title="文章分页按钮"></a>文章分页按钮</h3><p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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"># post_pagination (分页)</span></span><br><span class="line"><span class="comment"># value: 1 || 2 || false</span></span><br><span class="line"><span class="comment"># 1: The 'next post' will link to old post</span></span><br><span class="line"><span class="comment"># 2: The 'next post' will link to new post</span></span><br><span class="line"><span class="comment"># false: disable pagination</span></span><br><span class="line"><span class="attr">post_pagination:</span> <span class="literal">false</span></span><br></pre></td></tr></table></figure>
<table>
<thead>
<tr>
<th align="left">参数</th>
<th align="left">解释</th>
</tr>
</thead>
<tbody><tr>
<td align="left">post_pagination: false</td>
<td align="left">关闭分页按钮</td>
</tr>
<tr>
<td align="left">post_pagination: 1</td>
<td align="left">下一篇显示的是旧文章</td>
</tr>
<tr>
<td align="left">post_pagination: 2</td>
<td align="left">下一篇显示的是新文章</td>
</tr>
</tbody></table>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/01/6296486226891.jpg"/></div></div>
<h2 id="页面锚点"><a href="#页面锚点" class="headerlink" title="页面锚点"></a>页面锚点</h2><p>开启页面锚点后,当你在进行滚动时,页面链接会根据标题ID进行替换</p>
<blockquote>
<p>注意: 每替换一次,会留下一个歷史记录。所以如果一篇文章有很多锚点的话,网页的歷史记录会很多。</p>
</blockquote>
<p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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"># anchor</span></span><br><span class="line"><span class="comment"># when you scroll in post , the url will update according to header id.</span></span><br><span class="line"><span class="attr">anchor:</span></span><br><span class="line"> <span class="attr">button:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">false</span></span><br><span class="line"> <span class="attr">always_show:</span> <span class="literal">false</span></span><br><span class="line"> <span class="attr">icon:</span> <span class="comment"># the unicode value of Font Awesome icon, such as '\3423'</span></span><br><span class="line"> <span class="attr">auto_update:</span> <span class="literal">false</span> <span class="comment"># when you scroll in post, the URL will update according to header id.</span></span><br></pre></td></tr></table></figure>
<h2 id="头像"><a href="#头像" class="headerlink" title="头像"></a>头像</h2><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">avatar:</span></span><br><span class="line"> <span class="attr">img:</span> <span class="string">https://bu.dusays.com/2022/05/02/626f92e193879.jpg</span></span><br><span class="line"> <span class="attr">effect:</span> <span class="literal">true</span> <span class="comment"># 头像会一直转圈</span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/01/629648341b408.jpg"/></div></div>
<h2 id="图片描述"><a href="#图片描述" class="headerlink" title="图片描述"></a>图片描述</h2><p>可开启图片<code>Figcaption</code>描述文字显示,优先显示图片的<code>title</code>属性,然后是<code>alt</code>属性。</p>
<p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">photofigcaption:</span> <span class="literal">false</span></span><br></pre></td></tr></table></figure>
<h2 id="文章内容复制相关配置"><a href="#文章内容复制相关配置" class="headerlink" title="文章内容复制相关配置"></a>文章内容复制相关配置</h2><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="section"># copy settings</span></span><br><span class="line"><span class="section"># copyright: Add the copyright information after copied content (复制的内容后面加上版权信息)</span></span><br><span class="line">copy:</span><br><span class="line"> enable: true</span><br><span class="line"> copyright:</span><br><span class="line"><span class="code"> enable: true</span></span><br><span class="line"><span class="code"> limit_count: 50</span></span><br></pre></td></tr></table></figure>
<table>
<thead>
<tr>
<th align="left">配置</th>
<th align="left">解释</th>
</tr>
</thead>
<tbody><tr>
<td align="left">enable</td>
<td align="left">是否开启网站复制权限</td>
</tr>
<tr>
<td align="left">copyright</td>
<td align="left">复制的内容后面加上版权信息</td>
</tr>
<tr>
<td align="left">enable</td>
<td align="left">是否开启复制版权信息添加</td>
</tr>
<tr>
<td align="left">limit_count</td>
<td align="left">字数限制,当复制文字大于这个字数限制时,将在复制的内容后面加上版权信息</td>
</tr>
</tbody></table>
<p>添加版权后的效果:</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"gallery-group-main"</span>></span></span><br><span class="line">{% galleryGroup '壁纸' '收藏的一些壁纸' '/Gallery/wallpaper' https://bu.dusays.com/2021/03/06/38a2c5cd8b44e.jpg %}</span><br><span class="line">{% galleryGroup '漫威' '关于漫威的图片' '/Gallery/marvel' https://i.loli.net/2019/12/25/8t97aVlp4hgyBGu.jpg %}</span><br><span class="line">{% galleryGroup 'OH MY GIRL' '关于OH MY GIRL的图片' '/Gallery/ohmygirl' https://i.loli.net/2019/12/25/hOqbQ3BIwa6KWpo.jpg %}</span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"></span><br><span class="line">作者: 唐志远</span><br><span class="line">链接: https://fe32.top/articles/hexo541u/</span><br><span class="line">来源: 唐志远个人博客</span><br><span class="line">著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。</span><br></pre></td></tr></table></figure>
<h2 id="Footer-设置"><a href="#Footer-设置" class="headerlink" title="Footer 设置"></a>Footer 设置</h2><h3 id="博客年份"><a href="#博客年份" class="headerlink" title="博客年份"></a>博客年份</h3><p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">footer:</span></span><br><span class="line"> <span class="attr">owner:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">since:</span> <span class="number">2018</span> <span class="comment"># 站点起始时间</span></span><br></pre></td></tr></table></figure>
<h3 id="页脚自定义文本"><a href="#页脚自定义文本" class="headerlink" title="页脚自定义文本"></a>页脚自定义文本</h3><p><code>custom_text</code>是一个给你用来在页脚自定义文本的选项。通常你可以在这里写声明文本等。支持 HTML。</p>
<p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<p>本人的页脚如下,若你在配置时没有出现github徽章,请参考教程 <a href="https://fe32.top/articles/kfwr2gpa/">添加Github徽标</a> 。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">custom_text:</span> <span class="string">I</span> <span class="string">wish</span> <span class="string">you</span> <span class="string">to</span> <span class="string">become</span> <span class="string">your</span> <span class="string">own</span> <span class="string">sun,</span> <span class="literal">no</span> <span class="string">need</span> <span class="string">to</span> <span class="string">rely</span> <span class="string">on</span> <span class="string">who's</span> <span class="string">light.<p><a</span> <span class="string">target="_blank"</span> <span class="string">href="https://hexo.io/"><img</span> <span class="string">src="https://img.shields.io/badge/Frame-Hexo-blue?style=flat&logo=hexo"</span> <span class="string">title="博客框架为Hexo"></a>&nbsp;<a</span> <span class="string">target="_blank"</span> <span class="string">href="https://butterfly.js.org/"><img</span> <span class="string">src="https://img.shields.io/badge/Theme-Butterfly-6513df?style=flat&logo=bitdefender"</span> <span class="string">title="主题采用butterfly"></a>&nbsp;<a</span> <span class="string">target="_blank"</span> <span class="string">href="https://www.jsdelivr.com/"><img</span> <span class="string">src="https://img.shields.io/badge/CDN-jsDelivr-orange?style=flat&logo=jsDelivr"</span> <span class="string">title="本站使用JsDelivr为静态资源提供CDN加速"></a></span> <span class="string">&nbsp;<a</span> <span class="string">target="_blank"</span> <span class="string">href="https://vercel.com/</span> <span class="string">"><img src="</span><span class="string">https://img.shields.io/badge/Hosted-Vervel-brightgreen?style=flat&logo=Vercel"</span> <span class="string">title="本站采用双线部署,默认线路托管于Vercel"></a>&nbsp;<a</span> <span class="string">target="_blank"</span> <span class="string">href="https://vercel.com/</span> <span class="string">"><img src="</span><span class="string">https://img.shields.io/badge/Hosted-Coding-0cedbe?style=flat&logo=Codio"</span> <span class="string">title="本站采用双线部署,联通线路托管于Coding"></a>&nbsp;<a</span> <span class="string">target="_blank"</span> <span class="string">href="https://github.com/"><img</span> <span class="string">src="https://img.shields.io/badge/Source-Github-d021d6?style=flat&logo=GitHub"</span> <span class="string">title="本站项目由Gtihub托管"></a>&nbsp;<a</span> <span class="string">target="_blank"</span> <span class="string">href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img</span> <span class="string">src="https://img.shields.io/badge/Copyright-BY--NC--SA%204.0-d42328?style=flat&logo=Claris"</span> <span class="string">title="本站采用知识共享署名-非商业性使用-相同方式共享4.0国际许可协议进行许可"></a></p></span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/01/6296491902973.jpg"/></div></div>
<p>对于部分人需要写 ICP 的,也可以写在<code>custom_text</code>里。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">custom_text:</span> <span class="string"><a</span> <span class="string">href="icp链接"><img</span> <span class="string">class="icp-icon"</span> <span class="string">src="icp图片"><span>备案号:xxxxxx</span></a></span></span><br></pre></td></tr></table></figure>
<h2 id="右下角按钮"><a href="#右下角按钮" class="headerlink" title="右下角按钮"></a>右下角按钮</h2><h3 id="简繁转换"><a href="#简繁转换" class="headerlink" title="简繁转换"></a>简繁转换</h3><p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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="attr">translate:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">false</span></span><br><span class="line"> <span class="comment"># 默认按钮显示文字(网站是简体,应设置为'default: 繁')</span></span><br><span class="line"> <span class="attr">default:</span> <span class="string">繁</span></span><br><span class="line"> <span class="comment"># the language of website (1 - Traditional Chinese/ 2 - Simplified Chinese)</span></span><br><span class="line"> <span class="comment"># 网站默认语言,1: 繁体中文, 2: 简体中文</span></span><br><span class="line"> <span class="attr">defaultEncoding:</span> <span class="number">2</span></span><br><span class="line"> <span class="comment"># Time delay 延迟时间,若不在前, 要设定延迟翻译时间, 如100表示100ms,默认为0</span></span><br><span class="line"> <span class="attr">translateDelay:</span> <span class="number">0</span></span><br><span class="line"> <span class="comment"># 当文字是简体时,按钮显示的文字</span></span><br><span class="line"> <span class="attr">msgToTraditionalChinese:</span> <span class="string">'繁'</span></span><br><span class="line"> <span class="comment"># 当文字是繁体时,按钮显示的文字</span></span><br><span class="line"> <span class="attr">msgToSimplifiedChinese:</span> <span class="string">'簡'</span></span><br></pre></td></tr></table></figure>
<h3 id="夜间模式"><a href="#夜间模式" class="headerlink" title="夜间模式"></a>夜间模式</h3><p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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"># dark mode</span></span><br><span class="line"><span class="attr">darkmode:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">false</span></span><br><span class="line"> <span class="comment"># dark 和 light 两种模式切换按钮</span></span><br><span class="line"> <span class="attr">button:</span> <span class="literal">true</span></span><br><span class="line"> <span class="comment"># Switch dark/light mode automatically (自動切換 dark mode和 light mode)</span></span><br><span class="line"> <span class="comment"># autoChangeMode: 1 Following System Settings, if the system doesn't support dark mode, it will switch dark mode between 6 pm to 6 am</span></span><br><span class="line"> <span class="comment"># autoChangeMode: 2 Switch dark mode between 6 pm to 6 am</span></span><br><span class="line"> <span class="comment"># autoChangeMode: false</span></span><br><span class="line"> <span class="attr">autoChangeMode:</span> <span class="literal">false</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p><a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/2.0.0">v2.0.0</a> 开始增加一个选项,可开启自动切换 light mode 和 dark mode。<br>autoChangeMode: 1 跟随系统而变化,不支持的浏览器/系统将按照时间晚上6点到早上6点之间切换为 dark mode。<br>autoChangeMode: 2 只按照时间 晚上6点到早上6点之间切换为 dark mode,其余时间为light mode。<br>autoChangeMode: false 取消自动切换。</p>
</blockquote>
<h3 id="阅读模式"><a href="#阅读模式" class="headerlink" title="阅读模式"></a>阅读模式</h3><p>阅读模式下会去掉除文章外的内容,避免干扰阅读。只会出现在文章页面,右下角会有阅读模式按钮。</p>
<p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">readmode:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/01/629649534ee1a.jpg"/></div></div>
<h3 id="滚动状态百分比"><a href="#滚动状态百分比" class="headerlink" title="滚动状态百分比"></a>滚动状态百分比</h3><p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># show scroll percent in scroll-to-top button</span></span><br><span class="line"><span class="attr">rightside_scroll_percent:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure>
<h3 id="按钮排序"><a href="#按钮排序" class="headerlink" title="按钮排序"></a>按钮排序</h3><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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"># Don't modify the following settings unless you know how they work (非必要请不要修改 )</span></span><br><span class="line"><span class="comment"># Choose: readmode,translate,darkmode,hideAside,toc,chat,comment</span></span><br><span class="line"><span class="comment"># Don't repeat 不要重复</span></span><br><span class="line"><span class="attr">rightside_item_order:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">false</span></span><br><span class="line"> <span class="attr">hide:</span> <span class="comment"># readmode,translate,darkmode,hideAside</span></span><br><span class="line"> <span class="attr">show:</span> <span class="comment"># toc,chat,comment</span></span><br></pre></td></tr></table></figure>
<h2 id="侧边栏设置"><a href="#侧边栏设置" class="headerlink" title="侧边栏设置"></a>侧边栏设置</h2><h3 id="侧边排版"><a href="#侧边排版" class="headerlink" title="侧边排版"></a>侧边排版</h3><p>可自行决定哪个项目需要显示,可决定位置,也可以设置不显示侧边栏。</p>
<p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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"><span class="attr">aside:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">hide:</span> <span class="literal">false</span></span><br><span class="line"> <span class="attr">button:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">mobile:</span> <span class="literal">true</span> <span class="comment"># display on mobile</span></span><br><span class="line"> <span class="attr">position:</span> <span class="string">right</span> <span class="comment"># left or right</span></span><br><span class="line"> <span class="attr">display:</span></span><br><span class="line"> <span class="attr">archive:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">tag:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">category:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">card_author:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">description:</span></span><br><span class="line"> <span class="attr">button:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">icon:</span> <span class="string">fab</span> <span class="string">fa-github</span></span><br><span class="line"> <span class="attr">text:</span> <span class="string">Follow</span> <span class="string">Me</span></span><br><span class="line"> <span class="attr">link:</span> <span class="string">https://github.com/xxxxxx</span></span><br><span class="line"> <span class="attr">card_announcement:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">content:</span> <span class="string">This</span> <span class="string">is</span> <span class="string">my</span> <span class="string">Blog</span></span><br><span class="line"> <span class="attr">card_recent_post:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">limit:</span> <span class="number">5</span> <span class="comment"># if set 0 will show all</span></span><br><span class="line"> <span class="attr">sort:</span> <span class="string">date</span> <span class="comment"># date or updated</span></span><br><span class="line"> <span class="attr">sort_order:</span> <span class="comment"># Don't modify the setting unless you know how it works</span></span><br><span class="line"> <span class="attr">card_categories:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">limit:</span> <span class="number">8</span> <span class="comment"># if set 0 will show all</span></span><br><span class="line"> <span class="attr">expand:</span> <span class="string">none</span> <span class="comment"># none/true/false</span></span><br><span class="line"> <span class="attr">sort_order:</span> <span class="comment"># Don't modify the setting unless you know how it works</span></span><br><span class="line"> <span class="attr">card_tags:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">limit:</span> <span class="number">40</span> <span class="comment"># if set 0 will show all</span></span><br><span class="line"> <span class="attr">color:</span> <span class="literal">false</span></span><br><span class="line"> <span class="attr">orderby:</span> <span class="string">random</span> <span class="comment"># Order of tags, random/name/length</span></span><br><span class="line"> <span class="attr">order:</span> <span class="number">1</span> <span class="comment"># Sort of order. 1, asc for ascending; -1, desc for descending</span></span><br><span class="line"> <span class="attr">sort_order:</span> <span class="comment"># Don't modify the setting unless you know how it works</span></span><br><span class="line"> <span class="attr">card_archives:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">type:</span> <span class="string">monthly</span> <span class="comment"># yearly or monthly</span></span><br><span class="line"> <span class="attr">format:</span> <span class="string">MMMM</span> <span class="string">YYYY</span> <span class="comment"># eg: YYYY年MM月</span></span><br><span class="line"> <span class="attr">order:</span> <span class="number">-1</span> <span class="comment"># Sort of order. 1, asc for ascending; -1, desc for descending</span></span><br><span class="line"> <span class="attr">limit:</span> <span class="number">8</span> <span class="comment"># if set 0 will show all</span></span><br><span class="line"> <span class="attr">sort_order:</span> <span class="comment"># Don't modify the setting unless you know how it works</span></span><br><span class="line"> <span class="attr">card_webinfo:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">post_count:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">last_push_date:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">sort_order:</span> <span class="comment"># Don't modify the setting unless you know how it works</span></span><br></pre></td></tr></table></figure>
<h3 id="访问人数-UV-和-PV"><a href="#访问人数-UV-和-PV" class="headerlink" title="访问人数(UV 和 PV)"></a>访问人数(UV 和 PV)</h3><p>详细信息请查看 <a href="http://busuanzi.ibruce.info/">busuanzi官方网站</a> 。</p>
<p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">busuanzi:</span></span><br><span class="line"> <span class="attr">site_uv:</span> <span class="literal">true</span> <span class="comment"># 本站总访客数</span></span><br><span class="line"> <span class="attr">site_pv:</span> <span class="literal">true</span> <span class="comment"># 本站总访问量 </span></span><br><span class="line"> <span class="attr">page_pv:</span> <span class="literal">true</span> <span class="comment"># 本文总阅读量</span></span><br></pre></td></tr></table></figure>
<h3 id="运行时间"><a href="#运行时间" class="headerlink" title="运行时间"></a>运行时间</h3><p>网页已运行时间。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">runtimeshow:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">publish_date:</span> <span class="number">6</span><span class="string">/7/2018</span> <span class="number">00</span><span class="string">:00:00</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></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/01/62964967422c1.jpg"/></div></div>
<h3 id="最新评论"><a href="#最新评论" class="headerlink" title="最新评论"></a>最新评论</h3><blockquote>
<p><a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.1.0">v3.1.0</a> 以上支持。如果未配置任何评论,前先不要开启该功能。<br>最新评论只会在刷新时才会去读取,并不会实时变化。<br>由于 API 有 访问次数限制,为了避免调用太多,主题默认存取期限为 10 分鐘。也就是説,调用后资料会存在 localStorage 里,10分鐘内刷新网站只会去 localStorage 读取资料。 10 分鐘期限一过,刷新页面时才会去调取 API 读取新的数据。( 3.6.0 新增了 storage 配置,可自行配置缓存时间)。</p>
</blockquote>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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"># Aside widget - Newest Comments</span></span><br><span class="line"><span class="attr">newest_comments:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">sort_order:</span> <span class="comment"># Don't modify the setting unless you know how it works</span></span><br><span class="line"> <span class="attr">limit:</span> <span class="number">6</span> <span class="comment"># 显示的数量</span></span><br><span class="line"> <span class="attr">storage:</span> <span class="number">10</span> <span class="comment"># 设置缓存时间,单位 分钟 </span></span><br><span class="line"> <span class="attr">avatar:</span> <span class="literal">true</span> <span class="comment"># 是否显示头像</span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/01/62964981292d6.jpg"/></div></div>
<h2 id="网站背景"><a href="#网站背景" class="headerlink" title="网站背景"></a>网站背景</h2><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 图片格式 url(http://xxxxxx.com/xxx.jpg)</span></span><br><span class="line"><span class="comment"># 颜色(HEX值/RGB值/颜色单词/渐变色)</span></span><br><span class="line"><span class="comment"># 留空 不显示背景</span></span><br><span class="line"><span class="attr">background:</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>如果你的网站根目录不是’/‘,使用本地图片时,需加上你的根目录。<br>例如:网站是 <code>https://yoursite.com/blog</code>,引用一张img/xx.png图片,则设置background为 `url(/blog/img/xx.png)</p>
</blockquote>
<h2 id="打字效果"><a href="#打字效果" class="headerlink" title="打字效果"></a>打字效果</h2><p>传送门:<a href="https://github.com/disjukr/activate-power-mode">activate-power-mode</a>。</p>
<p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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"># Typewriter Effect (打字效果)</span></span><br><span class="line"><span class="comment"># https://github.com/disjukr/activate-power-mode</span></span><br><span class="line"><span class="attr">activate_power_mode:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">false</span></span><br><span class="line"> <span class="attr">colorful:</span> <span class="literal">true</span> <span class="comment"># open particle animation (冒光特效)</span></span><br><span class="line"> <span class="attr">shake:</span> <span class="literal">true</span> <span class="comment"># open shake (抖動特效)</span></span><br><span class="line"> <span class="attr">mobile:</span> <span class="literal">false</span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/13/6370e4439260e.gif"/></div></div>
<h2 id="footer-背景"><a href="#footer-背景" class="headerlink" title="footer 背景"></a>footer 背景</h2><p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># footer是否显示图片背景(与top_img一致)</span></span><br><span class="line"><span class="attr">footer_bg:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure>
<table>
<thead>
<tr>
<th align="left">配置的值</th>
<th align="left">效果</th>
</tr>
</thead>
<tbody><tr>
<td align="left">留空/false</td>
<td align="left">显示默认的颜色</td>
</tr>
<tr>
<td align="left">img链接</td>
<td align="left">图片的链接,显示所配置的图片</td>
</tr>
<tr>
<td align="left">顔色(<br>HEX值 - #0000FF<br>RGB值 - rgb(0,0,255)<br>顔色单词 - orange<br>渐变色 - linear-gradient( 135deg, #E2B0FF 10%, #9F44D3 100%)<br>)</td>
<td align="left">对应的颜色</td>
</tr>
<tr>
<td align="left">true</td>
<td align="left">显示跟 top_img 一样</td>
</tr>
</tbody></table>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/02/629793c0c616c.webp"/></div></div>
<h2 id="背景特效"><a href="#背景特效" class="headerlink" title="背景特效"></a>背景特效</h2><div class="tabs" id="span-"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#span--1">静止彩带</button></li><li class="tab"><button type="button" data-href="#span--2">动态彩带</button></li><li class="tab"><button type="button" data-href="#span--3">canvas_nest</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="span--1"><p>可设置每次刷新更换彩带,或者每次点击更换彩带。详细配置可查看<a href="https://github.com/hustcc/ribbon.js">canvas_ribbon</a>。<br>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">canvas_ribbon:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">false</span></span><br><span class="line"> <span class="attr">size:</span> <span class="number">150</span></span><br><span class="line"> <span class="attr">alpha:</span> <span class="number">0.6</span></span><br><span class="line"> <span class="attr">zIndex:</span> <span class="number">-1</span></span><br><span class="line"> <span class="attr">click_to_change:</span> <span class="literal">false</span> <span class="comment">#設置是否每次點擊都更換彩带</span></span><br><span class="line"> <span class="attr">mobile:</span> <span class="literal">false</span> <span class="comment"># false 手機端不顯示 true 手機端顯示</span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/13/6370e38d4a270.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--2"><p>好看的彩带背景,会飘动。<br>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">canvas_fluttering_ribbon:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">mobile:</span> <span class="literal">true</span> <span class="comment"># false 手机端不显示 true 手机端显示</span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/13/6370e41b8070a.gif"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--3"><p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">canvas_nest:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">color:</span> <span class="string">'0,0,255'</span> <span class="comment">#color of lines, default: '0,0,0'; RGB values: (R,G,B).(<span class="doctag">note:</span> use ',' to separate.)</span></span><br><span class="line"> <span class="attr">opacity:</span> <span class="number">0.7</span> <span class="comment"># the opacity of line (0~1), default: 0.5.</span></span><br><span class="line"> <span class="attr">zIndex:</span> <span class="number">-1</span> <span class="comment"># z-index property of the background, default: -1.</span></span><br><span class="line"> <span class="attr">count:</span> <span class="number">99</span> <span class="comment"># the number of lines, default: 99.</span></span><br><span class="line"> <span class="attr">mobile:</span> <span class="literal">false</span> <span class="comment"># false 手機端不顯示 true 手機端顯示</span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/13/6370e60740d1a.gif"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div>
<h2 id="鼠标点击效果"><a href="#鼠标点击效果" class="headerlink" title="鼠标点击效果"></a>鼠标点击效果</h2><div class="tabs" id="span-"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#span--1"><i class="fas fa-fire-alt"></i>烟花</button></li><li class="tab"><button type="button" data-href="#span--2"><i class="fas fa-heart"></i>爱心</button></li><li class="tab"><button type="button" data-href="#span--3"><i class="fab fa-amilia"></i>文字</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="span--1"><p><code>zIndex</code>建议只在<code>-1</code>和<code>9999</code>上选。<br><code>-1</code> 代表烟火效果在底部。<br><code>9999</code> 代表烟火效果在前面。</p>
<p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">fireworks:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">zIndex:</span> <span class="number">9999</span> <span class="comment"># -1 or 9999</span></span><br><span class="line"> <span class="attr">mobile:</span> <span class="literal">false</span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/13/6370e635f16d5.gif"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--2"><p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 点击出現爱心</span></span><br><span class="line"><span class="attr">click_heart:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">mobile:</span> <span class="literal">false</span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/13/6370e65628a0e.gif"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--3"><p>修改主题配置文件<code>_config.butterfly.yml</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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="attr">ClickShowText:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">false</span></span><br><span class="line"> <span class="attr">text:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">I</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">LOVE</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">YOU</span></span><br><span class="line"> <span class="attr">fontSize:</span> <span class="string">15px</span></span><br><span class="line"> <span class="attr">random:</span> <span class="literal">false</span> <span class="comment"># 文字随机显示</span></span><br><span class="line"> <span class="attr">mobile:</span> <span class="literal">false</span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/11/13/6370e671d8174.gif"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div>
<h2 id="自定义字体和字体大小"><a href="#自定义字体和字体大小" class="headerlink" title="自定义字体和字体大小"></a>自定义字体和字体大小</h2><h3 id="全局字体"><a href="#全局字体" class="headerlink" title="全局字体"></a>全局字体</h3><p>修改主题配置文件<code>_config.butterfly.yml</code>中的<code>font-family</code>属性即可,如不需要配置,请留空。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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"># Global font settings</span></span><br><span class="line"><span class="comment"># Don't modify the following settings unless you know how they work (非必要不要修改)</span></span><br><span class="line"><span class="attr">font:</span></span><br><span class="line"> <span class="attr">global-font-size:</span></span><br><span class="line"> <span class="attr">code-font-size:</span></span><br><span class="line"> <span class="attr">font-family:</span> <span class="string">-apple-system,</span> <span class="string">BlinkMacSystemFont,</span> <span class="string">"Segoe UI"</span><span class="string">,</span> <span class="string">"Helvetica Neue"</span><span class="string">,</span> <span class="string">Lato,</span> <span class="string">Roboto,</span> <span class="string">"PingFang SC"</span><span class="string">,</span> <span class="string">"Microsoft JhengHei"</span><span class="string">,</span> <span class="string">"Microsoft YaHei"</span><span class="string">,</span> <span class="string">sans-serif</span></span><br><span class="line"> <span class="attr">code-font-family:</span> <span class="string">consolas,</span> <span class="string">Menlo,</span> <span class="string">"PingFang SC"</span><span class="string">,</span> <span class="string">"Microsoft JhengHei"</span><span class="string">,</span> <span class="string">"Microsoft YaHei"</span><span class="string">,</span> <span class="string">sans-serif</span></span><br></pre></td></tr></table></figure>
<h3 id="Blog-标题字体"><a href="#Blog-标题字体" class="headerlink" title="Blog 标题字体"></a>Blog 标题字体</h3><p>修改主题配置文件<code>_config.butterfly.yml</code>中的<code>blog_title_font</code>属性即可,如不需要配置,请留空。<br>如不需要使用网络字体,只需要把font_link留空就行。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Font settings for the site title and site subtitle</span></span><br><span class="line"><span class="comment"># 左上角网站名字 主页居中网站名字</span></span><br><span class="line"><span class="attr">blog_title_font:</span></span><br><span class="line"> <span class="attr">font_link:</span> <span class="string">https://fonts.googleapis.com/css?family=Titillium+Web&display=swap</span></span><br><span class="line"> <span class="attr">font-family:</span> <span class="string">Titillium</span> <span class="string">Web,</span> <span class="string">'PingFang SC'</span><span class="string">,</span> <span class="string">'Hiragino Sans GB'</span><span class="string">,</span> <span class="string">'Microsoft JhengHei'</span><span class="string">,</span> <span class="string">'Microsoft YaHei'</span><span class="string">,</span> <span class="string">sans-serif</span></span><br></pre></td></tr></table></figure>
<h2 id="网站副标题"><a href="#网站副标题" class="headerlink" title="网站副标题"></a>网站副标题</h2><p>可设置主页中显示的网站副标题或者喜欢的座右铭。</p>
<p>修改主题配置文件<code>_config.butterfly.yml</code>中的<code>subtitle</code>。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Site</span></span><br><span class="line"><span class="attr">subtitle:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">false</span></span><br><span class="line"> <span class="comment"># Typewriter Effect (打字效果)</span></span><br><span class="line"> <span class="attr">effect:</span> <span class="literal">true</span></span><br><span class="line"> <span class="comment"># loop (循環打字)</span></span><br><span class="line"> <span class="attr">loop:</span> <span class="literal">true</span></span><br><span class="line"> <span class="comment"># source調用第三方服務</span></span><br><span class="line"> <span class="comment"># source: false 關閉調用</span></span><br><span class="line"> <span class="comment"># source: 1 調用搏天api的隨機語錄(簡體)</span></span><br><span class="line"> <span class="comment"># source: 2 調用一言網的一句話(簡體)</span></span><br><span class="line"> <span class="comment"># source: 3 調用一句網(簡體)</span></span><br><span class="line"> <span class="comment"># source: 4 調用今日詩詞(簡體)</span></span><br><span class="line"> <span class="comment"># subtitle 會先顯示 source , 再顯示 sub 的內容</span></span><br><span class="line"> <span class="attr">source:</span> <span class="literal">false</span></span><br><span class="line"> <span class="comment"># 如果有英文逗號' , ',請使用轉義字元 &#44;</span></span><br><span class="line"> <span class="comment"># 如果有英文雙引號' " ',請使用轉義字元 &quot;</span></span><br><span class="line"> <span class="comment"># 開頭不允許轉義字元,如需要,請把整個句子用雙引號包住</span></span><br><span class="line"> <span class="comment"># 如果關閉打字效果,subtitle只會顯示sub的第一行文字</span></span><br><span class="line"> <span class="attr">sub:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">窗外有月色和雨&#44;而我在想你。</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">There</span> <span class="string">is</span> <span class="string">moonlight</span> <span class="string">and</span> <span class="string">rain</span> <span class="string">outside</span> <span class="string">the</span> <span class="string">window,</span> <span class="string">and</span> <span class="string">I</span> <span class="string">miss</span> <span class="string">you.</span></span><br></pre></td></tr></table></figure>
<p>预览效果见本站主页:<a href="https://fe32.top/">唐志远个人博客</a>。</p>
<h2 id="页面加载动画preloader"><a href="#页面加载动画preloader" class="headerlink" title="页面加载动画preloader"></a>页面加载动画preloader</h2><blockquote>
<p>主题版本 < 5.0</p>
</blockquote>
<p>当进入网页时,因为加载速度的问题,可能会导致top_img图片出现断层显示,或者网页加载不全而出现等待时间,开启preloader后,会显示加载动画,等页面加载完,加载动画会消失。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 加载动画 Loading Animation</span></span><br><span class="line"><span class="attr">preloader:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>主题版本 >= 5.0</p>
</blockquote>
<p>preloader 设置更改,增加 <a href="https://codebyzach.github.io/pace/">pace.js</a> 加载动画条</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 加载动画 Loading Animation</span></span><br><span class="line"><span class="attr">preloader:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">false</span></span><br><span class="line"> <span class="comment"># source</span></span><br><span class="line"> <span class="comment"># 1. fullpage-loading</span></span><br><span class="line"> <span class="comment"># 2. pace (progress bar)</span></span><br><span class="line"> <span class="attr">source:</span> <span class="number">1</span></span><br><span class="line"> <span class="comment"># pace theme (see https://codebyzach.github.io/pace/)</span></span><br><span class="line"> <span class="attr">pace_css_url:</span></span><br></pre></td></tr></table></figure>
<h2 id="字数统计"><a href="#字数统计" class="headerlink" title="字数统计"></a>字数统计</h2><ol>
<li>安装插件:在你的博客根目录,打开cmd命令窗口执行<code>npm install hexo-wordcount --save</code>。</li>
<li>开启配置:修改主题配置文件<code>_config.butterfly.yml</code>中的<code>wordcount</code>。</li>
</ol>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">wordcount:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">post_wordcount:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">min2read:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">total_wordcount:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure>
<h2 id="图片大图查看模式"><a href="#图片大图查看模式" class="headerlink" title="图片大图查看模式"></a>图片大图查看模式</h2><blockquote>
<p>只能开启一个。<br>如果你并不想为某张图片添加大图查看模式,你可以使用 html 格式引用图片,并为图片添加 <code>no-lightbox</code> class 名,例如:<code><img src="xxxx.jpg" class="no-lightbox"></code>。</p>
</blockquote>
<div class="tabs" id="span-"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#span--1">fancybox(推荐)</button></li><li class="tab"><button type="button" data-href="#span--2">medium_zoom</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="span--1"><p>修改主题配置文件<code>_config.butterfly.yml</code>中<code>fancybox</code>属性。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># fancybox http://fancyapps.com/fancybox/3/</span></span><br><span class="line"><span class="attr">fancybox:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--2"><p>修改主题配置文件<code>_config.butterfly.yml</code>中<code>medium_zoom</code>属性。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">medium_zoom:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div>
<h2 id="Pjax"><a href="#Pjax" class="headerlink" title="Pjax"></a>Pjax</h2><p>当用户点击链接,通过 <strong>ajax</strong> 更新页面需要变化的部分,然后使用 <strong>HTML5</strong> 的 <strong>pushState</strong> 修改浏览器的 <strong>URL</strong> 地址。</p>
<p>这样可以不用重复加载相同的资源(css/js), 从而提升网页的加载速度。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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"># Pjax [Beta]</span></span><br><span class="line"><span class="comment"># It may contain bugs and unstable, give feedback when you find the bugs.</span></span><br><span class="line"><span class="comment"># https://github.com/MoOx/pjax</span></span><br><span class="line"><span class="attr">pjax:</span> </span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="comment"># 对于一些第三方插件,有些并不支持 pjax 。</span></span><br><span class="line"> <span class="comment"># 你可以把网页加入到 exclude 里,这个网页会被 pjax 排除在外。</span></span><br><span class="line"> <span class="comment"># 点击该网页会重新加载网站。</span></span><br><span class="line"> <span class="attr">exclude:</span> </span><br><span class="line"> <span class="bullet">-</span> <span class="string">/music/</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">/no-pjax/</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>使用 pjax 后,一些自己DIY的js可能会无效,跳转页面时需要重新调用,请参考<a href="https://github.com/MoOx/pjax">Pjax文档</a>。</p>
</blockquote>
<h2 id="Inject"><a href="#Inject" class="headerlink" title="Inject"></a>Inject</h2><blockquote>
<p><a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/2.3.0">v2.3.0</a>以上支持。</p>
</blockquote>
<p>如想添加额外的 js/css/meta 等等东西,可以在 Inject 里添加,head(<code></body></code>标签之前), bottom(<code></html></code>标签之前)。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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"># Inject</span></span><br><span class="line"><span class="comment"># Insert the code to head (before '</head>' tag) and the bottom (before '</body>' tag)</span></span><br><span class="line"><span class="comment"># 插入代码到头部 </head> 之前 和 底部 </body> 之前</span></span><br><span class="line"><span class="attr">inject:</span></span><br><span class="line"> <span class="attr">head:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string"><link</span> <span class="string">rel="stylesheet"</span> <span class="string">href="/xxx.css"></span></span><br><span class="line"> <span class="attr">bottom:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string"><script</span> <span class="string">src="xxxx"></script></span></span><br></pre></td></tr></table></figure>
<h2 id="CDN"><a href="#CDN" class="headerlink" title="CDN"></a>CDN</h2><p>这部分不再维护,请移步 <a href="https://butterfly.js.org/posts/ceeb73f/#CDN">官方文档</a> 或 参考以下教程。</p>
<p><a href="https://blog.csdn.net/VariatioZbw/article/details/107822562">常用CDN网站汇总</a><br><a href="https://blog.zhheo.com/p/790087d9.html">张洪:Butterfly CDN链接更改指南,替换jsdelivr提升访问速度</a><br><a href="https://anzhiy.cn/posts/fe76.html">安知鱼:目前可用cdn整理</a></p>
<h2 id="搜索系统"><a href="#搜索系统" class="headerlink" title="搜索系统"></a>搜索系统</h2><div class="tag link"><a class="link-card" title="基于 Hexo 键入搜索功能" href="https://fe32.top/articles/hexo1607/"><div class="left"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/30/62bdbf2939508.png"/></div><div class="right"><p class="text">基于 Hexo 键入搜索功能</p><p class="url">https://fe32.top/articles/hexo1607/</p></div></a></div>
<h2 id="分享"><a href="#分享" class="headerlink" title="分享"></a>分享</h2><div class="tag link"><a class="link-card" title="基于 Hexo 键入分享功能" href="https://fe32.top/articles/hexo1609/"><div class="left"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/06/30/62bdbf2939508.png"/></div><div class="right"><p class="text">基于 Hexo 键入分享功能</p><p class="url">https://fe32.top/articles/hexo1609/</p></div></a></div>
<h2 id="在线聊天"><a href="#在线聊天" class="headerlink" title="在线聊天"></a>在线聊天</h2><div class="tag link"><a class="link-card" title="基于 Hexo 键入在线聊天功能" href="https://fe32.top/articles/hexo1610/"><div class="left"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/07/17/62d3a15f8bcf7.png"/></div><div class="right"><p class="text">基于 Hexo 键入在线聊天功能</p><p
基于 Hexo 从零开始搭建个人博客(四)
https://fe32.top/articles/hexo1604/
2022-05-26T15:38:14.000Z
2024-03-13T07:46:46.760Z
<blockquote>
<p>阅读本篇前,请先阅读前几篇文章:<br><a href="https://fe32.top/articles/hexo1601/">基于 Hexo 从零开始搭建个人博客(一)</a><br><a href="https://fe32.top/articles/hexo1602/">基于 Hexo 从零开始搭建个人博客(二)</a><br><a href="https://fe32.top/articles/hexo1603/">基于 Hexo 从零开始搭建个人博客(三)</a></p>
</blockquote>
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><ol>
<li>博客搭建过程遇到任何问题,优先在本页面搜索,检查是否已经有该配置教程。</li>
<li>遇到问题可以优先在文章评论区留言,注意留言时请填写正确的邮箱以确保能收到站长的回复。</li>
<li>实在解决不了的问题可添加博主 Wechat ,添加好友时请备注自己的姓名+专业,如 张三 计算机科学与技术。</li>
</ol>
<h2 id="Front-matter"><a href="#Front-matter" class="headerlink" title="Front-matter"></a>Front-matter</h2><p><strong>Front-matter</strong> 是 <strong>markdown</strong> 文件最上方以<code>---</code>分隔的区域,用于指定个别档案的变数。</p>
<ul>
<li>Page Front-matter 用于页面配置</li>
<li>Post Front-matter 用于文章页配置</li>
</ul>
<div class="note purple no-icon flat"><p>如果标注可选的参数,可根据自己需要添加,不用全部都写在markdown里</p>
</div>
<h3 id="Page-Front-matter"><a href="#Page-Front-matter" class="headerlink" title="Page Front-matter"></a>Page Front-matter</h3><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">title:</span><br><span class="line">date:</span><br><span class="line">updated:</span><br><span class="line">type:</span><br><span class="line">comments:</span><br><span class="line">description:</span><br><span class="line">keywords:</span><br><span class="line">top<span class="emphasis">_img:</span></span><br><span class="line"><span class="emphasis">mathjax:</span></span><br><span class="line"><span class="emphasis">katex:</span></span><br><span class="line"><span class="emphasis">aside:</span></span><br><span class="line"><span class="emphasis">aplayer:</span></span><br><span class="line"><span class="emphasis">highlight_</span>shrink:</span><br><span class="line">---</span><br></pre></td></tr></table></figure>
<table>
<thead>
<tr>
<th align="left">写法</th>
<th align="left">解释</th>
</tr>
</thead>
<tbody><tr>
<td align="left">title</td>
<td align="left">【必需】页面标题</td>
</tr>
<tr>
<td align="left">date</td>
<td align="left">【必需】页面创建日期</td>
</tr>
<tr>
<td align="left">type</td>
<td align="left">【必需】标签、分类和友情链接三个页面需要配置</td>
</tr>
<tr>
<td align="left">updated</td>
<td align="left">【可选】页面更新日期</td>
</tr>
<tr>
<td align="left">description</td>
<td align="left">【可选】页面描述</td>
</tr>
<tr>
<td align="left">keywords</td>
<td align="left">【可选】页面关键字</td>
</tr>
<tr>
<td align="left">comments</td>
<td align="left">【可选】显示页面评论模块(默认 true)</td>
</tr>
<tr>
<td align="left">top_img</td>
<td align="left">【可选】页面顶部图片</td>
</tr>
<tr>
<td align="left">mathjax</td>
<td align="left">【可选】显示mathjax(当设置mathjax的per_page: false时,才需要配置,默认 false)</td>
</tr>
<tr>
<td align="left">katex</td>
<td align="left">【可选】显示katex(当设置katex的per_page: false时,才需要配置,默认 false)</td>
</tr>
<tr>
<td align="left">aside</td>
<td align="left">【可选】显示侧边栏 (默认 true)</td>
</tr>
<tr>
<td align="left">aplayer</td>
<td align="left">【可选】在需要的页面加载aplayer的js和css,请参考文章下面的音乐 配置</td>
</tr>
<tr>
<td align="left">highlight_shrink</td>
<td align="left">【可选】配置代码框是否展开(true/false)(默认为设置中highlight_shrink的配置)</td>
</tr>
</tbody></table>
<h3 id="Post-Front-matter"><a href="#Post-Front-matter" class="headerlink" title="Post Front-matter"></a>Post Front-matter</h3><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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><br><span class="line">title:</span><br><span class="line">date:</span><br><span class="line">updated:</span><br><span class="line">tags:</span><br><span class="line">categories:</span><br><span class="line">keywords:</span><br><span class="line">description:</span><br><span class="line">top<span class="emphasis">_img:</span></span><br><span class="line"><span class="emphasis">comments:</span></span><br><span class="line"><span class="emphasis">cover:</span></span><br><span class="line"><span class="emphasis">toc:</span></span><br><span class="line"><span class="emphasis">toc_</span>number:</span><br><span class="line">toc<span class="emphasis">_style_</span>simple:</span><br><span class="line">copyright:</span><br><span class="line">copyright<span class="emphasis">_author:</span></span><br><span class="line"><span class="emphasis">copyright_</span>author<span class="emphasis">_href:</span></span><br><span class="line"><span class="emphasis">copyright_</span>url:</span><br><span class="line">copyright<span class="emphasis">_info:</span></span><br><span class="line"><span class="emphasis">mathjax:</span></span><br><span class="line"><span class="emphasis">katex:</span></span><br><span class="line"><span class="emphasis">aplayer:</span></span><br><span class="line"><span class="emphasis">highlight_</span>shrink:</span><br><span class="line"><span class="section">aside:</span></span><br><span class="line"><span class="section">---</span></span><br></pre></td></tr></table></figure>
<table>
<thead>
<tr>
<th align="left">写法</th>
<th align="left">解释</th>
</tr>
</thead>
<tbody><tr>
<td align="left">title</td>
<td align="left">【必需】文章标题</td>
</tr>
<tr>
<td align="left">date</td>
<td align="left">【必需】文章创建日期</td>
</tr>
<tr>
<td align="left">updated</td>
<td align="left">【可选】文章更新日期</td>
</tr>
<tr>
<td align="left">tags</td>
<td align="left">【可选】文章标籤</td>
</tr>
<tr>
<td align="left">categories</td>
<td align="left">【可选】文章分类</td>
</tr>
<tr>
<td align="left">keywords</td>
<td align="left">【可选】文章关键字</td>
</tr>
<tr>
<td align="left">description</td>
<td align="left">【可选】文章描述</td>
</tr>
<tr>
<td align="left">top_img</td>
<td align="left">【可选】文章顶部图片</td>
</tr>
<tr>
<td align="left">cover</td>
<td align="left">【可选】文章缩略图(如果没有设置top_img,文章页顶部将显示缩略图,可设为false/图片地址/留空)</td>
</tr>
<tr>
<td align="left">comments</td>
<td align="left">【可选】显示文章评论模块(默认 true)</td>
</tr>
<tr>
<td align="left">toc</td>
<td align="left">【可选】显示文章TOC(默认为设置中toc的enable配置)</td>
</tr>
<tr>
<td align="left">toc_number</td>
<td align="left">【可选】显示toc_number(默认为设置中toc的number配置)</td>
</tr>
<tr>
<td align="left">toc_style_simple</td>
<td align="left">【可选】显示 toc 简洁模式</td>
</tr>
<tr>
<td align="left">copyright</td>
<td align="left">【可选】显示文章版权模块(默认为设置中post_copyright的enable配置)</td>
</tr>
<tr>
<td align="left">copyright_author</td>
<td align="left">【可选】文章版权模块的文章作者</td>
</tr>
<tr>
<td align="left">copyright_author_href</td>
<td align="left">【可选】文章版权模块的文章作者链接</td>
</tr>
<tr>
<td align="left">copyright_url</td>
<td align="left">【可选】文章版权模块的文章连结链接</td>
</tr>
<tr>
<td align="left">copyright_info</td>
<td align="left">【可选】文章版权模块的版权声明文字</td>
</tr>
<tr>
<td align="left">mathjax</td>
<td align="left">【可选】显示mathjax(当设置mathjax的per_page: false时,才需要配置,默认 false)</td>
</tr>
<tr>
<td align="left">katex</td>
<td align="left">【可选】显示katex(当设置katex的per_page: false时,才需要配置,默认 false)</td>
</tr>
<tr>
<td align="left">aplayer</td>
<td align="left">【可选】在需要的页面加载aplayer的js和css,请参考文章下面的音乐 配置</td>
</tr>
<tr>
<td align="left">highlight_shrink</td>
<td align="left">【可选】配置代码框是否展开(true/false)(默认为设置中highlight_shrink的配置)</td>
</tr>
<tr>
<td align="left">aside</td>
<td align="left">【可选】显示侧边栏 (默认 true)</td>
</tr>
</tbody></table>
<blockquote>
<p>注意:我的博客根目录路径为 【G:/hexo-blog/blog-demo】,下文所说的根目录都是此路径,将用<code>[BlogRoot]</code>代替。如果不清楚根目录路径,请回到教程 <a href="https://fe32.top/articles/hexo1602/">基于 Hexo 从零开始搭建个人博客(二)</a>,查看你执行<code>hexo init xxx</code>这条命令时所选择的路径,例如我选择的路径是【G:/hexo-blog】,我的博客根目录即为【G:/hexo-blog/xxx】。</p>
</blockquote>
<h2 id="标签页"><a href="#标签页" class="headerlink" title="标签页"></a>标签页</h2><ol>
<li>前往你的Hexo博客根目录,打开cmd命令窗口执行<code>hexo new page tags</code>。</li>
<li>在【BlogRoot/source/】会生成一个含有<code>index.md</code>文件的<code>tags</code>文件夹。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/29/62937c8344bbb.jpg"/></div></div></li>
<li>修改【BlogRoot/source/tags/index.md】,添加<code>type: "tags"</code>。<figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">title: tags</span><br><span class="line">date: 2022-05-29 21:42:56</span><br><span class="line">type: "tags"</span><br><span class="line">orderby: random</span><br><span class="line"><span class="section">order: 1</span></span><br><span class="line"><span class="section">---</span></span><br></pre></td></tr></table></figure></li>
</ol>
<table>
<thead>
<tr>
<th align="left">写法</th>
<th align="left">解释</th>
</tr>
</thead>
<tbody><tr>
<td align="left">type</td>
<td align="left">【必须】页面类型,必须为 tags</td>
</tr>
<tr>
<td align="left">orderby</td>
<td align="left">【可选】排序方式 :random/name/length</td>
</tr>
<tr>
<td align="left">order</td>
<td align="left">【可选】排序次序: 1, asc for ascending; -1, desc for descending</td>
</tr>
</tbody></table>
<h2 id="分类页"><a href="#分类页" class="headerlink" title="分类页"></a>分类页</h2><ol>
<li>前往你的Hexo博客根目录,打开cmd命令窗口执行<code>hexo new page categories</code>。</li>
<li>在【BlogRoot/source/】会生成一个含有<code>index.md</code>文件的<code>categories</code>文件夹。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/29/62937c8627deb.jpg"/></div></div></li>
<li>修改【BlogRoot/source/categories/index.md】,添加<code>type: "categories"</code>。<figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">title: categories</span><br><span class="line">date: 2022-05-29 21:57:07</span><br><span class="line"><span class="section">type: "categories"</span></span><br><span class="line"><span class="section">---</span></span><br></pre></td></tr></table></figure></li>
</ol>
<h2 id="友情链接"><a href="#友情链接" class="headerlink" title="友情链接"></a>友情链接</h2><h3 id="创建友情链接页面"><a href="#创建友情链接页面" class="headerlink" title="创建友情链接页面"></a>创建友情链接页面</h3><ol>
<li>前往你的Hexo博客根目录,打开cmd命令窗口执行<code>hexo new page link</code>。</li>
<li>在【BlogRoot/source/】会生成一个含有<code>index.md</code>文件的<code>link</code>文件夹。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/29/629385dc3918c.jpg"/></div></div></li>
<li>修改【BlogRoot/source/link/index.md】,添加<code>type: "link"</code>。<figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">title: link</span><br><span class="line">date: 2022-05-29 22:03:35</span><br><span class="line"><span class="section">type: "link"</span></span><br><span class="line"><span class="section">---</span></span><br></pre></td></tr></table></figure></li>
</ol>
<h3 id="友情链接页面添加友链信息"><a href="#友情链接页面添加友链信息" class="headerlink" title="友情链接页面添加友链信息"></a>友情链接页面添加友链信息</h3><p>前往Hexo博客目录(【BlogRoot/source/_data】)创建一个文件<code>link.yml</code>(如果沒有 _data 文件夹,请自行创建)。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/29/629385e182b48.jpg"/></div></div>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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="bullet">-</span> <span class="attr">class_name:</span> <span class="string">友情链接</span></span><br><span class="line"> <span class="attr">class_desc:</span> <span class="string">那些人,那些事</span></span><br><span class="line"> <span class="attr">link_list:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">唐志远</span></span><br><span class="line"> <span class="attr">link:</span> <span class="string">https://fe32.top/</span></span><br><span class="line"> <span class="attr">avatar:</span> <span class="string">https://bu.dusays.com/2022/05/02/626f92e193879.jpg</span></span><br><span class="line"> <span class="attr">descr:</span> <span class="string">古今之成大事者,不惟有超世之才,亦必有坚忍不拔之志。</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">class_name:</span> <span class="string">网站</span></span><br><span class="line"> <span class="attr">class_desc:</span> <span class="string">值得推荐的网站</span></span><br><span class="line"> <span class="attr">link_list:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Twitter</span></span><br><span class="line"> <span class="attr">link:</span> <span class="string">https://twitter.com/</span></span><br><span class="line"> <span class="attr">avatar:</span> <span class="string">https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png</span></span><br><span class="line"> <span class="attr">descr:</span> <span class="string">社交分享平台</span></span><br></pre></td></tr></table></figure>
<p><code>class_name</code>和<code>class_desc</code>支持 html 格式,如不需要,也可以留空。</p>
<p>如果你想设置成本站友链页的效果,请参考教程:<a href="https://fe32.top/articles/0xiipgum/">基于Butterfly的外挂标签引入</a> 。</p>
<h2 id="图库"><a href="#图库" class="headerlink" title="图库"></a>图库</h2><p>图库页面只是普通的页面,你只需要<code>hexo new page xxxxx</code>创建你的页面就行。</p>
<p>然后使用标签外挂 <a href="https://butterfly.js.org/posts/4aa8abbe/#Gallery%E7%9B%B8%E5%86%8A%E5%9C%96%E5%BA%AB">galleryGroup</a>,具体用法请查看对应的内容。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="string"><div</span> <span class="string">class="gallery-group-main"></span></span><br><span class="line">{<span class="string">%</span> <span class="string">galleryGroup</span> <span class="string">'ACG'</span> <span class="string">'那些二次元的故事'</span> <span class="string">'/gallery/ACG'</span> <span class="string">https://bu.dusays.com/2022/11/26/638228a86935a.webp</span> <span class="string">%</span>}</span><br><span class="line">{<span class="string">%</span> <span class="string">galleryGroup</span> <span class="string">'岳阳'</span> <span class="string">'2017年5月岳阳'</span> <span class="string">'/gallery/YY'</span> <span class="string">https://bu.dusays.com/2022/11/27/63831b681e9f8.jpg</span> <span class="string">%</span>}</span><br><span class="line">{<span class="string">%</span> <span class="string">galleryGroup</span> <span class="string">'OH MY GIRL'</span> <span class="string">'关于OH MY GIRL的图片'</span> <span class="string">'/Gallery/ohmygirl'</span> <span class="string">https://i.loli.net/2019/12/25/hOqbQ3BIwa6KWpo.jpg</span> <span class="string">%</span>}</span><br><span class="line"><span class="string"></div></span></span><br></pre></td></tr></table></figure>
<div class="gallery-group-main">
<figure class="gallery-group">
<img class="gallery-group-img no-lightbox" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src='https://bu.dusays.com/2022/11/26/638228a86935a.webp' alt="Group Image Gallery">
<figcaption>
<div class="gallery-group-name">ACG</div>
<p>那些二次元的故事</p>
<a href='/gallery/ACG' target='_blank'></a>
</figcaption>
</figure>
<figure class="gallery-group">
<img class="gallery-group-img no-lightbox" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src='https://bu.dusays.com/2022/11/27/63831b681e9f8.jpg' alt="Group Image Gallery">
<figcaption>
<div class="gallery-group-name">岳阳</div>
<p>2017年5月岳阳</p>
<a href='/gallery/YY' target='_blank'></a>
</figcaption>
</figure>
<figure class="gallery-group">
<img class="gallery-group-img no-lightbox" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src='https://i.loli.net/2019/12/25/hOqbQ3BIwa6KWpo.jpg' alt="Group Image Gallery">
<figcaption>
<div class="gallery-group-name">OH MY GIRL</div>
<p>关于OH MY GIRL的图片</p>
<a href='/Gallery/ohmygirl' target='_blank'></a>
</figcaption>
</figure>
</div>
<h3 id="子页面"><a href="#子页面" class="headerlink" title="子页面"></a>子页面</h3><p>子页面也是普通的页面,你只需要<code>hexo new page xxxxx</code>创建你的页面就行。</p>
<p>然后使用标签外挂 <a href="https://butterfly.js.org/posts/4aa8abbe/#Gallery%E7%9B%B8%E5%86%8A">gallery</a>,具体用法请查看对应的内容。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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="string">%</span> <span class="string">gallery</span> <span class="string">%</span>}</span><br><span class="line"><span class="type">![](https://i.loli.net/2019/12/25/Fze9jchtnyJXMHN.jpg)</span></span><br><span class="line"><span class="type">![](https://i.loli.net/2019/12/25/ryLVePaqkYm4TEK.jpg)</span></span><br><span class="line"><span class="type">![](https://i.loli.net/2019/12/25/gEy5Zc1Ai6VuO4N.jpg)</span></span><br><span class="line"><span class="type">![](https://i.loli.net/2019/12/25/d6QHbytlSYO4FBG.jpg)</span></span><br><span class="line"><span class="type">![](https://i.loli.net/2019/12/25/6nepIJ1xTgufatZ.jpg)</span></span><br><span class="line"><span class="type">![](https://i.loli.net/2019/12/25/E7Jvr4eIPwUNmzq.jpg)</span></span><br><span class="line"><span class="type">![](https://i.loli.net/2019/12/25/mh19anwBSWIkGlH.jpg)</span></span><br><span class="line"><span class="type">![](https://i.loli.net/2019/12/25/2tu9JC8ewpBFagv.jpg)</span></span><br><span class="line">{<span class="string">%</span> <span class="string">endgallery</span> <span class="string">%</span>}</span><br></pre></td></tr></table></figure>
<div class="gallery">
<div class="fj-gallery data" data-rowHeight="220" data-limit="10">
<span class="gallery-data">[{"url":"https://i.loli.net/2019/12/25/Fze9jchtnyJXMHN.jpg","alt":""},{"url":"https://i.loli.net/2019/12/25/ryLVePaqkYm4TEK.jpg","alt":""},{"url":"https://i.loli.net/2019/12/25/gEy5Zc1Ai6VuO4N.jpg","alt":""},{"url":"https://i.loli.net/2019/12/25/d6QHbytlSYO4FBG.jpg","alt":""},{"url":"https://i.loli.net/2019/12/25/6nepIJ1xTgufatZ.jpg","alt":""},{"url":"https://i.loli.net/2019/12/25/E7Jvr4eIPwUNmzq.jpg","alt":""},{"url":"https://i.loli.net/2019/12/25/mh19anwBSWIkGlH.jpg","alt":""},{"url":"https://i.loli.net/2019/12/25/2tu9JC8ewpBFagv.jpg","alt":""}]</span>
</div>
<button class="gallery-load-more"><span>加载更多</span><i class="fa-solid fa-arrow-down"></i></button>
</div>
<div class="note pink icon-padding flat"><i class="note-icon fas fa-fan"></i><p>如果你想要使用 <code>/photo/ohmygirl</code> 这样的链接显示你的图片内容</p>
<p>你可以把创建好的 <code>ohmygirl</code>整个文件夹移到 <code>photo</code>文件夹里去</p>
</div>
<h2 id="404页面"><a href="#404页面" class="headerlink" title="404页面"></a>404页面</h2><p>主題內置了一个简单的404页面,可在设置中开放。</p>
<blockquote>
<p>如需本地预览,请访问 <a href="http://localhost:4000/404.html">http://localhost:4000/404.html</a></p>
</blockquote>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># A simple 404 page</span></span><br><span class="line"><span class="attr">error_404:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">subtitle:</span> <span class="string">"页面沒有找到"</span></span><br><span class="line"> <span class="attr">background:</span>
基于 Hexo 从零开始搭建个人博客(三)
https://fe32.top/articles/hexo1603/
2022-05-25T17:37:23.000Z
2024-02-23T15:35:21.199Z
<blockquote>
<p>阅读本篇前,请确保已经完成下面两篇文章的步骤:<br><a href="https://fe32.top/articles/hexo1601/">基于 Hexo 从零开始搭建个人博客(一)</a><br><a href="https://fe32.top/articles/hexo1602/">基于 Hexo 从零开始搭建个人博客(二)</a></p>
</blockquote>
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><ol>
<li>博客搭建过程遇到任何问题,优先在本页面搜索,检查是否已经有该配置教程。</li>
<li>遇到问题可以优先在文章评论区留言,注意留言时请填写正确的邮箱以确保能收到站长的回复。</li>
<li>实在解决不了的问题可添加博主 Wechat ,添加好友时请备注自己的<code>姓名+专业</code>,如 <code>张三 计算机科学与技术</code>。</li>
</ol>
<h2 id="安装主题"><a href="#安装主题" class="headerlink" title="安装主题"></a>安装主题</h2><!-- 本站基于`Hexo`搭建,用的 🦋 [hexo-theme-butterfly](https://github.com/jerryc127/hexo-theme-butterfly.git) 主题 [v3.7.1](https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1),请注意最新的🦋 [hexo-theme-butterfly](https://github.com/jerryc127/hexo-theme-butterfly.git) 版本已经更新到 [v4.8.1](https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.8.1) 。
> 如果你是 [v3.7.1](https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1) 之外的版本,可能有些地方会有出入,请留意。 -->
<blockquote>
<p>本站基于<code>Hexo</code>搭建,用的 🦋 <a href="https://github.com/jerryc127/hexo-theme-butterfly.git">hexo-theme-butterfly</a> 主题,已经升级到 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.7.0">v4.7.0</a> 。 请注意最新的🦋 <a href="https://github.com/jerryc127/hexo-theme-butterfly.git">hexo-theme-butterfly</a> 版本已经更新到 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/4.8.1">v4.8.1</a> 。<br><br>如果你是 <a href="https://github.com/jerryc127/hexo-theme-butterfly/releases/tag/3.7.1">v3.7.1</a> 的版本,请移步 <a href="https://old.fe32.top/" target="_blank">v3.7.1</a> 站点进行浏览。</p>
</blockquote>
<!-- 本人用的是`git clone`方式安装的 `hexo-theme-butterfly`,后续魔改时更改的文件都是【G:/hexo-blog/blog-demo/node_modules/hexo-theme-butterfly】文件夹中的文件。如果你是`git clone`克隆方式安装的主题,请在【G:/hexo-blog/blog-demo/themes/butterfly】文件夹下修改对应的文件。 -->
<p>本人用的是<code>git clone</code>方式安装的 <code>hexo-theme-butterfly</code>,后续魔改时更改的文件都是【G:/hexo-blog/blog-demo/themes/butterfly】文件夹中的文件。如果你是<code>npm</code>方式安装的主题,请在【G:/hexo-blog/blog-demo/node_modules/hexo-theme-butterfly】文件夹下修改对应的文件。</p>
<div class="tabs" id="span-"><ul class="nav-tabs"><li class="tab"><button type="button" data-href="#span--1"><i class="fab fa-npm"></i>npm安装</button></li><li class="tab active"><button type="button" data-href="#span--2"><i class="fab fa-github-square"></i>Git安装(Github 推荐)</button></li><li class="tab"><button type="button" data-href="#span--3"><i class="fab fa-git-square"></i>Git安装 (Gitee)</button></li></ul><div class="tab-contents"><div class="tab-item-content" id="span--1"><blockquote><p>此方法只支持 Hexo 5.0.0以上版本</p></blockquote>
<p>在你的博客根目录(我这里路径为【G:/hexo-blog/blog-demo】)打开cmd命令窗口执行<code>npm i hexo-theme-butterfly@3.7.1</code>。</p>
<ul>
<li>安装最新主题版本</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm i hexo-theme-butterfly</span><br></pre></td></tr></table></figure>
<ul>
<li>安装指定主题版本</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm i hexo-theme-butterfly@3.7.1</span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/26/628f966a256e9.jpg"/></div></div>
<!-- <details class="folding-tag" cyan><summary> 查看截图 </summary>
<div class='content'>
<p> –></p><!--
</div>
</details> -->
<p>安装成功后可在【G:/hexo-blog/blog-demo/node_modules】文件夹下找到<code>hexo-theme-butterfly</code>。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/26/628f9689c193e.jpg"/></div></div>
<!-- <details class="folding-tag" cyan><summary> 查看截图 </summary>
<div class='content'>
<p> –></p><!--
</div>
</details> -->
<p></p>
<div class="note info no-icon flat"><p>升级方法:在博客根目录下,运行 <code>npm update hexo-theme-butterfly</code>。<br>升级前请将<code>hexo-theme-butterfly</code>文件夹备份,npm更新会直接覆盖成新的包。</p>
</div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content active" id="span--2"><p>在你的博客根目录里(我这里路径为【G:/hexo-blog/blog-demo】),打开Git工具,执行命令即可。</p>
<ul>
<li>安装最新主题版本</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly</span><br></pre></td></tr></table></figure>
<ul>
<li>安装指定主题版本</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> -b 3.7.1 https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly</span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/26/628f988e1737b.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/26/628f9890f1042.jpg"/></div></div>
<p>安装成功后可在【G:/hexo-blog/blog-demo/themes】文件夹下找到<code>butterfly</code>,可以将<code>landscape</code>文件夹删掉。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/26/628f989404550.jpg"/></div></div>
<div class="note info no-icon flat"><p>升级方法:在主题目录下,运行<code>git pull</code></p>
</div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--3"><p>在你的博客根目录里【我这里路径为【G:/hexo-blog/blog-demo】),打开Git工具,执行命令即可。</p>
<ul>
<li>安装最新主题版本</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> https://gitee.com/iamjerryw/hexo-theme-butterfly.git themes/butterfly </span><br></pre></td></tr></table></figure>
<ul>
<li>安装指定主题版本</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> -b 3.7.1 https://gitee.com/iamjerryw/hexo-theme-butterfly.git themes/butterfly </span><br></pre></td></tr></table></figure>
<div class="note info no-icon flat"><p>升级方法:在主题目录下,运行<code>git pull</code></p>
</div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div>
<h2 id="应用主题"><a href="#应用主题" class="headerlink" title="应用主题"></a>应用主题</h2><p>修改站点配置文件<code>_config.yml</code>,把主题改为<code>butterfly</code></p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">theme:</span> <span class="string">butterfly</span></span><br></pre></td></tr></table></figure>
<h2 id="安装插件"><a href="#安装插件" class="headerlink" title="安装插件"></a>安装插件</h2><p>如果你没有<code>pug</code>以及<code>stylus</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">npm install hexo-renderer-pug hexo-renderer-stylus --save</span><br></pre></td></tr></table></figure>
<h2 id="升级建议"><a href="#升级建议" class="headerlink" title="升级建议"></a>升级建议</h2><p>为了减少升级主题后带来的不便,请使用以下方法(<strong>建议,可以不做</strong>)。</p>
<div class="tabs" id="span-"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#span--1">_config.butterfly.yml</button></li><li class="tab"><button type="button" data-href="#span--2">butterfly.yml</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="span--1"><p>把主题文件夹中的 <code>_config.yml</code> 复制到 Hexo 根目录里,同时重新命名为 <code>_config.butterfly.yml</code>。</p>
<p>以后只需要在 <code>_config.butterfly.yml</code>进行配置就行。</p>
<p>Hexo会自动合併主题中的<code>_config.yml</code>和 <code>_config.butterfly.yml</code>里的配置,如果存在同名配置,会使用<code>_config.butterfly.yml</code>的配置,其优先度较高。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/cf493023d33ff.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="span--2"><div class="note pink no-icon flat"><p>从 3.3.0 开始,这种方法不再支持</p>
</div>
<p><del>为了减少升级主题后带来的不便, <code>Butterfly</code> 使用了 <a href="https://hexo.io/docs/data-files.html">data files</a> 特性。</del><br><del>推荐把主题默认的配置文件<code>_config.yml</code> 复製到 Hexo 根目录下的<code>source/_data/</code> 目录下,然后将文件名改为 <code>butterfly.yml</code>(如果<code>source/_data/</code> 的目录不存在就创建一个)。</del> </p>
<div class="note warning flat"><p><del>注意,如果你创建了 <code>butterfly.yml</code>, 它将会替换主题默认配置文件<code>_config.yml</code>里的配置项 (不是合併而是替换,3.1.0开始将会是合併) 採用<code>butterfly.yml</code>的目的是,因为升级主题的时候,有可能会覆盖到配置文件,以至于每次更新的时候都需要重新配置文件。如果使用<code>butterfly.yml</code>,就算主题目录下的<code>_config.yml</code>被覆盖,主题只会去<code>butterfly.yml</code>读取配置。 由于主题在添加功能或者修復Bugs的情况下,可能会涉及到配置文件的修改。这时候,如果升级主题,需要把新增加的配置添加到<code>butterfly.yml</code>去,不然很大机会会出现报错。( 3.1.0 之后,应该不会出现这种情况)</del></p>
</div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas
基于 Hexo 从零开始搭建个人博客(二)
https://fe32.top/articles/hexo1602/
2022-05-24T16:51:23.000Z
2023-05-17T10:01:12.000Z
<blockquote>
<p>阅读本篇前,请先配置好相应的环境,请仔细阅读教程 <a href="https://fe32.top/articles/hexo1601/">基于 Hexo 从零开始搭建个人博客(一)</a>。</p>
</blockquote>
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><ol>
<li>博客搭建过程遇到任何问题,优先在本页面搜索,检查是否已经有该配置教程。</li>
<li>遇到问题可以优先在文章评论区留言,注意留言时请填写正确的邮箱以确保能收到站长的回复。</li>
<li>实在解决不了的问题可添加博主 Wechat ,添加好友时请备注自己的姓名+专业,如 张三 计算机科学与技术。</li>
</ol>
<h2 id="初始化-Hexo-项目"><a href="#初始化-Hexo-项目" class="headerlink" title="初始化 Hexo 项目"></a>初始化 Hexo 项目</h2><ol>
<li>在目标路径(我这里选的路径为【G:/hexo-blog】)打开cmd命令窗口,执行<code>hexo init</code>初始化项目。<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">hexo init blog-demo(项目名)</span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/26/628f9b8d1f815.jpg"/></div></div>
<!-- > 若出现`spawn failed`的问题,请自行百度,有很多教程。 --></li>
<li>进入<code>blog-demo</code> ,输入<code>npm i</code>安装相关依赖。<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="built_in">cd</span> blog-demo //进入blog-demo文件夹</span><br><span class="line">npm i</span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/25/628e4950c04ea.jpg"/></div></div></li>
<li>初始化项目后,<code>blog-demo</code>有如下结构:<br>【node_modules】:依赖包<br>【scaffolds】:生成文章的一些模板<br>【source】:用来存放你的文章<br>【themes】:主题<br>【.npmignore】:发布时忽略的文件(可忽略)<br>【_config.landscape.yml】:主题的配置文件<br>【_config.yml】:博客的配置文件<br>【package.json】:项目名称、描述、版本、运行和开发等信息<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/25/628e495733539.jpg"/></div></div></li>
<li>输入<code>hexo server</code>或者<code>hexo s</code>。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/26/628e54121d098.jpg"/></div></div></li>
<li>打开浏览器,输入地址:<a href="http://localhost:4000/">http://localhost:4000/</a> ,看到下面的效果,说明你的博客已经构建成功了。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/26/628e5423df640.webp"/></div></div></li>
</ol>
<h2 id="将静态博客挂载到-GitHub-Pages"><a href="#将静态博客挂载到-GitHub-Pages" class="headerlink" title="将静态博客挂载到 GitHub Pages"></a>将静态博客挂载到 GitHub Pages</h2><p>上一篇文章中,我们已经完成了对 GitHub 账户的注册以及 Github Pages 的创建,并且为 GitHub 配置了 SSH key, 我们将博客部署到 GitHub Pages 上即可。</p>
<h3 id="安装-hexo-deployer-git"><a href="#安装-hexo-deployer-git" class="headerlink" title="安装 hexo-deployer-git"></a>安装 hexo-deployer-git</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install hexo-deployer-git --save</span><br></pre></td></tr></table></figure>
<h3 id="修改-config-yml-文件"><a href="#修改-config-yml-文件" class="headerlink" title="修改 _config.yml 文件"></a>修改 _config.yml 文件</h3><p>在<code>blog-demo</code>目录下的<code>_config.yml</code>,就是整个<code>Hexo框架</code>的配置文件了。可以在里面修改大部分的配置。详细可参考官方的<a href="https://hexo.io/zh-cn/docs/configuration">配置描述</a>。</p>
<p>修改最后一行的配置,将repository修改为你自己的github项目地址即可。</p>
<blockquote>
<p>请检查自己的默认分支是不是<code>master</code>。</p>
</blockquote>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">deploy:</span></span><br><span class="line"> <span class="attr">type:</span> <span class="string">git</span></span><br><span class="line"> <span class="attr">repository:</span> <span class="string">git@github.com:tzy13755126023/tzy13755126023.github.io.git</span></span><br><span class="line"> <span class="attr">branch:</span> <span class="string">master</span></span><br></pre></td></tr></table></figure>
<h3 id="部署项目到-GitHub"><a href="#部署项目到-GitHub" class="headerlink" title="部署项目到 GitHub"></a>部署项目到 GitHub</h3><p>修改好配置后,运行如下命令,将代码部署到 GitHub。</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">hexo clean</span><br><span class="line">hexo generate</span><br><span class="line">hexo deploy</span><br></pre></td></tr></table></figure>
<ul>
<li>hexo clean:删除之前生成的文件,若未生成过静态文件,可忽略此命令。</li>
<li>hexo generate:生成静态文章,可以用<code>hexo g</code>缩写</li>
<li>hexo deploy:部署文章,可以用<code>hexo d</code>缩写</li>
</ul>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/26/628e5ef06d42a.jpg"/></div></div>
<blockquote>
<p>注意:deploy时可能要你输入 username 和 password。</p>
</blockquote>
<p>如果出现<code>Deploy done</code>,则说明部署成功了。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/26/628e5f017030e.jpg"/></div></div>
<p>打开浏览器访问:<a href="https://tzy13755126023.github.io/">https://tzy13755126023.github.io</a> ,这时候我们就可以看到博客内容了。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/26/628e5f7360e9c.webp"/></div></div>
<blockquote>
<p>如果出现了本地 css 样式正常,部署到 github 上样式错乱,请移步 <a href="https://fe32.top/articles/hexo1612/">Hexo + Butterfly 一些常见问题</a> 一文,其中提到过关于 本地样式正常,部署后就样式错乱的问题。</p>
</blockquote>
<h2 id="设置个人域名"><a href="#设置个人域名" class="headerlink" title="设置个人域名"></a>设置个人域名</h2><p>现在你的个人网站的地址是 <code>yourname.github.io</code>。如果觉得不够定制化,可以购买一个专属域名。</p>
<blockquote>
<p>这一步不是必要的,如果目前还不想买域名可以先跳过。</p>
</blockquote>
<p>腾讯云、阿里云等都是不错的选择,博主选择的阿里云的<code>.com</code>。购买域名后,实名认证进入阿里云控制台,点云解析进去,找到你刚买的域名,点进去添加两条解析记录,记录值 请填写<code><用户名>.github.io</code>,如下图所示:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/26/628e6464cee7b.jpg"/></div></div>
<p>以我的域名<code>fe32.top</code>为例,进行如下说明:</p>
<table>
<thead>
<tr>
<th align="left">主机记录</th>
<th align="left">解释</th>
</tr>
</thead>
<tbody><tr>
<td align="left">www</td>
<td align="left">解析后的域名为 <a href="http://www.fe32.top/">www.fe32.top</a></td>
</tr>
<tr>
<td align="left">@</td>
<td align="left">直接解析主域名 yafine-blog.cn</td>
</tr>
<tr>
<td align="left">*</td>
<td align="left">泛解析,匹配其他所有域名 *.yafine-blog.cn</td>
</tr>
<tr>
<td align="left">mail</td>
<td align="left">将域名解析为 mail.yafine-blog.cn,通常用于解析邮箱服务器</td>
</tr>
<tr>
<td align="left">二级域名</td>
<td align="left">如 abc.yafine-blog.cn,填写 abc</td>
</tr>
<tr>
<td align="left">手机网站</td>
<td align="left">如 m.yafine-blog.cn,填写 m</td>
</tr>
<tr>
<td align="left">显性URL</td>
<td align="left">不支持泛解析(泛解析:将所有子域名解析到同一地址)</td>
</tr>
</tbody></table>
<blockquote>
<p>将域名指向云服务器,请选择「A」; 将域名指向另一个域名,请选择「CNAME」; 建立邮箱请选择「MX」,根据邮箱服务商提供的 MX 记录填写。</p>
</blockquote>
<table>
<thead>
<tr>
<th align="left">记录类型</th>
<th align="left">解释</th>
</tr>
</thead>
<tbody><tr>
<td align="left">A</td>
<td align="left">用来指定域名的 IPv4 地址(如 8.8.8.8),如果需要将域名指向一个 IP 地址,就需要添加 A 记录。</td>
</tr>
<tr>
<td align="left">CNAME</td>
<td align="left">如果需要将域名指向另一个域名,再由另一个域名提供 IP 地址,就需要添加 CNAME 记录。</td>
</tr>
<tr>
<td align="left">MX</td>
<td align="left">如果需要设置邮箱,让邮箱能收到邮件,就需要添加 MX 记录。</td>
</tr>
<tr>
<td align="left">TXT</td>
<td align="left">在这里可以填写任何东西,长度限制 255。绝大多数的 TXT 记录是用来做 SPF 记录(反垃圾邮件)</td>
</tr>
<tr>
<td align="left">NS</td>
<td align="left">域名服务器记录,如果需要将子域名交给其他 DNS 服务商解析,就需要添加 NS 记录。</td>
</tr>
<tr>
<td align="left">AAAA</td>
<td align="left">用来指定主机名(或域名)对应的 IPv6 地址(例如:ff06:0:0:0:0:0:0:c3)记录。</td>
</tr>
<tr>
<td align="left">SRV</td>
<td align="left">记录了哪台计算机提供了哪个服务。格式为:服务的名字、点、协议的类型,例如:_xmpp-server_tcp。</td>
</tr>
<tr>
<td align="left">显性 URL</td>
<td align="left">从一个地址 301 重定向到另一个地址的时候,就需要添加显性 URL 记录(注:DNSPod 目前只支持 301 重定向)。</td>
</tr>
<tr>
<td align="left">隐性 URL</td>
<td align="left">类似于显性 URL,区别在于隐性 URL 不会改变地址栏的域名。</td>
</tr>
</tbody></table>
<blockquote>
<p>这时候你的项目根目录应该会出现一个名为<code>CNAME</code>的文件。如果没有的话,打开博客<code>/source</code>目录,我的是<code>G:/hexo-blog/blog-demo/source</code>,新建<code>CNAME</code>文件,注意没有后缀。然后在里面写上你的域名(例如:fe32.top),保存。最后运行<code>hexo g</code>、<code>hexo d</code>上传到<code>github</code>。这样到最后当你在地址栏输入<code>xxx.github.io</code>时,才会自动跳转到你的域名。</p>
</blockquote>
<p>打开你的<code>github</code>博客项目,点击<code>settings</code>,点击<code>Pages</code>,拉到下面<code>Custom domain</code>处,填上你自己的域名 ,保存。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif"
Web页面全链路性能优化指南
https://fe32.top/articles/we0522bs/
2022-05-22T15:30:06.000Z
2023-05-17T10:01:12.000Z
<p>性能优化不单指优化一个页面的打开速度,在开发环境将一个项目的启动时间缩短使开发体验更好也属于性能优化,大文件上传时为其添加分片上传、断点续传也属于性能优化。在项目开发以及用户使用的过程中,能够让任何一个链路快一点,都可以被叫做性能优化。</p>
<p>本文会对web页面的全链路进行完整的讲解并针对每一步找到能做的性能优化点,本文的目标是极致的性能优化。</p>
<p>本文的大致流程为先讲理论知识,比如如何评价一个页面的性能好与不好、如果获取性能指标,如何使用各种性能相关工具,浏览器如何获取并渲染页面。</p>
<p>接下来我们会进入性能优化环节,在这个环节我会详细讲解在页面的整个流程中,哪些地方可以做哪些优化。</p>
<h1 id="浏览器渲染原理"><a href="#浏览器渲染原理" class="headerlink" title="浏览器渲染原理"></a>浏览器渲染原理</h1><p>我们需要知道浏览器是如何渲染一个页面的,我们才能知道如何对页面进行性能优化,所以这里我们对一些基础知识进行讲解。</p>
<h2 id="进程与线程"><a href="#进程与线程" class="headerlink" title="进程与线程"></a>进程与线程</h2><p>浏览器有多种进程,其中最主要的5种进程如下。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/23/628b96c2e0bec.webp"/></div></div>
<ol>
<li>浏览器进程 负责界面展示、用户交互、子进程管理、提供存储等</li>
<li>渲染进程 每个页面都有一个单独的渲染进程,用于渲染页面,包含webworker线程</li>
<li>网络进程 主要处理网络资源加载(HTML、CSS、JS、IMAGE、AJAX等)</li>
<li>GPU进程 3D绘制,提高性能</li>
<li>插件进程 chrome插件,每个插件占用一个进程</li>
</ol>
<h2 id="输入url到页面展示完整过程"><a href="#输入url到页面展示完整过程" class="headerlink" title="输入url到页面展示完整过程"></a>输入url到页面展示完整过程</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/23/628b9dcaedcbd.jpg" alt="图1"/></div><span class="image-caption">图1</span></div>
<h3 id="用户输入"><a href="#用户输入" class="headerlink" title="用户输入"></a>用户输入</h3><p>用户在<code>浏览器进程</code>输入并按下回车健后,浏览器判断用户输入的url是否为正确的url,如果不是,则使用默认的搜索引擎将该关键字拼接成url。</p>
<h3 id="卸载原页面并重定向到新页面"><a href="#卸载原页面并重定向到新页面" class="headerlink" title="卸载原页面并重定向到新页面"></a>卸载原页面并重定向到新页面</h3><p>然后浏览器会将现有页面卸载掉并重定向到用户新输入的url页面,也就是图中【Process Unload Event】和【Redirect】流程。</p>
<p>此时浏览器会准备一个<code>渲染进程</code>用于渲染即将到来的页面,和一个<code>网络进程</code>用于发送网络请求。</p>
<h3 id="处理Service-Worker"><a href="#处理Service-Worker" class="headerlink" title="处理Service Worker"></a>处理Service Worker</h3><p>如果当前页面注册了Service Worker那么它可以拦截当前网站所有的请求,进行判断是否需要向远程发送网络请求。也就是图中【Service Worker Init】与【Service Worker Fecth Event 】步骤</p>
<p>如果不需要发送网络请求,则取本地文件。如果需要则进行下一步。</p>
<h3 id="网络请求"><a href="#网络请求" class="headerlink" title="网络请求"></a>网络请求</h3><blockquote>
<p>OSI网络七层模型:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层<br>在实际应用中物理层、数据链路层被统称为物理层,会话层、表示层、应用层被统称为应用层,所以实际使用时通常分为4个层级:<br>【物理层】>【网络层(IP)】>【传输层(TCP/UDP)】>【应用层(HTTP)】<br>也就是图中【HTTP Cache】、【DNS】、【TCP】、【Request】、【Response】步骤</p>
</blockquote>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/23/628b99a83152f.webp" alt="图2"/></div><span class="image-caption">图2</span></div>
<p>浏览器会拿着url通过网络进程进行如下步骤:</p>
<ol>
<li>根据url查询本地是否已经有强制缓存,如果有则判断缓存是否过期,如果没过期则直接返回缓存内容,也就是图1中【HTTP Cache】步骤</li>
<li>如果没有强制缓存或者缓存已过期,则将该请求加入队列进行排队准备发送网络请求,也就是图2中【正在排队】,然后进入DNS解析阶段,也就是图1中【DNS】以及图2中的【DNS查找】,DNS根据域名解析出对应的IP地址。(DNS基于UDP)。</li>
<li>使用IP寻址找到对方,然后根据IP地址+端口号创建一个TCP连接(三次握手),也就是图1中【TCP】以及图2中的【初始连接】创建完成后利用TCP连接来传输数据。(TCP会将数据拆分为多个数据包,进行有序传输,如果丢包会重发,TCP的特点是可靠、有序)</li>
<li>判断当前协议是否为https,如果为https,则进行SSL协商,将数据进行加密,如果为http协议则不进行加密(明文传输),也就是图2中的【SSL】。</li>
<li>开始发送http请求(请求行/请求头/请求体),也就是图1中【Request】以及图2中的【已发送请求】。HTTP协议有多个版本,目前使用最多的版本为HTTP/1.1,HTTP/1.1发送完成后默认不会断开。keep-alive 默认打开,为了下次传输数据时复用上次创建的连接。每个域名最多同时建立6个TCP连接,所以同一时间最多发生6个请求。<blockquote>
<p><strong>HTTP协议</strong>的各个版本特性如下:<br><code>HTTP/0.9</code>没有请求头和响应头,不区分传输的内容类型,因为当时只传输HTML。<br><code>HTTP/1.0</code>提供了请求头和响应头,可以传输不同类型的内容数据。根据请求响应头的不同来处理不同的资源,HTTP1.0每次发完请求都会断开TCP连接。有新的请求时再次创建TCP连接。<br><code>HTTP/1.1</code>默认开启了 keep-alive ,它能够让一个TCP连接中传输多个HTTP请求,也叫链路复用。但一个TCP连接同一时间只能发送一个HTTP请求,为了不阻塞多个请求,Chrome允许创建6个TCP连接,所以在HTTP/1.1中,最多能够同时发送6个网络请求。<br><code>HTTP/2.0</code>HTTP/2.0使用同一个TCP连接来发送数据,他把多个请求通过二进制分贞层实现了分贞,然后把数据传输给服务器。也叫<code>多路复用</code>,多个请求复用同一个TCP连接。HTTP/2.0会将所有以:开头的请求头做一个映射表,然后使用<code>hpack</code>进行压缩,使用这种方式会使请求头更小。服务器可主动推送数据给客户端。<br><code>HTTP/3.0</code>使用UDP实现,在UDP上一层加入一层<code>QUIC协议</code>,解决了TCP协议中的队头阻塞问题。</p>
</blockquote>
</li>
<li>服务器收到数据后解析HTTP请求(请求行/请求头/请求体),处理完成后生成状态码和HTTP响应(响应行/响应头/响应体)后返回给客户端,也就是图2的【等待中】在做的事情。</li>
<li>客户端接收到HTTP响应后根据状态码进行对应的处理,如果状态码为304则直接代表协商缓存生效,直接取本地的缓存文件。如果不是则下载内容。也就是图1中【Response】以及图2中的【下载内容】步骤。</li>
</ol>
<h3 id="服务端响应"><a href="#服务端响应" class="headerlink" title="服务端响应"></a>服务端响应</h3><p>在<code>网络请求</code>第<code>6</code>步中,服务器收到HTTP请求后需要根据请求信息来进行解析,并返回给客户端想要的数据,这也就服务端响应。</p>
<p>服务端可以响应并返回给客户端很多种类型的资源,这里主要介绍html类型。</p>
<p>目前前端处理服务端响应html请求主要分为SSR服务端渲染与CSR客户端渲染,CSR就是返回一个空的HTML模版,然后浏览器加载js后通过js动态渲染页面。SSR是服务端在接受到请求时事先在服务端渲染好html返回给客户端后,客户端再进行客户端激活。</p>
<p>在打开一个站点的首屏页的完整链路中,使用SSR服务端渲染时的速度要远大于CSR客户端渲染,并且SSR对SEO友好。所以对于首屏加载速度比较敏感或者需要优化SEO的站点来说,使用SSR是更好的选择。</p>
<h3 id="浏览器渲染详细流程"><a href="#浏览器渲染详细流程" class="headerlink" title="浏览器渲染详细流程"></a>浏览器渲染详细流程</h3><p>浏览器渲染详细流程主要在<code>网络请求</code>中的地7步。浏览器下载完<code>html</code>内容后进行解析何渲染页面的流程。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/23/628ba7881b1a8.webp"/></div></div>
<p>渲染流程分为4种情况,</p>
<ol>
<li>HTML中无任何CSS相关标签</li>
<li>CSS相关标签在HTML最顶部,且在解析到内容标签(<code><div /></code>)时已经解析完CSS相关标签</li>
<li>CSS相关标签在HTML最顶部,但在解析到内容标签(<code><div /></code>)时CSS相关标签尚未解析完</li>
<li>CSS相关标签在HTML最底部</li>
</ol>
<p>下面的流程是对上图的文字版解析。读者可将以上4种情况分别带入到如下的渲染流程中走一遍。就能理解浏览器的完整渲染过程了。</p>
<h4 id="【HTML】"><a href="#【HTML】" class="headerlink" title="【HTML】"></a>【HTML】</h4><p>浏览器收到html资源后先预扫描<code><link /></code>和<code><script /></code>并加载对应资源。</p>
<h4 id="【HTML-Parser】"><a href="#【HTML-Parser】" class="headerlink" title="【HTML Parser】"></a>【HTML Parser】</h4><p>对HTML字符串从上到下逐行解析,每解析完成一部分都会拿着解析结果进入下一步骤。</p>
<h4 id="【DOM-Tree】"><a href="#【DOM-Tree】" class="headerlink" title="【DOM Tree】"></a>【DOM Tree】</h4><p><code>css</code>相关标签跳过此步骤。</p>
<p>如果当前解析结果为<code><div /></code>相关标签,则生成DOM树<code>(window.document)</code>后进入下一步。</p>
<p>如果当前解析结果为<code><script /></code>相关标签且并且没有添加异步属性,则先停止【HTML Parser】的进行,等待<code><script /></code>资源加载完成后,然后按照以下2种情况处理,当处理完成后便停止当前<code><script /></code>标签后续步骤的执行,并继续进行新标签【HTML Parser】步骤的解析。</p>
<ol>
<li>如果HTML从未解析到过<code>css</code>相关节点则立即执行<code><script /></code>。(此时页面会把<code><script /></code>之前的内容都显示在页面上)</li>
<li>如果HTML已经解析到过<code>css</code>相关节点则等待<code>css</code>相关节点解析完成后再执行<code><script /></code>。(在CSS解析完的一瞬间会触发之前所有等待CSS资源解析的任务,假如在解析<code><script /></code>之前还有<code><div /></code>的话,理论上<code><div /></code>应该在执行<code><script /></code>之前被绘制到页面上,但因为Chrome是按照贞为单位来进行元素的绘制的,如果绘制<code><div /></code>与执行<code><script /></code>的时间在一贞之内,则会因为在绘制<code><div /></code>时被<code>js</code>阻塞,所以实际上需要等<code>js</code>执行完才会实际完成<code><div /></code>的绘制)。</li>
</ol>
<h4 id="【Style-Sheets】"><a href="#【Style-Sheets】" class="headerlink" title="【Style Sheets】"></a>【Style Sheets】</h4><p><code><div /></code>相关标签跳过此步骤。</p>
<p>如果当前解析结果为<code>css</code>相关标签,则等待其CSS资源加载完成,同时继续进行下一行的 【HTML Parser】。</p>
<h4 id="【CSS-Parser】"><a href="#【CSS-Parser】" class="headerlink" title="【CSS Parser】"></a>【CSS Parser】</h4><p><code><div /></code>相关标签跳过此步骤。</p>
<p>当CSS资源加载完毕后,对CSS从上到下逐行解析。</p>
<h4 id="【Style-Rules】"><a href="#【Style-Rules】" class="headerlink" title="【Style Rules】"></a>【Style Rules】</h4><p><code><div /></code>相关标签跳过此步骤。</p>
<p>当CSS解析完毕后,生成CSS规则树,也叫CSSOM,也就是<code>window.document.styleSheets</code>。</p>
<h4 id="【Attachment】"><a href="#【Attachment】" class="headerlink" title="【Attachment】"></a>【Attachment】</h4><p>根据DOM树与CSS规则树计算出每个节点的具体样式。分为两种情况:</p>
<ol>
<li>如果当前节点为<div />相关节点<br>如果HTML从未解析到过css相关标签则使用HTML默认样式,如果已经解析到过css相关标签则阻塞等待css标签也完成【Attachment】步骤后才进入下一步。</li>
<li>如果当前节点为css相关节点<br>则需要根据是否在之前已经渲染过CSS资源中对应的DOM节点,如果已经渲染过则需要重绘。如果未渲染过任何相关DOM节点则此步骤为最后一步。</li>
</ol>
<h4 id="【Render-Tree】"><a href="#【Render-Tree】" class="headerlink" title="【Render Tree】"></a>【Render Tree】</h4><p>生成渲染树,在此阶段已经可以将具体的某个<code><div /></code>与对应的CSS样式对应起来了。有了渲染树后浏览器就能根据当前浏览器的状态计算出某个DOM节点的样式、大小、宽度、是否独占一行等信息。计算完成后把一些不需要显示出来的节点在渲染树中删掉。如<code>display: none</code>。</p>
<h4 id="【Layout】"><a href="#【Layout】" class="headerlink" title="【Layout】"></a>【Layout】</h4><p>通过渲染树进行分层(根据定位属性、透明属性、transform属性、clip属性等)生成图层树。</p>
<h4 id="【Painting】"><a href="#【Painting】" class="headerlink" title="【Painting】"></a>【Painting】</h4><p>绘制所有图层,并转交给合成线程来最最终的合并所有图层的处理。</p>
<h4 id="【Display】"><a href="#【Display】" class="headerlink" title="【Display】"></a>【Display】</h4><p>最终生成页面并显示到浏览器上。</p>
<h2 id="浏览器处理每一帧的流程"><a href="#浏览器处理每一帧的流程" class="headerlink" title="浏览器处理每一帧的流程"></a>浏览器处理每一帧的流程</h2><p>浏览器在渲染完页面之后还需要不间断的处理很多内容的,比如动画、用户事件、定时器等。因此当浏览器渲染完页面后,还会在之后的每一帧到来时执行以下的流程。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/24/628bb016bb39b.webp"/></div></div>
<ul>
<li>【Input events】处理用户事件,先处理【阻塞事件Blocking】包括<code>touch</code>和<code>wheel</code>事件,后处理【非阻塞事件Non-blocking】包括<code>click</code>和<code>keypress</code>。</li>
<li>【JS】处理完用户事件后执行【定时器Timers】</li>
<li>【Begin frame】处理完定时器后开始进行【每帧事件Per frame events】的处理,包括窗口大小改变、滚动、媒体查询的更改、动画事件。</li>
<li>【rAF】处理完帧事件后执行<code>requestAnimationFrame</code>回调函数和<code>IntersectionObserver</code>回调函数。</li>
<li>【Layout】然后【重新计算样式Recalc style】、【更新布局Update layout】、【调整Observer回调的大小Resize Observer callbacks】</li>
<li>【Paint】然后【合成更新Compositing update】、【Paint invalidation】、【Record】</li>
</ul>
<h1 id="Chrome性能优化相关工具"><a href="#Chrome性能优化相关工具" class="headerlink" title="Chrome性能优化相关工具"></a>Chrome性能优化相关工具</h1><p>了解完浏览器渲染原理,我们还需要知道根据哪些指标才能判断一个页面性能的好坏,在Chrome中这些指标应该怎么获取。以及Chrome都为我们提供了哪些性能相关的工具,如何使用。</p>
<h2 id="Chrome-Performance-性能"><a href="#Chrome-Performance-性能" class="headerlink" title="Chrome Performance(性能)"></a>Chrome Performance(性能)</h2><p>Performance既是一个Chrome工具,可用于性能检测。</p>
<p>同样又是一套JS API,可在Chrome中执行。</p>
<h3 id="Chrome-Performance-工具的使用"><a href="#Chrome-Performance-工具的使用" class="headerlink" title="Chrome Performance 工具的使用"></a>Chrome Performance 工具的使用</h3><p>打开Chrome调试面板选择<strong>Performance</strong>,中文版为<strong>性能</strong>,点击刷新按钮,Performance会刷新并录制当前页面,然后我们就可以在面板中看到如下的各种性能相关细节。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/24/628bb12aad445.webp"/></div></div>
<h3 id="Performance-API介绍"><a href="#Performance-API介绍" class="headerlink" title="Performance API介绍"></a>Performance API介绍</h3><p>js中存在Performance API,可用于性能检测,具体如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/24/628bb154a5ffe.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/23/628b9dcaedcbd.jpg"/></div></div>
<ul>
<li>【Process Unload Event】等待上一个页面卸载。在我们输入url后浏览器需要卸载上一个页面的内容然后再去执行navigationStart导航开始。</li>
<li>【Redirect】浏览器卸载完上一个页面后会执行redirectStart然后将当前页面重定向到用户新输入的url页面。完成重定向后会执行redirectEnd</li>
<li>【Service Worker Init】如果当前页面注册了Service Worker那么执行workerStart对Service Worker进行初始化操作。</li>
<li>【Service Worker Fecth Event 】浏览器准备好发送请求,在发送之前会执行fetchStart</li>
<li>【HTTP Cache】如果有缓存则直接取缓存,如果没有的话则继续解析</li>
<li>【DNS】如果没有缓存则执行domainLookupStart 然后去解析DNS,解析完会执行domainLookupEnd</li>
<li>【TCP】DNS解析完会执行contentStart,然后进行TCP三次握手,如果是HTTPS则执行secureConnectionStart进行SSL协商。完成后会执行contentEnd。</li>
<li>【Request】TCP连接创建完成后执行requestStart,然后开始真正的发送请求</li>
<li>【Response】请求被响应且首字节返回时会先执行responseStart,响应全部接收完毕后会执行responseEnd</li>
<li>【Processing】响应完执行domLoading开始加载dom,dom加载完毕后执行domInteractive,此时dom已经可以交互。然后执行domContentLoadedEventStart,当dom整个节点全部加载完毕并执行完DOMContentLoaded事件后会触发domContentLoadedEventEnd简称DCL当dom整个加载完成会执行domComplete,此时页面资源已经全部加载完毕。</li>
<li>【onLoad】当页面资源已经全部加载完毕后会执行loadEventStart,触发window.onload事件,load事件完成后会执行loadEventEnd。</li>
</ul>
<h3 id="使用Performance-API获取性能相关指标"><a href="#使用Performance-API获取性能相关指标" class="headerlink" title="使用Performance API获取性能相关指标"></a>使用Performance API获取性能相关指标</h3><p>接下来我们来了解一下目前常用的性能指标,并且我们需要知道其中一些关键指标如何用Performance API获取。</p>
<h4 id="TTFB-首字节时间"><a href="#TTFB-首字节时间" class="headerlink" title="TTFB 首字节时间"></a>TTFB 首字节时间</h4><p><code>TTFB(Time To First Byte)</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">const</span> { responseStart, requestStart } = performance.<span class="property">timing</span></span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">TTFB</span> = responseStart - requestStart</span><br></pre></td></tr></table></figure>
<h4 id="FP-首次绘制"><a href="#FP-首次绘制" class="headerlink" title="FP 首次绘制"></a>FP 首次绘制</h4><p><code>FP (First Paint) 首次绘制</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">const</span> paint = performance.<span class="title function_">getEntriesByType</span>(<span class="string">'paint'</span>)</span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">FP</span> = paint[<span class="number">0</span>].<span class="property">startTime</span></span><br></pre></td></tr></table></figure>
<h4 id="FCP-首次内容绘制"><a href="#FCP-首次内容绘制" class="headerlink" title="FCP 首次内容绘制"></a>FCP 首次内容绘制</h4><p><code>FCP (First Contentful Paint) 首次内容绘制</code>: 标记浏览器渲染来自 DOM 第一位内容的时间点,该内容可能是<strong>文本</strong>、<strong>图像</strong>、<strong>SVG</strong> 甚至 <strong>元素</strong>。</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">const</span> paint = performance.<span class="title function_">getEntriesByType</span>(<span class="string">'paint'</span>)</span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">FCP</span> = paint[<span class="number">1</span>].<span class="property">startTime</span></span><br></pre></td></tr></table></figure>
<h4 id="FMP-首次有效绘制"><a href="#FMP-首次有效绘制" class="headerlink" title="FMP 首次有效绘制"></a>FMP 首次有效绘制</h4><p><code>FMP(First Meaningful Paint) 首次有效绘制</code>: 例如,在 YouTube 观看页面上,主视频就是主角元素。</p>
<p>图片可以没加载完成,但整体的骨架已经加载完成了。</p>
<p>1秒内完成FMP的概率超过80%,那就代表这个网站是一个性能较好的网站。</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">let</span> <span class="variable constant_">FMP</span> = <span class="number">0</span></span><br><span class="line"><span class="keyword">const</span> performanceObserverFMP = <span class="keyword">new</span> <span class="title class_">PerformanceObserver</span>(<span class="function">(<span class="params">entryList, observer</span>) =></span> {</span><br><span class="line"> <span class="keyword">const</span> entries = entryList.<span class="title function_">getEntries</span>()</span><br><span class="line"> observer.<span class="title function_">disconnect</span>()</span><br><span class="line"> <span class="variable constant_">FMP</span> = entries[<span class="number">0</span>].<span class="property">startTime</span></span><br><span class="line">})</span><br><span class="line"><span class="comment">// 需要在元素中添加 elementtiming="meaningful"</span></span><br><span class="line">performanceObserverFMP.<span class="title function_">observe</span>({ <span class="attr">entryTypes</span>: [<span class="string">'element'</span>] })</span><br></pre></td></tr></table></figure>
<h4 id="TTI-可交互时间"><a href="#TTI-可交互时间" class="headerlink" title="TTI 可交互时间"></a>TTI 可交互时间</h4><p><code>TTI (Time to Interactive) 可交互时间</code> : DOM树构建完毕,可以绑定事件的时间</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">const</span> { domInteractive, fetchStart } = performance.<span class="property">timing</span></span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">TTI</span> = domInteractive - fetchStart</span><br></pre></td></tr></table></figure>
<h4 id="LCP-最大内容渲染"><a href="#LCP-最大内容渲染" class="headerlink" title="LCP 最大内容渲染"></a>LCP 最大内容渲染</h4><p><code>LCP (Largest Contentful Paint) 最大内容渲染</code>: 代表在viewport中最大的页面元素加载的时间。LCP的数据会通过PerformanceEntry对象记录, 每次出现更大的内容渲染, 则会产生一个新的PerformanceEntry对象(2019年11月新增)。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> <span class="variable constant_">LCP</span> = <span class="number">0</span></span><br><span class="line"><span class="keyword">const</span> performanceObserverLCP = <span class="keyword">new</span> <span class="title class_">PerformanceObserver</span>(<span class="function">(<span class="params">entryList, observer</span>) =></span> {</span><br><span class="line"> <span class="keyword">const</span> entries = entryList.<span class="title function_">getEntries</span>()</span><br><span class="line"> observer.<span class="title function_">disconnect</span>()</span><br><span class="line"> <span class="variable constant_">LCP</span> = entries[entries.<span class="property">length</span> - <span class="number">1</span>].<span class="property">startTime</span></span><br><span class="line">})</span><br><span class="line">performanceObserverLCP.<span class="title function_">observe</span>({ <span class="attr">entryTypes</span>: [<span class="string">'largest-contentful-paint'</span>] })</span><br></pre></td></tr></table></figure>
<h4 id="DCL"><a href="#DCL" class="headerlink" title="DCL"></a>DCL</h4><p><code>DCL (DomContentloaded)</code>: 当 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,无需等待样式表、图像和子框架的完成加载。</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">const</span> { domContentLoadedEventEnd, fetchStart } = performance.<span class="property">timing</span></span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">DCL</span> = domContentLoadedEventEnd - fetchStart</span><br></pre></td></tr></table></figure>
<h4 id="L-全部加载完毕"><a href="#L-全部加载完毕" class="headerlink" title="L 全部加载完毕"></a>L 全部加载完毕</h4><p><code>L (onLoad)</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">const</span> { loadEventStart, fetchStart } = performance.<span class="property">timing</span></span><br><span class="line"><span class="keyword">const</span> L = loadEventStart - fetchStart</span><br></pre></td></tr></table></figure>
<h4 id="FID-首次输入延迟"><a href="#FID-首次输入延迟" class="headerlink" title="FID 首次输入延迟"></a>FID 首次输入延迟</h4><p><code>FID (First Input Delay) 首次输入延迟</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="keyword">let</span> <span class="variable constant_">FID</span> = <span class="number">0</span></span><br><span class="line"><span class="keyword">const</span> performanceObserverFID = <span class="keyword">new</span> <span class="title class_">PerformanceObserver</span>(<span class="function">(<span class="params">entryList, observer</span>) =></span> {</span><br><span class="line"> <span class="keyword">const</span> entries = entryList.<span class="title function_">getEntries</span>()</span><br><span class="line"> observer.<span class="title function_">disconnect</span>()</span><br><span class="line"> <span class="variable constant_">FID</span> = entries[<span class="number">0</span>].<span class="property">processingStart</span> - entries[<span class="number">0</span>].<span class="property">startTime</span></span><br><span class="line">})</span><br><span class="line">performanceObserverFID.<span class="title function_">observe</span>({ <span class="attr">type</span>: [<span class="string">'first-input'</span>], <span class="attr">buffered</span>: <span class="literal">true</span> })</span><br></pre></td></tr></table></figure>
<h4 id="TBT-页面阻塞总时长"><a href="#TBT-页面阻塞总时长" class="headerlink" title="TBT 页面阻塞总时长"></a>TBT 页面阻塞总时长</h4><p><code>TBT (Total Blocking Time) 页面阻塞总时长</code>: TBT汇总所有加载过程中阻塞用户操作的时长,在FCP和TTI之间任何long task中阻塞部分都会被汇总。</p>
<h4 id="CLS-累积布局偏移"><a href="#CLS-累积布局偏移" class="headerlink" title="CLS 累积布局偏移"></a>CLS 累积布局偏移</h4><p><code>CLS (Cumulative Layout Shift) 累积布局偏移</code>: 总结起来就是一个元素初始时和其hidden之间的任何时间如果元素偏移了, 则会被计算进去, 具体的计算方法可看这篇文章 <a href="https://web.dev/cls/">https://web.dev/cls/</a></p>
<h4 id="SI"><a href="#SI" class="headerlink" title="SI"></a>SI</h4><p><code>SI (Speed Index)</code>: 指标用于显示页面可见部分的显示速度, 单位是时间。</p>
<h2 id="Coverage-覆盖率"><a href="#Coverage-覆盖率" class="headerlink" title="Coverage(覆盖率)"></a>Coverage(覆盖率)</h2><p>获取代码未使用占比。获取代码未使用占比</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/24/628bb981db5c8.webp"/></div></div>
<h2 id="Lighthouse"><a href="#Lighthouse" class="headerlink" title="Lighthouse"></a>Lighthouse</h2><p>获取性能报告并查看推荐优化项。可以在本地安装命令行工具来使用,也可以通过Chrome来使用。</p>
<div class="tabs" id=""><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#-1">命令行方式使用</button></li><li class="tab"><button type="button" data-href="#-2">在Chrome中使用</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="-1"><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 -g lighthouse</span><br><span class="line">lighthouse --view https://m.baidu.com</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="-2"><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/24/628cf77a92095.webp"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/24/628cf77e139ff.webp"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div>
<h2 id="Network-网络"><a href="#Network-网络" class="headerlink" title="Network(网络)"></a>Network(网络)</h2><h3 id="网络请求中的Timing-时间"><a href="#网络请求中的Timing-时间" class="headerlink" title="网络请求中的Timing(时间)"></a>网络请求中的Timing(时间)</h3><p>能获取网络请求的时间消耗细节,可以根据耗时来决定优化策略。优先优化耗时最长的。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/24/628cf966b69da.webp"/></div></div>
<ul>
<li>正在排队:网络请求队列的排队时间</li>
<li>已停止:阻塞住用于处理其他事情的时间</li>
<li>DNS查找:用于DNS解析IP地址的时间</li>
<li>初始连接:创建TCP连接时间</li>
<li>SSL:用于SSL协商的时间</li>
<li>已发送请求:用于发送请求的时间</li>
<li>等待中:请求发出至接收响应的时间也可以理解为服务端处理请求的时间</li>
<li>下载内容:下载响应的时间</li>
</ul>
<h3 id="网络请求的优先级"><a href="#网络请求的优先级" class="headerlink" title="网络请求的优先级"></a>网络请求的优先级</h3><p>浏览器会根据资源的类型决定优先请求哪些资源,优先级高的请求能够优先被加载。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/24/628cf9e853256.webp"/></div></div>
<p>右击此处勾选优先级可打开优先级功能,在请求中便可看到网络请求的优先级。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/24/628cf9efddd7f.webp"/></div></div>
<p>不同资源类型的优先级排序如下:</p>
<ul>
<li>最高:html、style</li>
<li>高:font、fetch、script</li>
<li>低:image、track</li>
</ul>
<h3 id="网页总资源信息"><a href="#网页总资源信息" class="headerlink" title="网页总资源信息"></a>网页总资源信息</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/24/628cfb20c1315.webp"/></div></div>
<ul>
<li><p>58个请求:网页一共多少个请求</p>
</li>
<li><p>6.9 MB 项资源:网页资源一共6.9MB大小</p>
</li>
<li><p>DOMContentLoaded 454 毫秒:DOM加载完毕的时长</p>
</li>
<li><p>加载时间 1.02 秒:onload完毕的时长</p>
</li>
</ul>
<h3 id="Network配置"><a href="#Network配置" class="headerlink" title="Network配置"></a>Network配置</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/24/628cfcbe97759.webp"/></div></div>
<h1 id="网页性能优化"><a href="#网页性能优化" class="headerlink" title="网页性能优化"></a>网页性能优化</h1><p>上面我们分别讲解了进程与线程、浏览器打开一个页面的完整过程、浏览器处理每一帧时的流程、Chrome性能相关的各种工具以及性能相关的各种指标。以上内容都掌握之后我们再考虑性能优化遍有了思路,我们在页面展示的任意一个步骤中对其进行优化都能对整个网页的展示性能产生影响。</p>
<p>下面列出了一个页面能优化的所有内容,读者可根据自己的业务情况结合性能工具来做适合自己的优化方式。</p>
<h2 id="网络优化策略"><a href="#网络优化策略" class="headerlink" title="网络优化策略"></a>网络优化策略</h2><h3 id="减少HTTP请求数"><a href="#减少HTTP请求数" class="headerlink" title="减少HTTP请求数"></a>减少HTTP请求数</h3><p>合并JS、合并CSS、合理内嵌JS和CSS、使用雪碧图。</p>
<h3 id="使用HTTP缓存"><a href="#使用HTTP缓存" class="headerlink" title="使用HTTP缓存"></a>使用HTTP缓存</h3><p>使用强制缓存可以不走网络请求,直接走本地缓存数据来加载资源。</p>
<p>使用协商缓存可以减少数据传输,当不需要更新数据时可通知客户端直接使用本地缓存。</p>
<h3 id="使用-HTTP-x2F-2-0"><a href="#使用-HTTP-x2F-2-0" class="headerlink" title="使用 HTTP/2.0"></a>使用 HTTP/2.0</h3><p>HTTP/2.0使用同一个TCP连接来发送数据,他把多个请求通过二进制分贞层实现了分贞,然后把数据传输给服务器。也叫多路复用,多个请求复用同一个TCP连接。</p>
<p>HTTP/2.0会将所有以:开头的请求头做一个映射表,然后使用hpack进行压缩,使用这种方式会使请求头更小。</p>
<p>服务器可主动推送数据给客户端。</p>
<h3 id="避免重定向"><a href="#避免重定向" class="headerlink" title="避免重定向"></a>避免重定向</h3><p>301、302 重定向会降低响应速度。</p>
<h3 id="使用-dns-prefetch"><a href="#使用-dns-prefetch" class="headerlink" title="使用 dns-prefetch"></a>使用 dns-prefetch</h3><p>DNS请求虽然占用的带宽较少,但会有很高的延迟,由其在移动端网络会更加明显。</p>
<p>使用 dns-prefetch 可以对网站中使用到的域名提前进行解析。提高资源加载速度。</p>
<p>通过<strong>DNS预解析技术</strong>可以很好的降低延迟,在访问以图片为主的移动端网站时,使用DNS预解析的情意中下页面加载时间可以减少5%。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><link rel="dns-prefetch" href="https://a.hagan.com/<span class="string">"></span></span><br></pre></td></tr></table></figure>
<h3 id="使用域名分片"><a href="#使用域名分片" class="headerlink" title="使用域名分片"></a>使用域名分片</h3><p>在HTTP/1.1中,一个域名同时最多创建6个TCP连接,将资源放在多个域名下可提高请求的并发数。</p>
<h3 id="CDN"><a href="#CDN" class="headerlink" title="CDN"></a>CDN</h3><p>静态资源全上CDN,CDN能非常有效的加快网站静态资源的访问速度。</p>
<h3 id="压缩"><a href="#压缩" class="headerlink" title="压缩"></a>压缩</h3><p>gzip压缩、html压缩、js压缩、css压缩、图片压缩。</p>
<h3 id="使用contenthash"><a href="#使用contenthash" class="headerlink" title="使用contenthash"></a>使用contenthash</h3><p>contenthash可以根据文件内容在文件名中加hash,可用于浏览器缓存文件,当文件没有改变时便直接取本地缓存数据。</p>
<h3 id="合理使用preload、prefetch"><a href="#合理使用preload、prefetch" class="headerlink" title="合理使用preload、prefetch"></a>合理使用preload、prefetch</h3><p>preload预加载、prefetch空闲时间加载。</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"><link rel="preload" as="style" href="/static/style<span class="selector-class">.css</span>"></span><br><span class="line"><link rel="preload" as="<span class="attribute">font</span>" href="/static/<span class="attribute">font</span><span class="selector-class">.woff</span>"></span><br><span class="line"><link rel="preload" as="script" href="/static/script<span class="selector-class">.js</span>"></span><br><span class="line"></span><br><span class="line"><link rel="prefetch" as="style" href="/static/style<span class="selector-class">.css</span>"></span><br><span class="line"><link rel="prefetch" as="<span class="attribute">font</span>" href="/static/<span class="attribute">font</span><span class="selector-class">.woff</span>"></span><br><span class="line"><link rel="prefetch" as="script" href="/static/script<span class="selector-class">.js</span>"></span><br></pre></td></tr></table></figure>
<p>两者都不会阻塞<code>onload</code>事件,<code>prefetch</code>会在页面空闲时候再进行加载,是提前预加载之后可能要用到的资源,不一定是当前页面使用的,<code>preload</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><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE <span class="keyword">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"X-UA-Compatible"</span> <span class="attr">content</span>=<span class="string">"IE=edge"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1.0"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"preload"</span> <span class="attr">as</span>=<span class="string">"style"</span> <span class="attr">href</span>=<span class="string">"./preload.css"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>Document<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">article</span>></span><span class="tag"></<span class="name">article</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure>
<p>如上代码,预加载了css但并没有使用。浏览器在页面<code>onload</code>完成一段时间后,发现还没有引用预加载的资源时,浏览器会在控制台输出下图的提示信息。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/24/628cfd2d0b279.webp"/></div></div>
<p>preload和prefetch可根据资源类型决定资源加载的优先级,详细优先级如代码:</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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="meta"><!DOCTYPE <span class="keyword">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"X-UA-Compatible"</span> <span class="attr">content</span>=<span class="string">"IE=edge"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1.0"</span>></span></span><br><span class="line"> <span class="comment"><!-- 最高 --></span></span><br><span class="line"> <span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"preload"</span> <span class="attr">as</span>=<span class="string">"style"</span> <span class="attr">href</span>=<span class="string">"./file.xxx"</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- 高 --></span></span><br><span class="line"> <span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"preload"</span> <span class="attr">as</span>=<span class="string">"font"</span> <span class="attr">href</span>=<span class="string">"./file.xxx"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"preload"</span> <span class="attr">as</span>=<span class="string">"fetch"</span> <span class="attr">href</span>=<span class="string">"./file.xxx"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"preload"</span> <span class="attr">as</span>=<span class="string">"script"</span> <span class="attr">href</span>=<span class="string">"./file.xxx"</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- 低 --></span></span><br><span class="line"> <span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"preload"</span> <span class="attr">as</span>=<span class="string">"image"</span> <span class="attr">href</span>=<span class="string">"./file.xxx"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"preload"</span> <span class="attr">as</span>=<span class="string">"track"</span> <span class="attr">href</span>=<span class="string">"./file.xxx"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>Document<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">article</span>></span><span class="tag"></<span class="name">article</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/24/628cfd767a1b3.webp"/></div></div>
<h2 id="浏览器渲染优化策略"><a href="#浏览器渲染优化策略" class="headerlink" title="浏览器渲染优化策略"></a>浏览器渲染优化策略</h2><h3 id="关键渲染路径"><a href="#关键渲染路径" class="headerlink" title="关键渲染路径"></a>关键渲染路径</h3><p>当通过JS或者其他任意方式修改DOM后,浏览器会进入如下流程。</p>
<p>【JS通过API修改DOM】>【计算样式】>【布局(重排)】>【绘制(重绘)】>【合成】。</p>
<p><code>Reflow 重排</code>:重排在Chrome Performance中叫做布局,通常添加或删除元素、修改元素大小、移动元素位置、获取位置信息都会触发页面的重排,因为重排可能会改变元素的大小位置等信息,这样的改变会影响到页面大量其它元素的大小位置信息,会耗费掉大量的性能,所以在实际应用中我们应该尽可能的减少重排。</p>
<p><code>Repaint 重绘</code>:重绘在Chrome Performance中叫做绘制,通常样式改变但没有影响位置时会触发重绘操作,重绘性能还好,但我们也需要尽量减少重绘,如果需要做一些动画,我们尽量使用CSS3动画,CSS3动画只需要在初始化时绘制一次,之后的动画都不会触发重绘操作。</p>
<h3 id="强制同步布局问题"><a href="#强制同步布局问题" class="headerlink" title="强制同步布局问题"></a>强制同步布局问题</h3><p>在同一个函数内,修改元素后又获取元素的位置时会触发强制同步布局,影响渲染性能。</p>
<p>强制同步布局会使js强制将【计算样式】和【布局(重排)】操作提前到当前函数任务中,这样会导致每次运行时执行一次【计算样式】和【重排】,这样一定会影响页面渲染性能,而正常情况下【计算样式】和【重排】操作会在函数结束后统一执行。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE <span class="keyword">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"X-UA-Compatible"</span> <span class="attr">content</span>=<span class="string">"IE=edge"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1.0"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>Document<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">article</span> <span class="attr">id</span>=<span class="string">"article"</span>></span><span class="tag"></<span class="name">article</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">script</span>></span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">const</span> domArticle = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">'#article'</span>)</span></span><br><span class="line"><span class="language-javascript"> <span class="comment">// const { offsetTop } = domArticle</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">function</span> <span class="title function_">reflow</span> () {</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">const</span> domH1 = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">'h1'</span>)</span></span><br><span class="line"><span class="language-javascript"> domH1.<span class="property">innerHTML</span> = <span class="string">'h1'</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> domArticle.<span class="title function_">appendChild</span>(domH1)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="comment">/**</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> * 强制同步布局</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> * 在修改元素后又获取元素的位置时会触发强制同步布局,影响渲染性能</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> * 解决办法是采用读写分离的原则,同一个函数内只读、只写</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> */</span></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">const</span> { offsetTop } = domArticle</span></span><br><span class="line"><span class="language-javascript"> <span class="variable language_">console</span>.<span class="title function_">log</span>(offsetTop)</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="variable language_">window</span>.<span class="property">onload</span> = <span class="function">() =></span> {</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < <span class="number">10</span>; i++) {</span></span><br><span class="line"><span class="language-javascript"> <span class="title function_">reflow</span>()</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> </span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure>
<p>在函数运行时执行了10次【计算样式】和【重排】</p>
<p>反复触发强制同步布局也叫<strong>布局抖动</strong></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/24/628cfe1e5ce7e.webp"/></div></div>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE <span class="keyword">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"X-UA-Compatible"</span> <span class="attr">content</span>=<span class="string">"IE=edge"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1.0"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>Document<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">article</span> <span class="attr">id</span>=<span class="string">"article"</span>></span><span class="tag"></<span class="name">article</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">script</span>></span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">const</span> domArticle = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">'#article'</span>)</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">const</span> { offsetTop } = domArticle</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">function</span> <span class="title function_">reflow</span> () {</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">const</span> domH1 = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">'h1'</span>)</span></span><br><span class="line"><span class="language-javascript"> domH1.<span class="property">innerHTML</span> = <span class="string">'h1'</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> domArticle.<span class="title function_">appendChild</span>(domH1)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="comment">/**</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> * 强制同步布局</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> * 在修改元素后又获取元素的位置时会触发强制同步布局,影响渲染性能</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> * 解决办法是采用读写分离的原则,同一个函数内只读、只写</span></span></span><br><span class="line"><span class="comment"><span class="language-javascript"> */</span></span></span><br><span class="line"><span class="language-javascript"> <span class="comment">// const { offsetTop } = domArticle</span></span></span><br><span class="line"><span class="language-javascript"> <span class="variable language_">console</span>.<span class="title function_">log</span>(offsetTop)</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="variable language_">window</span>.<span class="property">onload</span> = <span class="function">() =></span> {</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < <span class="number">10</span>; i++) {</span></span><br><span class="line"><span class="language-javascript"> <span class="title function_">reflow</span>()</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> </span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure>
<p>我们遵循读写分离的原则,将读取位置操作放到函数外,我们可以发现就算循环插入10个dom节点,也只需要执行一次【计算样式】和【重排】。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/24/628cfe54058e0.webp"/></div></div>
<h3 id="如何减少重排与重绘"><a href="#如何减少重排与重绘" class="headerlink" title="如何减少重排与重绘"></a>如何减少重排与重绘</h3><ol>
<li>脱离文档流(绝对定位、固定定位),脱离文档流的元素进行重排不会影响到其他元素。</li>
<li>图片渲染时增加宽高属性,宽高固定后,图片不会根据内容动态改变高度,便不会触发重排。</li>
<li>尽量用CSS3动画,CSS3动画能最大程度减少重排与重绘。</li>
<li>使用will-change: transform;将元素独立为一个单独的图层。(定位、透明、transform、clip都会产生独立图层)。</li>
</ol>
<h2 id="静态文件优化策略"><a href="#静态文件优化策略" class="headerlink" title="静态文件优化策略"></a>静态文件优化策略</h2><h3 id="图片格式"><a href="#图片格式" class="headerlink" title="图片格式"></a>图片格式</h3><h4 id="jpeg"><a href="#jpeg" class="headerlink" title="jpeg"></a>jpeg</h4><p>适合色彩丰富的图、Banner图。不适合:图形文字、图标、不支持透明度。</p>
<h4 id="png"><a href="#png" class="headerlink" title="png"></a>png</h4><p>适合纯色、透明、图标,支持纯透明和半透明。不适合色彩丰富图片,因为无损储存会导致储存体积大于jpeg。</p>
<h4 id="gif"><a href="#gif" class="headerlink" title="gif"></a>gif</h4><p>适合动画、可以动的图标。支持纯透明但不支持半透明,不适合色彩丰富的图片。</p>
<p>埋点信息通常也会使用gif发送,因为1x1的gif图发送的网络请求比普通的get请求要小一些。</p>
<h4 id="webp"><a href="#webp" class="headerlink" title="webp"></a>webp</h4><p>支持纯透明和半透明,可以保证图片质量和较小的体积,适合Chrome和移动端浏览器。不适合其他浏览器。</p>
<h4 id="svg"><a href="#svg" class="headerlink" title="svg"></a>svg</h4><p>矢量格式,大小非常小,但渲染成本过高,适合小且色彩单一的图标。</p>
<h3 id="图片优化"><a href="#图片优化" class="headerlink" title="图片优化"></a>图片优化</h3><ul>
<li>减少图片资源的尺寸和大小,节约用户流量。</li>
<li>设置alt=”xxx”属性,图像无法显示时会显示alt内容。</li>
<li>图片懒加载, loading=”lazy”为原生,建议使用IntersectionObserver自己做懒加载。</li>
<li>不同环境加载不同尺寸和像素的图片srcset与sizes的使用。</li>
<li>采用渐进式加载 先加载占位图,然后加载模糊小图,最后加载真正清晰的图。</li>
<li>使用Base64URL 减少图片请求数。</li>
<li>采用雪碧图合并图片,减少请求数。</li>
</ul>
<h3 id="HTML优化"><a href="#HTML优化" class="headerlink" title="HTML优化"></a>HTML优化</h3><ul>
<li>语义化HTML,代码简洁清晰,利于SEO,便于开发维护。</li>
<li>减少HTML嵌套关系,减少DOM节点数量。</li>
<li>提前声明字符编码,让浏览器快速确定如何渲染网页内容<code><html lang="en"> <meta charset="UTF-8"></code>。</li>
<li>删除多余空格、空行、注释、无用属性。</li>
<li>减少iframe,子iframe会阻塞父级的onload事件。可以使用js动态给iframe赋值,就能解决这个问题。</li>
<li>避免table布局。</li>
</ul>
<h3 id="CSS优化"><a href="#CSS优化" class="headerlink" title="CSS优化"></a>CSS优化</h3><ul>
<li>减少伪类选择器,减少选择器层数、减少通配符选择器、减少正则选择器。</li>
<li>避免css表达式<code>background-color: expression(...)</code>。</li>
<li>删除空格、空行、注释、减少无意义的单位、css压缩。</li>
<li>css外链,能走缓存。</li>
<li>添加媒体字段,只加载有效的css文件。<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><link rel="stylesheet" href="./small<span class="selector-class">.css</span>" media="screen and (<span class="attribute">max-width</span>:<span class="number">600px</span>)<span class="string">" /></span></span><br><span class="line"><span class="string"><link rel="</span>stylesheet<span class="string">" href="</span>./big.css<span class="string">" media="</span>screen and (min-width:<span class="number">601px</span>)<span class="string">"/></span></span><br></pre></td></tr></table></figure></li>
<li>使用css <code>contain</code>属性,能控制对应元素是否根据子集元素的改变进行重排。</li>
<li>减少<code>@import</code>使用,因为它使用串行加载。</li>
</ul>
<h3 id="JS优化"><a href="#JS优化" class="headerlink" title="JS优化"></a>JS优化</h3><ul>
<li>通过script的async、defer属性异步加载,不阻塞DOM渲染。</li>
<li>减少DOM操作,缓存访问过的元素。</li>
<li>不直接操作真实DOM,可以先修改,然后一次性应用到DOM上。(虚拟DOM、DOM碎片节点)。</li>
<li>使用webworker解决复杂运算,避免复杂运算阻塞主线程,webworker线程位于渲染进程。</li>
<li>图片懒加载,使用<code>IntersectionObserver</code>实现。</li>
</ul>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE <span class="keyword">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"X-UA-Compatible"</span> <span class="attr">content</span>=<span class="string">"IE=edge"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1.0"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">style</span>></span><span class="language-css"></span></span><br><span class="line"><span class="language-css"> <span class="selector-tag">img</span> {</span></span><br><span class="line"><span class="language-css"> <span class="attribute">height</span>: <span class="number">200px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">display</span>: block;</span></span><br><span class="line"><span class="language-css"> }</span></span><br><span class="line"><span class="language-css"> </span><span class="tag"></<span class="name">style</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>Document<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">img</span> <span class="attr">src</span>=<span class="string">"./loading.gif"</span> <span class="attr">data-src</span>=<span class="string">"./01.jpg"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">img</span> <span class="attr">src</span>=<span class="string">"./loading.gif"</span> <span class="attr">data-src</span>=<span class="string">"./02.jpg"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">img</span> <span class="attr">src</span>=<span class="string">"./loading.gif"</span> <span class="attr">data-src</span>=<span class="string">"./03.jpg"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">img</span> <span class="attr">src</span>=<span class="string">"./loading.gif"</span> <span class="attr">data-src</span>=<span class="string">"./04.jpg"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">img</span> <span class="attr">src</span>=<span class="string">"./loading.gif"</span> <span class="attr">data-src</span>=<span class="string">"./05.jpg"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">img</span> <span class="attr">src</span>=<span class="string">"./loading.gif"</span> <span class="attr">data-src</span>=<span class="string">"./06.jpg"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">img</span> <span class="attr">src</span>=<span class="string">"./loading.gif"</span> <span class="attr">data-src</span>=<span class="string">"./07.jpg"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">img</span> <span class="attr">src</span>=<span class="string">"./loading.gif"</span> <span class="attr">data-src</span>=<span class="string">"./08.jpg"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">img</span> <span class="attr">src</span>=<span class="string">"./loading.gif"</span> <span class="attr">data-src</span>=<span class="string">"./09.jpg"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">img</span> <span class="attr">src</span>=<span class="string">"./loading.gif"</span> <span class="attr">data-src</span>=<span class="string">"./10.jpg"</span> /></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">script</span>></span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">const</span> intersectionObserver = <span class="keyword">new</span> <span class="title class_">IntersectionObserver</span>(<span class="function">(<span class="params">changes</span>) =></span> {</span></span><br><span class="line"><span class="language-javascript"> changes.<span class="title function_">forEach</span>(<span class="function">(<span class="params">item, index</span>) =></span> {</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">if</span> (item.<span class="property">intersectionRatio</span> > <span class="number">0</span>) {</span></span><br><span class="line"><span class="language-javascript"> intersectionObserver.<span class="title function_">unobserve</span>(item.<span class="property">target</span>)</span></span><br><span class="line"><span class="language-javascript"> item.<span class="property">target</span>.<span class="property">src</span> = item.<span class="property">target</span>.<span class="property">dataset</span>.<span class="property">src</span></span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> })</span></span><br><span class="line"><span class="language-javascript"> });</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">const</span> domImgList = <span class="variable language_">document</span>.<span class="title function_">querySelectorAll</span>(<span class="string">"img"</span>);</span></span><br><span class="line"><span class="language-javascript"> domImgList.<span class="title function_">forEach</span>(<span class="function">(<span class="params">domImg</span>) =></span> intersectionObserver.<span class="title function_">observe</span>(domImg));</span></span><br><span class="line"><span class="language-javascript"> </span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure>
<ul>
<li>虚拟滚动</li>
<li>使用<code>requestAnimationFrame</code>来做动画,使用<code>requestIdleCallback</code>来进行空闲时的任务处理。</li>
<li>尽量避免使用eval,性能差。</li>
<li>使用事件委托,能减少事件绑定个数。事件越多性能越差。</li>
<li>尽量使用canvas、css3动画。</li>
<li>通过chrome覆盖率(Coverage)工具排查代码中未使用过的代码并将其删除。</li>
<li>通过chrome性能(Performance)工具查看每个函数的执行性能并优化。</li>
</ul>
<h3 id="字体优化"><a href="#字体优化" class="headerlink" title="字体优化"></a>字体优化</h3><p>FOUT(Flash of Unstyled Text)等待一段时间,如果没加载完成,先显示默认。加载 后再进行切换。</p>
<p>FOIT(F1ash of Invisib1e Text) 字体加载完毕后显示,加载超时降级系统字体(白屏)。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE <span class="keyword">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"X-UA-Compatible"</span> <span class="attr">content</span>=<span class="string">"IE=edge"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1.0"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">style</span>></span><span class="language-css"></span></span><br><span class="line"><span class="language-css"> <span class="keyword">@font-face</span> {</span></span><br><span class="line"><span class="language-css"> <span class="attribute">font-family</span>: <span class="string">'hagan'</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">src</span>: <span class="built_in">url</span>(<span class="string">'./font.ttc'</span>);</span></span><br><span class="line"><span class="language-css"> <span class="attribute">font-display</span>: swap;</span></span><br><span class="line"><span class="language-css"> <span class="comment">/* b1ock 35 内不显示,如果没加载完毕用默认的 */</span></span></span><br><span class="line"><span class="language-css"> <span class="comment">/* swap 显示老字体 在替换*/</span></span></span><br><span class="line"><span class="language-css"> <span class="comment">/* fa11back 缩短不显示时间,如果没加载完毕用默认的,和b1ock类似*</span></span></span><br><span class="line"><span class="comment"><span class="language-css"> /* optional 替换可能用字体 可能不替换*/</span></span></span><br><span class="line"><span class="language-css"> }</span></span><br><span class="line"><span class="language-css"> <span class="selector-tag">article</span> {</span></span><br><span class="line"><span class="language-css"> <span class="attribute">font-family</span>: hagan;</span></span><br><span class="line"><span class="language-css"> }</span></span><br><span class="line"><span class="language-css"> </span><span class="tag"></<span class="name">style</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>Document<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">article</span>></span>ABC abc<span class="tag"></<span class="name">article</span>></span> </span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure>
<h2 id="浏览器储存优化策略"><a href="#浏览器储存优化策略" class="headerlink" title="浏览器储存优化策略"></a>浏览器储存优化策略</h2><h3 id="Cookie"><a href="#Cookie" class="headerlink" title="Cookie"></a>Cookie</h3><p><code>cookie</code>在过期之前一直有效,最大储存大小为4k,限制字段个数,不适合大量的数据储存,每次请求会携带<code>cookie</code>,主要用来做身份校验。</p>
<p>优化方式:</p>
<ol>
<li>需要合理设置<code>cookie</code>有效期。</li>
<li>根据不同子域划分<code>cookie</code>来减少<code>cookie</code>传输。</li>
<li>静态资源域名和<code>cookie</code>域名采用不同域名,避免静态资源请求携带<code>cookie</code>。</li>
</ol>
<h3 id="LocalStorage"><a href="#LocalStorage" class="headerlink" title="LocalStorage"></a>LocalStorage</h3><p>Chrome下最多储存5M,除非手动清除,否则一直存在。可以利用<code>localStorage</code>储存静态资源。比如储存网页的<code>.js</code>、<code>.css</code>,这样会使页面打开速度非常快。例如 <a href="https://m.baidu.com/">https://m.baidu.com</a></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">// index.js</span></span><br><span class="line"><span class="keyword">const</span> name = <span class="string">'hagan'</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">showName</span> () {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(name)</span><br><span class="line">}</span><br><span class="line"><span class="title function_">showName</span>()</span><br></pre></td></tr></table></figure>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"><!-- index.html --></span></span><br><span class="line"><span class="meta"><!DOCTYPE <span class="keyword">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"X-UA-Compatible"</span> <span class="attr">content</span>=<span class="string">"IE=edge"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1.0"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>Document<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">script</span> <span class="attr">src</span>=<span class="string">"https://lib.baomitu.com/axios/0.26.1/axios.js"</span>></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">script</span>></span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="title function_">cacheFile</span>(<span class="string">'/index.js'</span>)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">cacheFile</span> (url) {</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">const</span> fileContent = <span class="variable language_">localStorage</span>.<span class="title function_">getItem</span>(url)</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">if</span> (fileContent) {</span></span><br><span class="line"><span class="language-javascript"> <span class="built_in">eval</span>(fileContent)</span></span><br><span class="line"><span class="language-javascript"> } <span class="keyword">else</span> {</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">const</span> { data } = <span class="keyword">await</span> axios.<span class="title function_">get</span>(url)</span></span><br><span class="line"><span class="language-javascript"> <span class="built_in">eval</span>(data)</span></span><br><span class="line"><span class="language-javascript"> <span class="variable language_">localStorage</span>.<span class="title function_">setItem</span>(url, data)</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> </span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure>
<h3 id="SessionStorage"><a href="#SessionStorage" class="headerlink" title="SessionStorage"></a>SessionStorage</h3><p>会话级别储存,可用于页面间的传值。</p>
<h3 id="IndexDB"><a href="#IndexDB" class="headerlink" title="IndexDB"></a>IndexDB</h3><p>浏览器的本地数据库,大小几乎无上限。</p>
<h2 id="其他优化策略"><a href="#其他优化策略" class="headerlink" title="其他优化策略"></a>其他优化策略</h2><ul>
<li>关键资源个数越多,首次页面加载时间就会越长。</li>
<li>关键资源的大小,内容越小下载时间越短。</li>
<li>优化白屏,合理使用内联css、js。</li>
<li>预渲染,打包时进行预渲染,生成静态HTML文件,用户访问时直接返回静态HTML。</li>
<li>服务端渲染同构,加速首屏速度(耗费服务端资源),有利于SEO优化。首屏使用服务端渲染,后续交互使用客户端渲染。</li>
</ul>
<h2 id="使用PWA提高用户体验"><a href="#使用PWA提高用户体验" class="headerlink" title="使用PWA提高用户体验"></a>使用PWA提高用户体验</h2><p>webapp用户体验差的一大原因是不能离线访问。用户粘性低的一大原因是无法保存入口,PWA就是为了解决webapp的用户体验问题而诞生的。使用PWA能令站点拥有快速、可靠、安全等特性。</p>
<ol>
<li>Web App Manifest 将网站添加到电脑桌面、手机桌面,类似Native的体验。</li>
<li>Service Worker 配合Cache API,能做到离线缓存各种内容。</li>
<li>Push API 配合 Notification API,能做到类似Native的消息推送与实时提醒。</li>
<li>App Shell 配合 App Skeleton,能做App壳与骨架屏。</li>
</ol>
<!-- <div class="note info flat"><p>原文链接:<a href="https://mp.weixin.qq.com/s/wJxj5QbOHwH9cKmqU5eSQw">Web页面全链路性能优化指南</a></p>
</div>
7个 Vue3 中的组件通信方式
https://fe32.top/articles/vu9856es/
2022-05-15T14:56:04.000Z
2023-05-17T10:01:12.000Z
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/16/628126e1ac761.webp"/></div></div>
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本文采用<code><script setup /></code>的编写方式,比<code>options API</code>更自由。然后我们会讲以下七种组件通信方式:</p>
<ul>
<li>props</li>
<li>emit</li>
<li>v-model</li>
<li>refs</li>
<li>provide/inject</li>
<li>eventBus</li>
<li>vuex/pinia</li>
</ul>
<h2 id="举个例子"><a href="#举个例子" class="headerlink" title="举个例子"></a>举个例子</h2><p>本文将使用如下演示,如下图所示:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/16/628126e4580e2.webp"/></div></div>
<p>上图中,列表和输入框分别是父组件和子组件。根据不同的通信方式,会调整父子组件。</p>
<h2 id="Props"><a href="#Props" class="headerlink" title="Props"></a>Props</h2><p>props 是 Vue 中最常见的父子通信方式,使用起来也比较简单。</p>
<p>根据上面的demo,我们在父组件中定义了数据和对数据的操作,子组件只渲染一个列表。</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><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><template></span><br><span class="line"> <!-- child component --></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">child-components</span> <span class="attr">:list</span>=<span class="string">"list"</span>></span><span class="tag"></<span class="name">child-components</span>></span></span></span><br><span class="line"> <!-- parent component --></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"child-wrap input-group"</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">input</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">v-model</span>=<span class="string">"value"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">type</span>=<span class="string">"text"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">class</span>=<span class="string">"form-control"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">placeholder</span>=<span class="string">"Please enter"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"input-group-append"</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">button</span> @<span class="attr">click</span>=<span class="string">"handleAdd"</span> <span class="attr">class</span>=<span class="string">"btn btn-primary"</span> <span class="attr">type</span>=<span class="string">"button"</span>></span></span></span><br><span class="line"><span class="language-xml"> add</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">button</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"></template></span><br><span class="line"><span class="language-xml"><span class="tag"><<span class="name">script</span> <span class="attr">setup</span>></span><span class="language-javascript"></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">import</span> { ref } <span class="keyword">from</span> <span class="string">'vue'</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">import</span> <span class="title class_">ChildComponents</span> <span class="keyword">from</span> <span class="string">'./child.vue'</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">const</span> list = <span class="title function_">ref</span>([<span class="string">'JavaScript'</span>, <span class="string">'HTML'</span>, <span class="string">'CSS'</span>])</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">const</span> value = <span class="title function_">ref</span>(<span class="string">''</span>)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="comment">// event handling function triggered by add</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">const</span> <span class="title function_">handleAdd</span> = (<span class="params"></span>) => {</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> list.<span class="property">value</span>.<span class="title function_">push</span>(value.<span class="property">value</span>)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> value.<span class="property">value</span> = <span class="string">''</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml">}</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"></span><span class="tag"></<span class="name">script</span>></span></span></span><br></pre></td></tr></table></figure>
<p>子组件只需要渲染父组件传递的值。</p>
<p>代码如下:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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"><template></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">ul</span> <span class="attr">class</span>=<span class="string">"parent list-group"</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span> <span class="attr">class</span>=<span class="string">"list-group-item"</span> <span class="attr">v-for</span>=<span class="string">"i in props.list"</span> <span class="attr">:key</span>=<span class="string">"i"</span>></span>{{ i }}<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"></template></span><br><span class="line"><span class="language-xml"><span class="tag"><<span class="name">script</span> <span class="attr">setup</span>></span><span class="language-javascript"></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">import</span> { defineProps } <span class="keyword">from</span> <span class="string">'vue'</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">const</span> props = <span class="title function_">defineProps</span>({</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="attr">list</span>: {</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="attr">type</span>: <span class="title class_">Array</span>,</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="attr">default</span>: <span class="function">() =></span> [],</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> },</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml">})</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"></span><span class="tag"></<span class="name">script</span>></span></span></span><br></pre></td></tr></table></figure>
<h2 id="Emit"><a href="#Emit" class="headerlink" title="Emit"></a>Emit</h2><p><code>Emit</code>也是<code>Vue</code>中最常见的组件通信方式,用于子组件向父组件传递消息。</p>
<p>我们在父组件中定义列表,子组件只需要传递添加的值。</p>
<p>子组件代码如下:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><template></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"child-wrap input-group"</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">input</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">v-model</span>=<span class="string">"value"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">type</span>=<span class="string">"text"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">class</span>=<span class="string">"form-control"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">placeholder</span>=<span class="string">"Please enter"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"input-group-append"</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">button</span> @<span class="attr">click</span>=<span class="string">"handleSubmit"</span> <span class="attr">class</span>=<span class="string">"btn btn-primary"</span> <span class="attr">type</span>=<span class="string">"button"</span>></span></span></span><br><span class="line"><span class="language-xml"> add</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">button</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"></template></span><br><span class="line"><span class="language-xml"><span class="tag"><<span class="name">script</span> <span class="attr">setup</span>></span><span class="language-javascript"></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">import</span> { ref, defineEmits } <span class="keyword">from</span> <span class="string">'vue'</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">const</span> value = <span class="title function_">ref</span>(<span class="string">''</span>)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">const</span> emits = <span class="title function_">defineEmits</span>([<span class="string">'add'</span>])</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">const</span> <span class="title function_">handleSubmit</span> = (<span class="params"></span>) => {</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="title function_">emits</span>(<span class="string">'add'</span>, value.<span class="property">value</span>)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> value.<span class="property">value</span> = <span class="string">''</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml">}</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"></span><span class="tag"></<span class="name">script</span>></span></span></span><br></pre></td></tr></table></figure>
<p>点击子组件中的【添加】按钮后,我们会发出一个自定义事件,并将添加的值作为参数传递给父组件。</p>
<p>父组件代码如下:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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"><template></span><br><span class="line"> <!-- parent component --></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">ul</span> <span class="attr">class</span>=<span class="string">"parent list-group"</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span> <span class="attr">class</span>=<span class="string">"list-group-item"</span> <span class="attr">v-for</span>=<span class="string">"i in list"</span> <span class="attr">:key</span>=<span class="string">"i"</span>></span>{{ i }}<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"> <!-- child component --></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">child-components</span> @<span class="attr">add</span>=<span class="string">"handleAdd"</span>></span><span class="tag"></<span class="name">child-components</span>></span></span></span><br><span class="line"></template></span><br><span class="line"><span class="language-xml"><span class="tag"><<span class="name">script</span> <span class="attr">setup</span>></span><span class="language-javascript"></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">import</span> { ref } <span class="keyword">from</span> <span class="string">'vue'</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">import</span> <span class="title class_">ChildComponents</span> <span class="keyword">from</span> <span class="string">'./child.vue'</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">const</span> list = <span class="title function_">ref</span>([<span class="string">'JavaScript'</span>, <span class="string">'HTML'</span>, <span class="string">'CSS'</span>])</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="comment">// event handling function triggered by add</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">const</span> <span class="title function_">handleAdd</span> = value => {</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> list.<span class="property">value</span>.<span class="title function_">push</span>(value)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml">}</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"></span><span class="tag"></<span class="name">script</span>></span></span></span><br></pre></td></tr></table></figure>
<p>在父组件中,只需要监听子组件的自定义事件,然后执行相应的添加逻辑即可。</p>
<h2 id="v-model"><a href="#v-model" class="headerlink" title="v-model"></a>v-model</h2><p><code>v-model</code>是<code>Vue</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"><<span class="name">ChildComponent</span> <span class="attr">v-model:title</span>=<span class="string">"pageTitle"</span> /></span></span><br></pre></td></tr></table></figure>
<p>这是以下代码的简写形式</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"><<span class="name">ChildComponent</span> <span class="attr">:title</span>=<span class="string">"pageTitle"</span> @<span class="attr">update:title</span>=<span class="string">"pageTitle = $event"</span> /></span></span><br></pre></td></tr></table></figure>
<p>这确实容易了很多。现在我们将使用<code>v-model</code>来实现上面的示例。</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><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><template></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"child-wrap input-group"</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">input</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">v-model</span>=<span class="string">"value"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">type</span>=<span class="string">"text"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">class</span>=<span class="string">"form-control"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">placeholder</span>=<span class="string">"Please enter"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"input-group-append"</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">button</span> @<span class="attr">click</span>=<span class="string">"handleAdd"</span> <span class="attr">class</span>=<span class="string">"btn btn-primary"</span> <span class="attr">type</span>=<span class="string">"button"</span>></span></span></span><br><span class="line"><span class="language-xml"> add</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">button</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"></template></span><br><span class="line"><span class="language-xml"><span class="tag"><<span class="name">script</span> <span class="attr">setup</span>></span><span class="language-javascript"></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">import</span> { ref, defineEmits, defineProps } <span class="keyword">from</span> <span class="string">'vue'</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">const</span> value = <span class="title function_">ref</span>(<span class="string">''</span>)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">const</span> props = <span class="title function_">defineProps</span>({</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="attr">list</span>: {</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="attr">type</span>: <span class="title class_">Array</span>,</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="attr">default</span>: <span class="function">() =></span> [],</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> },</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml">})</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">const</span> emits = <span class="title function_">defineEmits</span>([<span class="string">'update:list'</span>])</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="comment">// Add action</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">const</span> <span class="title function_">handleAdd</span> = (<span class="params"></span>) => {</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">const</span> arr = props.<span class="property">list</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> arr.<span class="title function_">push</span>(value.<span class="property">value</span>)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="title function_">emits</span>(<span class="string">'update:list'</span>, arr)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> value.<span class="property">value</span> = <span class="string">''</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml">}</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"></span><span class="tag"></<span class="name">script</span>></span></span></span><br></pre></td></tr></table></figure>
<p>在子组件中,我们先定义<code>props</code>和<code>emits</code>,添加完成后再发出指定的事件。</p>
<blockquote>
<p>注意:update:<code>*</code>是<code>Vue</code>中固定的写法,<code>*</code>代表<code>props</code>中的一个属性名。</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><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><template></span><br><span class="line"> <!-- parent component --></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">ul</span> <span class="attr">class</span>=<span class="string">"parent list-group"</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span> <span class="attr">class</span>=<span class="string">"list-group-item"</span> <span class="attr">v-for</span>=<span class="string">"i in list"</span> <span class="attr">:key</span>=<span class="string">"i"</span>></span>{{ i }}<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"> <!-- child component --></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">child-components</span> <span class="attr">v-model:list</span>=<span class="string">"list"</span>></span><span class="tag"></<span class="name">child-components</span>></span></span></span><br><span class="line"></template></span><br><span class="line"><span class="language-xml"><span class="tag"><<span class="name">script</span> <span class="attr">setup</span>></span><span class="language-javascript"></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">import</span> { ref } <span class="keyword">from</span> <span class="string">'vue'</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">import</span> <span class="title class_">ChildComponents</span> <span class="keyword">from</span> <span class="string">'./child.vue'</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">const</span> list = <span class="title function_">ref</span>([<span class="string">'JavaScript'</span>, <span class="string">'HTML'</span>, <span class="string">'CSS'</span>])</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"></span><span class="tag"></<span class="name">script</span>></span></span></span><br></pre></td></tr></table></figure>
<h2 id="Refs"><a href="#Refs" class="headerlink" title="Refs"></a>Refs</h2><p>使用API选项时,我们可以通过<code>this.$refs.name</code>获取指定的元素或组件,但在组合API中不行。如果我们想通过<code>ref</code>获取,需要定义一个同名的<code>Ref</code>对象,在组件挂载后可以访问。</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></pre></td><td class="code"><pre><span class="line"><template></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">ul</span> <span class="attr">class</span>=<span class="string">"parent list-group"</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span> <span class="attr">class</span>=<span class="string">"list-group-item"</span> <span class="attr">v-for</span>=<span class="string">"i in childRefs?.list"</span> <span class="attr">:key</span>=<span class="string">"i"</span>></span></span></span><br><span class="line"><span class="language-xml"> {{ i }}</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"> <!-- <span class="title class_">The</span> value <span class="keyword">of</span> the child component ref is the same <span class="keyword">as</span> that <span class="keyword">in</span> the <script> --></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">child-components</span> <span class="attr">ref</span>=<span class="string">"childRefs"</span>></span><span class="tag"></<span class="name">child-components</span>></span></span></span><br><span class="line"> <!-- parent component --></span><br><span class="line"></template></span><br><span class="line"><span class="language-xml"><span class="tag"><<span class="name">script</span> <span class="attr">setup</span>></span><span class="language-javascript"></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">import</span> { ref } <span class="keyword">from</span> <span class="string">'vue'</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">import</span> <span class="title class_">ChildComponents</span> <span class="keyword">from</span> <span class="string">'./child.vue'</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">const</span> childRefs = <span class="title function_">ref</span>(<span class="literal">null</span>)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"></span><span class="tag"></<span class="name">script</span>></span></span></span><br></pre></td></tr></table></figure>
<p>子组件代码如下:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><template></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"child-wrap input-group"</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">input</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">v-model</span>=<span class="string">"value"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">type</span>=<span class="string">"text"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">class</span>=<span class="string">"form-control"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">placeholder</span>=<span class="string">"Please enter"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"input-group-append"</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">button</span> @<span class="attr">click</span>=<span class="string">"handleAdd"</span> <span class="attr">class</span>=<span class="string">"btn btn-primary"</span> <span class="attr">type</span>=<span class="string">"button"</span>></span></span></span><br><span class="line"><span class="language-xml"> add</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">button</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"></template></span><br><span class="line"><span class="language-xml"><span class="tag"><<span class="name">script</span> <span class="attr">setup</span>></span><span class="language-javascript"></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">import</span> { ref, defineExpose } <span class="keyword">from</span> <span class="string">'vue'</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">const</span> list = <span class="title function_">ref</span>([<span class="string">'JavaScript'</span>, <span class="string">'HTML'</span>, <span class="string">'CSS'</span>])</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">const</span> value = <span class="title function_">ref</span>(<span class="string">''</span>)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="comment">// event handling function triggered by add</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">const</span> <span class="title function_">handleAdd</span> = (<span class="params"></span>) => {</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> list.<span class="property">value</span>.<span class="title function_">push</span>(value.<span class="property">value</span>)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> value.<span class="property">value</span> = <span class="string">''</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml">}</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="title function_">defineExpose</span>({ list })</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"></span><span class="tag"></<span class="name">script</span>></span></span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>注意:默认情况下,<code>setup</code>组件是关闭的,通过模板<code>ref</code>获取组件的公共实例。如果需要公开,需要通过<code>defineExpose API</code>公开。</p>
</blockquote>
<h2 id="provide-x2F-inject"><a href="#provide-x2F-inject" class="headerlink" title="provide/inject"></a>provide/inject</h2><p><code>provide/inject</code>是 Vue 中提供的一对 API。无论层级多深,API 都可以实现父组件到子组件的数据传递。</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><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><template></span><br><span class="line"> <!-- child component --></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">child-components</span>></span><span class="tag"></<span class="name">child-components</span>></span></span></span><br><span class="line"> <!-- parent component --></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"child-wrap input-group"</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">input</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">v-model</span>=<span class="string">"value"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">type</span>=<span class="string">"text"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">class</span>=<span class="string">"form-control"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">placeholder</span>=<span class="string">"Please enter"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"input-group-append"</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">button</span> @<span class="attr">click</span>=<span class="string">"handleAdd"</span> <span class="attr">class</span>=<span class="string">"btn btn-primary"</span> <span class="attr">type</span>=<span class="string">"button"</span>></span></span></span><br><span class="line"><span class="language-xml"> add</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">button</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"></template></span><br><span class="line"><span class="language-xml"><span class="tag"><<span class="name">script</span> <span class="attr">setup</span>></span><span class="language-javascript"></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">import</span> { ref, provide } <span class="keyword">from</span> <span class="string">'vue'</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">import</span> <span class="title class_">ChildComponents</span> <span class="keyword">from</span> <span class="string">'./child.vue'</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">const</span> list = <span class="title function_">ref</span>([<span class="string">'JavaScript'</span>, <span class="string">'HTML'</span>, <span class="string">'CSS'</span>])</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">const</span> value = <span class="title function_">ref</span>(<span class="string">''</span>)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="comment">// Provide data to child components.</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="title function_">provide</span>(<span class="string">'list'</span>, list.<span class="property">value</span>)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="comment">// event handling function triggered by add</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">const</span> <span class="title function_">handleAdd</span> = (<span class="params"></span>) => {</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> list.<span class="property">value</span>.<span class="title function_">push</span>(value.<span class="property">value</span>)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> value.<span class="property">value</span> = <span class="string">''</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml">}</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"></span><span class="tag"></<span class="name">script</span>></span></span></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"><template></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">ul</span> <span class="attr">class</span>=<span class="string">"parent list-group"</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">li</span> <span class="attr">class</span>=<span class="string">"list-group-item"</span> <span class="attr">v-for</span>=<span class="string">"i in list"</span> <span class="attr">:key</span>=<span class="string">"i"</span>></span>{{ i }}<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">ul</span>></span></span></span><br><span class="line"></template></span><br><span class="line"><span class="language-xml"><span class="tag"><<span class="name">script</span> <span class="attr">setup</span>></span><span class="language-javascript"></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">import</span> { inject } <span class="keyword">from</span> <span class="string">'vue'</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="comment">// Accept data provided by parent component</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="keyword">const</span> list = <span class="title function_">inject</span>(<span class="string">'list'</span>)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"></span><span class="tag"></<span class="name">script</span>></span></span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>注意:使用<code>provide</code>进行数据传输时,尽量使用<code>readonly</code>封装数据,避免子组件修改父组件传递的数据。</p>
</blockquote>
<h2 id="eventBus"><a href="#eventBus" class="headerlink" title="eventBus"></a>eventBus</h2><p>Vue3 中移除了<code>eventBus</code>,但可以借助第三方工具来完成。Vue 官方推荐使用<code>mitt</code>或<code>tiny-emitter</code>。</p>
<p>在大多数情况下,不建议使用全局事件总线来实现组件通信。虽然比较简单粗暴,但是维护事件总线从长远来看是个大问题,这里就不解释了。有关详细信息,您可以阅读特定工具的文档。</p>
<p>7、vuex/pinia</p>
<p><code>Vuex</code>和<code>Pinia</code>是 Vue3 中的状态管理工具,使用这两个工具可以轻松实现组件通信。由于这两个工具都比较强大,这里就不一一展示了。有关详细信息,请参阅文档。</p>
<div class="note info flat"><p>原文链接:<a href="https://javascript.plainenglish.io/7-component-communications-in-vue-3-f4d2d795481d">7 Component Communications in Vue
基于 Hexo 从零开始搭建个人博客(一)
https://fe32.top/articles/hexo1601/
2022-05-08T13:30:46.000Z
2023-07-23T11:33:26.000Z
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><ol>
<li>博客搭建过程遇到任何问题,优先在本页面搜索,检查是否已经有该配置教程。</li>
<li>遇到问题可以优先在文章评论区留言,注意留言时请填写正确的邮箱以确保能收到站长的回复。</li>
<li>实在解决不了的问题可添加博主 Wechat ,添加好友时请备注自己的姓名+专业,如 张三 计算机科学与技术。</li>
</ol>
<h2 id="环境准备"><a href="#环境准备" class="headerlink" title="环境准备"></a>环境准备</h2><blockquote>
<p>本教程主要面向 Windows 用户</p>
</blockquote>
<ul>
<li>操作系统:Windows10</li>
<li>Node</li>
<li>Git</li>
<li>Hexo</li>
<li>idea(推荐使用 Visual Studio Code)</li>
<li>一个 GitHub 帐号</li>
<li>一个云服务器(可选)</li>
<li>一个域名(可选)</li>
</ul>
<h2 id="安装-Node"><a href="#安装-Node" class="headerlink" title="安装 Node"></a>安装 Node</h2><div class="tabs" id="安装方式-"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#安装方式--1">方式一</button></li><li class="tab"><button type="button" data-href="#安装方式--2">方式二</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="安装方式--1"><p>对于前端工程师,或正打算学习前端相关知识,建议用<code>nvm</code>来管理<code>Node</code>版本,请移步教程 <a href="https://fe32.top/articles/9r95s1wt/">nvm,npm与nrm</a>。</p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="安装方式--2"><p>如果你不需要灵活切换<code>Node</code>版本,走下面的教程即可。</p>
<details class="folding-tag" yellow><summary> 查看步骤 </summary>
<div class='content'>
<ol><li>打开<code>Node</code>官网,下载和自己系统相配的<code>Node</code>的安装程序,否则会出现安装问题。<br>下载地址:<a href="https://nodejs.org/en/download/">https://nodejs.org/en/download/</a><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/10/6279474912af2.png"/></div></div><del>如果后面美化过程中需要拉取豆瓣信息,哔哩哔哩番剧等,建议安装较低的 Node 版本(v12.18.0)</del><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/10/6279474fd4a8e.png"/></div></div><!-- 查看历史版本,找到 v12.18.0 进行下载。 --><del>查看历史版本,找到 v12.18.0 进行下载。</del> ,装最高的版本就好!</li><li>下载后傻瓜式安装即可,安装的目录可以使用默认目录【C:/Program Files/nodejs/】,也可以自定义路径。</li><li>安装完成后,检查是否安装成功。在键盘按下<kbd>win</kbd> + <kbd>R</kbd>键,输入<code>CMD</code>,然后回车,打开CMD窗口,执行<code>node -v</code>命令,看到版本信息,则说明安装成功。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/10/6279501e8eb8f.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/10/627950224668f.png"/></div></div></li><li>修改<code>npm</code>源。npm下载各种模块,默认是从国处服务器下载,速度较慢,建议配置成淘宝镜像。打开CMD窗口,运行如下命令:<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.npm.taobao.org</span><br></pre></td></tr></table></figure></li></ol>
</div>
</details><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div>
<h2 id="安装-Hexo"><a href="#安装-Hexo" class="headerlink" title="安装 Hexo"></a>安装 Hexo</h2><p>在目标路径打开CMD窗口,输入<code>npm install -g hexo-cli</code>安装<code>Hexo</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">npm install -g hexo-cli</span><br></pre></td></tr></table></figure>
<p>安装完后输入<code>hexo -v</code>验证是否安装成功。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/12/627bed30030f8.png"/></div></div>
<h2 id="注册-GitHub-帐号"><a href="#注册-GitHub-帐号" class="headerlink" title="注册 GitHub 帐号"></a>注册 GitHub 帐号</h2><ol>
<li>进入官网 <a href="https://github.com/">https://github.com/</a></li>
<li>点击右上角的 Sign up(注册),不是Sign in(登录)。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/12/627d2c0449341.webp"/></div></div></li>
<li>填写自己的邮箱、密码、用户名等信息,然后用邮箱验证即可完成。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/12/627d2c05ee628.png"/></div></div></li>
</ol>
<h2 id="创建-GitHub-仓库"><a href="#创建-GitHub-仓库" class="headerlink" title="创建 GitHub 仓库"></a>创建 GitHub 仓库</h2><p>点击右上角的<code>+</code>按钮,选择<code>New repository</code>,创建一个<code><用户名>.github.io</code>的仓库。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/08/62776cef6aeb4.webp"/></div></div>
<p>找不到创建入口的,访问:<a href="https://github.com/new">https://github.com/new</a></p>
<ul>
<li>仓库的格式必须为:<code><用户名>.github.io</code></li>
<li>Description:为描述仓库(选填)</li>
<li>勾选 <code>Initialize this repository with a README</code> 初始化一个 README.md 文件</li>
<li>点击 <code>Creat repository</code> 进行创建</li>
</ul>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/13/627d4109113e8.png"/></div></div>
<h2 id="Git安装-amp-amp-连接Github"><a href="#Git安装-amp-amp-连接Github" class="headerlink" title="Git安装 && 连接Github"></a>Git安装 && 连接Github</h2><h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><ol>
<li>进入官网:<a href="https://git-scm.com/downloads">https://git-scm.com/downloads</a> ,由于官网下载太慢可以通过淘宝的开源镜像下载 网址:<a href="https://registry.npmmirror.com/binary.html?path=git-for-windows/v2.36.1.windows.1/">https://registry.npmmirror.com/binary.html?path=git-for-windows/v2.36.1.windows.1/</a> ,下载版本更具自己的需求选择即可。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/13/627d410b6156f.png"/></div></div></li>
<li>下载后傻瓜式安装Git即可,安装的目录可以使用默认目录【C:/Program Files/Git】,也可以自定义路径。</li>
<li>点击电脑左下角开始即可看见Git Bash。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/13/627d410ddc940.webp"/></div></div>
<ul>
<li>Git CMD 是windows 命令行的指令风格</li>
<li>Git Bash 是linux系统的指令风格(建议使用)</li>
<li>Git GUI是图形化界面(新手学习不建议使用)</li>
</ul>
</li>
<li>打开<code>Git Bash</code>后如下图所示即代表安装完成。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/14/627e8ea336578.png"/></div></div></li>
</ol>
<h3 id="环境配置"><a href="#环境配置" class="headerlink" title="环境配置"></a>环境配置</h3><ol>
<li>常用命令<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">git config -l //查看所有配置</span><br><span class="line"></span><br><span class="line">git config --system --list //查看系统配置</span><br><span class="line"></span><br><span class="line">git config --global --list //查看用户(全局)配置</span><br></pre></td></tr></table></figure></li>
<li>配置用户名和邮箱<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><br><span class="line">git config --global user.name <span class="string">"你的用户名"</span></span><br><span class="line"></span><br><span class="line">git config --global user.email <span class="string">"你的邮箱"</span></span><br></pre></td></tr></table></figure>
输入后没有报错即代表设置成功。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/13/627e7a4e5cedd.png"/></div></div>
通过git <code>config -l</code> 检查是否配置成功,至此git安装及配置全部完成。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/13/627e7a861d92d.png"/></div></div></li>
</ol>
<h3 id="连接Github"><a href="#连接Github" class="headerlink" title="连接Github"></a>连接Github</h3><ol>
<li>生成ssh公钥,执行以下命令<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">ssh-keygen -t rsa -C <span class="string">"你的邮箱"</span></span><br></pre></td></tr></table></figure>
之后打开C盘下用户文件夹下的.ssh的文件夹,会看到 id_rsa.pub<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/14/627e87126fc59.png"/></div></div>
用记事本打开上述图片中的公钥(id_rsa.pub),复制里面的内容,然后开始在github中配置ssh密钥。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/14/627e87156038a.png"/></div></div></li>
<li>将 SSH KEY 配置到 GitHub<br>进入github,点击右上角头像 选择<code>settings</code><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/14/627e877fb746b.png"/></div></div>
进入设置页后选择 <code>SSH and GPG keys</code><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/14/627e8802e6729.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/14/627e881524f28.png"/></div></div>
<blockquote>
<p>注意: 要是有【Key type】的选择项 ,选择默认<code>Authentication Key</code> 即可。</p>
</blockquote>
</li>
<li>测试连接,输入以下命令<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">ssh -T git@github.com</span><br></pre></td></tr></table></figure>
出现如下信息,说明已经大功告成!<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif"
如何给自己的Github主页来个豪华套餐?
https://fe32.top/articles/gi0506hb/
2022-05-05T16:52:04.000Z
2023-05-17T10:01:12.000Z
<h2 id="仓库信息"><a href="#仓库信息" class="headerlink" title="仓库信息"></a>仓库信息</h2><div class="git-decorate">
<div class="git-decorate-fl-center">
<!-- <a class="ghcard" rel="external nofollow noopener noreferrer" href="https://github.com/abhisheknaiidu/awesome-github-profile-readme/"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://github-readme-stats.vercel.app/api/pin/?username=abhisheknaiidu&repo=awesome-github-profile-readme&theme=buefy&show_owner=true"/></a> -->
<a class="ghcard" rel="external nofollow noopener noreferrer" href="https://github.com/thmsgbrt/thmsgbrt"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://github-readme-stats.vercel.app/api/pin/?username=thmsgbrt&repo=thmsgbrt&theme=buefy&show_owner=true"/></a>
</div>
<p><a href="https://www.producthunt.com/posts/awesome-github-profiles?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-awesome-github-profiles" target="_blank"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=277987&theme=light" alt="Awesome GitHub Profiles - Best curated list of developers readme, updated every 15 min | Product Hunt" style="width: 200px; height: 44px;" width="200" height="44" /></a></p>
<div class="git-decorate-fl-center">
<a href="https://arbeitnow.com/?utm_source=awesome-github-profile-readme"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://img.shields.io/static/v1?label=&labelColor=505050&message=arbeitnow&color=%230076D6&style=flat&logo=google-chrome&logoColor=%230076D6" alt="website"/></a>
<img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://img.shields.io/static/v1?label=%F0%9F%8C%9F&message=If%20Useful&style=style=flat&color=BC4E99" alt="Star Badge"/>
<a href="https://discord.gg/XTW52Kt"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://img.shields.io/discord/733027681184251937.svg?style=flat&label=Join%20Community&color=7289DA" alt="Join Community Badge"/></a>
<a href="https://twitter.com/abhisheknaiidu" ><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://img.shields.io/twitter/follow/abhisheknaiidu.svg?style=social" /> </a>
</div>
<div class="git-decorate-fl-center">
<a href="https://github.com/abhisheknaiidu/awesome-github-profile-readme/stargazers"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://img.shields.io/github/stars/abhisheknaiidu/awesome-github-profile-readme" alt="Stars Badge"/></a>
<a href="https://github.com/abhisheknaiidu/awesome-github-profile-readme/network/members"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://img.shields.io/github/forks/abhisheknaiidu/awesome-github-profile-readme" alt="Forks Badge"/></a>
<a href="https://github.com/abhisheknaiidu/awesome-github-profile-readme/pulls"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://img.shields.io/github/issues-pr/abhisheknaiidu/awesome-github-profile-readme" alt="Pull Requests Badge"/></a>
<a href="https://github.com/abhisheknaiidu/awesome-github-profile-readme/issues"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://img.shields.io/github/issues/abhisheknaiidu/awesome-github-profile-readme" alt="Issues Badge"/></a>
<a href="https://github.com/abhisheknaiidu/awesome-github-profile-readme/graphs/contributors"><img alt="GitHub contributors" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://img.shields.io/github/contributors/abhisheknaiidu/awesome-github-profile-readme?color=2b9348"></a>
<a href="https://github.com/abhisheknaiidu/awesome-github-profile-readme/blob/master/LICENSE"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://img.shields.io/github/license/abhisheknaiidu/awesome-github-profile-readme?color=2b9348" alt="License Badge"/></a>
</div>
</div>
<h2 id="“豪华装修”案例分享"><a href="#“豪华装修”案例分享" class="headerlink" title="“豪华装修”案例分享"></a>“豪华装修”案例分享</h2><ol>
<li><p>技术大佬型🤖:展示自己的技术栈、开源项目、最近文章</p>
<!-- <a class="ghcard" rel="external nofollow noopener noreferrer" href="https://github.com/thmsgbrt/thmsgbrt"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://github-readme-stats.vercel.app/api/pin/?username=thmsgbrt&repo=thmsgbrt&theme=buefy&show_owner=true"/></a> -->
<p>地址:<a href="https://github.com/thmsgbrt/thmsgbrt">https://github.com/thmsgbrt/thmsgbrt</a></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/07/62755bad5e775.webp"/></div></div>
</li>
<li><p>代码模式👨🏽💻:展示自己最近的code内容,code语言和开发时间。</p>
<!-- <a class="ghcard" rel="external nofollow noopener noreferrer" href="https://github.com/guilyx/guilyx"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://github-readme-stats.vercel.app/api/pin/?username=guilyx&repo=guilyx&theme=buefy&show_owner=true"/></a> -->
<p>地址:<a href="https://github.com/guilyx/guilyx">https://github.com/guilyx/guilyx</a></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/07/62755bb37bdf4.webp"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/07/62755bb6c4b9d.webp"/></div></div>
</li>
<li><p>极简主义✨<br><br>地址:<a href="https://github.com/Volcano-Yang/Volcano-Yang">https://github.com/Volcano-Yang/Volcano-Yang</a></p>
<!-- <a class="ghcard" rel="external nofollow noopener noreferrer" href="https://github.com/Volcano-Yang/Volcano-Yang"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://github-readme-stats.vercel.app/api/pin/?username=Volcano-Yang&repo=Volcano-Yang&theme=buefy&show_owner=true"/></a> -->
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/07/62755bba78f63.webp"/></div></div>
<!-- <a class="ghcard" rel="external nofollow noopener noreferrer" href="https://github.com/alwinw/alwinw"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://github-readme-stats.vercel.app/api/pin/?username=alwinw&repo=alwinw&theme=buefy&show_owner=true"/></a> -->
<p>地址:<a href="https://github.com/alwinw/alwinw">https://github.com/alwinw/alwinw</a></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/07/62756662597e2.webp"/></div></div></li>
</ol>
<h2 id="手把手教你如何装修自己的github主页"><a href="#手把手教你如何装修自己的github主页" class="headerlink" title="手把手教你如何装修自己的github主页"></a>手把手教你如何装修自己的github主页</h2><ol>
<li>创建同名仓库<br>点击右上角的<code>+</code>按钮,选择<code>New repository</code>,创建和自己github用户名相同的仓库<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/08/62776cef6aeb4.webp"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/08/62776d93f12b7.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/08/62776d9e1300f.png"/></div></div></li>
<li>创建完成,默认的README.md有一句话:Hi there 👋 。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/08/62776dab04b54.png"/></div></div></li>
<li>修改README.md ,借用别人代码的时候要注意,有的组件必须将数据源指向自己才能展示正确的数据,比如start组件等。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/08/62776dbd517ef.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/08/62776dc81d503.png"/></div></div></li>
<li>修改完提交内容到该仓库主干就大功告成了<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif"
边缘渲染是如何提升前端性能的?
https://fe32.top/articles/cs0504xn/
2022-05-04T15:21:46.000Z
2023-05-17T10:01:10.000Z
<h2 id="前端渲染的发展"><a href="#前端渲染的发展" class="headerlink" title="前端渲染的发展"></a>前端渲染的发展</h2><p>在讲ESR(Edge Side Rendering,边缘渲染)如何提速渲染之前,我们有必要先了解一下前端渲染的发展历史以及前端各项性能指标优化是如何被提上议程的,之后我们再反观ESR的出现就会发现也是水到渠成。</p>
<p>其实整个前端渲染方式也是随着前端技术的演进而不断革新的,大致可以分为如下历程。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/05/6272a581143d1.webp"/></div></div>
<h3 id="SSR(Server-Side-Rendering)时代(JSP、PHP)"><a href="#SSR(Server-Side-Rendering)时代(JSP、PHP)" class="headerlink" title="SSR(Server Side Rendering)时代(JSP、PHP)"></a>SSR(Server Side Rendering)时代(JSP、PHP)</h3><p>最早期的前端渲染(2005年Ajax推出之前)都是和后端混写的,比如JSP、PHP等写法。但是前后端写法杂糅在一起导致开发效率低下,比如改个样式还要重新编译一遍,并且页面也会写的很重。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/05/6272a59c03acb.webp"/></div></div>
<h3 id="CSR(Client-Side-Rendering)时代"><a href="#CSR(Client-Side-Rendering)时代" class="headerlink" title="CSR(Client Side Rendering)时代"></a>CSR(Client Side Rendering)时代</h3><p>后面有了Ajax技术之后,再加上通过CDN缓存静态资源之后,前端SPA + CSR渲染有了飞跃式的发展,这种模式前端处理所有逻辑、内容填充和路由,数据加载部分通过Ajax从后端获取,因此很好的解决了前后端分工开发的问题。其具体请求时间线可参见下图。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/05/6272a5ac23724.webp"/></div></div>
<p>但是由于请求是全异步的,其一是对SEO不利,其二是需要HTML + JS处理数据拼接才能在前端完成渲染,其首屏白屏时间会较长,特别在一些低端机型上体验更是堪忧</p>
<h3 id="SSR时代(Node)"><a href="#SSR时代(Node)" class="headerlink" title="SSR时代(Node)"></a>SSR时代(Node)</h3><p>再后来随着Node引领的全栈技术的发展,前端又回到了当初的SSR路上,只不过这次的回归是一次螺旋式的上升。首先是前后端全是JS语法,大部分代码都是可复用的,其次是SEO场景友好,服务端渲染好后直接返回最终的HTML,减少了白屏等待时间,过多异步请求的导致的性能问题也可下放到服务端解决,也能有效避免多次的数据获取、内容填充,浏览器只绑定相关的JS逻辑、事件即可。其具体请求时间线可参见下图。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/05/6272a5d3db82f.webp"/></div></div>
<h3 id="ESR(Edge-Side-Rendering)时代"><a href="#ESR(Edge-Side-Rendering)时代" class="headerlink" title="ESR(Edge Side Rendering)时代"></a>ESR(Edge Side Rendering)时代</h3><p>后面随着边缘计算的发展,由于CDN节点距离用户更近,有更短网络延时的优势,我们可以将页面进行动静拆分,将静态内容缓存在CDN先快速返回给用户,然后在CDN节点上发起动态内容的请求,之后将动态内容与静态部分以流的形式进行拼接,从而进一步提高了用户的首屏加载时间,尤其在边缘地区或者弱网环境也有能拥有很好的用户体验,此外还减少原先SSR服务器压力。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/05/6272a5f997171.webp"/></div></div>
<h2 id="原理和优势"><a href="#原理和优势" class="headerlink" title="原理和优势"></a>原理和优势</h2><p>刚才也提到了,ESR就是借助边缘计算能力,将返回的内容进行静态+动态部分拆分并以流的形式返回。静态部分依托CDN的缓存能力,优先返回给用户,随后在CDN节点上继续发起动态数据请求,并拼接在静态部分之后,继续流式返回。因此,其优势也是显而易见:</p>
<ol>
<li>TTFB(Time To First Byte)很短:<br>因为静态内容在CDN缓存住了,会很快的返回给用户。</li>
<li>FP(First Paint)很短:<br>因为在静态内容返回后,已经可以开始HTML的解析以及 JS, CSS的下载和执行。</li>
<li>FMP(First Meaningful Paint)很短:<br>因为动态内容的请求是在CDN发起,相比于客户端与服务端直连,请求减少了TCP建连和网络传输开销,而且由于动态部分是以chunked形式流式返回,FMP就会很短,比如搜索网站的第一个搜索结果就会首先绘制出来。</li>
</ol>
<h2 id="应用场景举例"><a href="#应用场景举例" class="headerlink" title="应用场景举例"></a>应用场景举例</h2><h3 id="场景一:将SSR服务直接部署在边缘节点,中心服务提供数据接口"><a href="#场景一:将SSR服务直接部署在边缘节点,中心服务提供数据接口" class="headerlink" title="场景一:将SSR服务直接部署在边缘节点,中心服务提供数据接口"></a>场景一:将SSR服务直接部署在边缘节点,中心服务提供数据接口</h3><p>直接将SSR服务搬到边缘部署,具体流程如下图。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/05/6272a6ca6a405.webp"/></div></div>
<h3 id="场景二:边缘服务读取缓存的静态部分HTML,中心服务提供动态HTML"><a href="#场景二:边缘服务读取缓存的静态部分HTML,中心服务提供动态HTML" class="headerlink" title="场景二:边缘服务读取缓存的静态部分HTML,中心服务提供动态HTML"></a>场景二:边缘服务读取缓存的静态部分HTML,中心服务提供动态HTML</h3><p>SSR服务部署在中心,边缘流式返回HTML内容(利用HTTP Transfer-Encoding: chunked 分块传输机制),需要分离静态与动态部分,具体流程如下图。</p>
<ul>
<li>边缘服务:<br>请求静态HTML并返回,同时请求中心SSR服务,获取动态内容并返回</li>
<li>SSR服务:<br>去除静态HTML,把动态部分返回给边缘服务</li>
</ul>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/05/6272a6e9e8138.webp"/></div></div>
<h3 id="举例"><a href="#举例" class="headerlink" title="举例"></a>举例</h3><p>以一个Demo网站为例,顶部导航可以视为静态部分缓存在边缘CDN,下面的卡片是动态部分回源到中心服务获取数据。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/05/6272b2b037be9.webp"/></div></div>
<p>通过Demo对比,可发现ESR比SSR的有着明显优势,其静态顶导首先绘出,后面动态数据也比SSR的返回要快。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/05/6272b24fdb6e1.webp"/></div></div>
<p>此外,结合如下的埋点统计,ESR的优势更加得以印证。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/05/05/6272ad9517860.webp"/></div></div>
<h2 id="结语和展望"><a href="#结语和展望" class="headerlink" title="结语和展望"></a>结语和展望</h2><ul>
<li>技术实现: ESR适应于对页面渲染性能较高的场景,借助边缘计算在SSR的基础上进一步优化首屏绘制的时间,降低用户页面的白屏等待时间;</li>
<li>部署方式: 目前实现方式主要借助于边缘faas部署ESR服务,具有快速访问、弹性扩缩容、低运维成本等优点;<br>后期提供ER(边缘js运行时)部署,用户无需关心边缘节点,只需专注于代码本身,修改代码上传发布即可,相对于node服务,js运行时能够提供更高的运行效率</li>
<li>技术展望:
推荐一些面向 Web 开发者的杀手级网站
https://fe32.top/articles/wb0502st/
2022-05-02T05:18:54.000Z
2023-05-17T10:01:12.000Z
一些面向Web开发者的杀手级网站
在 JavaScript 中写好异步代码的14条Linting规则
https://fe32.top/articles/js0417es/
2022-04-17T12:06:30.000Z
2023-05-17T10:01:12.000Z
<p>在<code>JavaScript</code>中调试异步代码有时感觉就像在雷区中导航。 你不知道<code>console.logs</code>会在何时何地打印出来,你也不知道你的代码是如何执行的。</p>
<p>很难正确地构造异步代码,以便它按照您的意图以正确的顺序执行。</p>
<p>如果您在编写异步代码时得到一些指导,并在您即将犯错时获得有用的信息,那不是很好吗?</p>
<p>幸运的是,在我们将它们投入生产之前,我们有一些 linters 可以捕获我们的一些错误。 以下是 linting 规则的编译列表,专门帮助您在 <code>JavaScript</code> 和 <code>Node.js</code>中编写异步代码。</p>
<p>即使您最终没有在项目中使用这些规则,阅读它们的描述也会更好地理解异步代码并提高您的开发人员技能。</p>
<p>以下规则默认随 <code>ESLint</code> 一起提供。 通过将它们添加到您的 <code>.eslintrc</code> 配置文件来启用它们。</p>
<h2 id="no-async-promise-executor"><a href="#no-async-promise-executor" class="headerlink" title="no-async-promise-executor"></a>no-async-promise-executor</h2><p>不建议将<code>async</code>函数传递给<code>new Promise</code>的构造函数。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ❌</span></span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">async</span> (resolve, reject) => {});</span><br><span class="line"></span><br><span class="line"><span class="comment">// ✅</span></span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {});</span><br></pre></td></tr></table></figure>
<p>首先,你在 <code>Promise</code> 的构造函数里去使用 <code>async</code> ,那么包装个 <code>Promise</code> 可能就是没啥必要的。另外,如果 <code>async</code> 函数抛出了异常,新构造的 <code>Promise</code> 实例并不会 <code>reject</code> ,那么这个错误就捕获不到了。</p>
<h2 id="no-await-in-loop"><a href="#no-await-in-loop" class="headerlink" title="no-await-in-loop"></a>no-await-in-loop</h2><p>不建议在循环里使用 <code>await</code> ,有这种写法通常意味着程序没有充分利用 <code>JavaScript</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="comment">// ❌</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">const</span> url <span class="keyword">of</span> urls) {</span><br><span class="line"> <span class="keyword">const</span> response = <span class="keyword">await</span> <span class="title function_">fetch</span>(url);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>建议将这些异步任务改为并发执行,你可以大大提升代码的执行效率。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ✅</span></span><br><span class="line"><span class="keyword">const</span> responses = [];</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">const</span> url <span class="keyword">of</span> urls) {</span><br><span class="line"> <span class="keyword">const</span> response = <span class="title function_">fetch</span>(url);</span><br><span class="line"> responses.<span class="title function_">push</span>(response);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">await</span> <span class="title class_">Promise</span>.<span class="title function_">all</span>(responses);</span><br></pre></td></tr></table></figure>
<h2 id="no-promise-executor-return"><a href="#no-promise-executor-return" class="headerlink" title="no-promise-executor-return"></a>no-promise-executor-return</h2><p>不建议在 <code>Promise</code> 构造函数中返回值,<code>Promise</code> 构造函数中返回的值是没法用的,并且返回值也不会影响到 <code>Promise</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="comment">// ❌</span></span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<p>正常的做法是将返回值传递给 <code>resolve</code> ,如果出错了就传给 <code>reject</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="comment">// ✅</span></span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> <span class="title function_">resolve</span>(result);</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<h2 id="require-atomic-updates"><a href="#require-atomic-updates" class="headerlink" title="require-atomic-updates"></a>require-atomic-updates</h2><p>不建议将赋值操作和 <code>await</code> 组合使用,这可能会导致条件竞争。</p>
<p>看看下面的代码,你觉得 <code>totalPosts</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">// ❌</span></span><br><span class="line"><span class="keyword">let</span> totalPosts = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">getPosts</span>(<span class="params">userId</span>) {</span><br><span class="line"> <span class="keyword">const</span> users = [{ <span class="attr">id</span>: <span class="number">1</span>, <span class="attr">posts</span>: <span class="number">5</span> }, { <span class="attr">id</span>: <span class="number">2</span>, <span class="attr">posts</span>: <span class="number">3</span> }];</span><br><span class="line"> <span class="keyword">await</span> <span class="title function_">sleep</span>(<span class="title class_">Math</span>.<span class="title function_">random</span>() * <span class="number">1000</span>);</span><br><span class="line"> <span class="keyword">return</span> users.<span class="title function_">find</span>(<span class="function">(<span class="params">user</span>) =></span> user.<span class="property">id</span> === userId).<span class="property">posts</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">addPosts</span>(<span class="params">userId</span>) {</span><br><span class="line"> totalPosts += <span class="keyword">await</span> <span class="title function_">getPosts</span>(userId);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">await</span> <span class="title class_">Promise</span>.<span class="title function_">all</span>([<span class="title function_">addPosts</span>(<span class="number">1</span>), <span class="title function_">addPosts</span>(<span class="number">2</span>)]);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Post count:'</span>, totalPosts);</span><br></pre></td></tr></table></figure>
<p><code>totalPosts</code> 会打印 <strong>3</strong> 或 <strong>5</strong> ,并不会打印 <strong>8</strong> ,你可以在浏览器里自己试一下。</p>
<p>问题在于读取 <code>totalPosts</code> 和更新 <code>totalPosts</code> 之间有一个时间间隔。这会导致竞争条件,当值在单独的函数调用中更新时,更新不会反映在当前函数范围中。因此,两个函数都会将它们的结果添加到 <code>totalPosts</code> 的初始值0。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// ✅</span></span><br><span class="line"><span class="keyword">let</span> totalPosts = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">getPosts</span>(<span class="params">userId</span>) {</span><br><span class="line"> <span class="keyword">const</span> users = [{ <span class="attr">id</span>: <span class="number">1</span>, <span class="attr">posts</span>: <span class="number">5</span> }, { <span class="attr">id</span>: <span class="number">2</span>, <span class="attr">posts</span>: <span class="number">3</span> }];</span><br><span class="line"> <span class="keyword">await</span> <span class="title function_">sleep</span>(<span class="title class_">Math</span>.<span class="title function_">random</span>() * <span class="number">1000</span>);</span><br><span class="line"> <span class="keyword">return</span> users.<span class="title function_">find</span>(<span class="function">(<span class="params">user</span>) =></span> user.<span class="property">id</span> === userId).<span class="property">posts</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">addPosts</span>(<span class="params">userId</span>) {</span><br><span class="line"> <span class="keyword">const</span> posts = <span class="keyword">await</span> <span class="title function_">getPosts</span>(userId);</span><br><span class="line"> totalPosts += posts; <span class="comment">// variable is read and immediately updated</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">await</span> <span class="title class_">Promise</span>.<span class="title function_">all</span>([<span class="title function_">addPosts</span>(<span class="number">1</span>), <span class="title function_">addPosts</span>(<span class="number">2</span>)]);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Post count:'</span>, totalPosts);</span><br></pre></td></tr></table></figure>
<h2 id="max-nested-callbacks"><a href="#max-nested-callbacks" class="headerlink" title="max-nested-callbacks"></a>max-nested-callbacks</h2><p>防止回调地狱,避免大量的深度嵌套:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* eslint max-nested-callbacks: ["error", 3] */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ❌</span></span><br><span class="line"><span class="title function_">async1</span>(<span class="function">(<span class="params">err, result1</span>) =></span> {</span><br><span class="line"> <span class="title function_">async2</span>(result1, <span class="function">(<span class="params">err, result2</span>) =></span> {</span><br><span class="line"> <span class="title function_">async3</span>(result2, <span class="function">(<span class="params">err, result3</span>) =></span> {</span><br><span class="line"> <span class="title function_">async4</span>(result3, <span class="function">(<span class="params">err, result4</span>) =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(result4);</span><br><span class="line"> });</span><br><span class="line"> });</span><br><span class="line"> });</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="comment">// ✅</span></span><br><span class="line"><span class="keyword">const</span> result1 = <span class="keyword">await</span> <span class="title function_">asyncPromise1</span>();</span><br><span class="line"><span class="keyword">const</span> result2 = <span class="keyword">await</span> <span class="title function_">asyncPromise2</span>(result1);</span><br><span class="line"><span class="keyword">const</span> result3 = <span class="keyword">await</span> <span class="title function_">asyncPromise3</span>(result2);</span><br><span class="line"><span class="keyword">const</span> result4 = <span class="keyword">await</span> <span class="title function_">asyncPromise4</span>(result3);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(result4);</span><br></pre></td></tr></table></figure>
<p>回调地狱让代码难以阅读和维护,建议将回调都重构为 <code>Promise</code> 并使用现代的 <code>async/await</code> 语法。</p>
<h2 id="no-return-await"><a href="#no-return-await" class="headerlink" title="no-return-await"></a>no-return-await</h2><p>返回异步结果时不一定要写 <code>await</code> ,如果你要等待一个 <code>Promise</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="comment">// ❌</span></span><br><span class="line"><span class="keyword">async</span> () => {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">await</span> <span class="title function_">getUser</span>(userId);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>从一个 <code>async</code> 函数返回的所有值都包含在一个 <code>Promise</code> 中,你可以直接返回这个 <code>Promise</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="comment">// ✅</span></span><br><span class="line"><span class="keyword">async</span> () => {</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">getUser</span>(userId);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>当然,也有个例外,如果外面有 <code>try...catch</code> 包裹,删除 <code>await</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 👎</span></span><br><span class="line"><span class="keyword">async</span> () => {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">await</span> <span class="title function_">getUser</span>(userId);</span><br><span class="line"> } <span class="keyword">catch</span> (error) {</span><br><span class="line"> <span class="comment">// Handle getUser error</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 👍</span></span><br><span class="line"><span class="keyword">async</span> () => {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">const</span> user = <span class="keyword">await</span> <span class="title function_">getUser</span>(userId);</span><br><span class="line"> <span class="keyword">return</span> user;</span><br><span class="line"> } <span class="keyword">catch</span> (error) {</span><br><span class="line"> <span class="comment">// Handle getUser error</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="prefer-promise-reject-errors"><a href="#prefer-promise-reject-errors" class="headerlink" title="prefer-promise-reject-errors"></a>prefer-promise-reject-errors</h2><p>建议在 <code>reject Promise</code> 时强制使用 <code>Error</code> 对象,这样可以更方便的追踪错误堆栈。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ❌</span></span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">reject</span>(<span class="string">'An error occurred'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ✅</span></span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">reject</span>(<span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">'An error occurred'</span>));</span><br></pre></td></tr></table></figure>
<h2 id="node-x2F-handle-callback-err"><a href="#node-x2F-handle-callback-err" class="headerlink" title="node/handle-callback-err"></a>node/handle-callback-err</h2><p>强制在 Node.js 的异步回调里进行异常处理。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ❌</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">callback</span>(<span class="params">err, data</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(data);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// ✅</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">callback</span>(<span class="params">err, data</span>) {</span><br><span class="line"> <span class="keyword">if</span> (err) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(err);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(data);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>在 <code>Node.js</code> 中,通常将异常作为第一个参数传递给回调函数。忘记处理这些异常可能会导致你的应用程序出现不可预知的问题。</p>
<blockquote>
<p>如果函数的第一个参数命名为 err 时才会触发这个规则,你也可以去 .eslintrc 文件里自定义异常参数名。</p>
</blockquote>
<h2 id="node-x2F-no-sync"><a href="#node-x2F-no-sync" class="headerlink" title="node/no-sync"></a>node/no-sync</h2><p>不建议在存在异步替代方案的 <code>Node.js</code> 核心 <code>API</code> 中使用同步方法。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ❌</span></span><br><span class="line"><span class="keyword">const</span> file = fs.<span class="title function_">readFileSync</span>(path);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ✅</span></span><br><span class="line"><span class="keyword">const</span> file = <span class="keyword">await</span> fs.<span class="title function_">readFile</span>(path);</span><br></pre></td></tr></table></figure>
<p>在 <code>Node.js</code> 中对 <code>I/O</code> 操作使用同步方法会阻塞事件循环。大多数场景下,执行 <code>I/O</code> 操作时使用异步方法是更好的选择。</p>
<h2 id="typescript-eslint-x2F-await-thenable"><a href="#typescript-eslint-x2F-await-thenable" class="headerlink" title="@typescript-eslint/await-thenable"></a>@typescript-eslint/await-thenable</h2><p>不建议 await 非 <code>Promise</code> 函数或值。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ❌</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">getValue</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> someValue;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">await</span> <span class="title function_">getValue</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// ✅</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">getValue</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> someValue;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">await</span> <span class="title function_">getValue</span>();</span><br></pre></td></tr></table></figure>
<h2 id="typescript-eslint-x2F-no-floating-promises"><a href="#typescript-eslint-x2F-no-floating-promises" class="headerlink" title="@typescript-eslint/no-floating-promises"></a>@typescript-eslint/no-floating-promises</h2><p>建议 <code>Promise</code> 附加异常处理的代码。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ❌</span></span><br><span class="line"><span class="title function_">myPromise</span>()</span><br><span class="line"> .<span class="title function_">then</span>(<span class="function">() =></span> {});</span><br><span class="line"></span><br><span class="line"><span class="comment">// ✅</span></span><br><span class="line"><span class="title function_">myPromise</span>()</span><br><span class="line"> .<span class="title function_">then</span>(<span class="function">() =></span> {})</span><br><span class="line"> .<span class="title function_">catch</span>(<span class="function">() =></span> {});</span><br></pre></td></tr></table></figure>
<p>养成个好的习惯,永远做好异常处理!</p>
<h2 id="typescript-eslint-x2F-no-misused-promises"><a href="#typescript-eslint-x2F-no-misused-promises" class="headerlink" title="@typescript-eslint/no-misused-promises"></a>@typescript-eslint/no-misused-promises</h2><p>不建议将 <code>Promise</code> 传递到并非想要处理它们的地方,例如 if 条件。</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><br><span class="line"><span class="comment">// ❌</span></span><br><span class="line"><span class="keyword">if</span> (<span class="title function_">getUserFromDB</span>()) {}</span><br><span class="line"></span><br><span class="line"><span class="comment">// ✅ 👎</span></span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">await</span> <span class="title function_">getUserFromDB</span>()) {}</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="comment">// ✅ 👍</span></span><br><span class="line"><span class="keyword">const</span> user = <span class="keyword">await</span> <span class="title function_">getUserFromDB</span>();</span><br><span class="line"><span class="keyword">if</span> (user) {}</span><br></pre></td></tr></table></figure>
<div class="note info flat"><p>原文链接:<a
前端架构设计中如何做好技术决策?
https://fe32.top/articles/fe0417mn/
2022-04-17T09:51:18.000Z
2023-05-17T10:01:12.000Z
<h2 id="原则-0-遵守公认的好的设计原则,比如说:"><a href="#原则-0-遵守公认的好的设计原则,比如说:" class="headerlink" title="原则 0: 遵守公认的好的设计原则,比如说:"></a>原则 0: 遵守公认的好的设计原则,比如说:</h2><ul>
<li>DRY - Don’t repeat yourself (不要重复自己)</li>
<li>KISS - Keep it Simple, Silly (让设计尽可能的简单)</li>
<li>YAGNI - You aren’t gonna need it (只做刚刚好的设计,不要过度设计)</li>
<li>… 其他</li>
</ul>
<h2 id="原则1-找出最本源的需求,而不应该局限于当前的技术实现和资源"><a href="#原则1-找出最本源的需求,而不应该局限于当前的技术实现和资源" class="headerlink" title="原则1: 找出最本源的需求,而不应该局限于当前的技术实现和资源"></a>原则1: 找出最本源的需求,而不应该局限于当前的技术实现和资源</h2><p>很多时候我们很容易被表面需求所误导,类似于乔布斯的名言:“如果亨利福特在发明汽车之前去做市场调查,他得到的答案一定是大家想要一辆更快的马车。”,如果我们在做设计和技术决策的时候,没有找出用户的真实需求,很容易就会在错误的方向上狂奔,做很多无用功!</p>
<p>要找出本源的需求,还是需要多问为什么,多和干系人沟通,少考虑技术细节,少被现有的技术所误导或局限。</p>
<h3 id="案例:设计部门希望设计系统支持Angular"><a href="#案例:设计部门希望设计系统支持Angular" class="headerlink" title="案例:设计部门希望设计系统支持Angular"></a>案例:设计部门希望设计系统支持Angular</h3><p>我们设计部门最近希望我们的设计系统提供 Angular 版本,因为当前只支持 React 版本。从这个需求来看,表面是是要我们开发 Angular 版本,其实如果仔细追问他们到底为什么需要 Angular 版本,是因为有一个团队还在用 Angular ,他们希望这个团队能用我们的设计系统,但是人家表示用不了。其实本源的需求是希望有更多的团队用设计系统,而不是要支持 Angular 。</p>
<p>那要满足这个团队的这个需求,是不是非要做一个 Angular 版本不可呢?当然不需要,如果我能提供一个类似于 BootStrap 的 HTML 和 CSS 版本,其实他们一样能用起来,而这么做成本不高,并且别的团队也可以用。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/17/93dbe070fab56.webp"/></div></div>
<h2 id="原则2-聚焦于-“收益”、“成本”和“风险”三者之间的平衡,而不是技术本身"><a href="#原则2-聚焦于-“收益”、“成本”和“风险”三者之间的平衡,而不是技术本身" class="headerlink" title="原则2: 聚焦于 “收益”、“成本”和“风险”三者之间的平衡,而不是技术本身"></a>原则2: 聚焦于 “收益”、“成本”和“风险”三者之间的平衡,而不是技术本身</h2><p>每一次技术决策,其实本质上就是一次取舍(Trade-Offs)</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/17/65c4ca9ebb947.webp"/></div></div>
<p>每一次取舍(Trade-Offs),本质上就是在“收益”、“成本”和“风险”三者之间的平衡。</p>
<p>既然每一个决策都涉及到收益成本风险,那么就不能只看收益而无视成本和风险。就像前一个案例中提到的,设计部门考虑的是 Angular 版本带来的收益,但是他们却忽略了打造一套 Angular 版本的设计系统所需要的成本,以及可能带来的巨大风险。</p>
<p>所以在做技术决策的时候,理性的考虑一下 决策背后的收益、成本和风险的关系是很必要的,而不是仅靠喜好或者直觉来做决策。</p>
<h2 id="原则3-选择某个技术背后的生态系统而不是某个技术"><a href="#原则3-选择某个技术背后的生态系统而不是某个技术" class="headerlink" title="原则3: 选择某个技术背后的生态系统而不是某个技术"></a>原则3: 选择某个技术背后的生态系统而不是某个技术</h2><p>这条原则特别适用于前端领域,在前端,各种新技术、框架、工具层出不穷,如果总是追新,或者被某个软文吸引轻易选择了某个技术,最终会带来巨大的成本。</p>
<h3 id="案例:为什么我们从Preact迁移到React"><a href="#案例:为什么我们从Preact迁移到React" class="headerlink" title="案例:为什么我们从Preact迁移到React"></a>案例:为什么我们从Preact迁移到React</h3><p>在早些年的时候,我们前端选择了 Preact 作为UI渲染技术,这有早年 React License 的原因,也有 Preact 更小性能更好的原因。</p>
<p>然而这些年在使用过程中,还是有很多不足的地方,核心原因都是生态不够好。</p>
<p>比如说 Preact 调试很麻烦,因为它不像 React 有一个强大的 DevTools;比如说我们遇到过 Preact 在服务端渲染的内存泄漏问题,如果像我们这样大规模访问量的用户多一点,可能早就有人踩过坑了,不需要我们去花很长时间定位并最终去解决这个问题;比如最近我们在集成 Nextjs,Nextjs 是完全为 React 设计的,对 Preact 兼容性并不好。</p>
<p>这样的案例还很多,所以选择技术,它背后的生态和社区活跃度很重要。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/17/fa1f42d6dde72.webp"/></div></div>
<h2 id="原则4-不仅要考虑如何构建,还要考虑如何维护"><a href="#原则4-不仅要考虑如何构建,还要考虑如何维护" class="headerlink" title="原则4: 不仅要考虑如何构建,还要考虑如何维护"></a>原则4: 不仅要考虑如何构建,还要考虑如何维护</h2><p>这是一个常见的问题,很多人只管搭建新项目的时候爽,而不管后续维护是不是困难,用了一堆自己喜欢的新技术,最后难以维护。下一个人接手了,搞不好会推翻重写一遍,这样的循环一次又一次。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/17/abfb92e845531.webp"/></div></div>
<p>这样的错误我也常犯,比如2年前 React Hooks 刚出的时候,我就迫不及待用它来替代 Redux,结果上线后发现不好维护,有 Bug 也不好定位,不像以前 Redux,数据流特别清晰,借助工具非常好重现和定位问题,最终上线没多久就改回去了。</p>
<p>所以现在在做技术决策的时候,我们很注意的一个问题就是将来维护的时候是不是很麻烦。</p>
<p>包括我在代码审查的时候,有时候看到一些功能能运行的很好 PR,但是代码写的比较难懂的,或者没有遵守最佳实践的,只要是给未来的维护造成麻烦的,我都会毫不犹豫要求重写,避免增加未来的维护成本。</p>
<h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p>上面就是我们现在实践的五个技术决策原则:</p>
<ul>
<li>原则 0: 遵守公认的好的设计原则</li>
<li>原则 1: 找出最本源的需求,而不应该局限于当前的技术实现和资源</li>
<li>原则 2: 聚焦于 “收益”、“成本”和“风险”三者之间的平衡,而不是技术本身</li>
<li>原则 3: 选择某个技术背后的生态系统而不是某个技术</li>
<li>原则 4: 不仅要考虑如何构建,还要考虑如何维护</li>
</ul>
<p>这些原则绝大部分时候都可以很好的帮助我们做出正确的决策,避免踩坑。但我也会一直在反思曾经做过的决策,对于做出的不太好的决策,会反过来考虑是否要修订这些原则,最终通过不断完善决策原则,帮助我和团队更好的做出技术决策。</p>
<blockquote>
<p>最后,推荐一下左耳朵耗子写的一篇: <a href="https://coolshell.cn/articles/21672.html">我做系统架构的一些原则</a></p>
</blockquote>
<div class="note info flat"><p>原文链接:<a
iOS证书(.p12)和描述文件(.mobileprovision)申请
https://fe32.top/articles/io0411sc/
2022-04-10T09:13:22.000Z
2023-05-17T10:01:12.000Z
<p>如果你从事过iOS开发,大概都会了解到iOS有两种证书和描述文件:</p>
<table>
<thead>
<tr>
<th align="left">证书类型</th>
<th align="left">使用场景</th>
</tr>
</thead>
<tbody><tr>
<td align="left">开发(Development)证书和描述文件</td>
<td align="left">用于开发测试,在 HBuilderX 中打包后可在真机环境调试</td>
</tr>
<tr>
<td align="left">发布(Distribution)证书和描述文件</td>
<td align="left">用于提交 AppStore,在 HBuilderX 中提交云打包后提交到 AppStore 审核发布</td>
</tr>
</tbody></table>
<p>如果从未接触过 iOS,按照下面教程的所需环境、步骤操作,你将学会如何制作这 两种证书 和 描述文件。</p>
<h2 id="准备环境"><a href="#准备环境" class="headerlink" title="准备环境"></a>准备环境</h2><div class='checkbox green checked'><input type="checkbox" checked="checked"/>
<p>必需要有苹果开发者账号,并且加入了 “iOS Developer Program”</p>
</div>
<div class='checkbox green checked'><input type="checkbox" checked="checked"/>
<p>Mac OS 10.9以上系统(如果已经申请p12证书则不需要)</p>
</div>
<br>
<div class="tip cogs faa-horizontal"><p>苹果开发帐号说明<br>个人账号(Individual)/公司团队账号 (Company/Organization):</p>
<p>费用都是99美金一年,两者无本质区别,都可以发布应用到苹果市场。</p>
<p>区别在于个人账号在App Store销售者只能显示个人的ID,比如san zhang,单人使用。公司团队账号在App Store销售者可以显示类似Studios,或者自定义的团队名称,比如Mamshare INC,公司账号允许多个开发者协作开发,比个人帐号多一些帐号管理和级别权限的设置。</p>
<p>这两种帐号都可以用于开发,但在生成开发证书的时候,需要在生成mobileprovision描述文件时把需要安装的iPhone/iPad的设备UDID进行绑定(最多可以绑定100个设备),否则生成的ipa不能正常安装到测试设备上。</p>
<p>企业账号 (Enterprise):</p>
<p>费用299美金一年,该账号开发应用不能发布到App Store,只能用于企业自己内部使用的app通过网站下载,对测试的苹果iOS设备UDID数量不限制。</p>
</div>
<h2 id="生成证书请求文件"><a href="#生成证书请求文件" class="headerlink" title="生成证书请求文件"></a>生成证书请求文件</h2><blockquote>
<p>不管是<strong>申请开发 (Development) 证书</strong> 还是 <strong>发布 (Distribution) 证书</strong>,都需要使用<strong>证书请求 (.certSigningRequest) 文件</strong>,证书请求文件需在Mac OS上使用 <strong>“钥匙串访问”</strong> 工具生成。</p>
</blockquote>
<ol>
<li>打开“钥匙串访问”工具<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/32bfd54e10c8a.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/c13e3f6cfdad7.webp"/></div></div></li>
<li>打开菜单 “钥匙串访问”->“证书助理”,选择“从证书颁发机构请求证书…”:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/b7a241f628ba9.webp"/></div></div></li>
<li>打开创建请求证书页面,在页面中输入用户邮件地址、常用名称,选择存储到磁盘,点击 “继续” :<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/c58d3a5f6c005.webp"/></div></div></li>
<li>文件名称为 <strong>“CertificateSigningRequest.certSigningRequest”</strong>,选择保存位置,点击 <strong>“存储”</strong> 将证书请求文件保存到指定路径下,后面申请 <strong>开发(Development)证书</strong> 和 <strong>发布(Production)证书</strong> 时需要用到。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/0bdd9e49bea7f.webp" style="width:349px;"/></div></div></li>
<li>在保存位置可看到 生成的请求证书(CertificateSigningRequest.certSigningRequest)。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/ec8e6d555ce56.webp"/></div></div></li>
</ol>
<h2 id="登录苹果开发者账号"><a href="#登录苹果开发者账号" class="headerlink" title="登录苹果开发者账号"></a>登录苹果开发者账号</h2><ol>
<li>打开网站 <a href="https://developer.apple.com/devcenter/ios/index.action">iOS Dev Center</a> </li>
<li>使用苹果开发者账号登录 iOS Dev Center:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/71c5454c1c04e.webp"/></div></div></li>
<li>登录成功后在页面左侧选择 “Certificates,IDs & Profiles” 进入证书管理页面:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/348f64455500c.webp"/></div></div></li>
<li>在证书管理页面,可以看到所有已经申请的证书及描述文件:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/9a3dba58f4e05.webp"/></div></div></li>
</ol>
<h2 id="申请苹果-App-ID-(App的唯一标识)"><a href="#申请苹果-App-ID-(App的唯一标识)" class="headerlink" title="申请苹果 App ID (App的唯一标识)"></a>申请苹果 App ID (App的唯一标识)</h2><ol>
<li>选择页面的“Identifiers”可查看到已申请的所有 App 应用标识,点击页面上的加号来创建一个新的应用标识:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/c4a3a60630d53.webp"/></div></div></li>
<li>选择标识类型为“App”,然后点击 “Continue”<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/7c7c834630070.webp"/></div></div></li>
<li>平台选择“iOS,tvOS,watchOS”,Bundle ID 选择“Explicit”,在 Description 中填写描述,然后填写 Bundle ID,Bundle ID 要保持唯一性,建议填写反域名加应用标识的格式 如:“uni.xxxxxxxxx”,然后点击 “Continue”<div class="note red no-icon modern"><p>注意:在 HBuilderX 中 App 提交云端打包时界面上的 AppID 栏填写的就是这个 Bundle ID</p>
</div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/3b39bab22e0f3.webp"/></div></div></li>
<li>接下来需要选择应用需要使用的服务(如需要使用到消息推送功能,则选择“Push Notifications”),然后点击“Continue”<div class="note red no-icon modern"><p>注意:如果App用不到的服务一定不要勾选,以免响应审核</p>
</div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/cc7ca9ab0fb8a.webp"/></div></div></li>
<li>确认后选择提交,回到 identifiers 页面即可看到刚创建的App ID:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/ed5b4dd5984ff.webp"/></div></div></li>
</ol>
<h2 id="申请开发-Development-证书和描述文件"><a href="#申请开发-Development-证书和描述文件" class="headerlink" title="申请开发(Development)证书和描述文件"></a>申请开发(Development)证书和描述文件</h2><div class="note info no-icon flat"><p>开发(Development)证书 及 对应的描述文件用于开发阶段使用,可以直接将 App 安装到手机上,一个描述文件最多绑定100台测试设备(开发证书不能用于发布应用到 App Store)。</p>
</div>
<h3 id="申请开发-Development-证书"><a href="#申请开发-Development-证书" class="headerlink" title="申请开发(Development)证书"></a>申请开发(Development)证书</h3><ol>
<li>在证书管理页面选择 “Certificates” 可查看到已申请的所有证书(TYPE:Development 为开发证书,Distribution为发布证书),点击页面的加号来创建一个新的证书:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/849853215dfd2.webp"/></div></div></li>
<li>在 “Software” 栏下选中 “iOS App Development” 然后点击 “Continue”:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/0b929b10d830f.webp"/></div></div></li>
<li>用到刚刚生成的证书请求文件,点击“Choose File…”,选择刚刚保存到本地的 “CertificateSigningRequest.certSigningRequest”文件,点击 “Continue” 生成证书文件:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/b25182a1d7c0d.webp"/></div></div></li>
<li>生成证书后选择 “Download” 将证书下到本地 (development.cer):<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/3a52ae9d5fcf3.webp"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/ba00ed26cf9c7.webp"/></div></div></li>
<li>双击保存到本地的 development.cer 文件,会自动打开 “钥匙串访问” 工具说明导入证书成功,可以在证书列表中看到刚刚导入的证书,接下来需要导出 .p12 证书文件,选中导入的证书,右键选择 “导出xxxxxx”:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/258cffa0bc757.webp"/></div></div></li>
<li>输入文件名、选择路径后点击 “存储”:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/b2cc9e74411f8.webp"/></div></div></li>
<li>输入密码及确认密码后点击 “好”:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/296e61086ec36.webp"/></div></div></li>
<li>至此,我们已经完成了开发证书的制作(得到了 xxx.p12 证书文件)。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/e62f842827eb6.webp"/></div></div></li>
<li>如果出现开发证书不受信任,说明开发设备中 Apple Worldwide Developer Relations Certification Authority证书被删除了 或者 已安装的WWDRCA失效了。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/5e42531a2f00c.webp"/></div></div>
解决方案:<a href="https://www.apple.com/certificateauthority/">https://www.apple.com/certificateauthority/</a><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/1c9266bc34eee.webp"/></div></div></li>
</ol>
<h3 id="添加调试设备"><a href="#添加调试设备" class="headerlink" title="添加调试设备"></a>添加调试设备</h3><blockquote>
<p>开发描述文件必须绑定调试设备,只有授权的设备才可以直接安装 App,所以在申请开发描述文件之前,先添加调试的设备。<br>(如果已经添加设备,可跳过此节)</p>
</blockquote>
<ol>
<li>在证书管理页面选择 “Devices”,可查看到已添加的所有设备信息,点击页面上的加号来添加一个新设备:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/5faa18a9f989b.webp"/></div></div></li>
<li>填写设备名称 和 UDID(设备标识):<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/5f92123e2e3e5.webp"/></div></div>
<div class="note info no-icon modern"><p>获取设备UDID方法,将设备连接到电脑,启动 iTunes,点击此区域可切换显示设备的 UDID,右键选择复制。<br>直接点击链接<a href="https://www.pgyer.com/tools/udid">https://www.pgyer.com/tools/udid</a>,扫描二维码,会提示安装一个描述文件,点击安装之后,就会在浏览器中显示出设备信息。长按复制”设备信息UDID”下面的值,就是UDID了。</p>
</div></li>
</ol>
<h3 id="申请开发-Development-描述文件"><a href="#申请开发-Development-描述文件" class="headerlink" title="申请开发 (Development) 描述文件"></a>申请开发 (Development) 描述文件</h3><ol>
<li>在证书管理页面选择 “Profiles”,可查看到已申请的所有描述文件,点击页面上的加号来添加一个新的描述文件:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/800ebe7658b5b.webp"/></div></div></li>
<li>在 “Development” 栏下选中 “iOS App Development”,点击“Continue”按钮:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/1762f11243ea2.webp"/></div></div></li>
<li>这里要选择之前创建的 “App ID” (这里是“uni.xxxxxxxxx”),点击“Continue”:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/1d77f8d852cc4.webp"/></div></div></li>
<li>接下来选择需要绑定的证书,点击“Continue”:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/1ab102e5156f9.webp"/></div></div></li>
<li>选择授权调试设备,这里建议直接勾选 “Select All”,点击 “Continue”:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/bfac282287b3f.webp"/></div></div></li>
<li>输入描述文件的名称(如“xxxxProfile”), 点击 “Generate” 生成描述文件:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/816259708e02d.webp"/></div></div></li>
<li>点击“Download”下载保存开发描述文件(文件后缀为 .mobileprovision)<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/249a9f51f7cc0.webp"/></div></div></li>
<li>至此,我们已经得到了开发证书(.p12)及对应的描述文件(.mobileprovision)。</li>
</ol>
<h2 id="申请发布-Distribution-证书和描述文件"><a href="#申请发布-Distribution-证书和描述文件" class="headerlink" title="申请发布(Distribution)证书和描述文件"></a>申请发布(Distribution)证书和描述文件</h2><blockquote>
<p>发布 (Production) 证书用于正式发布环境下使用,用于提交到Appstore审核发布。发布证书打包的 ipa,不可以直接安装到手机上。</p>
</blockquote>
<h3 id="申请发布-Production-证书"><a href="#申请发布-Production-证书" class="headerlink" title="申请发布(Production)证书"></a>申请发布(Production)证书</h3><ol>
<li>在证书管理页面选择 “Certificates” 可查看到已申请的所有证书(TYPE:Development 为开发证书,Distribution为发布证书),点击页面的加号来创建一个新的证书:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/11/9234c7437cc3d.webp"/></div></div></li>
<li>在 “Software” 栏下选中 “App Store and Ad Hoc”,点击 “Continue”:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/11/f8ce9f59d3fe2.webp"/></div></div></li>
<li>接下来同样需要用到之前生成的证书请求文件,点击“Choose File…”选择刚刚保存到本地的 “CertificateSigningRequest.certSigningRequest”文件,点击 “Continue” 生成证书文件:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/b25182a1d7c0d.webp"/></div></div></li>
<li>生成证书成功,选择“Download” 将证书下载到本地 (ios_production.cer):<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/11/1f0ddf407133f.webp"/></div></div></li>
<li>同样双击保存到本地的 ios_production.cer 文件将证书导入到 “钥匙串访问”工具中,可以在证书列表中看到刚刚导入的证书,接下来需要导出 .p12 证书文件,选中导入的证书,右键选择 “导出xxxxxx”:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/11/44001c7d75409.webp"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/11/a3a6573917ccb.webp"/></div></div></li>
<li>输入文件名、选择路径后点击 “存储”:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/11/9be53fe2207b8.webp"/></div></div></li>
<li>输入密码及确认密码后点击 “好”:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/11/33cd5a9e191db.webp"/></div></div></li>
<li>如果弹出需要登录钥匙串的密码,输入当前设备账户密码即可。<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/11/9450f2ebf2100.webp"/></div></div></li>
<li>至此,我们已经完成了发布证书的制作(得到了 xxx.p12 证书文件),接下来,继续生成发布描述文件<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/11/85372dad2e6c5.webp"/></div></div></li>
</ol>
<h3 id="申请发布-Distribution-描述文件"><a href="#申请发布-Distribution-描述文件" class="headerlink" title="申请发布 (Distribution) 描述文件"></a>申请发布 (Distribution) 描述文件</h3><ol>
<li>在证书管理页面选择 “Profiles”,可查看到已申请的所有描述文件,点击页面上的加号来添加一个新的描述文件:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/800ebe7658b5b.webp"/></div></div></li>
<li>在 “Distribution” 栏下选中 “App Store”,点击“Continue”按钮:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/11/8e18a848436f3.webp"/></div></div></li>
<li>这里要选择之前创建的 “App ID” (这里是“uni.xxxxxxxxx”),点击“Continue”:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/11/25c1eff385108.webp"/></div></div></li>
<li>接下来选择需要绑定的发布证书(iOS Distribution),这里勾选刚刚生成的发布证书”,点击“Continue”:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/11/225d2ff7ab90f.webp"/></div></div></li>
<li>接下来输入描述文件的名称(如“AppProfileDistribution”), 点击 “Generate” 生成描述文件:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/11/b1373b52a866c.webp"/></div></div></li>
<li>然后点击 “Download” 将描述文件下载到本地(文件后缀为 .mobileprovision),<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif"
34个免费学习编码的最佳网站
https://fe32.top/articles/co0410de/
2022-04-10T05:12:33.000Z
2023-05-17T10:01:12.000Z
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><div class="tip cogs faa-horizontal"><p>对我来说,书籍或在线课程可以被认为是网页设计和开发领域的学习和专业发展非常有用的资源。因为它是作者在学习过程中的经验总结,以及将这些知识应用到实际项目中。</p>
<p>如果您有足够的钱能够购买最好的编程书籍和课程来发展您的技能,而不用看它的价格,自然是更好的。不过和我一样,刚开始还是个没有收入的学生的时候,花钱买编程方面的书也是一个很艰难的想法。因此,我经常收集一些电子书版本,免费的编程课程,以帮助我专业发展,而不必担心经济问题。</p>
<p>在今天的文章中,我将向您介绍 34 个有关 HTML、CSS 和 Javascript 的免费电子书。希望它对您在Web开发过程中有所帮助。(如果你负担得起,你应该买书或支持这些电子书的作者!)</p>
</div>
<h2 id="HTML-and-CSS-Book"><a href="#HTML-and-CSS-Book" class="headerlink" title="HTML and CSS Book"></a>HTML and CSS Book</h2><h3 id="Learn-to-Code-HTML-amp-CSS"><a href="#Learn-to-Code-HTML-amp-CSS" class="headerlink" title="Learn to Code HTML & CSS"></a>Learn to Code HTML & CSS</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/63a39bdf237fc.png"/></div></div>
<p>链接地址:<a href="https://learn.shayhowe.com/html-css/">https://learn.shayhowe.com/html-css/</a></p>
<h3 id="HTML5-Notes-for-Professionals-book"><a href="#HTML5-Notes-for-Professionals-book" class="headerlink" title="HTML5 Notes for Professionals book"></a>HTML5 Notes for Professionals book</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/e05625b1c22f4.png"/></div></div>
<p>链接地址:<a href="https://goalkicker.com/HTML5Book/">https://goalkicker.com/HTML5Book/</a></p>
<h3 id="Htmldog"><a href="#Htmldog" class="headerlink" title="Htmldog"></a>Htmldog</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/fe7ef9c50c8f4.png"/></div></div>
<p>链接地址:<a href="https://www.htmldog.com/">https://www.htmldog.com/</a></p>
<h3 id="CSS-Animation"><a href="#CSS-Animation" class="headerlink" title="CSS Animation"></a>CSS Animation</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/e5ee0679ca7d8.png"/></div></div>
<p>链接地址:<a href="https://cssanimation.rocks/">https://cssanimation.rocks/</a></p>
<h3 id="Web-Visual-Effects-with-CSS3"><a href="#Web-Visual-Effects-with-CSS3" class="headerlink" title="Web Visual Effects with CSS3"></a>Web Visual Effects with CSS3</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/82dea980302ef.png"/></div></div>
<p>链接地址:<a href="https://leanpub.com/web-visual-effects-with-css3/read">https://leanpub.com/web-visual-effects-with-css3/read</a></p>
<h3 id="The-Magic-Of-CSS"><a href="#The-Magic-Of-CSS" class="headerlink" title="The Magic Of CSS"></a>The Magic Of CSS</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/195dbf5b11c09.png"/></div></div>
<p>链接地址:<a href="https://adamschwartz.co/magic-of-css/chapters/1-the-box/">https://adamschwartz.co/magic-of-css/chapters/1-the-box/</a></p>
<h3 id="HTML-amp-CSS-Is-Hard"><a href="#HTML-amp-CSS-Is-Hard" class="headerlink" title="HTML & CSS Is Hard"></a>HTML & CSS Is Hard</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/d605dc02746b6.png"/></div></div>
<p>链接地址:<a href="https://www.internetingishard.com/html-and-css/">https://www.internetingishard.com/html-and-css/</a></p>
<h3 id="DIVE-INTO-HTML5"><a href="#DIVE-INTO-HTML5" class="headerlink" title="DIVE INTO HTML5"></a>DIVE INTO HTML5</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/2d6b2a9e27bfb.png"/></div></div>
<p>链接地址:<a href="http://diveintohtml5.info/index.html">http://diveintohtml5.info/index.html</a></p>
<h3 id="Dash"><a href="#Dash" class="headerlink" title="Dash"></a>Dash</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/5cbd4cefcbd42.png"/></div></div>
<p>链接地址:<a href="https://dash.generalassemb.ly/">https://dash.generalassemb.ly/</a></p>
<h3 id="MaintainableCSS"><a href="#MaintainableCSS" class="headerlink" title="MaintainableCSS"></a>MaintainableCSS</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/382e838352582.png"/></div></div>
<p>链接地址:<a href="https://maintainablecss.com/">https://maintainablecss.com/</a></p>
<h3 id="MarkSheet"><a href="#MarkSheet" class="headerlink" title="MarkSheet"></a>MarkSheet</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/133a98e1df804.png"/></div></div>
<p>链接地址:<a href="https://marksheet.io/">https://marksheet.io/</a></p>
<h3 id="Enduring-CSS"><a href="#Enduring-CSS" class="headerlink" title="Enduring CSS"></a>Enduring CSS</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/ff5ff9cf479ca.png"/></div></div>
<p>链接地址:<a href="https://ecss.benfrain.com/">https://ecss.benfrain.com/</a></p>
<h3 id="HTML-Canvas-Deep-Dive"><a href="#HTML-Canvas-Deep-Dive" class="headerlink" title="HTML Canvas Deep Dive"></a>HTML Canvas Deep Dive</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/7e688d65333ee.png"/></div></div>
<p>链接地址:<a href="https://joshondesign.com/p/books/canvasdeepdive/toc.html">https://joshondesign.com/p/books/canvasdeepdive/toc.html</a></p>
<h3 id="Code-Guide"><a href="#Code-Guide" class="headerlink" title="Code Guide"></a>Code Guide</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/ada452145f999.png"/></div></div>
<p>链接地址:<a href="https://codeguide.co/#html-doctype">https://codeguide.co/#html-doctype</a></p>
<h3 id="A-Guide-to-HTML5-and-CSS3"><a href="#A-Guide-to-HTML5-and-CSS3" class="headerlink" title="A Guide to HTML5 and CSS3"></a>A Guide to HTML5 and CSS3</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/92702a890613e.png"/></div></div>
<p>链接地址:<a href="https://html5hive.org/free-ebook-a-guide-to-html5-and-css3/">https://html5hive.org/free-ebook-a-guide-to-html5-and-css3/</a></p>
<h3 id="Understanding-Flexbox"><a href="#Understanding-Flexbox" class="headerlink" title="Understanding Flexbox"></a>Understanding Flexbox</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/7e5c141f2865f.png"/></div></div>
<p>链接地址:<a href="https://ohansemmanuel.github.io/uf_download.html">https://ohansemmanuel.github.io/uf_download.html</a></p>
<h2 id="Free-Javascript-Books-for-Beginners"><a href="#Free-Javascript-Books-for-Beginners" class="headerlink" title="Free Javascript Books for Beginners"></a>Free Javascript Books for Beginners</h2><h3 id="Eloquent-JavaScript"><a href="#Eloquent-JavaScript" class="headerlink" title="Eloquent JavaScript"></a>Eloquent JavaScript</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/1500d1d7575ed.png"/></div></div>
<p>链接地址:<a href="https://eloquentjavascript.net/">https://eloquentjavascript.net/</a></p>
<h3 id="JavaScript-ES2015-Enlightenment"><a href="#JavaScript-ES2015-Enlightenment" class="headerlink" title="JavaScript (ES2015+) Enlightenment"></a>JavaScript (ES2015+) Enlightenment</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/923979c6f6e5d.png"/></div></div>
<p>链接地址:<a href="https://frontendmasters.com/books/javascript-enlightenment/">https://frontendmasters.com/books/javascript-enlightenment/</a></p>
<h3 id="JavaScript®-Notes-for-Professionals-book"><a href="#JavaScript®-Notes-for-Professionals-book" class="headerlink" title="JavaScript® Notes for Professionals book"></a>JavaScript® Notes for Professionals book</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/304e0ea325d66.png"/></div></div>
<p>链接地址:<a href="https://goalkicker.com/JavaScriptBook/">https://goalkicker.com/JavaScriptBook/</a></p>
<h3 id="JavaScript-Garden"><a href="#JavaScript-Garden" class="headerlink" title="JavaScript Garden"></a>JavaScript Garden</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/57811193735e1.png"/></div></div>
<p>链接地址:<a href="https://bonsaiden.github.io/JavaScript-Garden/">https://bonsaiden.github.io/JavaScript-Garden/</a></p>
<h3 id="JavaScript-Info"><a href="#JavaScript-Info" class="headerlink" title="JavaScript Info"></a>JavaScript Info</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/66c7843ff87d4.png"/></div></div>
<p>链接地址:<a href="https://javascript.info/">https://javascript.info/</a></p>
<h3 id="The-Odin-Project"><a href="#The-Odin-Project" class="headerlink" title="The Odin Project"></a>The Odin Project</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/255c8c44fdc95.png"/></div></div>
<p>链接地址:<a href="https://www.theodinproject.com/courses/javascript">https://www.theodinproject.com/courses/javascript</a></p>
<h3 id="JS-Books"><a href="#JS-Books" class="headerlink" title="JS Books"></a>JS Books</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/51cab7ce630ad.png"/></div></div>
<p>链接地址:<a href="https://jsbooks.revolunet.com/">https://jsbooks.revolunet.com/</a></p>
<h3 id="Robust-Client-Side-JavaScript"><a href="#Robust-Client-Side-JavaScript" class="headerlink" title="Robust Client-Side JavaScript"></a>Robust Client-Side JavaScript</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/52cf790c149bd.png"/></div></div>
<p>链接地址:<a href="https://molily.de/robust-javascript/">https://molily.de/robust-javascript/</a></p>
<h3 id="Front-End-Web-Apps-with-Plain-JavaScript"><a href="#Front-End-Web-Apps-with-Plain-JavaScript" class="headerlink" title="Front-End Web Apps with Plain JavaScript"></a>Front-End Web Apps with Plain JavaScript</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/2424ae3741d49.png"/></div></div>
<p>链接地址:<a href="https://web-engineering.info/JsFrontendApp-Book">https://web-engineering.info/JsFrontendApp-Book</a></p>
<h3 id="Human-JavaScript"><a href="#Human-JavaScript" class="headerlink" title="Human JavaScript"></a>Human JavaScript</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/713ed0b886c59.png"/></div></div>
<p>链接地址:<a href="http://read.humanjavascript.com/ch02-the-big-decision.html">http://read.humanjavascript.com/ch02-the-big-decision.html</a></p>
<h3 id="Learn-JS"><a href="#Learn-JS" class="headerlink" title="Learn JS"></a>Learn JS</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/aec502a0f98a4.png"/></div></div>
<p>链接地址:<a href="https://www.learn-js.org/">https://www.learn-js.org/</a></p>
<h2 id="HTML-CSS-Javascript-Course-Free"><a href="#HTML-CSS-Javascript-Course-Free" class="headerlink" title="HTML, CSS, Javascript Course Free"></a>HTML, CSS, Javascript Course Free</h2><h3 id="Foundations-of-Front-End-Web-Development"><a href="#Foundations-of-Front-End-Web-Development" class="headerlink" title="Foundations of Front-End Web Development"></a>Foundations of Front-End Web Development</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/1ff8f02818d39.png"/></div></div>
<p>链接地址:<a href="https://www.udemy.com/course/foundations-of-front-end-development/">https://www.udemy.com/course/foundations-of-front-end-development/</a></p>
<h3 id="Web-Development-By-Doing-HTML-x2F-CSS-From-Scratch"><a href="#Web-Development-By-Doing-HTML-x2F-CSS-From-Scratch" class="headerlink" title="Web Development By Doing: HTML / CSS From Scratch"></a>Web Development By Doing: HTML / CSS From Scratch</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/519fc09cdb4c2.png"/></div></div>
<p>链接地址:<a href="https://www.udemy.com/course/web-development-learn-by-doing-html5-css3-from-scratch-introductory/">https://www.udemy.com/course/web-development-learn-by-doing-html5-css3-from-scratch-introductory/</a></p>
<h3 id="Master-the-Basics-of-HTML5-amp-CSS3-Beginner-Web-Development"><a href="#Master-the-Basics-of-HTML5-amp-CSS3-Beginner-Web-Development" class="headerlink" title="Master the Basics of HTML5 & CSS3: Beginner Web Development"></a>Master the Basics of HTML5 & CSS3: Beginner Web Development</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/edd188ea7e622.png"/></div></div>
<p>链接地址:<a href="https://www.udemy.com/course/master-the-basics-of-html5-css3-beginner-web-development/">https://www.udemy.com/course/master-the-basics-of-html5-css3-beginner-web-development/</a></p>
<h3 id="Learn-HTML5-and-CSS3-From-Scratch"><a href="#Learn-HTML5-and-CSS3-From-Scratch" class="headerlink" title="Learn HTML5 and CSS3 From Scratch"></a>Learn HTML5 and CSS3 From Scratch</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/ab8df8375735a.png"/></div></div>
<p>链接地址:<a href="https://www.youtube.com/watch?v=mU6anWqZJcc&ab_channel=freeCodeCamp.org">https://www.youtube.com/watch?v=mU6anWqZJcc&ab_channel=freeCodeCamp.org</a></p>
<h3 id="Learn-JavaScript-Code-Cademy"><a href="#Learn-JavaScript-Code-Cademy" class="headerlink" title="Learn JavaScript (Code Cademy)"></a>Learn JavaScript (Code Cademy)</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/8df87bac49a57.png"/></div></div>
<p>链接地址:<a href="https://www.codecademy.com/learn/introduction-to-javascript">https://www.codecademy.com/learn/introduction-to-javascript</a></p>
<h3 id="Computer-programming-Khan-Academy"><a href="#Computer-programming-Khan-Academy" class="headerlink" title="Computer programming (Khan Academy)"></a>Computer programming (Khan Academy)</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/f2ece8faffab2.png"/></div></div>
<p>链接地址:<a href="https://www.khanacademy.org/computing/computer-programming">https://www.khanacademy.org/computing/computer-programming</a></p>
<h3 id="Javascript-Essentials"><a href="#Javascript-Essentials" class="headerlink" title="Javascript Essentials"></a>Javascript Essentials</h3><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/04/10/c62b5c63e2752.png"/></div></div>
<p>链接地址:<a href="https://www.udemy.com/course/javascript-essentials/">https://www.udemy.com/course/javascript-essentials/</a></p>
<h2 id="Summary"><a href="#Summary" class="headerlink" title="Summary"></a>Summary</h2><p>我希望这篇文章将提供最好的网站来学习 html,css,javascript免费的网页开发,如果您有任何问题,请在留言区给我留言,我会尽快回复。</p>
<p>感谢您的阅读,祝编程愉快!</p>
<div class="note info flat"><p>原文链接:<a href="https://us.niemvuilaptrinh.com/article/34-ebooks-courses-free-for-learn-front-end">Top 34 Best Websites to Learn Coding for
17 个流行的Vue插件
https://fe32.top/articles/vu0326ep/
2022-03-26T10:34:40.000Z
2023-05-17T10:01:12.000Z
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/26/8f9d77fb7c34e.jpg"/></div></div> </br>
<p>Vue 被一个健康的插件和包的生态系统所加强,使开发变得可靠、快速和简单。由于Vue 是一个国际开发者社区所选择的框架,所以有一个不断增长的插件和包库,你可以在项目中使用。</p>
<p>本文列举了用于 Vue 2 和 Vue 3 的 17个 流行的 Vue 插件:</p>
<ul>
<li>Vuetify</li>
<li>NuxtJS</li>
<li>Vuex</li>
<li>Vuex Persisted State</li>
<li>VuePress</li>
<li>Vue Meta</li>
<li>Vue ChartJS</li>
<li>Vue Grid Layout</li>
<li>Vue Draggable</li>
<li>Vee-Validate</li>
<li>Vue Toastification</li>
<li>Vue Tour</li>
<li>Swiper.js</li>
<li>Vue2-Leaflet</li>
<li>TroisJS</li>
<li>Vue Scrollama</li>
<li>Vue QR Code Reader</li>
</ul>
<h2 id="Vuetify"><a href="#Vuetify" class="headerlink" title="Vuetify"></a>Vuetify</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/26/bea915de2678d.jpg"/></div></div>
<p>教程地址:<a href="https://www.vuemastery.com/courses/beautify-with-vuetify/getting-started-with-vuetify">https://www.vuemastery.com/courses/beautify-with-vuetify/getting-started-with-vuetify</a></p>
<p>你是否曾纠结于如何让应用在视觉上看起来更吸引人?Vuetify是一个基于 Material Design 的 UI 库,支持谷歌和 Android 的设计语言。</p>
<p>它是一个开源库,有数百个组件,从按钮, app bars,chips,modals 和更多。这些组件都是预先设计好的,非常实用,让你能够通过文档化的 props 和插槽与它们进行交互。预先定义的 CSS 类也可用于控制颜色、字体、网格间距、弹性框等。</p>
<h2 id="NuxtJS"><a href="#NuxtJS" class="headerlink" title="NuxtJS"></a>NuxtJS</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/26/7ea64fdb56960.jpg"/></div></div>
<p>Nuxt 基于一个强大的模块化架构。你可以从 50 多个模块中进行选择,让你的开发变得更快、更简单。对 PWA 的支持、添加谷歌分析到你的网页或生成网站地图,这些功能都无需重新发明轮子来获得。</p>
<p>NuxtJS 目前基于 Vue 2。但是 nux3 最近已经发布,并且已经完全重写以支持Vue 3。</p>
<h2 id="Vuex"><a href="#Vuex" class="headerlink" title="Vuex"></a>Vuex</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/26/19e17e397fd16.jpg"/></div></div>
<p>Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension (opens new window),提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。</p>
<h2 id="Vuex-Persisted-State"><a href="#Vuex-Persisted-State" class="headerlink" title="Vuex Persisted State"></a>Vuex Persisted State</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/26/8571620ef348f.jpg"/></div></div>
<p>Vuex 状态机制,一刷新就会重置,解决问题的方式一般都是存储起来(session/local Storage),<code>vuex-persistedstate</code> 插件帮我们集成了这些功能。</p>
<h2 id="VuePress"><a href="#VuePress" class="headerlink" title="VuePress"></a>VuePress</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/26/6e53451b2491a.jpg"/></div></div>
<p>VuePress 是一款使用 Vue 驱动的静态网站生成器,是 Vue 的作者 Evan You 为了方便文档的编写而开发的。</p>
<ul>
<li>默认主题与 Vue 官方文档一致</li>
<li>简洁,少配置,高性能</li>
<li>Markdown 专为技术文档提供拓展</li>
<li>自带 PWA</li>
<li>自定义主题,可定制程度完全由自己决定</li>
</ul>
<h2 id="Vue-Meta"><a href="#Vue-Meta" class="headerlink" title="Vue Meta"></a>Vue Meta</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/26/492fd5106b3ca.jpg"/></div></div>
<p>文档地址:<a href="https://vue-meta.nuxtjs.org/">https://vue-meta.nuxtjs.org/</a></p>
<p>基于 Vue 2.0 的 vue-meta 插件,主要用于管理 HMTL 头部标签,同时也支持 SSR。</p>
<p>vue-meta 有以下特点:</p>
<ul>
<li>在组件内设置 metaInfo,便可轻松实现头部标签的管理。</li>
<li>metaInfo 的数据都是响应的,如果数据变化,头部信息会自动更新 支持 SSR。</li>
</ul>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/26/a4b510cbe8788.jpg"/></div></div>
<h2 id="Vue-ChartJS"><a href="#Vue-ChartJS" class="headerlink" title="Vue ChartJS"></a>Vue ChartJS</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/26/86213e7cbb158.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/26/f25ad996c49cc.jpg"/></div></div>
<p>链接地址:<a href="https://www.npmjs.com/package/vue-chartjs">https://www.npmjs.com/package/vue-chartjs</a></p>
<p>想在你的 Vue 应用程序中添加图表?可以看看 Chart.js。它是一个为设计师和开发者提供的简单而灵活的 JS 图表库。它有许多漂亮的图表类型可供选择。</p>
<h2 id="Vue-Grid-Layout"><a href="#Vue-Grid-Layout" class="headerlink" title="Vue Grid Layout"></a>Vue Grid Layout</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/26/deeee9e405e55.gif"/></div></div>
<p>文档地址:<a href="https://jbaysolutions.github.io/vue-grid-layout/">https://jbaysolutions.github.io/vue-grid-layout/</a></p>
<p>Vue Grid Layout 是一个类似于 Gridster 的栅格布局系统, 适用于 Vue.js,灵感来源于 React Grid Layout。</p>
<p>特性:</p>
<ul>
<li>可拖拽</li>
<li>可调整大小</li>
<li>静态部件(不可拖拽、调整大小)</li>
<li>拖拽和调整大小时进行边界检查</li>
<li>增减部件时避免重建栅格</li>
<li>可序列化和还原的布局</li>
<li>自动化 RTL 支持</li>
<li>响应式</li>
</ul>
<h2 id="Vue-Draggable"><a href="#Vue-Draggable" class="headerlink" title="Vue Draggable"></a>Vue Draggable</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/26/f13e4b1c89e70.gif"/></div></div>
<p>Vue.Draggable 是一款基于 Sortable.js 实现的 vue 拖拽插件。支持移动设备、拖拽和选择文本、智能滚动,可以在不同列表间拖拽、不依赖 jQuery 为基础、vue2 过渡动画兼容、支持撤销操作,总之是一款非常优秀的 vue 拖拽组件。</p>
<p>主要特征:</p>
<ul>
<li>支持触控设备</li>
<li>支持拖动手柄和可选文本</li>
<li>智能自动滚动</li>
<li>支持不同列表之间的拖放</li>
<li>没有 jQuery 依赖</li>
<li>保持同步 HTML 和查看模型列表</li>
<li>兼容 Vue.js 2.0 转换组</li>
</ul>
<h2 id="Vee-Validate"><a href="#Vee-Validate" class="headerlink" title="Vee-Validate"></a>Vee-Validate</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/26/d553969304cba.jpg"/></div></div>
<p>地址:<a href="https://www.npmjs.com/package/vee-validate">https://www.npmjs.com/package/vee-validate</a></p>
<p>为了确保通过表单从用户那里收集正确类型的数据,有必要验证表单,以确保用户输入符合预期的模式。VeeValidate 是一个可以将这一层功能添加到任何表单组件的包。</p>
<p>主要特征:</p>
<ul>
<li>🍞简单:熟悉且易于设置的声明式验证</li>
<li>🧘♀️灵活:同步、异步、字段级或表单级验证</li>
<li>⚡️快速:使用直观的 API 和较小的占用空间更快地构建更快的表单</li>
<li>🏏Minimal:只处理复杂的表单问题,让您完全控制其他一切</li>
<li>😎UI Agnostic:适用于原生 HTML 元素或您最喜欢的 UI 库组件</li>
<li>🦾渐进式:无论您使用 Vue.js 作为渐进式增强还是在复杂的设置中都可以使用</li>
<li>✅内置规则:包含 25 条以上规则的配套库,可满足大多数 Web 应用程序的大部分需求</li>
<li>🌐i18n:来自世界各地的开发人员贡献的内置规则的 45 多个语言环境</li>
</ul>
<p>Marina Mosti 在 Vue Mastery 的 <a href="https://www.vuemastery.com/courses/validating-vue3-forms/why-vee-validate">Validating Vue 3 Forms</a> 课程中介绍了如何使用这个库。</p>
<h2 id="Vue-Toastification"><a href="#Vue-Toastification" class="headerlink" title="Vue Toastification"></a>Vue Toastification</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/26/7ffb3b071e226.gif"/></div></div>
<p>地址:<a href="https://vue-toastification.maronato.dev/">https://vue-toastification.maronato.dev/</a></p>
<p>Vue Toastification 是一个轻量、易用且美观的提示条通知组件,提供了大量的选项来支持大部分自定义选择。</p>
<h2 id="Vue-Tour"><a href="#Vue-Tour" class="headerlink" title="Vue Tour"></a>Vue Tour</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/26/010ab8e3352fb.gif"/></div></div>
<p>地址:<a href="https://github.com/pulsardev/vue-tour">https://github.com/pulsardev/vue-tour</a></p>
<p>Vue Tour 是轻巧、简单且可自定义的新手指引插件,可与 Vue.js 一起使用。它提供了一种快速简便的方法来指导用户使用您的应用程序。</p>
<h2 id="Swiper-js"><a href="#Swiper-js" class="headerlink" title="Swiper.js"></a>Swiper.js</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/26/d98f3d7c1eceb.gif"/></div></div>
<p>地址:<a href="https://swiperjs.com/vue">https://swiperjs.com/vue</a></p>
<p>Swiper.js 是一个预制的旋转木马组件,可以用来在各种图片之间滑动。Swiper.js 原生支持 Vue 3,提供了一个可以插入到你的项目的组件。对于 Vue 2,你可以使用其他包,如 vue-awesome-swiper。</p>
<h2 id="vue2-leaflet"><a href="#vue2-leaflet" class="headerlink" title="vue2-leaflet"></a>vue2-leaflet</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/26/6092bc3f900d9.jpg"/></div></div>
<p>地址:<a href="https://leafletjs.com/">https://leafletjs.com/</a></p>
<p>LeafletJS 是一个流行的开源库,用于移动友好的交互式地图。它非常轻巧,只有 39KB,并且具有大多数开发人员需要的所有映射功能。这个 Vue 2 包可以轻松集成到您现有的应用程序中,并可以访问 Leaflet 的所有功能。</p>
<p>主要特征:</p>
<ul>
<li>常用交互功能: 移动端缩放、 拖动、 平移、 拖动标记、 捏拉缩放</li>
<li>自定义切片图层</li>
<li>手机硬件加速</li>
<li>标记, 弹出窗口</li>
<li>图像叠加</li>
</ul>
<h2 id="TroisJS"><a href="#TroisJS" class="headerlink" title="TroisJS"></a>TroisJS</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/26/280ea461f1134.gif"/></div></div>
<p>地址:<a href="https://troisjs.github.io/">https://troisjs.github.io/</a></p>
<p>Trois(法语为Three)是一个 Vue 3 库,用于 <code>Three.JS</code>,这是一个流行的 WebGL 库。</p>
<p>Three.JS 对桌面和移动端都有良好的支持。这个库允许你使用 VueJS 组件为你的网站轻松创建 3D 内容。你可以使用这个库在你的网站上添加一个 3D 渲染器,并在你的 VueJs 文件的<code><template></code>部分中使用预先建立的组件指定场景细节,如材料、照明、网格、阴影等。</p>
<p>Trois.Js 是 Three.js 上面的一个包装器,因此不比原始库慢。它还通过自动处置几何体、材料、纹理、渲染器等来简化对象的处置,这在原始库中是不存在的。</p>
<h2 id="vue-scrollama"><a href="#vue-scrollama" class="headerlink" title="vue-scrollama"></a>vue-scrollama</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/26/1013a89337004.gif"/></div></div>
<p>地址:<a href="https://www.npmjs.com/package/vue-scrollama">https://www.npmjs.com/package/vue-scrollama</a></p>
<p>一个 Vue 组件,可轻松设置滚动驱动的交互(又称滚动讲演)。在引擎盖下使用 Scrollama。</p>
<h2 id="Vue-QR-Code-Reader"><a href="#Vue-QR-Code-Reader" class="headerlink" title="Vue QR Code Reader"></a>Vue QR Code Reader</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/26/a591c388fbdd1.jpg"/></div></div>
<p>地址:<a href="https://gruhn.github.io/vue-qrcode-reader/demos/CustomTracking.html">https://gruhn.github.io/vue-qrcode-reader/demos/CustomTracking.html</a></p>
<p>QR 码(QR code)在当今世界非常普遍。Vue 二维码阅读器是一个即插即用的包,允许你添加二维码扫描功能到你的应用程序。</p>
<p>主要特征:</p>
<ul>
<li>流式传输摄像头 并连续扫描二维码</li>
<li>拖放要解码的二维码图像</li>
<li>批量扫描 QR 码图像</li>
</ul>
<div class="note purple no-icon flat"><p>原文链接:<a href="https://www.vuemastery.com/blog/most-popular-vuejs-plugins-and-packages/">Most popular Vue.js plugins &
13个用于网站设计和开发的 React 图库
https://fe32.top/articles/rea323su/
2022-03-23T10:54:32.000Z
2023-05-17T10:01:12.000Z
<p>在本文中,我们将介绍 13 个用于网站设计和开发的 React 图片库示例。</p>
<h2 id="React-amp-CSS-Grid-Image-Gallery"><a href="#React-amp-CSS-Grid-Image-Gallery" class="headerlink" title="React & CSS Grid Image Gallery"></a>React & CSS Grid Image Gallery</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/23/11b754a8fe3c3.png"/></div></div> </br>
<p>You can see the results below.</p>
<iframe allowfullscreen="true" allowpaymentrequest="true" allowtransparency="true" class="cp_embed_iframe " frameborder="0" height="300" width="100%" name="cp_embed_1" scrolling="no" src="https://codepen.io/tvweinstock/embed/preview/wegZEW?height=300&theme-id=dark&default-tab=js%2Cresult&slug-hash=wegZEW&preview=true&user=tvweinstock&name=cp_embed_1" style="width: 100%; overflow:hidden; display:block;" title="CodePen Embed" loading="lazy" id="cp_embed_wegZEW"></iframe>
<div class="note blue no-icon flat"><p>链接地址:<a href="https://codepen.io/tvweinstock/pen/wegZEW">https://codepen.io/tvweinstock/pen/wegZEW</a></p>
</div>
<h2 id="React-Masonry-Grid"><a href="#React-Masonry-Grid" class="headerlink" title="React Masonry Grid"></a>React Masonry Grid</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/23/b2ba6ea0e7535.png"/></div></div> </br>
<p>You can see the results below.</p>
<iframe allowfullscreen="true" allowpaymentrequest="true" allowtransparency="true" class="cp_embed_iframe " frameborder="0" height="300" width="100%" name="cp_embed_2" scrolling="no" src="https://codepen.io/golle404/embed/preview/wWoXwz?height=300&theme-id=dark&default-tab=js%2Cresult&slug-hash=wWoXwz&preview=true&user=golle404&name=cp_embed_2" style="width: 100%; overflow:hidden; display:block;" title="CodePen Embed" loading="lazy" id="cp_embed_wWoXwz"></iframe>
<div class="note blue no-icon flat"><p>链接地址:<a href="https://codepen.io/golle404/pen/wWoXwz">https://codepen.io/golle404/pen/wWoXwz</a></p>
</div>
<h2 id="React-Photo-Gallery"><a href="#React-Photo-Gallery" class="headerlink" title="React Photo Gallery"></a>React Photo Gallery</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/23/9fb6dcc43bafb.png"/></div></div> </br>
<p>You can see the results below.</p>
<iframe allowfullscreen="true" allowpaymentrequest="true" allowtransparency="true" class="cp_embed_iframe " frameborder="0" height="300" width="100%" name="cp_embed_3" scrolling="no" src="https://codepen.io/alexboffey/embed/preview/YWdzYj?height=300&theme-id=dark&default-tab=js%2Cresult&slug-hash=YWdzYj&preview=true&user=alexboffey&name=cp_embed_3" style="width: 100%; overflow:hidden; display:block;" title="CodePen Embed" loading="lazy" id="cp_embed_YWdzYj"></iframe>
<div class="note blue no-icon flat"><p>链接地址:<a href="https://codepen.io/alexboffey/pen/YWdzYj">https://codepen.io/alexboffey/pen/YWdzYj</a></p>
</div>
<h2 id="React-Image-Viewer"><a href="#React-Image-Viewer" class="headerlink" title="React-Image-Viewer"></a>React-Image-Viewer</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/23/5e593cd38eda4.png"/></div></div> </br>
<p>You can see the results below.</p>
<iframe allowfullscreen="true" allowpaymentrequest="true" allowtransparency="true" class="cp_embed_iframe " frameborder="0" height="300" width="100%" name="cp_embed_4" scrolling="no" src="https://codepen.io/mawa_hujihara/embed/preview/ZezdeJ?height=300&theme-id=dark&default-tab=js%2Cresult&slug-hash=ZezdeJ&preview=true&user=mawa_hujihara&name=cp_embed_4" style="width: 100%; overflow:hidden; display:block;" title="CodePen Embed" loading="lazy" id="cp_embed_ZezdeJ"></iframe>
<div class="note blue no-icon flat"><p>链接地址:<a href="https://codepen.io/mawa_hujihara/pen/ZezdeJ">https://codepen.io/mawa_hujihara/pen/ZezdeJ</a></p>
</div>
<h2 id="React-Gallery-Lightbox"><a href="#React-Gallery-Lightbox" class="headerlink" title="React Gallery Lightbox"></a>React Gallery Lightbox</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/23/19359e6310f45.png"/></div></div> </br>
<p>You can see the results below.</p>
<iframe allowfullscreen="true" allowpaymentrequest="true" allowtransparency="true" class="cp_embed_iframe " frameborder="0" height="300" width="100%" name="cp_embed_5" scrolling="no" src="https://codepen.io/ivanodintsov/embed/preview/yqvZzO?height=300&theme-id=dark&default-tab=js%2Cresult&slug-hash=yqvZzO&preview=true&user=ivanodintsov&name=cp_embed_5" style="width: 100%; overflow:hidden; display:block;" title="CodePen Embed" loading="lazy" id="cp_embed_yqvZzO"></iframe>
<div class="note blue no-icon flat"><p>链接地址:<a href="https://codepen.io/ivanodintsov/pen/yqvZzO">https://codepen.io/ivanodintsov/pen/yqvZzO</a></p>
</div>
<h2 id="React-Image-Gallery-Example"><a href="#React-Image-Gallery-Example" class="headerlink" title="React-Image Gallery Example"></a>React-Image Gallery Example</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/23/4159cbcaf216b.png"/></div></div> </br>
<p>You can see the results below.</p>
<iframe allowfullscreen="true" allowpaymentrequest="true" allowtransparency="true" class="cp_embed_iframe " frameborder="0" height="300" width="100%" name="cp_embed_6" scrolling="no" src="https://codepen.io/monochromer/embed/preview/VWyjpq?height=300&theme-id=dark&default-tab=js%2Cresult&slug-hash=VWyjpq&preview=true&user=monochromer&name=cp_embed_6" style="width: 100%; overflow:hidden; display:block;" title="CodePen Embed" loading="lazy" id="cp_embed_VWyjpq"></iframe>
<div class="note blue no-icon flat"><p>链接地址:<a href="https://codepen.io/monochromer/pen/VWyjpq">https://codepen.io/monochromer/pen/VWyjpq</a></p>
</div>
<h2 id="React-Image-Gallery-Grid"><a href="#React-Image-Gallery-Grid" class="headerlink" title="React Image Gallery Grid"></a>React Image Gallery Grid</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/23/1d40ce5883023.png"/></div></div> </br>
<p>You can see the results below.</p>
<iframe allowfullscreen="true" allowpaymentrequest="true" allowtransparency="true" class="cp_embed_iframe " frameborder="0" height="300" width="100%" name="cp_embed_7" scrolling="no" src="https://codepen.io/tk89/embed/preview/QPKoVy?height=300&theme-id=dark&default-tab=js%2Cresult&slug-hash=QPKoVy&preview=true&user=tk89&name=cp_embed_7" style="width: 100%; overflow:hidden; display:block;" title="CodePen Embed" loading="lazy" id="cp_embed_QPKoVy"></iframe>
<div class="note blue no-icon flat"><p>链接地址:<a href="https://codepen.io/tk89/pen/QPKoVy">https://codepen.io/tk89/pen/QPKoVy</a></p>
</div>
<h2 id="React-Gallery"><a href="#React-Gallery" class="headerlink" title="React Gallery"></a>React Gallery</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/23/30616c0da4c6c.png"/></div></div> </br>
<p>You can see the results below.</p>
<iframe allowfullscreen="true" allowpaymentrequest="true" allowtransparency="true" class="cp_embed_iframe " frameborder="0" height="300" width="100%" name="cp_embed_8" scrolling="no" src="https://codepen.io/alexboffey/embed/preview/ammVER?height=300&theme-id=dark&default-tab=js%2Cresult&slug-hash=ammVER&preview=true&user=alexboffey&name=cp_embed_8" style="width: 100%; overflow:hidden; display:block;" title="CodePen Embed" loading="lazy" id="cp_embed_ammVER"></iframe>
<div class="note blue no-icon flat"><p>链接地址:<a href="https://codepen.io/alexboffey/pen/ammVER">https://codepen.io/alexboffey/pen/ammVER</a></p>
</div>
<h2 id="React-Gallery-Component"><a href="#React-Gallery-Component" class="headerlink" title="React Gallery Component"></a>React Gallery Component</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/23/3250b2a26efae.png"/></div></div> </br>
<p>You can see the results below.</p>
<iframe allowfullscreen="true" allowpaymentrequest="true" allowtransparency="true" class="cp_embed_iframe " frameborder="0" height="300" width="100%" name="cp_embed_9" scrolling="no" src="https://codepen.io/keyframers/embed/preview/oNYmomo?height=300&theme-id=dark&default-tab=js%2Cresult&slug-hash=oNYmomo&preview=true&user=keyframers&name=cp_embed_9" style="width: 100%; overflow:hidden; display:block;" title="CodePen Embed" loading="lazy" id="cp_embed_oNYmomo"></iframe>
<div class="note blue no-icon flat"><p>链接地址:<a href="https://codepen.io/team/keyframers/pen/oNYmomo">https://codepen.io/team/keyframers/pen/oNYmomo</a></p>
</div>
<h2 id="React-Photo-Gallery-With-Lightbox"><a href="#React-Photo-Gallery-With-Lightbox" class="headerlink" title="React-Photo-Gallery With Lightbox"></a>React-Photo-Gallery With Lightbox</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/23/b257c91639852.png"/></div></div> </br>
<p>You can see the results below.</p>
<iframe allowfullscreen="true" allowpaymentrequest="true" allowtransparency="true" class="cp_embed_iframe " frameborder="0" height="300" width="100%" name="cp_embed_10" scrolling="no" src="https://codepen.io/neptunian/embed/preview/Oxraod?height=300&theme-id=dark&default-tab=js%2Cresult&slug-hash=Oxraod&preview=true&user=neptunian&name=cp_embed_10" style="width: 100%; overflow:hidden; display:block;" title="CodePen Embed" loading="lazy" id="cp_embed_Oxraod"></iframe>
<div class="note blue no-icon flat"><p>链接地址:<a href="https://codepen.io/neptunian/pen/Oxraod">https://codepen.io/neptunian/pen/Oxraod</a></p>
</div>
<h2 id="react-grid-gallery"><a href="#react-grid-gallery" class="headerlink" title="react-grid-gallery"></a>react-grid-gallery</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/23/ee6334a47f195.png"/></div></div> </br>
<div class="note blue no-icon flat"><p>链接地址:<a href="https://benhowell.github.io/react-grid-gallery/">https://benhowell.github.io/react-grid-gallery/</a></p>
</div>
<h2 id="React-Photo-Gallery-1"><a href="#React-Photo-Gallery-1" class="headerlink" title="React Photo Gallery"></a>React Photo Gallery</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/23/f558196bfc2f4.png"/></div></div> </br>
<div class="note blue no-icon flat"><p>链接地址:<a href="https://neptunian.github.io/react-photo-gallery/">https://neptunian.github.io/react-photo-gallery/</a></p>
</div>
<h2 id="React-Image-Gallery"><a href="#React-Image-Gallery" class="headerlink" title="React Image Gallery"></a>React Image Gallery</h2><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/23/98022167cc702.png"/></div></div> </br>
<div class="note blue no-icon flat"><p>链接地址:<a href="https://github.com/xiaolin/react-image-gallery">https://github.com/xiaolin/react-image-gallery</a></p>
</div>
<div class="note info flat"><p>原文链接:<a href="https://us.niemvuilaptrinh.com/article/13-examples-of-react-photo-gallery-for-website">13个用于网站的React照片库示例</a></p>
</div>
<h2 id="writing-mode"><a href="#writing-mode" class="headerlink"
熟悉事件循环?那谈谈为什么会分为宏任务和微任务
https://fe32.top/articles/js0320ub/
2022-03-20T14:26:49.000Z
2023-05-17T10:01:10.000Z
<h1 id="什么是事件循环"><a href="#什么是事件循环" class="headerlink" title="什么是事件循环"></a>什么是事件循环</h1><p>在了解事件循环前,需要一些有关 JS 特性的前置知识。</p>
<p>JS 引擎是单线程的,直白来说就是一个时间点下 JS 引擎只能去做一件事情,而 Java 这种多线程语言,可以同时做几件事情。</p>
<p>JS 做的任务分为同步和异步两种,所谓 “异步”,简单说就是一个任务不是连续完成的,先执行第一段,等做好了准备,再回过头执行第二段,第二段也被叫做回调;同步则是连贯完成的。</p>
<p>像读取文件、网络请求这种任务属于异步任务:花费时间很长,但中间的操作不需要 JS 引擎自己完成,它只用等别人准备好了,把数据给他,他再继续执行回调部分。</p>
<p>如果没有特殊处理,JS 引擎在执行异步任务时,应该是存在等待的,不去做任何其他事情。用一个图来展示这个过程,可以看出,在执行异步任务时有大量的空闲时间被浪费。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/20/2864a552bef48.png"/></div></div>
<p>实际上这是大多数多线程语言的处理办法。但对于 JS 这种单线程语言来说,这种长时间的空闲等待是不可接受的:遇到其他紧急任务,Java 可以再开一个线程去处理,JS 却只能忙等。</p>
<p>所以采取了以下的“异步任务回调通知”模式:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/20/df1705e4029ee.png"/></div></div>
<p>在等待异步任务准备的同时,JS 引擎去执行其他同步任务,等到异步任务准备好了,再去执行回调。这种模式的优势显而易见,完成相同的任务,花费的时间大大减少,这种方式也被叫做非阻塞式。</p>
<p>而实现这个“通知”的,正是事件循环,把异步任务的回调部分交给事件循环,等时机合适交还给 JS 线程执行。事件循环并不是 JavaScript 首创的,它是计算机的一种运行机制。</p>
<p>事件循环是由一个队列组成的,异步任务的回调遵循先进先出,在 JS 引擎空闲时会一轮一轮地被取出,所以被叫做循环。</p>
<p>根据队列中任务的不同,分为宏任务和微任务。</p>
<h1 id="宏任务和微任务"><a href="#宏任务和微任务" class="headerlink" title="宏任务和微任务"></a>宏任务和微任务</h1><p>事件循环由宏任务和在执行宏任务期间产生的所有微任务组成。完成当下的宏任务后,会立刻执行所有在此期间入队的微任务。</p>
<p>这种设计是为了给紧急任务一个插队的机会,否则新入队的任务永远被放在队尾。区分了微任务和宏任务后,本轮循环中的微任务实际上就是在插队,这样微任务中所做的状态修改,在下一轮事件循环中也能得到同步。</p>
<p>常见的宏任务有:</p>
<ul>
<li>script(整体代码)</li>
<li>setTimout</li>
<li>setInterval</li>
<li>setImmediate(node 独有)</li>
<li>requestAnimationFrame(浏览器独有)</li>
<li>IO</li>
<li>UI render(浏览器独有)</li>
</ul>
<p>常见的微任务有:</p>
<ul>
<li>process.nextTick(node 独有)</li>
<li>Promise.then()</li>
<li>Object.observe</li>
<li>MutationObserver</li>
</ul>
<h2 id="宏任务-setTimeout-的误区"><a href="#宏任务-setTimeout-的误区" class="headerlink" title="宏任务 setTimeout 的误区"></a>宏任务 setTimeout 的误区</h2><p>setTimeout 的回调不一定在指定时间后能执行。而是在指定时间后,将回调函数放入事件循环的队列中。</p>
<p>如果时间到了,JS 引擎还在执行同步任务,这个回调函数需要等待;如果当前事件循环的队列里还有其他回调,需要等其他回调执行完。</p>
<p>另外,setTimeout 0ms 也不是立刻执行,它有一个默认最小时间,为 4ms。所以下面这段代码的输出结果不一定:</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="built_in">setTimeout</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'setTimeout'</span>)</span><br><span class="line">}, <span class="number">0</span>)</span><br><span class="line"><span class="title function_">setImmediate</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'setImmediate'</span>)</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<p>因为取出第一个宏任务之前在执行全局 Script,如果这个时间大于 4ms,这时 setTimeout 的回调函数已经放入队列,就先执行 setTimeout;如果准备时间小于 4ms,就会先执行 setImmediate。</p>
<h1 id="浏览器的事件循环"><a href="#浏览器的事件循环" class="headerlink" title="浏览器的事件循环"></a>浏览器的事件循环</h1><p>浏览器的事件循环由一个宏任务队列+多个微任务队列组成。</p>
<p>首先,执行第一个宏任务:全局 Script 脚本。产生的的宏任务和微任务进入各自的队列中。执行完 Script 后,把当前的微任务队列清空。完成一次事件循环。</p>
<p>接着再取出一个宏任务,同样把在此期间产生的回调入队。再把当前的微任务队列清空。以此往复。</p>
<p>宏任务队列只有一个,而每一个宏任务都有一个自己的微任务队列,每轮循环都是由一个宏任务+多个微任务组成。</p>
<p>下面的 Demo 展示了微任务的插队过程:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Promise</span>.<span class="title function_">resolve</span>().<span class="title function_">then</span>(<span class="function">()=></span>{</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'第一个回调函数:微任务1'</span>)</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="function">()=></span>{</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'第三个回调函数:宏任务2'</span>)</span><br><span class="line"> },<span class="number">0</span>)</span><br><span class="line">})</span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="function">()=></span>{</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'第二个回调函数:宏任务1'</span>)</span><br><span class="line"> <span class="title class_">Promise</span>.<span class="title function_">resolve</span>().<span class="title function_">then</span>(<span class="function">()=></span>{</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'第四个回调函数:微任务2'</span>)</span><br><span class="line"> })</span><br><span class="line">},<span class="number">0</span>)</span><br><span class="line"><span class="comment">// 第一个回调函数:微任务1</span></span><br><span class="line"><span class="comment">// 第二个回调函数:宏任务1</span></span><br><span class="line"><span class="comment">// 第四个回调函数:微任务2</span></span><br><span class="line"><span class="comment">// 第三个回调函数:宏任务2</span></span><br></pre></td></tr></table></figure>
<p>打印的结果不是从 1 到 4,而是先执行第四个回调函数,再执行第三个,因为它是一个微任务,比第三个回调函数有更高优先级。</p>
<h1 id="Node-的事件循环"><a href="#Node-的事件循环" class="headerlink" title="Node 的事件循环"></a>Node 的事件循环</h1><p>node 的事件循环比浏览器复杂很多。由 6 个宏任务队列+6 个微任务队列组成。</p>
<p>宏任务按照优先级从高到低依次是:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/20/da782d5259b8e.png"/></div></div>
<p>其执行规律是:在一个宏任务队列全部执行完毕后,去清空一次微任务队列,然后到下一个等级的宏任务队列,以此往复。</p>
<p>一个宏任务队列搭配一个微任务队列。六个等级的宏任务全部执行完成,才是一轮循环。</p>
<p>其中需要关注的是:Timers、Poll、Check 阶段,因为我们所写的代码大多属于这三个阶段。</p>
<ol>
<li>Timers:定时器 setTimeout/setInterval;</li>
<li>Poll :获取新的 I/O 事件, 例如操作读取文件等;</li>
<li>Check:setImmediate 回调函数在这里执行;</li>
</ol>
<p>除此之外,node 端微任务也有优先级先后:</p>
<ol>
<li>process.nextTick;</li>
<li>promise.then 等;</li>
</ol>
<p>清空微任务队列时,会先执行 process.nextTick,然后才是微任务队列中的其他。下面这段代码可以佐证浏览器和 node 的差异:</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"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Script开始'</span>)</span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'第一个回调函数,宏任务1'</span>)</span><br><span class="line"> <span class="title class_">Promise</span>.<span class="title function_">resolve</span>().<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'第四个回调函数,微任务2'</span>)</span><br><span class="line"> })</span><br><span class="line">}, <span class="number">0</span>)</span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'第二个回调函数,宏任务2'</span>)</span><br><span class="line"> <span class="title class_">Promise</span>.<span class="title function_">resolve</span>().<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'第五个回调函数,微任务3'</span>)</span><br><span class="line"> })</span><br><span class="line">}, <span class="number">0</span>)</span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">resolve</span>().<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'第三个回调函数,微任务1'</span>)</span><br><span class="line">})</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Script结束'</span>)</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></pre></td><td class="code"><pre><span class="line">node端:</span><br><span class="line"><span class="title class_">Script</span>开始</span><br><span class="line"><span class="title class_">Script</span>结束</span><br><span class="line">第三个回调函数,微任务<span class="number">1</span></span><br><span class="line">第一个回调函数,宏任务<span class="number">1</span></span><br><span class="line">第二个回调函数,宏任务<span class="number">2</span></span><br><span class="line">第四个回调函数,微任务<span class="number">2</span></span><br><span class="line">第五个回调函数,微任务<span class="number">3</span></span><br><span class="line"></span><br><span class="line">浏览器</span><br><span class="line"><span class="title class_">Script</span>开始</span><br><span class="line"><span class="title class_">Script</span>结束</span><br><span class="line">第三个回调函数,微任务<span class="number">1</span></span><br><span class="line">第一个回调函数,宏任务<span class="number">1</span></span><br><span class="line">第四个回调函数,微任务<span class="number">2</span></span><br><span class="line">第二个回调函数,宏任务<span class="number">2</span></span><br><span class="line">第五个回调函数,微任务<span class="number">3</span></span><br></pre></td></tr></table></figure>
<p>可以看出,在 node 端要等当前等级的所有宏任务完成,才能轮到微任务:<strong>第四个回调函数</strong> ,<strong>微任务2</strong> 在两个 setTimeout 完成后才打印。</p>
<p>因为浏览器执行时是一个宏任务+一个微任务队列,而 node 是一整个宏任务队列 + 一个微任务队列。</p>
<h2 id="node11-x-前后版本差异"><a href="#node11-x-前后版本差异" class="headerlink" title="node11.x 前后版本差异"></a>node11.x 前后版本差异</h2><p>node11.x 之前,其事件循环的规则就如上文所述:先取出完一整个宏任务队列中全部任务,然后执行一个微任务队列。</p>
<p>但在 11.x 之后,node 端的事件循环变得和浏览器类似:先执行一个宏任务,然后是一个微任务队列。但依然保留了宏任务队列和微任务队列的优先级。可以用下面的 Demo 佐证:</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></pre></td><td class="code"><pre><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Script开始'</span>)</span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'宏任务1(setTimeout)'</span>)</span><br><span class="line"> <span class="title class_">Promise</span>.<span class="title function_">resolve</span>().<span class="title function_">then</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'微任务promise2'</span>)</span><br><span class="line"> })</span><br><span class="line">}, <span class="number">0</span>)</span><br><span class="line"><span class="title function_">setImmediate</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'宏任务2'</span>)</span><br><span class="line">})</span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'宏任务3(setTimeout)'</span>)</span><br><span class="line">}, <span class="number">0</span>)</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Script结束'</span>)</span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">resolve</span>().<span class="title function_">then</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'微任务promise1'</span>)</span><br><span class="line">})</span><br><span class="line">process.<span class="title function_">nextTick</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'微任务nextTick'</span>)</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<p>在 node11.x 之前运行:</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="title class_">Script</span>开始</span><br><span class="line"><span class="title class_">Script</span>结束</span><br><span class="line">微任务nextTick</span><br><span class="line">微任务promise1</span><br><span class="line">宏任务<span class="number">1</span>(<span class="built_in">setTimeout</span>)</span><br><span class="line">宏任务<span class="number">3</span>(<span class="built_in">setTimeout</span>)</span><br><span class="line">微任务promise2</span><br><span class="line">宏任务<span class="number">2</span>(setImmediate)</span><br></pre></td></tr></table></figure>
<p>在 node11.x 之后运行:</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="title class_">Script</span>开始</span><br><span class="line"><span class="title class_">Script</span>结束</span><br><span class="line">微任务nextTick</span><br><span class="line">微任务promise1</span><br><span class="line">宏任务<span class="number">1</span>(<span class="built_in">setTimeout</span>)</span><br><span class="line">微任务promise2</span><br><span class="line">宏任务<span class="number">3</span>(<span class="built_in">setTimeout</span>)</span><br><span class="line">宏任务<span class="number">2</span>(setImmediate)</span><br></pre></td></tr></table></figure>
<p>可以发现,在不同的 node 环境下:</p>
<ol>
<li>微任务队列中 process.nextTick 都有更高优先级,即使它后进入微任务队列,也会先打印微任务nextTick再微任务promise1;</li>
<li>宏任务 setTimeout 比 setImmediate 优先级更高,宏任务2(setImmediate)是三个宏任务中最后打印的;</li>
<li>在 node11.x 之前,微任务队列要等当前优先级的所有宏任务先执行完,在两个 setTimeout 之后才打印微任务promise2;在 node11.x 之后,微任务队列只用等当前这一个宏任务先执行完。</li>
</ol>
<h1 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h1><p>事件循环中的任务被分为宏任务和微任务,是为了给高优先级任务一个插队的机会:微任务比宏任务有更高优先级。</p>
<p>node 端的事件循环比浏览器更复杂,它的宏任务分为六个优先级,微任务分为两个优先级。node 端的执行规律是一个宏任务队列搭配一个微任务队列,而浏览器是一个单独的宏任务搭配一个微任务队列。但是在 node11 之后,node 和浏览器的规律趋同。</p>
<div class="note info flat"><p>原文链接:<a
再见收费的Navicat,操作所有数据库就靠它了
https://fe32.top/articles/db0307ver/
2022-03-07T12:39:22.000Z
2023-05-17T10:01:12.000Z
<p>作为一名开发者,免不了要和数据库打交道,于是我们就需要一款顺手的数据库管理工具。</p>
<p>今天跟大家推荐一款免费的,功能和 Navicat 有一拼的数据库管理工具 DBeaver,试用完后体验真心不错,于是就来给大家安利一波。</p>
<h2 id="关于-DBeaver"><a href="#关于-DBeaver" class="headerlink" title="关于 DBeaver"></a>关于 DBeaver</h2><p>DBeaver 是一个跨平台的数据库管理工具,支持 Windows、Linux 和 macOS。它有两个版本,企业版和社区版,对于个人开发者来说,社区版的功能已经足够强大。</p>
<p>DBeaver 是由 Java 编写的,默认使用 JDK 11 进行编译。社区版基于 Apache-2.0 License 在 GitHub 上开源,目前已获得 24k+ 的星标。</p>
<blockquote>
<p><a href="https://github.com/dbeaver/dbeaver">https://github.com/dbeaver/dbeaver</a></p>
</blockquote>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/be2809a5394b3.png"/></div></div>
<p>DBeaver 支持几乎所有主流的数据库,包括关系型数据库和非关系数据库。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/310901a6834b7.png"/></div></div>
<h2 id="安装-DBeaver"><a href="#安装-DBeaver" class="headerlink" title="安装 DBeaver"></a>安装 DBeaver</h2><p>可以通过 DBeaver 官方下载安装包,也可以通过 GitHub 下载 release 版本。</p>
<div class="note info flat"><p><a href="https://dbeaver.io/download/">官方下载地址:https://dbeaver.io/download/</a></p>
</div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/2a0a6983e1602.png"/></div></div>
<p>根据自己电脑的操作系统下载对应的安装包,完整安装后,第一步要做的是配置 Maven 镜像,否则在后续下载数据库驱动的时候会非常的慢。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/e36114a9cb374.png"/></div></div>
<p>因为 DBeaver 是基于 <a href="https://github.com/itwanger/toBeBetterJavaer/blob/master/docs/maven/maven.md">Maven</a> 构建的,数据库驱动也就是链接数据库的 JDBC 驱动是通过 Maven 仓库下载的。选择「首选项」→「Maven」,添加阿里云镜像地址:</p>
<blockquote>
<p><a href="http://maven.aliyun.com/nexus/content/groups/public">http://maven.aliyun.com/nexus/content/groups/public</a></p>
</blockquote>
<p>和配置 Maven 镜像一样,如下图所示。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/34eb85c0b26ab.png"/></div></div>
<p>配置完成后,记得把阿里云镜像仓库置顶。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/ff2f54d468675.png"/></div></div>
<h2 id="管理数据源"><a href="#管理数据源" class="headerlink" title="管理数据源"></a>管理数据源</h2><p>像使用 Navicat 一样,我们需要先建立连接,这里就以 MySQL 为例。点击「连接」小图标,选择数据库。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/822d5a9661036.png"/></div></div>
<p>点击下一步,这时候需要填写数据库连接信息。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/3ce1be99c4a4b.png"/></div></div>
<p>点击「测试链接」,如果使用默认的 Maven 仓库时,下载驱动会非常慢,如下图所示,还容易失败「踩过的坑就不要再踩了」。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/b17a7d96b3ad9.png"/></div></div>
<p>如果你前面按照我说的配置了阿里云的 Maven 镜像,程序就不一样了,点了「测试链接」,瞬间会弹出「连接已成功」的提示框。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/e391bbd5b0037.png"/></div></div>
<p>链接成功后,就可以看到数据库中的表啊、视图啊、索引啊等等。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/dad8eb8a62a1b.png"/></div></div>
<h2 id="管理表"><a href="#管理表" class="headerlink" title="管理表"></a>管理表</h2><p>数据库连接成功后,最重要的还是操作表。</p>
<h3 id="查看表"><a href="#查看表" class="headerlink" title="查看表"></a>查看表</h3><p>选择一张表,双击后就可以看到表的属性了,可以查看表的列、约束(主键)、外键、索引等等信息。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/b26884c9e4242.png"/></div></div>
<p>点击「DDL(Data Definition Language,数据定义语言)」可以看到详细的建表语句。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/c03b4e755b99d.png"/></div></div>
<p>点击「数据」可以查看表的数据,底部有「新增」、「修改」、「删除」等行操作按钮。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/22cbed2ca17fb.png"/></div></div>
<p>可以在顶部的过滤框中填写筛选条件,然后直接查询结果。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/1ed21ab7c94bd.png"/></div></div>
<p>如果不想显示某一列的话,可以直接点击「自定义结果集」图表,将某个字段的状态设置为不可见即可。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/a198800c94a92.png"/></div></div>
<h3 id="新增表"><a href="#新增表" class="headerlink" title="新增表"></a>新增表</h3><p>在左侧选择「表」,然后右键选择「新建表」即可建表id。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/fffab991896f1.png"/></div></div>
<p>之后在右侧列的区域右键,选择「新建列」即可添加字段。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/21e349625e609.png"/></div></div>
<p>比如说我们新建一个主键 ID,如下图所示。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/71462f0754318.png"/></div></div>
<p>在 DBeaver 中,[v] 表示真,[] 表示否。紧接着在「约束」里选择 ID 将其设置为主键。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/b1e92430b2792.png"/></div></div>
<p>最后点击保存,会弹出一个建表语句的预览框,点击「执行」即可完成表的创建。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/35400fef586a7.png"/></div></div>
<h2 id="执行-SQL"><a href="#执行-SQL" class="headerlink" title="执行 SQL"></a>执行 SQL</h2><p>右键数据库表,选择右键菜单中的「SQL 编辑器」可以打开 SQL 编辑面板。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/ffd437fe81012.png"/></div></div>
<p>然后编辑 SQL 语句,点击运行的小图标就可以查询数据了。这个过程会有语法提示,非常 nice。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/9ccfb525c91c4.png"/></div></div>
<p>DBeaver 有一个很亮眼的操作就是,可以直接选中一条结果集,然后右键生成 SQL。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/63476632ed924.png"/></div></div>
<p>比如说 insert 语句,这样再插入一条重复性内容的时候就非常方便了。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/a7fbbd96b841c.png"/></div></div>
<h2 id="外观配置"><a href="#外观配置" class="headerlink" title="外观配置"></a>外观配置</h2><p>可以在首选项里对外观进行设置,比如说把主题修改为暗黑色。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/a8253c95bab91.png"/></div></div>
<p>然后界面就变成了暗黑系。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/80e9824bc3aaa.png"/></div></div>
<p>还可以设置字体大小等。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/ba3cbdf5af5ae.png"/></div></div>
<p>从整体的风格来看,DBeaver 和 Eclipse 有些类似,事实上也的确如此,DBeaver 是基于 Eclipse 平台构建的。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/07/f81150d8f9f7d.png"/></div></div>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>总体来说,DBeaver是一款非常优秀的开源数据库管理工具了,功能很全面,日常的开发基本上是够用了。对比收费的 Navicat 和 DataGrip,可以说非常良心了。大家如果遇到收费版不能使用的时候,可以来体验一下社区版 DBeaver。</p>
<div class="note info flat"><p>原文链接:<a
C#、TypeScript之父Anders Hejlsberg:“会用Excel就是程序员”
https://fe32.top/articles/in168cz8/
2022-03-03T12:21:22.000Z
2024-01-28T12:14:48.000Z
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/03/8f606c7d2caa6.png"/></div></div>
<br>
<p>作为Turbo Pascal、Delphi、C#和TypeScript等编程语言的创造者,Anders Hejlsberg是世界上最重要的计算机程序员之一。在构建微软.NET等开发环境方面极具影响力,Hejlsberg影响了数以百万计的软件开发人员。近日,Hejlsberg与TNS新闻编辑Darryl K.Taft进行了一场线上对话,围绕微软低代码/无代码、Web3等话题,分享了他对许多与软件开发相关的事情的看法。</p>
<h2 id="Excel是一种低代码-x2F-无代码工具"><a href="#Excel是一种低代码-x2F-无代码工具" class="headerlink" title="Excel是一种低代码/无代码工具"></a>Excel是一种低代码/无代码工具</h2><p>问:您对于低代码,无代码有什么看法?</p>
<p><strong>Anders Hejlsberg</strong>:我认为这个领域的潜力巨大。我们常会忽略,其实我们已经拥有了世界上最好的编程语言之一,或许这取决于你如何定义Excel中编程语言。可以说Excel是一种低代码/无代码的程序编写方式,人们运行程序,而他们使用的电子表格就是程序。你可以做一些事情,输入一些东西,然后运行程序,程序会在电子表格的底层运行,并且给出结果。</p>
<p>所以我认为挑战其实一直在于这些工具。当达到低代码环境的极限时,你需要找到设置合适的逃生舱口的方法,才不会陷入困境。而且这个逃生舱口,可以转移到一个更完整或完整的编程环境,这意味着有意义的可扩展性模型是必须的。</p>
<p>因此,我们需要思考的事情还有很多,但这些事情往往不是最重要的,因为人们往往会转而关注绘制方框和线条,以及这些示例的演示效果。但我常常很好奇,这些工具是否能适应现实世界?很多都不能,但有些可以。这是有趣的地方。</p>
<p>问:我的意思是,整体的主旨是努力让更多人进入开发的世界。其中一部分人想要走的更远,成为专业人士,或者至少更加熟练。</p>
<p><strong>Anders Hejlsberg</strong>:是的。如果你去问所有Excel用户,他们都不会自称程序员。但是,从某种意义上来说,他们是。</p>
<p>问:没错,我比较喜欢把这比作 “<strong>Blue Apron</strong>” 和 “<strong>HelloFresh</strong>” 这样的套餐服务——它们会让你开始学习烹饪,并且自己动手,做得更好。你认为在软件开发中下一个要克服的挑战是什么?</p>
<p><strong>Anders Hejlsberg</strong>:在我们编码和机器学习如何帮助我们之间,仍然有非常大的差距。因为人类大脑以及神经网络的工作方式,从来没有真正关于0和1以及绝对正确的答案,而都是统计和模糊逻辑。然而,所有的编程语言都牢固地扎根于逻辑和可证明性等等。这两种世界观之间存在着巨大的差距。看看我们最终如何弥合这一差距的(如果说我们最终做到了的话)。</p>
<h2 id="软件开发中的安全性"><a href="#软件开发中的安全性" class="headerlink" title="软件开发中的安全性"></a>软件开发中的安全性</h2><p>问:下面我们来谈谈安全问题。在软件开发中,安全性和整个shift left的作用有多重要?</p>
<p><strong>Anders Hejlsberg</strong>:这取决于你在堆栈中的位置。对于编程语言来说,安全性非常重要,起码这对我的工作影响很大,甚至可以说类型检查器是一种安全形式。这是软件中可利用的漏洞,所以从这个意义上来说,编程语言间接地考虑了安全性。</p>
<p>创造出可以分析代码并且指出可能存在安全漏洞的工具,是我们看待安全性的另一种方式。但很显然,这是个现实问题。世界各地也有人每天上班,他们的工作就是黑入西方国家的公司,从而获得薪水。这细思极恐。有很多人靠滥用科技谋生。虽然这一点令人难过,但这就是现实。</p>
<h2 id="Web3"><a href="#Web3" class="headerlink" title="Web3"></a>Web3</h2><p>问:您对Web3有什么看法?您认为是否需要新的工具进行Web3的开发呢?</p>
<p><strong>Anders Hejlsberg</strong>:首先,我们要对Web3的概念达成共识,因为人们对此总是各持己见。我确实比较喜欢创造者概念,以及创造数字资产并获得奖励的能力。但是,在这个领域也有很多骗子、挂羊头卖狗肉的人和急功近利的人。而且还会造成大量的环境污染,虽然可以将其归因于一些正在进行的采矿活动和能源的使用,所以这是一个多样的世界。我们将看到这一切的结果。因此,我喜欢它的某些部分,也反对它的某些部分。</p>
<h2 id="潜力巨大的项目:GitHub构建的Copilot"><a href="#潜力巨大的项目:GitHub构建的Copilot" class="headerlink" title="潜力巨大的项目:GitHub构建的Copilot"></a>潜力巨大的项目:GitHub构建的Copilot</h2><p>问:我知道您在微软需要做的事情很多,但在微软以外,有没有什么很棒的项目或者工作吸引您的关注?</p>
<p><strong>Anders Hejlsberg</strong>:我认为微软肯定有一些非常有趣的项目。比如开发者工具、人工智能、机器学习,还有很多其他有意思的事。不知道你是否了解GitHub正在构建的Copilot的项目,在世界上所有开源代码上训练一个机器学习网络,然后让它给出答案,是不是很棒?当然,这仍然有点像西大荒,因为有各种机遇和不可掌控的因素存在,以及知识产权等未解决问题,但它展现出巨大的潜力。所以我认为这很吸引人,我正在密切关注这个问题。</p>
<p>问:我认为这是2021年的最好的编程项目,涉及到相当大的领域。</p>
<p><strong>Anders Hejlsberg</strong>:是的,这个项目确实很吸引人。尤其是作为一名程序员,它在很大程度上让人洞察到我们谈论的是什么样的智能系统。但从某种意义上说,用智能这个词有点不恰当,如果有人以前写过这段代码,它就可以快速地从机器模型聚合内存中快速查找到,这就相当了不起。但是,如果以前没有人写过,它必须要思考解决方案,结果可能出人意料,所以在这方面还有很多需要思考的地方。但这是一个了不起的工具,因为它把再利用的概念提升到了一个新的水平,让我们不用再重复造轮子。</p>
<div class="note info flat"><p>原文链接:<a href="https://thenewstack.io/microsofts-programming-language-czar-anders-hejlsberg-the-interview">https://thenewstack.io/microsofts-programming-language-czar-anders-hejlsberg-the-interview</a><br>本文为CSDN翻译:<a
SQL编程思想:一切皆关系
https://fe32.top/articles/sql228ba/
2022-03-03T09:24:34.000Z
2023-05-17T10:01:12.000Z
<p>在计算机领域有许多伟大的设计理念和思想,例如:</p>
<ul>
<li>在 Unix 中,一切皆文件。</li>
<li>在面向对象的编程语言中,一切皆对象。</li>
</ul>
<p>关系数据库同样也有自己的设计思想:<strong>在 SQL 中,一切皆关系</strong>。</p>
<h2 id="关系模型"><a href="#关系模型" class="headerlink" title="关系模型"></a>关系模型</h2><p>关系模型(Relational model)由 E.F.Codd 博士于 1970 年提出,以集合论中的关系概念为基础;无论是现实世界中的实体对象还是它们之间的联系都使用关系表示。我们在数据库系统中看到的关系就是二维表(Table),由行(Row)和列(Column)组成。因此,也可以说关系表是由数据行构成的集合。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/03/7d6fa0f3b87a3.png"/></div></div>
<p>关系模型由<strong>数据结构</strong>、<strong>关系操作</strong>、<strong>完整性约束</strong> 三部分组成。</p>
<ul>
<li>关系模型中的数据结构就是关系表,包括 <strong>基础表</strong>、<strong>派生表</strong>(查询结果)和 <strong>虚拟表</strong>(视图)。</li>
<li>常用的关系操作包括 <strong>增加</strong>、<strong>删除</strong>、<strong>修改</strong> 和 <strong>查询</strong>(CRUD),使用的就是 SQL 语言。其中查询操作最为复杂,包括选择(Selection)、投影(Projection)、并集(Union)、交集(Intersection)、差集(Exception)以及笛卡儿积(Cartesian product)等。</li>
<li>完整性约束用于维护数据的完整性或者满足业务约束的需求,包括<strong>实体完整性</strong>(主键约束)、<strong>参照完整性</strong>(外键约束)以及<strong>用户定义的完整性</strong>(非空约束、唯一约束、检查约束和默认值)。</li>
</ul>
<p>我们今天的主题是关系操作语言,也就是 <code>SQL</code>。本文使用的示例数据来源于<a href="https://blog.csdn.net/horses/article/details/86518676">这篇文章</a>。</p>
<h2 id="面向集合"><a href="#面向集合" class="headerlink" title="面向集合"></a>面向集合</h2><p>SQL(结构化查询语言)是操作关系数据库的标准语言。SQL 非常接近英语,使用起来非常简单。它在设计之初就考虑了非技术人员的使用需求,我们通常只需说明想要的结果(What),而将数据处理的过程(How)交给数据库管理系统。所以说,SQL 才是真正给人用的编程语言!😎</p>
<p>接下来我们具体分析一下关系的各种操作语句;目的是为了让大家能够了解 <strong>SQL 是一种面向集合的编程语言,它的操作对象是集合,操作的结果也是集合</strong>。</p>
<blockquote>
<p>📝在关系数据库中,关系、表、集合三者通常表示相同的概念。</p>
</blockquote>
<p>下面是一个简单的查询语句:</p>
<figure class="highlight sql"><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">SELECT</span> employee_id, first_name, last_name, hire_date</span><br><span class="line"> <span class="keyword">FROM</span> employees;</span><br></pre></td></tr></table></figure>
<p>它的作用就是从 employees 表中查询员工信息。显然,我们都知道 FROM 之后是一个表(关系、集合)。不仅如此,整个查询语句的结果也是一个表。所以,我们可以将上面的查询作为表使用:</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span></span><br><span class="line"> <span class="keyword">FROM</span> (<span class="keyword">SELECT</span> employee_id, first_name, last_name, hire_date</span><br><span class="line"> <span class="keyword">FROM</span> employees) t;</span><br></pre></td></tr></table></figure>
<p>括号内的查询语句被称为派生表,我们给它指定了一个别名叫做 t。同样,整个查询结果也是一个表;这就意味着我们可以继续嵌套,虽然这么做很无聊。</p>
<p>我们再看一个 PostgreSQL 中的示例:</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- PostgreSQL</span></span><br><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span></span><br><span class="line"> <span class="keyword">FROM</span> <span class="built_in">upper</span>(<span class="string">'sql'</span>);</span><br><span class="line"><span class="operator">|</span> upper <span class="operator">|</span></span><br><span class="line"><span class="operator">|</span><span class="comment">-------|</span></span><br><span class="line"><span class="operator">|</span> <span class="keyword">SQL</span> <span class="operator">|</span></span><br></pre></td></tr></table></figure>
<p>upper() 是一个大写转换的函数。它出现再 FROM 子句中,意味着它的结果也是一个表,只不过是 1 行 1 列的特殊表。</p>
<p>SELECT 子句用于指定需要查询的字段,可以包含表达式、函数值等。SELECT 在关系操作中被称为投影(Projection),看下面的示意图应该就比较好理解了。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/03/872a1138bb744.png"/></div></div>
<p>除了 SELECT 之外,还有一些常用的 SQL 子句。</p>
<p>WHERE 用于指定数据过滤的条件,在关系运算中被称为选择(Selection),示意图如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/03/06a04e23529e3.png"/></div></div>
<p>ORDER BY 用于对查询的结果进行排序,示意图如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/03/96d1b82a6555a.png"/></div></div>
<p>总之,SQL 可以完成各种数据操作,例如过滤、分组、排序、限定数量等;所有这些操作的对象都是关系表,结果也是关系表。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/03/62691f71dc7bd.png"/></div></div>
<p>在这些关系操作中,有一个比较特殊,就是分组。</p>
<h2 id="GROUP-BY"><a href="#GROUP-BY" class="headerlink" title="GROUP BY"></a>GROUP BY</h2><p>分组( GROUP BY)操作和其他的关系操作不同,因为它改变了关系的结构。来看下面的示例:</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> department_id, <span class="built_in">count</span>(<span class="operator">*</span>), first_name</span><br><span class="line"> <span class="keyword">FROM</span> employees</span><br><span class="line"> <span class="keyword">GROUP</span> <span class="keyword">BY</span> department_id;</span><br></pre></td></tr></table></figure>
<p>该语句的目的是按照部门统计员工的数量,但是存在一个语法错误,就是 first_name 不能出现在查询列表中。原因在于按照部门进行分组的话,每个部门包含多个员工;无法确定需要显示哪个员工的姓名,这是一个逻辑上的错误。</p>
<p>所以说,GROUP BY 改变了集合元素(数据行)的结构,创建了一个全新的关系。分组操作的示意图如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/03/11c157ae1550d.png"/></div></div>
<p>尽管如此,GROUP BY 的结果仍然是一个集合。</p>
<h2 id="UNION"><a href="#UNION" class="headerlink" title="UNION"></a>UNION</h2><p>SQL 面向集合特性最明显的体现就是 <strong>UNION</strong>(并集运算)、<strong>INTERSECT</strong>(交集运算)和 <strong>EXCEPT/MINUS</strong>(差集运算)。</p>
<p>这些集合运算符的作用都是将两个集合并成一个集合,因此需要满足以下条件:</p>
<ul>
<li>两边的集合中字段的数量和顺序必须相同;</li>
<li>两边的集合中对应字段的类型必须匹配或兼容。</li>
</ul>
<p>具体来说,UNION 和 UNION ALL 用于计算两个集合的并集,返回出现在第一个查询结果或者第二个查询结果中的数据。它们的区别在于 UNION 排除了结果中的重复数据,UNION ALL 保留了重复数据。下面是 UNION 操作的示意图:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/03/8d6cfd5b642f7.png"/></div></div>
<p>INTERSECT 操作符用于返回两个集合中的共同部分,即同时出现在第一个查询结果和第二个查询结果中的数据,并且排除了结果中的重复数据。INTERSECT 运算的示意图如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/03/5527333e9a514.png"/></div></div>
<p>EXCEPT 或者 MINUS 操作符用于返回两个集合的差集,即出现在第一个查询结果中,但不在第二个查询结果中的记录,并且排除了结果中的重复数据。EXCEPT 运算符的示意图如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/03/38f2c72b0fc15.png"/></div></div>
<p>除此之外,DISTINCT 运算符用于消除重复数据,也就是排除集合中的重复元素。</p>
<blockquote>
<p>📝SQL 中的关系概念来自数学中的集合理论,因此 UNION、INTERSECT 和 EXCEPT 分别来自集合论中的并集(∪ \cup∪)、交集(∩ \cap∩)和差集(∖ \setminus∖)运算。需要注意的是,集合理论中的集合不允许存在重复的数据,但是 SQL 允许。因此,SQL 中的集合也被称为多重集合(multiset);多重集合与集合理论中的集合都是无序的,但是 SQL 可以通过 ORDER BY 子句对查询结果进行排序。</p>
</blockquote>
<h2 id="JOIN"><a href="#JOIN" class="headerlink" title="JOIN"></a>JOIN</h2><p>在 SQL 中,不仅实体对象存储在关系表中,对象之间的联系也存储在关系表中。因此,当我们想要获取这些相关的数据时,需要使用到另一个操作:连接查询(JOIN)。</p>
<p>常见的 SQL连接查类型包括<strong>内连接</strong>、<strong>外连接</strong>、<strong>交叉连接</strong>等。其中,外连接又可以分为<strong>左外连接</strong>、<strong>右外连接</strong>以及<strong>全外连接</strong>。</p>
<p>内连接(Inner Join)返回两个表中满足连接条件的数据,内连接的原理如下图所示:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/03/affec83915ff5.png"/></div></div>
<p>左外连接(Left Outer Join)返回左表中所有的数据;对于右表,返回满足连接条件的数据;如果没有就返回空值。左外连接的原理如下图所示:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/03/ee3cf568b8329.png"/></div></div>
<p>右外连接(Right Outer Join)返回右表中所有的数据;对于左表,返回满足连接条件的数据,如果没有就返回空值。右外连接与左外连接可以互换,以下两者等价:</p>
<figure class="highlight sql"><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">t1 <span class="keyword">RIGHT</span> <span class="keyword">JOIN</span> t2</span><br><span class="line">t2 <span class="keyword">LEFT</span> <span class="keyword">JOIN</span> t1</span><br></pre></td></tr></table></figure>
<p>全外连接(Full Outer Join)等价于左外连接加上右外连接,同时返回左表和右表中所有的数据;对于两个表中不满足连接条件的数据返回空值。全外连接的原理如下图所示:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/03/c7ca1e4c0c452.png"/></div></div>
<p>交叉连接也称为笛卡尔积(Cartesian Product)。两个表的交叉连接相当于一个表的所有行和另一个表的所有行两两组合,结果的数量为两个表的行数相乘。交叉连接的原理如下图所示:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/03/45ac03b347bf7.png"/></div></div>
<blockquote>
<p>📝其他类型的连接还有半连接(SEMI JOIN)、反连接(ANTI JOIN)。</p>
</blockquote>
<p>集合操作将两个集合合并成一个更大或更小的集合;连接查询将两个集合转换成一个更大或更小的集合,同时获得了一个更大的元素(更多的列)。很多时候集合操作都可以通过连接查询来实现,例如:</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> department_id</span><br><span class="line"> <span class="keyword">FROM</span> departments</span><br><span class="line"> <span class="keyword">UNION</span></span><br><span class="line"><span class="keyword">SELECT</span> department_id</span><br><span class="line"> <span class="keyword">FROM</span> employees;</span><br></pre></td></tr></table></figure>
<p>等价于:</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="built_in">COALESCE</span>(d.department_id, e.department_id)</span><br><span class="line"> <span class="keyword">FROM</span> departments d</span><br><span class="line"> <span class="keyword">FULL</span> <span class="keyword">JOIN</span> employees e <span class="keyword">ON</span> (e.department_id <span class="operator">=</span> d.department_id);</span><br></pre></td></tr></table></figure>
<p>我们已经介绍了许多查询的示例,接下来看看其他的数据操作。</p>
<h2 id="DML"><a href="#DML" class="headerlink" title="DML"></a>DML</h2><p>DML 表示数据操作语言,也就是插入、更新和删除。以下是一个插入语句示例:</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> test(id <span class="type">int</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">-- MySQL、SQL Server 等</span></span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> test(id) <span class="keyword">VALUES</span> (<span class="number">1</span>),(<span class="number">2</span>),(<span class="number">3</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">-- Oracle</span></span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> test(id)</span><br><span class="line">(<span class="keyword">SELECT</span> <span class="number">1</span> <span class="keyword">AS</span> id <span class="keyword">FROM</span> DUAL</span><br><span class="line"><span class="keyword">UNION</span> <span class="keyword">ALL</span></span><br><span class="line"><span class="keyword">SELECT</span> <span class="number">2</span> <span class="keyword">FROM</span> DUAL</span><br><span class="line"><span class="keyword">UNION</span> <span class="keyword">ALL</span></span><br><span class="line"><span class="keyword">SELECT</span> <span class="number">3</span> <span class="keyword">FROM</span> DUAL);</span><br></pre></td></tr></table></figure>
<p>我们通过一个 INSERT 语句插入了 3 条记录,或者说是插入了一个包含 3 条记录的关系表。因为,UNION ALL 返回的是一个关系表。VALUES 同样是指定了一个关系表,在 <code>SQL Server</code> 和 <code>PostgreSQL</code> 中支持以下语句:</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span></span><br><span class="line"><span class="keyword">FROM</span> (</span><br><span class="line"> <span class="keyword">VALUES</span>(<span class="number">1</span>),(<span class="number">2</span>),(<span class="number">3</span>)</span><br><span class="line">) test(id);</span><br></pre></td></tr></table></figure>
<p>前面我们已经说过,FROM 之后是一个关系表,所以这里的 VALUES 也是一样。由于我们经常插入单条记录,并没有意识到实际上是以表为单位进行操作。</p>
<p>同样,UPDATE 和 DELETE 语句也都是以关系表为单位的操作;只不过我们习惯了说更新一行数据或者删除几条记录。</p>
<div class="note info flat"><p>原文链接:<a href="https://blog.csdn.net/horses/article/details/104553075">不剪发的Tony老师 -
为什么魂斗罗只有128KB却可以实现那么长的剧情?
https://fe32.top/articles/contra128/
2022-02-19T10:34:13.000Z
2023-07-16T09:45:50.000Z
<p>今天跟大家分享一下为什么魂斗罗只有 128KB, 却可以实现那么长的剧情?</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/02/19/71f4b68b4545c.webp"/></div></div>
<br>
<p>现代程序员 A 和 1980 年代游戏程序员 B 的对话:</p>
<p>A:为什么你用 128KB 能实现这么多画面、音乐、动画?</p>
<p>B:128KB 还不够么?其实为了表现力已经相当奢侈了,加了很多不重要的细节。</p>
<p>A:就说你们的音乐,这个音乐,我压到最低码率的 mp3,也得至少 1MB 吧。</p>
<p>B:你怎么压的?一首背景音乐怎么可能超过 1KB。</p>
<p>A:那你实现全屏卷轴,用了多少显存?</p>
<p>B:一共就只有 2KB 显存,多了也放不下啊。</p>
<p>A:……</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/02/19/b2b185024780e.gif"/></div></div>
<h2 id="我们对“数据量”无法直观认识"><a href="#我们对“数据量”无法直观认识" class="headerlink" title="我们对“数据量”无法直观认识"></a>我们对“数据量”无法直观认识</h2><p>除非是专家,一般人根本无法估算到底多大算大,多小算小。一般人对“数据量”并没什么概念。</p>
<p>一篇 800 字的作文有多少数据量?按照 GBK 编码,约 1.6KB,按照 UTF-8 编码,则是 2.4KB。只写了 1 个字的作文,按理来说 1~3 字节就够了。但只写 1 个字的 word 文档,有 10956 字节。而由于硬盘格式化要求,再多占用 1332 字节。我就写了一个字,真的什么都没干。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/02/19/dc31f05aea8a8.webp"/></div></div>
<br>
<p>现实中常见的产品、流行的技术,实际上和时代背景密切相关。</p>
<p>当你抱着 15 寸笔记本还嫌小的时候,1990 年代初的家庭,可是一家人围着 14~18 寸的球面电视看的。把雪碧拿给古代人喝一口,估计他会齁得要死,必须喝点水压压惊。</p>
<p>当物质基础变得十分丰富的时候,一定会产生无法避免的“浪费”,这种“浪费”会进一步改变人感受的阈值,对度量的估计都变得紊乱了。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/02/19/e1df078bc5cd1.webp"/></div></div>
<h2 id="FC时代的图形技术"><a href="#FC时代的图形技术" class="headerlink" title="FC时代的图形技术"></a>FC时代的图形技术</h2><p>由于早期的记忆芯片(ROM)非常贵,而且大容量磁盘的技术也不成熟,所以暂且不论硬件计算能力,仅仅是想增加游戏的总容量也非常困难。所以自然会使用符合当时水平的数据结构。</p>
<p>以红白机 FC 为例,它的分辨率为 256x240。分辨率不算低,但却只有 2KB 显存,而且还要实现全屏卷轴效果。所以在 FC 设计之初,从硬件上就提供了充分利用显存的方法——使用 Tile(瓦片)。</p>
<p>对每一个场景来说,使用若干数量的瓦片,场景用有限的瓦片拼接即可。这种“二级”表示方法能极大节约存储量。</p>
<p>具体一些原理讲解可以看一些科普,比如这个:<a href="https://www.bilibili.com/video/BV19J411e763">https://www.bilibili.com/video/BV19J411e763</a></p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/02/19/6c9f69a761d8c.webp"/></div></div>
<h2 id="音频容量和代码容量"><a href="#音频容量和代码容量" class="headerlink" title="音频容量和代码容量"></a>音频容量和代码容量</h2><p>现代音乐格式往往直接保存声道的波形,这种做法保真度高、通用性强,但很显然占用空间多,一首曲子的容量以千字节、兆字节计算。</p>
<p>而八位芯片时代的音频解决方案,关键是一颗专用芯片,例如 FC 用的理光 2A03:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/02/19/6639096679711.webp"/></div></div>
<p>音频芯片可以产生合成音效,能提供的音色可以在一定程度上配置,但非常有限。听听 FC 游戏的音乐可以体会到常用的音色几乎一样。</p>
<p>我觉得这个音频芯片最厉害的地方是可以同时播放几个音轨(但不能是和弦那种“同时”),《魂斗罗》、《沙罗曼蛇》、《忍者龙剑传》的殿堂级音乐,主要是靠多个音轨的交替配合实现的。</p>
<p>每个音符只要记录音色、频率和音高就足够了,音频芯片自然会识别出来。把音符按时间排列好就是“乐谱”了,可以简单理解为“简谱”。</p>
<p>这种简谱需要的数据量十分有限,而且大部分游戏音乐都是循环播放,数据量更是小的可怜。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/02/19/c93b491e4e1fc.gif"/></div></div>
<p>代码也是类似的。</p>
<p>FC 时代的游戏,没有所谓的“引擎层”,或者说引擎层就是“硬件层”。任天堂的主机完全是为游戏而设计的,瓦片、调色板、音乐、音效等基本功能已经预先考虑到了,这样一来就节约了大量底层代码。</p>
<p>程序员要仔细研究文档,在硬件框架下思考问题,比如如何显示图片、如何卷动屏幕等等;而且还要非常熟悉硬件底层和汇编,不要浪费代码空间。</p>
<p>一来二去,代码也能写的非常小。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/02/19/cff6a1bc9bf3d.webp"/></div></div>
<br>
<div class="note info flat"><p>原文链接:<a
6 Fun Things You Can Do With Just CSS
https://fe32.top/articles/c6func118/
2022-01-18T13:21:48.000Z
2023-05-17T10:01:12.000Z
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/01/19/54a3fde280684.jpg"/></div></div></br>
<p>Love it or hate it, CSS(Cascading Style Sheets) is the language we use to style the HTML elements.</p>
<p>While there are some fabulous preprocessors like SCSS(that get compiled into CSS eventually) and offer more features than normal CSS, this does not mean that you can’t do amazing things by just using plain, standard CSS.</p>
<p><a href="https://css-tricks.com/how-many-css-properties-are-there/#:~:text=520%20distinct%20property%20names%20from,reports%20and%2066%20editors'%20drafts.">CSS has 520 distinct</a> and while you may not need to know more than 30 or 50 properties, exploring the uncommon CSS properties can surely help you achieve more with just your stylesheets.</p>
<p>Below are 6 amazing things you can do with just CSS:</p>
<h2 id="Create-Portraits-amp-Add-Effects"><a href="#Create-Portraits-amp-Add-Effects" class="headerlink" title="Create Portraits & Add Effects"></a>Create Portraits & Add Effects</h2><p>CSS allows you to easily add some eye-catching filters and effects to your photos, without you even touching JavaScript.</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/01/19/849b5df57b5ea.png"/></div></div>
<p>The above effect is created just by using the <code>background-clip</code> property of CSS.</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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="selector-tag">p</span>{</span><br><span class="line"> <span class="attribute">background-size</span>: <span class="number">70vh</span>;</span><br><span class="line"> <span class="attribute">background-position</span>: center;</span><br><span class="line"> <span class="attribute">line-height</span>: <span class="number">16px</span>;</span><br><span class="line"> <span class="attribute">background</span>: <span class="built_in">url</span>(<span class="string">"photo.png"</span>);</span><br><span class="line"> -webkit-<span class="attribute">background-clip</span>: text;</span><br><span class="line"> <span class="attribute">background-attachment</span>: fixed;</span><br><span class="line"> <span class="attribute">background-clip</span>:text;</span><br><span class="line"> <span class="attribute">background-repeat</span>: no-repeat;</span><br><span class="line"> -webkit-text-fill-<span class="attribute">color</span>: <span class="built_in">rgba</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">0</span>);</span><br><span class="line">}</span><br><span class="line"><span class="selector-tag">body</span> {</span><br><span class="line"> <span class="attribute">background</span>: black;</span><br><span class="line"> <span class="attribute">overflow</span>: hidden;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>While my <code>p</code> tag contains some text.<br>You can also use <code>mix-blend-mode</code> which determines how content should blend with the content of the element’s parent and the element’s background.</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/01/19/9a176839c87c9.gif"/></div></div>
<h2 id="Linearly-Scale-Text-In-Accordance-With-Viewport"><a href="#Linearly-Scale-Text-In-Accordance-With-Viewport" class="headerlink" title="Linearly Scale Text In Accordance With Viewport"></a>Linearly Scale Text In Accordance With Viewport</h2><p>Making your website respond to change in the viewport can be a cumbersome process that often involves rewriting parts of CSS, and testing multiple breakpoints.</p>
<p>But it does not have to be so complicated and tiresome always.</p>
<p>Using just one line of CSS, you can scale text between a minimum and maximum sizes as the viewport increases or decreases.</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/01/19/1be98a74a1feb.gif"/></div></div>
<p>This is done via the <code>clamp</code> property.</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">clamp(minimum, preferred, maximum);</span><br></pre></td></tr></table></figure>
<p>The values are pretty self-explanatory. The <code>clamp</code> property returns a value that is between the minimum and the maximum values. The middle argument, i.e. “preferred” is a percentage of the viewport.</p>
<p>Let’s see this in practice.</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">width</span>: <span class="built_in">clamp</span>(<span class="number">500px</span>, <span class="number">50%</span>, <span class="number">1500px</span>);</span><br></pre></td></tr></table></figure>
<p>When the viewport width is 1600px then the preferred value will be 50% of 1600px, that is, 800px. When the viewport is 700px, the preferred should be 50% of 700, that is, 350px.</p>
<p>But since the minimum is 500px, it will return 500px. The same logic applies to the maximum value of 1500px.</p>
<p>You can see from the media provided above as well. The minimum was 450px, hence you don’t see any change when the viewport becomes less than 900px.</p>
<h2 id="Simple-Photoshop"><a href="#Simple-Photoshop" class="headerlink" title="Simple Photoshop"></a>Simple Photoshop</h2><p>CSS provides various properties to edit and apply effects to images. Some of these properties are discussed above, such as the <code>mix-blend-mode</code> .</p>
<p>CSS offers various properties to manipulate images. Some of them are:</p>
<ol>
<li>Brightness: filter: brightness(90%)</li>
<li>Blur: filter: blur(9px)</li>
<li>Saturation: filter: saturate(4)</li>
<li>Opacity: filter: oapcity(0.3)</li>
<li>Hue: filter: hue-rotate(45deg)</li>
<li>Drop Shadow: filter: drop-shadow(30px 10px 4px #4444dd)</li>
</ol>
<p>The picture below has a blur filter applied to it.</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></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">img</span>{</span><br><span class="line"> <span class="attribute">filter</span>: <span class="built_in">blur</span>(<span class="number">2px</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/01/19/73f65d582a83c.png"/></div></div>
<p>There are more filters and you can read about them <span style="text-decoration:underline"><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function">here</a></span>.</p>
<h2 id="Fullscreen"><a href="#Fullscreen" class="headerlink" title="Fullscreen"></a>Fullscreen</h2><p>If you have ever made a blog site or any site that involves displaying images, you must have provided some functionality to make the images go full screen.</p>
<p>Even Medium does it, when you click on an image it enlarges.</p>
<p>While you need JavaScript to make elements go full screen, by using the <code>requestFullscreen()</code> method, there is a CSS pseudo-class <code>:fullscreen</code> that allows you to style elements once they are in fullscreen mode.</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></pre></td><td class="code"><pre><span class="line"><span class="selector-pseudo">:fullscreen</span> {</span><br><span class="line"> <span class="attribute">background-color</span>: yellow;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>You can easily apply filters or change background(since elements that go full screen preserve their aspect ratio, leaving a white background behind).</p>
<h2 id="Pie-Charts"><a href="#Pie-Charts" class="headerlink" title="Pie Charts"></a>Pie Charts</h2><p>You can easily create pie charts using CSS only, with just two lines of CSS.</p>
<p>Using <code>conic-gradient()</code> CSS function, you can create images consisting of the gradient with color transitions rotated around a center point.</p>
<p>The most practical use case of this function, in my opinion, is creating pie charts.</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">background</span>: <span class="built_in">conic-gradient</span>(red <span class="number">20%</span>, purple <span class="number">0</span> <span class="number">60%</span>, yellow <span class="number">0</span>);</span><br><span class="line"><span class="attribute">border-radius</span>: <span class="number">50%</span>;</span><br></pre></td></tr></table></figure>
<p>The above code will render the following:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/01/19/0883c17845de2.jpeg"/></div></div>
<p>The result of the <code>conic-gradient()</code> function is an object of the <span style="text-decoration:underline"> <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/gradient">gradient</a></span> datatype.</p>
<p>You can find browser compatibility <span style="text-decoration:underline"><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/conic-gradient()#browser_compatibility">here</a></span>.</p>
<h2 id="Gradient-Text"><a href="#Gradient-Text" class="headerlink" title="Gradient Text"></a>Gradient Text</h2><p>Gradients when used correctly can provide your site with a breath of fresh air.</p>
<p>Chances are you already know how to work with gradients. If not, you can always use sites like <span style="text-decoration:underline"><a href="https://www.grabient.com/">Grabient</a></span> which lets you customize the gradient and provide the CSS for it.</p>
<p>But do you know you can easily apply a gradient to text as well?</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.text</span> {</span><br><span class="line"> <span class="attribute">background</span>: <span class="built_in">linear-gradient</span>(to right, red <span class="number">0%</span>, purple <span class="number">100%</span>);</span><br><span class="line"> -webkit-<span class="attribute">background-clip</span>: text;</span><br><span class="line"> -webkit-text-fill-<span class="attribute">color</span>: transparent;</span><br><span class="line"> <span class="attribute">font</span>: {</span><br><span class="line"> size: <span class="number">10vw</span>;</span><br><span class="line"> };</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>And you get awesome gradient text with just this.</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/01/19/62e443571397c.jpeg"/></div></div>
<h2 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>HTML and CSS are the fundamental building blocks of web development and it’s a myth you can’t build some great sites using just these two technologies.</p>
<p>While you need JavaScript for functionality, HTML and CSS alone can help you build beautiful marketing and landing sites with bare minimum JavaScript(for converting leads).</p>
<p>I have seen many freelancers get away with just CSS and HTML skills.</p>
<p>I hope you enjoyed this article!</p>
<div class="note blue no-icon flat"><p>This article is from: <a href="https://javascript.plainenglish.io/6-fun-things-you-can-do-with-css-1acabc8bf072">6 Fun Things You Can Do With Just CSS</a></p>
</div>
<p>Recommended reading</p>
<ul>
<li><a href="https://javascript.plainenglish.io/5-html-tricks-nobody-is-talking-about-a0480104fe19">5 HTML Tricks Nobody is Talking About</a></li>
<li><a href="https://javascript.plainenglish.io/rest-is-dying-get-rid-of-it-d43e6ef80cbe">REST is Dying. Get Rid of It.</a></li>
<li><a href="https://javascript.plainenglish.io/the-10-most-popular-programming-articles-december-2021-e1aace2c795c">The 10 Most Popular Programming Articles(December
2021,CSS 杀疯了!
https://fe32.top/articles/amo9485t/
2021-12-16T06:34:26.000Z
2023-05-17T10:01:12.000Z
<div class="tip cogs faa-horizontal"><p>2021 年已接近尾声,CSS 作为前端最流行的技术之一,这一年到底有怎样的发展呢?一起来看看吧</p>
</div>
<blockquote>
<p>本文所有指标均来自 HTTP Archive 数据集。HTTP Archive 是一个社区运行的项目,自 2010 年以来一直在跟踪网络的构建方式。在幕后使用 WebPageTest 和 Lighthouse,每月测试大约 820 万个网站的元数据,并将其包含在公共 BigQuery 数据库中进行分析。<br><br>测试网站:820万个;<br><br>数据处理:39.5TB。</p>
</blockquote>
<h2 id="用法"><a href="#用法" class="headerlink" title="用法"></a>用法</h2><ol>
<li>样式表大小<br>下面是每个样式表文件的大小分布:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/b0dafc5c650ed.webp"/></div></div>
CSS文件每年的大小都在不断增长,中等页面的CSS文件大约 <span class='p blue'>70KB</span>,较大页面的CSS文件大约 <span class='p blue'>250KB</span>。与2020年相比,CSS文件大小的中位数增加了 <span class='p blue'>7.9%</span>,同时,移动端CSS所有的百分比都略低于Web端CSS。<br><br>在所有测试到的CSS文件中,最大的Web页面CSS文件大小为 64,628 KB,最大的移动页面CSS文件大小为 17,823 KB。</li>
<li>预处理器<br>页面的CSS大小并未受到预处理器的显著影响。<span class='p blue'>17%的Web页面</span> 和 <span class='p blue'>16.5%的移动页面</span>包含预处理器,略高于去年的 <span class='p blue'>15%</span>。具体使用的样式如下:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/7d7e584cd2cd9.webp"/></div></div></li>
<li>样式表数量<br>下面是每个页面使用的外部样式表的数量分布:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/f56585b7b61fa.webp"/></div></div>
今年每个页面的样式表分布相对于去年有所增加,第50-90百分位的都增加了一个,第10-15百分位的都没有变化。<br>今年,外部样式表数量最多的记录是 <span class='p blue'>2368</span> ,比2020年的 <span class='p blue'>1379</span> ,增加了大约一倍。</li>
<li>样式表规则<br>下面是每个页面的样式规则数量分布:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/b26131bfbbd17.webp"/></div></div>
与去年相比,较高百分位的数量几乎没有变化,较低百分位的数量略有上升。在多数百分位的数量分布中,Web页面相对于移动页面的样式规则更多一点。</li>
</ol>
<h2 id="选择器"><a href="#选择器" class="headerlink" title="选择器"></a>选择器</h2><ol>
<li>class<br>最常用的class名称如下:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/35070855a14f4.webp"/></div></div></li>
<li>id<br>最常用的id名称如下:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/7cbe1543d690d.webp"/></div></div></li>
<li>属性选择器<br>最常用的属性选择器如下:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/4a4140c792c8e.webp"/></div></div>
最常用的属性选择器是<code>type</code>,它可以用于选择表单控件,如复选框、单选按钮、文本输入等。</li>
<li>伪类<br>最常用的伪类如下:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/57df71153723f.webp"/></div></div>
和 2020 年一样,用户操作伪类<code>:hover</code>、<code>:focus</code>和<code>:active</code>占据了前三名,它们都出现在至少三分之二的页面中。</li>
<li>伪元素<br>最常用的伪元素如下:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/f4c651c70a6ba.webp"/></div></div></li>
<li>!important<br>每个页面使用<code>!important</code>的比例如下:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/1a33651a0f232.webp"/></div></div>
调查发现,移动页面中最多有17,990 条<code>!important</code>规则,Web页面中最多有17,648 条<code>!important</code>规则。<br><br>使用<code>!important</code>规则的样式属性分布如下:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/e8af10d72b1e7.webp"/></div></div></li>
</ol>
<h2 id="值和单位"><a href="#值和单位" class="headerlink" title="值和单位"></a>值和单位</h2><ol>
<li>长度<br>最受欢迎的长度单位:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/a844e17c2dd0a.webp"/></div></div>
像素单位仍然是迄今为止最常用的长度单位,出现在了大约71%的页面中。<br><br>下面是每个属性的长度类型分布:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/089a4e29d7f26.webp"/></div></div>
下面是最常用的字体相对长度单位分布:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/fad793e568883.webp"/></div></div>
0长度的使用单位分布如下:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/3e3cf15233c17.webp"/></div></div>
接近88%的值都直接省略了单位,剩下的有单位的几乎都是<code>px</code>。</li>
<li>计算<br>下面是使用<code>calc()函数</code>的最常用的属性:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/29ab56627bce3.webp"/></div></div>
去过去几年一样,最常用的用法<code>calc()</code>是用来设置<code>width</code>的。<br><br>下面是<code>calc()函数</code>中最常用的长度单位:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/75c78f9891b13.webp"/></div></div>
<code>calc()函数</code>中单位数量的分布如下:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/3fa09b8430d29.webp"/></div></div>
<code>calc()</code>值是相对简单的,绝大多数使用两种不同单位进行计算,例如从百分比值的计算结果中减去像素。总共 99% 的<code>calc()表达式</code>使用了一种或两种单位类型。</li>
<li>全局关键字<br>下面是最常用的全局关键字以及其分布:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/4c86493e717de.webp"/></div></div></li>
<li>颜色<br>最常用的颜色格式如下:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/44a4206f96d34.webp"/></div></div>
很长一段时间内,#RRGGBB格式一直是使用最多的颜色格式,超过了一半的颜色声明会使用这种格式。颜色值的 75% 都是使用十六进制和 RGB 语法表示的。<br><br>最常用的颜色关键字值:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/f58e142314df0.webp"/></div></div></li>
</ol>
<h2 id="图片"><a href="#图片" class="headerlink" title="图片"></a>图片</h2><ol>
<li>CSS中图片格式<br>下面是CSS中图像格式比例分布:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/75df2a9bb62f4.webp"/></div></div></li>
<li>CSS中图片格式<br>下面是CSS样式文件在中加载的图像数量分布:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/cd4000b071c49.webp"/></div></div>
大多数的CSS不会加载大量的图片。在调查中,站点图片最多的CSS,其Web页面CSS中加载了<span class='p blue'>6088</span> 张图片,移动页面CSS中加载了 <span class='p blue'>6089</span> 张图片。</li>
<li>CSS中图片大小<br>下面是通过CSS加载的外部图片的大小分布:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/c44d1e78ecc9e.webp"/></div></div>
总体来说,移动页面会比Web页面CSS加载的外部图片略小。在调查中,有一个页面CSS加载的图片总大小达到314,386.1 KB。<br><br>下面是按照图片格式在移动页面上通过CSS加载的外部图像大小分布:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/e611e4be0b24d.webp"/></div></div>
有趣的是,在第 90 个百分位数时,GIF 图像平均比 SVG 文件还要小。</li>
<li>渐变<br>下面是常见的使用渐变的属性:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/0d75110164472.webp"/></div></div>
下面是最常用的渐变值:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/7917c98fd4195.webp"/></div></div></li>
</ol>
<h2 id="布局"><a href="#布局" class="headerlink" title="布局"></a>布局</h2><p>下面是最长使用的布局类型:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/135a2532bec00.webp"/></div></div>
<p>需要注意,这里并不是页面主要布局的方式,而是说<code>position: absolute</code>出现在我们分析的页面的 93% 的样式中。同样,<code>display: grid</code> 出现在 36% 的页面样式中,但这并不意味着所有页面的 37% 是 <code>Grid</code>布局,只是该组合出现在样式表的某些地方。</p>
<ol>
<li>Flexbox和Grid布局<br>下面是在移动设备上采用Flexbox和Grid布局的比例分布:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/cd58d5ad78e98.png"/></div></div>
使用<code>Flexbox</code>和<code>Grid</code>布局一直在持续增长。2019 年,<code>Flexbox</code> 布局的采用率为 41%;2020 年为 63%。今年,<code>Flexbox</code> 在移动端和桌面端分别达到 71% 和 73%。同时,Grid 布局的采用率每年都在会翻倍,从 2% 到 4%,现在是 8%。</li>
<li>多列布局<br>使用多列布局的页面的百分比:20%。</li>
<li>border-box数量<br>每个页面中使用box-sizing: border-box的数量分布:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/5ed47f0b3d1f4.webp"/></div></div></li>
<li>过渡和动画<br>动画仍然是被广泛使用的,animation属性在 77% 的移动页面和 73% 的桌面页面上使用。transition属性在 85% 的移动页面和 90% 的桌面页面上使用。<br>最常使用过渡的属性:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/39ea847df09de.webp"/></div></div>
过渡持续时间的分布:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/7e221dd1f580e.webp"/></div></div>
即使在第 90 个百分位,过渡持续时间的中位数也仅为半秒。<br><br>延迟过渡的分布:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/1bedad42f820c.webp"/></div></div>
可以看到,最高的过渡延迟中位数是 1.7 秒,第 10 个百分位数的中位数延迟大约不到负三分之一秒,这表明大量过渡是在生成的动画中途开始的。<br><br>过渡计时功能的分布:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/81017d0353660.webp"/></div></div>
最常用的动画类型:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/a93ca75cf4d6d.webp"/></div></div></li>
</ol>
<h2 id="响应式设计"><a href="#响应式设计" class="headerlink" title="响应式设计"></a>响应式设计</h2><ol>
<li>媒体查询功能<br>最常用的用作媒体查询的功能如下:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/d8e6963f301c1.webp"/></div></div>
<code>max-width</code> 和 <code>min-width</code> 是迄今为止最受欢迎的查询功能。</li>
<li>媒体查询断点<br>最常用的媒体查询断点值:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/597649aaf08bc.webp"/></div></div>
今为止最常见的断点是 767 和 768 px,这与 iPad 在纵向模式下的分辨率非常吻合。767px 被大量用作最大宽度断点,而很少用作最小宽度值。786px经常被用作最小和最大断点。</li>
<li>媒体查询属性<br>通过媒体查询最常更改的属性:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/3c71126912824.webp"/></div></div>
最常设置的属性是<code>display</code>,紧随其后的是<code>color</code>, <code>width</code>, 和 <code>height</code>。</li>
</ol>
<h2 id="特征查询"><a href="#特征查询" class="headerlink" title="特征查询"></a>特征查询</h2><p>特征查询 ( @supports) 的使用继续保持增长。2019 年,有 30% 的页面在使用,2020年为 39% 。2021 年,将近 48% 的页面使用特征查询来决定在什么上下文中应用哪种 CSS。</p>
<p>最常使用的特征查询的依据分布如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/f6d558955cdc8.webp"/></div></div>
<p>粘性定位是最受欢迎的查询依据,占所有特征查询的一半以上。</p>
<h2 id="自定义属性"><a href="#自定义属性" class="headerlink" title="自定义属性"></a>自定义属性</h2><p>2019-2021 年自定义属性使用的变化如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/76e8ee59c15bf.webp"/></div></div>
<p>图片今年,28.6% 的移动页面和 28.3% 的桌面页面定义了自定义属性(也就是CSS变量)。并且,35.2% 的移动页面和 35.6% 的桌面页面包含至少一个<code>var()</code>值。</p>
<ol>
<li>命名<br>对于自定义属性的命名,最常使用的自定义属性名称如下:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/9d24977937ff0.webp"/></div></div>
使用最多的自定义属性是<code>--wp</code> 开头的,这些都是 <code>WordPress</code> 网站中的属性。</li>
<li>用法<br>最常用自定义属性的属性如下:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/c76d6db7d9331.webp"/></div></div>
自定义属性值类型的分布如下:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/dad81a350d319.webp"/></div></div></li>
<li>复杂<br>自定义属性是可以包含其他自定义属性的:<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-pseudo">:root</span> {</span><br><span class="line"> <span class="attr">--base-hue</span>: <span class="number">335</span>; <span class="comment">/* depth = 0 */</span></span><br><span class="line"> <span class="attr">--base-color</span>: <span class="built_in">hsl</span>(<span class="built_in">var</span>(--base-hue) <span class="number">90%</span> <span class="number">50%</span>); <span class="comment">/* depth = 1 */</span></span><br><span class="line"> <span class="attr">--background</span>: <span class="built_in">linear-gradient</span>(<span class="built_in">var</span>(--base-color), black); <span class="comment">/* depth = 2 */</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
自定义属性深度的分布如下:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/234c724f34ca1.webp"/></div></div>
绝大多数自定义属性的值深度为零:它们没有在自己的值中包含其他自定义属性的值。近三分之一有一个深度级别,除此之外,几乎没有深度为两个或更多的自定义属性值。<br><br>除此之外,使用自定义属性值的选择器, 60% 设置在根元素上(使用:root或html选择器),大约 5% 设置在<code><body></code>元素上。其余的应用于根元素的某些后代而不是<code><body></code>. 这意味着大约三分之二的自定义属性被用作实际上是全局常量。</li>
</ol>
<h2 id="国际化"><a href="#国际化" class="headerlink" title="国际化"></a>国际化</h2><p>对于不同语言,其可能存在不用的书写形式,比如:</p>
<ul>
<li>英文字符是从左到右书写的;</li>
<li>阿拉伯语、希伯来语和乌尔都语都是从右到左书写,</li>
<li>汉语、蒙古语、日语可以从上到下书写。</li>
</ul>
<p>对于这些复杂的情况,<code>HTML</code>和<code>CSS</code>都提供了对应的处理方法。</p>
<ol>
<li>方向<br>可以使用 CSS 的direction属性来强制执行文本的方向。该属性在 11% 的元素上被使用,在 3% 的页面上的元素上被使用。在使用 CSS 设置方向的页面中,92% 的 <code><html></code> 元素和 82% 的 <code><body></code> 元素被设置为<code>ltr</code>(从左到右),只有 9%页面中将该属性设置为<code>rtl</code>(从右到左)。</li>
<li>逻辑属性<br>另一个对国际化有用的 CSS 特性就是“逻辑”属性。比如<code>margin-block-start</code> 、<code>padding-inline-end</code>等,以及<code>text-align</code>属性的属性值<code>start</code>和<code>end</code>等。这些属性和值都和文本流的方向相关。<br>下面是逻辑属性的属性类型分布:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/361a79bfbc98f.webp"/></div></div>
统计发现,只有 4% 的页面使用了逻辑属性。在这些页面中,大约 33% 的页面使用它来设置 <code>text-align</code> 为 <code>start</code> 或 <code>end</code>。另外 46% 设置了逻辑边距和填充。</li>
</ol>
<h2 id="CSS-in-JS"><a href="#CSS-in-JS" class="headerlink" title="CSS in JS"></a>CSS in JS</h2><p>下面是使用的CSS-in-JS 库的分布情况:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/394faf0f06da6.webp"/></div></div>
<p>统计发现,大约 3% 的页面使用了 CSS-in-JS。其中<code>Styled Component</code>就占了一半多。</p>
<h2 id="简写"><a href="#简写" class="headerlink" title="简写"></a>简写</h2><ol>
<li>简写属性<br>有些样式表中混合了简写属性<code>background</code>和普通属性<code>background-size</code>,在相应的简写属性之后出现的普通属性最常见的如下:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/3a861fae108e8.webp"/></div></div></li>
<li>background<br>可以看到,<code>background</code>相关的普通属性是使用最多的,下面是最常用的<code>background</code>属性:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/5d4eed03a7605.webp"/></div></div></li>
<li>padding和margin<br>下面是最常用的padding和margin属性:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/d97290cee1ae8.webp"/></div></div></li>
<li>font<br>下面是最常用的字体属性:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/8fa40a945c0de.webp"/></div></div></li>
<li>Flexbox<br>下面是最常用的 Flexbox 相关属性:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/425f83641b43c.webp"/></div></div></li>
<li>Grid<br>下面是最常用的 Grid 相关属性:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/1c0c71a6a46a4.webp"/></div></div></li>
</ol>
<h2 id="错误类型"><a href="#错误类型" class="headerlink" title="错误类型"></a>错误类型</h2><p>下面来看<code>CSS</code>中常见的一些错误。</p>
<ol>
<li>重复声明<br>“声明重复”的数量——通过确定有多少声明使用相同的属性和值,以及有多少声明在页面的内部是唯一的,从而粗略估计样式表的效率。<br><br>下面是每个页面重复声明的分布:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/2794b83a86a3d.webp"/></div></div></li>
<li>不存在的属性<br>最常见的不存在的属性如下:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/e5fe573cf38f0.webp"/></div></div></li>
</ol>
<h2 id="预处理器Sass"><a href="#预处理器Sass" class="headerlink" title="预处理器Sass"></a>预处理器Sass</h2><p><code>Sass</code>是最流行的CSS预处理器之一,最常用的<code>Sass</code>函数调用如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/5b862810e89c5.webp"/></div></div>
<p>可以发现, <code>Sass</code>函数中有 28% 是修改颜色的函数(例如<code>darken</code>、<code>mix</code>),另外 6% 用于读取颜色(例如<code>alpha</code>、<code>blue</code>)。</p>
<p>下面是 <code>Sass</code> 中最常用的流控制结构:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/14b61d98147c8.webp"/></div></div>
<p>下面是 <code>Sass</code> 中最常用的规则嵌套:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/16/437fa762aeae1.webp"/></div></div>
<p><code>Sass</code>的一个主要优点就是能够将规则嵌套在其他规则中,从而避免编写重复的选择器模式。统计发现,87% 的<code>Sass</code>样式表使用了规则嵌套。</p>
<p>这就是2021年<code>CSS</code>的使用趋势,期待<code>CSS</code>在未来的发展~</p>
<p>关于本文:</p>
<blockquote>
<p>原文:<a href="https://almanac.httparchive.org/en/2021/css">https://almanac.httparchive.org/en/2021/css</a> <br><br>作者:Eric A. Meyer、Shuvam
记录关于 Vue + element-ui 的系列问题
https://fe32.top/articles/ele265ui/
2021-11-25T10:43:44.000Z
2023-07-15T16:51:04.000Z
<div class="tip cogs faa-horizontal animated"><p>这里将记录自己工作或学习中关于<code>Vue+element-ui</code>的系列问题,如果您有有更好的解决方法可以在下面进行评论喔~ </p>
</div>
<h2 id="DatePicker快捷选项"><a href="#DatePicker快捷选项" class="headerlink" title="DatePicker快捷选项"></a>DatePicker快捷选项</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> i18n <span class="keyword">from</span> <span class="string">'@/lang'</span> <span class="comment">// 如果不需要 中英文则注释掉</span></span><br><span class="line"><span class="comment">//时间选择</span></span><br><span class="line"><span class="keyword">const</span> rangePickerOptions = {</span><br><span class="line"> <span class="attr">shortcuts</span>: [{</span><br><span class="line"> <span class="comment">// text: "今天",</span></span><br><span class="line"> <span class="attr">text</span>: i18n.<span class="title function_">t</span>(<span class="string">'shortcuts.t1'</span>),</span><br><span class="line"> <span class="title function_">onClick</span>(<span class="params">picker</span>) {</span><br><span class="line"> <span class="keyword">const</span> end = <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line"> <span class="keyword">const</span> start = <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line"> picker.$emit(<span class="string">"pick"</span>, [start, end]);</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// text: "昨天",</span></span><br><span class="line"> <span class="attr">text</span>: i18n.<span class="title function_">t</span>(<span class="string">'shortcuts.t2'</span>),</span><br><span class="line"> <span class="title function_">onClick</span>(<span class="params">picker</span>) {</span><br><span class="line"> <span class="keyword">const</span> end = <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line"> <span class="keyword">const</span> start = <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line"> start.<span class="title function_">setTime</span>(start.<span class="title function_">getTime</span>() - <span class="number">3600</span> * <span class="number">1000</span> * <span class="number">24</span> * <span class="number">1</span>);</span><br><span class="line"> picker.$emit(<span class="string">"pick"</span>, [start, end]);</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// text: "最近一周",</span></span><br><span class="line"> <span class="attr">text</span>: i18n.<span class="title function_">t</span>(<span class="string">'shortcuts.t3'</span>),</span><br><span class="line"> <span class="title function_">onClick</span>(<span class="params">picker</span>) {</span><br><span class="line"> <span class="keyword">const</span> end = <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line"> <span class="keyword">const</span> start = <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line"> start.<span class="title function_">setTime</span>(start.<span class="title function_">getTime</span>() - <span class="number">3600</span> * <span class="number">1000</span> * <span class="number">24</span> * <span class="number">7</span>);</span><br><span class="line"> picker.$emit(<span class="string">"pick"</span>, [start, end]);</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// text: "当前月",</span></span><br><span class="line"> <span class="attr">text</span>: i18n.<span class="title function_">t</span>(<span class="string">'shortcuts.t4'</span>),</span><br><span class="line"> <span class="title function_">onClick</span>(<span class="params">picker</span>) {</span><br><span class="line"> <span class="keyword">const</span> start = <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line"> <span class="keyword">const</span> end = <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line"> start.<span class="title function_">setDate</span>(<span class="number">1</span>);</span><br><span class="line"> start.<span class="title function_">getTime</span>();</span><br><span class="line"> picker.$emit(<span class="string">"pick"</span>, [start, end]);</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// text: "上个月",</span></span><br><span class="line"> <span class="attr">text</span>: i18n.<span class="title function_">t</span>(<span class="string">'shortcuts.t5'</span>),</span><br><span class="line"> <span class="title function_">onClick</span>(<span class="params">picker</span>) {</span><br><span class="line"> <span class="keyword">const</span> oDate = <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line"> <span class="keyword">let</span> year = oDate.<span class="title function_">getFullYear</span>();</span><br><span class="line"> <span class="keyword">let</span> month = oDate.<span class="title function_">getMonth</span>();</span><br><span class="line"> <span class="keyword">let</span> start, end;</span><br><span class="line"> <span class="keyword">if</span> (month == <span class="number">0</span>) {</span><br><span class="line"> year--;</span><br><span class="line"> start = <span class="keyword">new</span> <span class="title class_">Date</span>(year, <span class="number">11</span>, <span class="number">1</span>);</span><br><span class="line"> end = <span class="keyword">new</span> <span class="title class_">Date</span>(year, <span class="number">11</span>, <span class="number">31</span>);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> start = <span class="keyword">new</span> <span class="title class_">Date</span>(year, month - <span class="number">1</span>, <span class="number">1</span>);</span><br><span class="line"> end = <span class="keyword">new</span> <span class="title class_">Date</span>(year, month, <span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line"> picker.$emit(<span class="string">"pick"</span>, [start, end]);</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// text: "最近一个月",</span></span><br><span class="line"> <span class="attr">text</span>: i18n.<span class="title function_">t</span>(<span class="string">'shortcuts.t6'</span>),</span><br><span class="line"> <span class="title function_">onClick</span>(<span class="params">picker</span>) {</span><br><span class="line"> <span class="keyword">const</span> end = <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line"> <span class="keyword">const</span> start = <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line"> start.<span class="title function_">setTime</span>(start.<span class="title function_">getTime</span>() - <span class="number">3600</span> * <span class="number">1000</span> * <span class="number">24</span> * <span class="number">30</span>);</span><br><span class="line"> picker.$emit(<span class="string">"pick"</span>, [start, end]);</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// text: "最近三个月",</span></span><br><span class="line"> <span class="attr">text</span>: i18n.<span class="title function_">t</span>(<span class="string">'shortcuts.t7'</span>),</span><br><span class="line"> <span class="title function_">onClick</span>(<span class="params">picker</span>) {</span><br><span class="line"> <span class="keyword">const</span> end = <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line"> <span class="keyword">const</span> start = <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line"> start.<span class="title function_">setTime</span>(start.<span class="title function_">getTime</span>() - <span class="number">3600</span> * <span class="number">1000</span> * <span class="number">24</span> * <span class="number">90</span>);</span><br><span class="line"> picker.$emit(<span class="string">"pick"</span>, [start, end]);</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// text: "最近半年",</span></span><br><span class="line"> <span class="attr">text</span>: i18n.<span class="title function_">t</span>(<span class="string">'shortcuts.t8'</span>),</span><br><span class="line"> <span class="title function_">onClick</span>(<span class="params">picker</span>) {</span><br><span class="line"> <span class="keyword">const</span> end = <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line"> <span class="keyword">const</span> start = <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line"> start.<span class="title function_">setTime</span>(start.<span class="title function_">getTime</span>() - <span class="number">3600</span> * <span class="number">1000</span> * <span class="number">24</span> * <span class="number">180</span>);</span><br><span class="line"> picker.$emit(<span class="string">"pick"</span>, [start, end]);</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// text: "今年至今",</span></span><br><span class="line"> <span class="attr">text</span>: i18n.<span class="title function_">t</span>(<span class="string">'shortcuts.t9'</span>),</span><br><span class="line"> <span class="title function_">onClick</span>(<span class="params">picker</span>) {</span><br><span class="line"> <span class="keyword">const</span> end = <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line"> <span class="keyword">const</span> start = <span class="keyword">new</span> <span class="title class_">Date</span>(<span class="keyword">new</span> <span class="title class_">Date</span>().<span class="title function_">getFullYear</span>(), <span class="number">0</span>);</span><br><span class="line"> picker.$emit(<span class="string">"pick"</span>, [start, end]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line">}</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> rangePickerOptions</span><br></pre></td></tr></table></figure>
<p>在组件中直接引入,然后在<code>data</code> 声明变量使用就好。</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> rangePickerOptions <span class="keyword">from</span> <span class="string">"@/utils/range-pick-opt"</span>;</span><br></pre></td></tr></table></figure>
<p><code>template</code> 代码</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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="tag"><<span class="name">el-date-picker</span></span></span><br><span class="line"><span class="tag"> <span class="attr">v-model</span>=<span class="string">"PerFilter.interval"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">type</span>=<span class="string">"daterange"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">unlink-panels</span></span></span><br><span class="line"><span class="tag"> <span class="attr">size</span>=<span class="string">"small"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">format</span>=<span class="string">"yyyy-MM-dd"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">value-format</span>=<span class="string">"yyyy-MM-dd"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">:range-separator</span>=<span class="string">"$t('tagText.to')"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">:start-placeholder</span>=<span class="string">"$t('tagText.order_time_st')"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">:end-placeholder</span>=<span class="string">"$t('tagText.order_time_ed')"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">:picker-options</span>=<span class="string">"rangePickerOptions"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">popper-class</span>=<span class="string">"date-picker-class"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">clearable</span></span></span><br><span class="line"><span class="tag">></span><span class="tag"></<span class="name">el-date-picker</span>></span></span><br></pre></td></tr></table></figure>
<p>效果如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/a410a6733bd53.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/9945aa3244865.jpg"/></div></div>
<h2 id="Select滚动加载-支持远程搜索"><a href="#Select滚动加载-支持远程搜索" class="headerlink" title="Select滚动加载 支持远程搜索"></a>Select滚动加载 支持远程搜索</h2><ol>
<li>滚动加载数据<br>在入口文件 <code>main.js</code> 中加入以下代码生成自定义指令<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="title class_">Vue</span>.<span class="title function_">directive</span>(<span class="string">'loadmore'</span>, {</span><br><span class="line"> <span class="title function_">bind</span>(<span class="params">el, binding</span>) {</span><br><span class="line"> <span class="comment">// 获取element-ui定义好的scroll盒子</span></span><br><span class="line"> <span class="keyword">const</span> <span class="variable constant_">SELECTWRAP_DOM</span> = el.<span class="title function_">querySelector</span>(<span class="string">'.el-select-dropdown .el-select-dropdown__wrap'</span>)</span><br><span class="line"> <span class="variable constant_">SELECTWRAP_DOM</span>.<span class="title function_">addEventListener</span>(<span class="string">'scroll'</span>, <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">const</span> <span class="variable constant_">CONDITION</span> = <span class="variable language_">this</span>.<span class="property">scrollHeight</span> - <span class="variable language_">this</span>.<span class="property">scrollTop</span> <= <span class="variable language_">this</span>.<span class="property">clientHeight</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="variable constant_">CONDITION</span>) {</span><br><span class="line"> binding.<span class="title function_">value</span>()</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<code>template</code> 代码<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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="tag"><<span class="name">el-select</span></span></span><br><span class="line"><span class="tag"> <span class="attr">v-model</span>=<span class="string">"form.tid"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">clearable</span></span></span><br><span class="line"><span class="tag"> <span class="attr">filterable</span></span></span><br><span class="line"><span class="tag"> <span class="attr">remote</span></span></span><br><span class="line"><span class="tag"> <span class="attr">reserve-keyword</span></span></span><br><span class="line"><span class="tag"> <span class="attr">v-loadmore</span>=<span class="string">"loadMore"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">:remote-method</span>=<span class="string">"remoteMethod"</span></span></span><br><span class="line"><span class="tag">></span></span><br><span class="line"> <span class="tag"><<span class="name">el-option</span></span></span><br><span class="line"><span class="tag"> <span class="attr">v-for</span>=<span class="string">"(item, index) in list"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">:key</span>=<span class="string">"index"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">:label</span>=<span class="string">"item.username"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">:value</span>=<span class="string">"item.uid"</span></span></span><br><span class="line"><span class="tag"> ></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span>></span>自定义的内容<span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">el-option</span>></span></span><br><span class="line"><span class="tag"></<span class="name">el-select</span>></span></span><br></pre></td></tr></table></figure>
<code>data</code>中需要声明的变量:<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">data(){</span><br><span class="line"> return{</span><br><span class="line"> status:'loadmore',</span><br><span class="line"> form:{</span><br><span class="line"> tid:0</span><br><span class="line"> },</span><br><span class="line"> list: [],</span><br><span class="line"> loadmorePage: {</span><br><span class="line"> pageNum: 1,</span><br><span class="line"> pageSize: 20,</span><br><span class="line"> },</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<code>methods</code>中的方法:<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></pre></td><td class="code"><pre><span class="line"><span class="title function_">loadMore</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">status</span> == <span class="string">"nomore"</span>) {</span><br><span class="line"> <span class="comment">// 已经到最后一页了 return</span></span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">loadmorePage</span>.<span class="property">pageNum</span> += <span class="number">1</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">getList</span>(); <span class="comment">// 获取目标数据</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">async</span> <span class="title function_">getList</span>(<span class="params">value = <span class="string">""</span></span>) {</span><br><span class="line"> <span class="keyword">const</span> { <span class="title class_">Code</span>, <span class="title class_">Data</span>, <span class="title class_">Page</span> } = <span class="keyword">await</span> <span class="title function_">getApi</span>({</span><br><span class="line"> <span class="title class_">Page</span>: {</span><br><span class="line"> <span class="title class_">PageNum</span>: <span class="variable language_">this</span>.<span class="property">loadmorePage</span>.<span class="property">pageNum</span>,</span><br><span class="line"> <span class="title class_">PageSize</span>: <span class="variable language_">this</span>.<span class="property">loadmorePage</span>.<span class="property">pageSize</span>,</span><br><span class="line"> },</span><br><span class="line"> <span class="attr">filter</span>: value,</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">if</span> (!<span class="title class_">Data</span> || !<span class="title class_">Data</span>.<span class="property">length</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">status</span> = <span class="string">"nomore"</span>;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// haxNextPage 是否有下一页</span></span><br><span class="line"> <span class="keyword">if</span> (!<span class="title class_">Page</span> || !<span class="title class_">Page</span>.<span class="property">haxNextPage</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">status</span> = <span class="string">"nomore"</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">list</span> = <span class="variable language_">this</span>.<span class="property">list</span>.<span class="title function_">concat</span>(<span class="title class_">Data</span>);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">status</span> = <span class="string">"loadmore"</span>;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">list</span> = <span class="variable language_">this</span>.<span class="property">list</span>.<span class="title function_">concat</span>(<span class="title class_">Data</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
效果如下:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/961fbc0125a50.png"/></div></div>
往下滚动 加载第二页的数据<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/f44a0aca83753.png"/></div></div>
往下滚动 加载第三页的数据<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/1d5d15491c2c8.png"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/490d33e1da61c.png"/></div></div>
往下滚动 继续 加载下一页的数据<br>直到加载完所有的数据 则 return</li>
<li>远程搜索<br><code>methods</code>中的方法:<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="title function_">remoteMethod</span>(<span class="params">query</span>) {</span><br><span class="line"> <span class="comment">// query 搜索框输入的值</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">list</span> = []; <span class="comment">// 置空数据</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">loadmorePage</span>.<span class="property">pageNum</span> = <span class="number">1</span>; <span class="comment">// 重置页码</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">getList</span>(query); <span class="comment">// 按 query 搜索</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
效果如下:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/07/a64d8d276354a.png"/></div></div></li>
</ol>
<h2 id="上传图片"><a href="#上传图片" class="headerlink" title="上传图片"></a>上传图片</h2><p><code>template</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><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="tag"><<span class="name">el-form-item</span> <span class="attr">prop</span>=<span class="string">"srvimage"</span> <span class="attr">label</span>=<span class="string">"图片"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">el-upload</span></span></span><br><span class="line"><span class="tag"> <span class="attr">class</span>=<span class="string">"avatar-uploader"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">:show-file-list</span>=<span class="string">"false"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">list-type</span>=<span class="string">"picture-card"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">action</span>=<span class="string">"#"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">accept</span>=<span class="string">".jpg, .jpeg, .png, .gif, .bmp, .JPG, .JPEG, .PBG, .GIF, .BMP"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">:limit</span>=<span class="string">"1"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">:before-upload</span>=<span class="string">"beforeAvatarUpload"</span></span></span><br><span class="line"><span class="tag"> ></span></span><br><span class="line"> <span class="tag"><<span class="name">img</span> <span class="attr">v-if</span>=<span class="string">"imageUrl"</span> <span class="attr">:src</span>=<span class="string">"imageUrl"</span> <span class="attr">class</span>=<span class="string">"avatar"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">i</span> <span class="attr">v-else</span> <span class="attr">class</span>=<span class="string">"el-icon-plus avatar-uploader-icon"</span>></span><span class="tag"></<span class="name">i</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">el-upload</span>></span></span><br><span class="line"><span class="tag"></<span class="name">el-form-item</span>></span></span><br></pre></td></tr></table></figure>
<p><code>data</code> 中需要声明的变量:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">data(){</span><br><span class="line"> return{</span><br><span class="line"> imageUrl: "",</span><br><span class="line"> }</span><br><span class="line">}</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// Api接口 在页面引入即可</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">traderServerPicture</span>(<span class="params">data</span>) {</span><br><span class="line"> <span class="keyword">let</span> config = {</span><br><span class="line"> <span class="attr">headers</span>: {</span><br><span class="line"> <span class="string">"Content-Type"</span>: <span class="string">"multipart/form-data;"</span>,</span><br><span class="line"> },</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">request</span>({</span><br><span class="line"> <span class="attr">url</span>: <span class="string">"mt4/tdserver/picture"</span>,</span><br><span class="line"> <span class="attr">method</span>: <span class="string">"post"</span>,</span><br><span class="line"> data,</span><br><span class="line"> config</span><br><span class="line"> })</span><br><span class="line">}</span><br><span class="line"><span class="comment">//上传标题图片</span></span><br><span class="line"><span class="title function_">beforeAvatarUpload</span>(<span class="params">file</span>) {</span><br><span class="line"> <span class="comment">//上传前对图片类型和大小进行判断</span></span><br><span class="line"> <span class="keyword">const</span> isJPG = file.<span class="property">type</span> === <span class="string">"image/jpeg"</span>;</span><br><span class="line"> <span class="keyword">const</span> isGIF = file.<span class="property">type</span> === <span class="string">"image/gif"</span>;</span><br><span class="line"> <span class="keyword">const</span> isPNG = file.<span class="property">type</span> === <span class="string">"image/png"</span>;</span><br><span class="line"> <span class="keyword">const</span> isBMP = file.<span class="property">type</span> === <span class="string">"image/bmp"</span>;</span><br><span class="line"> <span class="keyword">const</span> isLt2M = file.<span class="property">size</span> / <span class="number">1024</span> / <span class="number">1024</span> < <span class="number">2</span>;</span><br><span class="line"> <span class="keyword">if</span> (!isJPG && !isGIF && !isPNG && !isBMP) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">"上传头像图片只能是 JPG/GIF/PNG/BMP 格式!"</span>);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (!isLt2M) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">"上传头像图片大小不能超过 2MB!"</span>);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//将文件转化为formdata数据上传</span></span><br><span class="line"> <span class="keyword">let</span> formData = <span class="keyword">new</span> <span class="title class_">FormData</span>();</span><br><span class="line"> formData.<span class="title function_">append</span>(<span class="string">"uploadfile"</span>, file);</span><br><span class="line"> <span class="title function_">traderServerPicture</span>(formData).<span class="title function_">then</span>(<span class="function">(<span class="params">res</span>) =></span> {</span><br><span class="line"> <span class="comment">// 你的业务逻辑</span></span><br><span class="line"> })</span><br><span class="line"> .<span class="title function_">catch</span>(<span class="function">(<span class="params">error</span>) =></span> {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(error);</span><br><span class="line"> });</span><br><span class="line">},</span><br></pre></td></tr></table></figure>
<h2 id="寻轮调用接口"><a href="#寻轮调用接口" class="headerlink" title="寻轮调用接口"></a>寻轮调用接口</h2><p>给页面绑定一个ID,目的是为了 离开该页面后 不再轮询调用接口。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/03/23/c10517793844b.png"/></div></div>
<p><code>data</code>中需要声明的变量:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">data(){</span><br><span class="line"> return{</span><br><span class="line"> timer: null, </span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><code>methods</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="title function_">getDataListSeconds</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">let</span> baseURL =</span><br><span class="line"> process.<span class="property">env</span>.<span class="property">NODE_ENV</span> === <span class="string">"production"</span></span><br><span class="line"> ? process.<span class="property">env</span>.<span class="property">VUE_APP_BASE_API</span></span><br><span class="line"> : <span class="string">"/api"</span>;</span><br><span class="line"> <span class="title function_">axios</span>({</span><br><span class="line"> <span class="attr">url</span>: <span class="string">`<span class="subst">${baseURL}</span>/xxx/xxx/list`</span>,</span><br><span class="line"> <span class="attr">method</span>: <span class="string">"post"</span>,</span><br><span class="line"> <span class="attr">headers</span>: {</span><br><span class="line"> <span class="string">"X-Token"</span>: <span class="title function_">getToken</span>()</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> .<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =></span> {</span><br><span class="line"> <span class="comment">// 你的业务逻辑</span></span><br><span class="line"> })</span><br><span class="line"> .<span class="title function_">catch</span>(<span class="function"><span class="params">error</span> =></span> {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(error);</span><br><span class="line"> });</span><br><span class="line">},</span><br><span class="line"><span class="comment">// 定时获取接口信息</span></span><br><span class="line"><span class="title function_">intervalData</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">timer</span> = <span class="built_in">setInterval</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="comment">// 离开页面时 不再调用接口</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">"isInterval"</span>) === <span class="literal">null</span>) {</span><br><span class="line"> <span class="built_in">clearInterval</span>(<span class="variable language_">this</span>.<span class="property">timer</span>);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">getDataListSeconds</span>();</span><br><span class="line"> }, <span class="number">1000</span>);</span><br><span
关于JavaScript常用的工具函数汇总
https://fe32.top/articles/ut0927il/
2021-09-27T09:36:06.000Z
2023-05-17T10:01:12.000Z
汇总了一些常用的JavaScript工具函数
CSS 实现新拟态(Neumorphism) UI 风格
https://fe32.top/articles/np2021sm/
2021-09-23T12:12:37.000Z
2023-05-17T10:01:12.000Z
<p>新拟态是一种图形样式,其原理是为界面的UI元素赋予真实感。</p>
<p>原生名词有几个叫法,Neumorphism / soft ui,翻译过来是新拟态或者是软ui。国内的翻译叫,新拟物风格。Neumorphism,是New +Skeuomorphism的组合词。按照我个人的通俗理解,就是将界面的一部分凸起来,另一部分凹下去,形成的一种错落有致的拟物风格。代表作是乌克兰设计师 Alexander Plyuto 在各平台发布的新作品「Skeuomorph Mobile Banking」</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/09/23/fc95469eba5e8.png"/></div></div>
<p>新拟态 UI 风格与扁平、投影风格的对比</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/09/23/c5512f7e684f4.jpg"/></div></div>
<p>从上面这张对比图可以看出,扁平风格就像是一张纸贴在墙面上,投影风格像是浮在半空中,而新拟态风格则像是墙面上直接凸起了一块。要实现这种风格,精髓在于一个白色的阴影+一个常规阴影。一个示例如下图所示:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/09/23/6f8cef3022d51.jpg"/></div></div>
<p>Neumorphism css 在线生成器地址:<a href="https://neumorphism.io/">https://neumorphism.io</a></p>
<p>前端实现的效果:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/09/23/baff5338c18b9.jpg"/></div></div>
<p>代码如下:</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE <span class="keyword">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1.0"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>Document<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">href</span>=<span class="string">"./font_ue8tdlm62pf/iconfont.css"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">style</span>></span><span class="language-css"></span></span><br><span class="line"><span class="language-css"> <span class="selector-class">.container</span> {</span></span><br><span class="line"><span class="language-css"> <span class="attribute">width</span>: <span class="number">1018px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">margin</span>: <span class="number">0</span> auto;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">height</span>: <span class="number">660px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">background-color</span>: <span class="number">#e6eef4</span>;</span></span><br><span class="line"><span class="language-css"> }</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"> <span class="selector-class">.top</span> {</span></span><br><span class="line"><span class="language-css"> <span class="attribute">height</span>: <span class="number">200px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">flex-direction</span>: row;</span></span><br><span class="line"><span class="language-css"> }</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"> <span class="selector-class">.top-left</span> {</span></span><br><span class="line"><span class="language-css"> <span class="attribute">width</span>: <span class="number">400px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="comment">/* height: 400px; */</span></span></span><br><span class="line"><span class="language-css"> <span class="attribute">float</span>: left;</span></span><br><span class="line"><span class="language-css"> <span class="comment">/* background-color: rgb(92, 89, 89); */</span></span></span><br><span class="line"><span class="language-css"> <span class="attribute">margin</span>: <span class="number">50px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">border-radius</span>: <span class="number">39px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">background</span>: <span class="number">#e6eef4</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">box-shadow</span>: <span class="number">33px</span> <span class="number">33px</span> <span class="number">67px</span> <span class="number">#cad1d7</span>,</span></span><br><span class="line"><span class="language-css"> -<span class="number">33px</span> -<span class="number">33px</span> <span class="number">67px</span> <span class="number">#ffffff</span>;</span></span><br><span class="line"><span class="language-css"> }</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"> <span class="selector-class">.d1</span>,</span></span><br><span class="line"><span class="language-css"> <span class="selector-class">.d2</span>,</span></span><br><span class="line"><span class="language-css"> <span class="selector-class">.d3</span>,</span></span><br><span class="line"><span class="language-css"> <span class="selector-class">.d4</span> {</span></span><br><span class="line"><span class="language-css"> <span class="attribute">display</span>: inline-block;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">width</span>: <span class="number">144px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">height</span>: <span class="number">144px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">border-radius</span>: <span class="number">50%</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">background-color</span>: red;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">margin</span>: <span class="number">24px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">border-radius</span>: <span class="number">50%</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">background</span>: <span class="number">#e6eef4</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">box-shadow</span>: inset <span class="number">14px</span> <span class="number">14px</span> <span class="number">22px</span> <span class="number">#cfd6dc</span>,</span></span><br><span class="line"><span class="language-css"> inset -<span class="number">14px</span> -<span class="number">14px</span> <span class="number">22px</span> <span class="number">#fdffff</span>;</span></span><br><span class="line"><span class="language-css"> }</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"> <span class="selector-class">.aircraft</span> {</span></span><br><span class="line"><span class="language-css"> <span class="attribute">font-size</span>: <span class="number">80px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">display</span>: flex;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">justify-content</span>: center;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">align-items</span>: center;</span></span><br><span class="line"><span class="language-css"> <span class="comment">/* margin-top: 10%; */</span></span></span><br><span class="line"><span class="language-css"> <span class="attribute">padding-top</span>: <span class="number">30px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">color</span>: <span class="number">#4d8af0</span>;</span></span><br><span class="line"><span class="language-css"> }</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"> <span class="selector-class">.top-right</span> {</span></span><br><span class="line"><span class="language-css"> <span class="attribute">width</span>: <span class="number">400px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">height</span>: <span class="number">400px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">float</span>: right;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">background-color</span>: green;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">margin</span>: <span class="number">50px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">border-radius</span>: <span class="number">39px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">background</span>: <span class="number">#e6eef4</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">box-shadow</span>: <span class="number">33px</span> <span class="number">33px</span> <span class="number">67px</span> <span class="number">#cad1d7</span>,</span></span><br><span class="line"><span class="language-css"> -<span class="number">33px</span> -<span class="number">33px</span> <span class="number">67px</span> <span class="number">#ffffff</span>;</span></span><br><span class="line"><span class="language-css"> }</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"> <span class="selector-class">.d21</span>,</span></span><br><span class="line"><span class="language-css"> <span class="selector-class">.d31</span>,</span></span><br><span class="line"><span class="language-css"> <span class="selector-class">.d41</span> {</span></span><br><span class="line"><span class="language-css"> <span class="attribute">display</span>: inline-block;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">width</span>: <span class="number">88px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">height</span>: <span class="number">88px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">border-radius</span>: <span class="number">50%</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">background-color</span>: red;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">margin</span>: <span class="number">21px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">border-radius</span>: <span class="number">50%</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">background</span>: <span class="number">#e6eef4</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">box-shadow</span>: <span class="number">6px</span> <span class="number">6px</span> <span class="number">12px</span> <span class="number">#cad1d7</span>,</span></span><br><span class="line"><span class="language-css"> -<span class="number">6px</span> -<span class="number">6px</span> <span class="number">12px</span> <span class="number">#ffffff</span>;</span></span><br><span class="line"><span class="language-css"> }</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"> <span class="selector-class">.r-aircraft</span> {</span></span><br><span class="line"><span class="language-css"> <span class="attribute">font-size</span>: <span class="number">40px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">display</span>: flex;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">justify-content</span>: center;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">align-items</span>: center;</span></span><br><span class="line"><span class="language-css"> <span class="comment">/* margin-top: 10%; */</span></span></span><br><span class="line"><span class="language-css"> <span class="attribute">padding-top</span>: <span class="number">24px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">color</span>: <span class="number">#4d8af0</span>;</span></span><br><span class="line"><span class="language-css"> }</span></span><br><span class="line"><span class="language-css"></span></span><br><span class="line"><span class="language-css"> <span class="selector-tag">p</span> {</span></span><br><span class="line"><span class="language-css"> <span class="attribute">text-align</span>: center;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">font-size</span>: <span class="number">24px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">color</span>: <span class="number">#576f87</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">font-weight</span>: bold;</span></span><br><span class="line"><span class="language-css"> }</span></span><br><span class="line"><span class="language-css"> </span><span class="tag"></<span class="name">style</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"container"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"top"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"top-left"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">class</span>=<span class="string">"d1"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">i</span> <span class="attr">class</span>=<span class="string">"iconfont aircraft"</span>></span><span class="tag"></<span class="name">i</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">class</span>=<span class="string">"d2"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">i</span> <span class="attr">class</span>=<span class="string">"iconfont aircraft"</span>></span><span class="tag"></<span class="name">i</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">class</span>=<span class="string">"d3"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">i</span> <span class="attr">class</span>=<span class="string">"iconfont aircraft"</span>></span><span class="tag"></<span class="name">i</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">class</span>=<span class="string">"d4"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">i</span> <span class="attr">class</span>=<span class="string">"iconfont aircraft"</span>></span><span class="tag"></<span class="name">i</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"top-right"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">i</span> <span class="attr">class</span>=<span class="string">"iconfont aircraft"</span>></span><span class="tag"></<span class="name">i</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>音乐<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">class</span>=<span class="string">"d21"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">i</span> <span class="attr">class</span>=<span class="string">"iconfont r-aircraft"</span>></span><span class="tag"></<span class="name">i</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">class</span>=<span class="string">"d31"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">i</span> <span class="attr">class</span>=<span class="string">"iconfont r-aircraft"</span>></span><span class="tag"></<span class="name">i</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">class</span>=<span class="string">"d41"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">i</span> <span class="attr">class</span>=<span class="string">"iconfont r-aircraft"</span>></span><span class="tag"></<span class="name">i</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure>
<p>新拟态UI风格Css在线生成:<a href="http://www.fly63.com/tool/neumorphism/">http://www.fly63.com/tool/neumorphism/</a></p>
<p>效果如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/09/23/261f177f88ad8.jpg"/></div></div>
<br>
<div class="note info flat"><p>本文摘自:<a href="http://www.fly63.com/article/detial/10541">CSS 实现新拟态(Neumorphism) UI
uni-app中websocket的使用 断开重连、心跳机制
https://fe32.top/articles/soc145ket/
2021-09-10T11:51:31.000Z
2024-03-26T07:54:35.901Z
心跳机制,也就是客户端间隔一段时间就向服务器发送一条消息,如果服务器收到消息就回复一条信息过来,如果一定时间内没有回复,则表示已经与服务器断开连接了,这个时候就需要进行重连。
正则大全
https://fe32.top/articles/reg384nt/
2021-09-03T13:30:20.000Z
2023-05-17T10:01:12.000Z
<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><div class="tip cogs faa-horizontal animated"><p>这里介绍一些常用正则,也可以安装正则插件,比如 VS code中的 any-rule 插件。安装后 <kbd>Ctrl</kbd> + <kbd>Shift</kbd> +<kbd>P</kbd> , 输入关键字搜索就行,比如 手机 车牌号等等。</p>
</div>
<h1 id="RegExp"><a href="#RegExp" class="headerlink" title="RegExp"></a>RegExp</h1><ol>
<li>火车车次<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^[GCDZTSPKXLY1-9]\d{1,4}$/</span><br></pre></td></tr></table></figure></li>
<li>手机机身码(IMEI)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^\d{15,17}$/</span><br></pre></td></tr></table></figure></li>
<li>必须带端口号的网址(或ip)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^((ht|f)tps?:\/\/)?[\w-]+(\.[\w-]+)+:\d{1,5}\/?$/</span><br></pre></td></tr></table></figure></li>
<li>网址(url,支持端口和”?+参数”和”#+参数)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^(((ht|f)tps?):\/\/)?[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?$/</span><br></pre></td></tr></table></figure></li>
<li>统一社会信用代码<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}$/</span><br></pre></td></tr></table></figure></li>
<li>统一社会信用代码(宽松匹配)(15位/18位/20位数字/字母)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^(([0-9A-Za-z]{15})|([0-9A-Za-z]{18})|([0-9A-Za-z]{20}))$/</span><br></pre></td></tr></table></figure></li>
<li>迅雷链接<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^thunderx?:\/\/[a-zA-Z\d]+=$/</span><br></pre></td></tr></table></figure></li>
<li>ed2k链接(宽松匹配)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^ed2k:\/\/\|file\|.+\|\/$/</span><br></pre></td></tr></table></figure></li>
<li>磁力链接(宽松匹配)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^magnet:\?xt=urn:btih:[0-9a-fA-F]{40,}.*$/</span><br></pre></td></tr></table></figure></li>
<li>子网掩码<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])(?:\.(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])){3}$/</span><br></pre></td></tr></table></figure></li>
<li>linux”隐藏文件”路径<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^\/(?:[^/]+\/)*\.[^/]*/</span><br></pre></td></tr></table></figure></li>
<li>linux文件夹路径<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^\/(?:[^/]+\/)*$/</span><br></pre></td></tr></table></figure></li>
<li>linux文件路径<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^\/(?:[^/]+\/)*[^/]+$/</span><br></pre></td></tr></table></figure></li>
<li>window”文件夹”路径<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^[a-zA-Z]:\\(?:\w+\\?)*$/</span><br></pre></td></tr></table></figure></li>
<li>window下”文件”路径<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^[a-zA-Z]:\\(?:\w+\\)*\w+\.\w+$/</span><br></pre></td></tr></table></figure></li>
<li>股票代码(A股)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^(s[hz]|S[HZ])(000[\d]{3}|002[\d]{3}|300[\d]{3}|600[\d]{3}|60[\d]{4})$/</span><br></pre></td></tr></table></figure></li>
<li>大于等于0, 小于等于150, 支持小数位出现5, 如145.5, 用于判断考卷分数<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^150$|^(?:\d|[1-9]\d|1[0-4]\d)(?:\.5)?$/</span><br></pre></td></tr></table></figure></li>
<li>html注释<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^<!--[\s\S]*?-->$/</span><br></pre></td></tr></table></figure></li>
<li>md5格式(32位)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^([a-f\d]{32}|[A-F\d]{32})$/</span><br></pre></td></tr></table></figure></li>
<li>GUID/UUID<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^[a-f\d]{4}(?:[a-f\d]{4}-){4}[a-f\d]{12}$/i</span><br></pre></td></tr></table></figure></li>
<li>版本号(version)格式必须为X.Y.Z<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^\d+(?:\.\d+){2}$/</span><br></pre></td></tr></table></figure></li>
<li>视频(video)链接地址(视频格式可按需增删)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^https?:\/\/(.+\/)+.+(\.(swf|avi|flv|mpg|rm|mov|wav|asf|3gp|mkv|rmvb|mp4))$/i</span><br></pre></td></tr></table></figure></li>
<li>图片(image)链接地址(图片格式可按需增删)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^https?:\/\/(.+\/)+.+(\.(gif|png|jpg|jpeg|webp|svg|psd|bmp|tif))$/i</span><br></pre></td></tr></table></figure></li>
<li>24小时制时间(HH:mm:ss)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d$/</span><br></pre></td></tr></table></figure></li>
<li>12小时制时间(hh:mm:ss)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^(?:1[0-2]|0?[1-9]):[0-5]\d:[0-5]\d$/</span><br></pre></td></tr></table></figure></li>
<li>base64格式<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^\s*data:(?:[a-z]+\/[a-z0-9-+.]+(?:;[a-z-]+=[a-z0-9-]+)?)?(?:;base64)?,([a-z0-9!$&',()*+;=\-._~:@/?%\s]*?)\s*$/i</span><br></pre></td></tr></table></figure></li>
<li>数字/货币金额(支持负数、千分位分隔符)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^-?\d+(,\d{3})*(\.\d{1,2})?$/</span><br></pre></td></tr></table></figure></li>
<li>数字/货币金额 (只支持正数、不支持校验千分位分隔符)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/(?:^[1-9]([0-9]+)?(?:\.[0-9]{1,2})?$)|(?:^(?:0)$)|(?:^[0-9]\.[0-9](?:[0-9])?$)/</span><br></pre></td></tr></table></figure></li>
<li>银行卡号(10到30位, 覆盖对公/私账户, 参考<a href="https://pay.weixin.qq.com/wiki/doc/api/xiaowei.php?chapter=22_1">微信支付</a>)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^[1-9]\d{9,29}$/</span><br></pre></td></tr></table></figure></li>
<li>中文姓名<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^(?:[\u4e00-\u9fa5·]{2,16})$/</span><br></pre></td></tr></table></figure></li>
<li>英文姓名<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/(^[a-zA-Z][a-zA-Z\s]{0,20}[a-zA-Z]$)/</span><br></pre></td></tr></table></figure></li>
<li>车牌号(新能源)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-HJ-NP-Z](?:((\d{5}[A-HJK])|([A-HJK][A-HJ-NP-Z0-9][0-9]{4}))|[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳])$/</span><br></pre></td></tr></table></figure></li>
<li>车牌号(非新能源)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-HJ-NP-Z][A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]$/</span><br></pre></td></tr></table></figure></li>
<li>车牌号(新能源+非新能源)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-HJ-NP-Z][A-HJ-NP-Z0-9]{4,5}[A-HJ-NP-Z0-9挂学警港澳]$/</span><br></pre></td></tr></table></figure></li>
<li>手机号(mobile phone)中国(严谨), 根据工信部2019年最新公布的手机号段<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^(?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[189]))\d{8}$/</span><br></pre></td></tr></table></figure></li>
<li>手机号(mobile phone)中国(宽松), 只要是13,14,15,16,17,18,19开头即可<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^(?:(?:\+|00)86)?1[3-9]\d{9}$/</span><br></pre></td></tr></table></figure></li>
<li>手机号(mobile phone)中国(最宽松), 只要是1开头即可, 如果你的手机号是用来接收短信, 优先建议选择这一条<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^(?:(?:\+|00)86)?1\d{10}$/</span><br></pre></td></tr></table></figure></li>
<li>date(日期)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^\d{1,4}(-)(1[0-2]|0?[1-9])\1(0?[1-9]|[1-2]\d|30|31)$/</span><br></pre></td></tr></table></figure></li>
<li>email(邮箱)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/</span><br></pre></td></tr></table></figure></li>
<li>座机(tel phone)电话(国内),如: 0341-86091234<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^(?:(?:\d{3}-)?\d{8}|^(?:\d{4}-)?\d{7,8})(?:-\d+)?$/</span><br></pre></td></tr></table></figure></li>
<li>身份证号(1代,15位数字)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^[1-9]\d{7}(?:0\d|10|11|12)(?:0[1-9]|[1-2][\d]|30|31)\d{3}$/</span><br></pre></td></tr></table></figure></li>
<li>身份证号(2代,18位数字),最后一位是校验位,可能为数字或字符X<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^[1-9]\d{5}(?:18|19|20)\d{2}(?:0[1-9]|10|11|12)(?:0[1-9]|[1-2]\d|30|31)\d{3}[\dXx]$/</span><br></pre></td></tr></table></figure></li>
<li>身份证号, 支持1/2代(15位/18位数字)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/(^\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(^\d{6}(18|19|20)\d{2}(0[1-9]|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)$)/</span><br></pre></td></tr></table></figure></li>
<li>护照(包含香港、澳门)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/(^[EeKkGgDdSsPpHh]\d{8}$)|(^(([Ee][a-fA-F])|([DdSsPp][Ee])|([Kk][Jj])|([Mm][Aa])|(1[45]))\d{7}$)/</span><br></pre></td></tr></table></figure></li>
<li>帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线组合<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^[a-zA-Z]\w{4,15}$/</span><br></pre></td></tr></table></figure></li>
<li>中文/汉字<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0])+$/</span><br></pre></td></tr></table></figure></li>
<li>小数<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^\d+\.\d+$/</span><br></pre></td></tr></table></figure></li>
<li>数字<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^\d{1,}$/</span><br></pre></td></tr></table></figure></li>
<li>html标签(宽松匹配)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/<(\w+)[^>]*>(.*?<\/\1>)?/</span><br></pre></td></tr></table></figure></li>
<li>qq号格式正确<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^[1-9][0-9]{4,10}$/</span><br></pre></td></tr></table></figure></li>
<li>数字和字母组成<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^[A-Za-z0-9]+$/</span><br></pre></td></tr></table></figure></li>
<li>英文字母<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^[a-zA-Z]+$/</span><br></pre></td></tr></table></figure></li>
<li>小写英文字母组成<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^[a-z]+$/</span><br></pre></td></tr></table></figure></li>
<li>大写英文字母<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^[A-Z]+$/</span><br></pre></td></tr></table></figure></li>
<li>密码强度校验,最少6位,包括至少1个大写字母,1个小写字母,1个数字,1个特殊字符<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Z])(?=\S*[a-z])(?=\S*[!@#$%^&*? ])\S*$/</span><br></pre></td></tr></table></figure></li>
<li>用户名校验,4到16位(字母,数字,下划线,减号)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^[a-zA-Z0-9_-]{4,16}$/</span><br></pre></td></tr></table></figure></li>
<li>ip-v4[:端口]<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])(?::(?:[0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?$/</span><br></pre></td></tr></table></figure></li>
<li>ip-v6[:端口]<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^(?:(?:(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))|\[(?:(?:(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))\](?::(?:[0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?$/i</span><br></pre></td></tr></table></figure></li>
<li>16进制颜色<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/</span><br></pre></td></tr></table></figure></li>
<li>微信号(wx),6至20位,以字母开头,字母,数字,减号,下划线<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^[a-zA-Z][-_a-zA-Z0-9]{5,19}$/</span><br></pre></td></tr></table></figure></li>
<li>邮政编码(中国)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^(0[1-7]|1[0-356]|2[0-7]|3[0-6]|4[0-7]|5[1-7]|6[1-7]|7[0-5]|8[013-6])\d{4}$/</span><br></pre></td></tr></table></figure></li>
<li>中文和数字<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^((?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0])|(\d))+$/</span><br></pre></td></tr></table></figure></li>
<li>不能包含字母<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^[^A-Za-z]*$/</span><br></pre></td></tr></table></figure></li>
<li>java包名<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^([a-zA-Z_]\w*)+([.][a-zA-Z_]\w*)+$/</span><br></pre></td></tr></table></figure></li>
<li>mac地址<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^((([a-f0-9]{2}:){5})|(([a-f0-9]{2}-){5}))[a-f0-9]{2}$/i</span><br></pre></td></tr></table></figure></li>
<li>匹配连续重复的字符<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/(.)\1+/</span><br></pre></td></tr></table></figure></li>
<li>数字和英文字母组成,并且同时含有数字和英文字母<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^(?=.*[a-zA-Z])(?=.*\d).+$/</span><br></pre></td></tr></table></figure></li>
<li>香港身份证<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^[a-zA-Z]\d{6}\([\dA]\)$/</span><br></pre></td></tr></table></figure></li>
<li>澳门身份证<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^[1|5|7]\d{6}[(\d)]{3}$/</span><br></pre></td></tr></table></figure></li>
<li>台湾身份证<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span
Company Job Abbreviations
https://fe32.top/articles/jo4116ba/
2021-08-25T13:56:40.000Z
2024-01-28T12:12:02.000Z
<h2 id="PM"><a href="#PM" class="headerlink" title="PM"></a>PM</h2><p> 项目经理( Project Manager )<br> 从职业角度,是指企业建立以项目经理责任制为核心,对项目实行质量、安全、进度、成本管理的责任保证体系和全面提高项目管理水平设立的重要管理岗位。项目经理是为项目的成功策划和执行负总责的人。<br> 项目经理是项目团队的领导者,项目经理首要职责是在预算范围内按时优质地领导项目小组完成全部项目工作内容,并使客户满意。为此项目经理必须在一系列的项目计划、组织和控制活动中做好领导工作,从而实现项目目标。<br> 当然在互联网公司这个有着项目经理or产品经理的意思。</p>
<h2 id="RD"><a href="#RD" class="headerlink" title="RD"></a>RD</h2><p> 研发(Research and Development)<br> 如:软件RD工程师就是软件研发工程师,诸如PHP程序猿,Java程序猿,无论是爱疯的还是安卓的都是属于这一类别。偏向于后端的技术实现。</p>
<h2 id="FE"><a href="#FE" class="headerlink" title="FE"></a>FE</h2><p> 前端(Front-End);前端开发(Front-End Development)<br> FE是web前端研发、前端开发的意思!</p>
<h2 id="UE"><a href="#UE" class="headerlink" title="UE"></a>UE</h2><p> 用户体验(User Experience,简称UX或 UE)<br> 是一种纯主观的在用户使用一个产品(服务)的过程中建立起来的心理感受。因为它是纯主观的,就带有一定的不确定因素。<br> 个体差异也决定了每个用户的真实体验是无法通过其他途径来完全模拟或再现的。但是对于一个界定明确的用户群体来讲,其用户体验的共性是能够经由良好设计的实验来认识到。<br> 计算机技术和互联网的发展,使技术创新形态正在发生转变,以用户为中心、以人为本越来越得到重视,用户体验也因此被称做创新2.0模式的精髓。<br> 另外还有有个组合叫法:UED(产品交互设计师,用户体验师)。</p>
<h2 id="UI"><a href="#UI" class="headerlink" title="UI"></a>UI</h2><p> 用户界面(User Interface)<br> UI设计则是指对软件的人机交互、操作逻辑、界面美观的整体设计。好的UI设计不仅是让软件变得有个性有品味,还要让软件的操作变得舒适、简单、自由、充分体现软件的定位和特点。<br> UI还有其它的意义,如Unit Interval,Univ of Iowa,Unlock Instruction,Urgent Interrupt。</p>
<h2 id="QA"><a href="#QA" class="headerlink" title="QA"></a>QA</h2><p> 测试(QUALITY ASSURANCE,中文意思是“质量保证”)<br> 其在ISO8402:1994中的定义是“为了提供足够的信任表明实体能够满足质量要求,而在质量管理体系中实施并根据需要进行证实的全部有计划和有系统的活动”。有些推行ISO9000的组织会设置这样的部门或岗位,负责ISO9000标准所要求的有关质量保证的职能,担任这类工作的人员就叫做QA人员。</p>
<h2 id="OP"><a href="#OP" class="headerlink" title="OP"></a>OP</h2><p> 运维(Operations)<br> OP这个词语代表的意思很多,这个简称来自于英文的Operations一词。我也不清楚谁最早用op代表运维工程师,不过2010年开始,这个词慢慢被很多人所知道。<br> OP工作内容主要就是维护公司的服务器能够正常提供服务,细分的话包括系统部分,网络部分,应用程序部分,数据库部分,具体根据公司的规模和职位职能不同,运维的定义也不同。现在市面上主要的OP有三种:网络游戏运维,网站运维,大型项目测试和生产环境运维。</p>
<h2 id="DBA"><a href="#DBA" class="headerlink" title="DBA"></a>DBA</h2><p> 数据库管理员(Database Administrator,简称DBA)<br> 是一个负责管理和维护数据库服务器的人。数据库管理员负责全面管理和控制数据库系统。这个职位对不同的人意味着不同的意义。<br> 另外还有DB,既数据库(Database)。</p>
<p>还有就是互联网产品设计常用文档类型的缩写: BRD、MRD、PRD、FSD等</p>
<h2 id="MRD"><a href="#MRD" class="headerlink" title="MRD"></a>MRD</h2><p> 市场需求文档(Market Requirements Document)<br> 获得老大的认同后,产品进入实施,需要先出MRD,具体来说要有更细致的市场与竞争对手分析,通过哪些功能来实现商业目的,功能/非功能需求分哪几块,功能的优先级等等。实际工作中,这个阶段PD可能的产出物有Mind Manager的思维图,Excel的Feature List等。<br>市场需求文档(MRD)重点放在为一个被提议的新产品或者现有产品的改进定义市场需求。与BRD指出商业问题和解决这些问题的解决方案不同,MRD更深入提议解决方案的细节。它包括一些或者所有这些细节:</p>
<ol>
<li>解决商业问题所需要的特色</li>
<li>市场竞争分析</li>
<li>功能和非功能需求</li>
<li>特色/需求的优先级</li>
<li>用例</li>
</ol>
<p>MRD通常是由拥有产品经理,产品营销经理或者行业分析师头衔的人撰写的。MRD通常是一份连续的5-25页Word文档,或者正如之后描述那样在一些机构中甚至更长。</p>
<h2 id="PRD"><a href="#PRD" class="headerlink" title="PRD"></a>PRD</h2><p> 产品需求文档(Product Requirements Document)<br>进步一细化,这部分是PD写得最多的内容,也就是传统意义上的需求分析,我们这里主要指UC(use case)文档。主要内容有,功能使用的具体描述(每个UC一般有用例简述、行为者、前置条件、后置条件、UI描述、流程/子流程/分支流程,等几大块),Visio做的功能点业务流程,界面的说明,demo等。Demo方面,可能用dreamweaver、ps甚至画图板简单画一下,有时候也会有UI/UE支持,出高保真的demo,开发将来可以直接用的那种。<br> 产品需求文档(PRD)重点放在为一个被提议的新产品或者现有产品的改进定义市场需求。与MRD侧重于从市场需要角度看需求的不同,PRD侧重于从产品本身角度看待需求。通常在特点和功能需求上更深入细节,并也可能包括屏幕截图和用户界面流程。在那些MRD不包括具体需求和用例的机构中,PRD就包含这些具体内容。PRD通常是由拥有产品经理,行业分析师或者产品分析师头衔的人撰写的。PRD通常是一份连续的20-50页Word文档,或者针对复杂产品甚至更长。<br> 提醒:一些机构将这里描述的MRD和PRD合并成一个文档,并称最后的文档为MRD。在这种情况下,MRD包括本段描述的内容,也包括上一段描述PRD的内容,并且可能超过50页。</p>
<h2 id="FSD"><a href="#FSD" class="headerlink" title="FSD"></a>FSD</h2><p> 功能详细说明(Functional Specifications Document)<br>有一点像“概要设计”,这步就开始往开发衔接了,产品UI、业务逻辑的细节都要确定,细化文档并保持更新。相应的,有很多内容,比如表结构设计,要由项目经理来编写了。<br> 功能规格文档(FSD)把焦点集中在实现,定义产品功能需求的全部细节。FSD可能通过一张张的截屏和一条条功能点来定义产品规格。这是一份可以直接让工程师创建产品的文档。<br>与MRD和PRD侧重于以市场需要和产品角度看需求不同,FSD把重点放在了以表格形式定义产品细节,再让工程师实现这些细节。FSD也可能包括完整的屏幕截图和UI设计细节。<br>FSD通常是由拥有产品分析师,工程领导或者项目经理头衔的人撰写的 – 作者通常属于工程部门。通常一个连续几十页的Word或类似文档。</p>
<h2 id="CEO"><a href="#CEO" class="headerlink" title="CEO"></a>CEO</h2><p> CEO(Chief Executive Officer),即首席执行官,是美国人在20世纪60年代进行公司治理结构改革创新时的产物。<br> 由于市场风云变幻,决策的速度和执行的力度比以往任何时候都更加重要。 传统的“董事会决策、经理层执行”的公司体制已经难以满足决策的需要。而且, 决策层和执行层之间存在的信息传递时滞和沟通障碍、决策成本的增加,已经严 重影响经理层对企业重大决 策的快速反应和执行能力。而解决这一问题的首要一 点,就是让经理人拥有更多自主决策的权力,让经理人更多为自己的决策奋斗、对 自己的行为负责。CEO就是这种变革的产物。CEO在某种意义上代表着将原来董事会 手中的一些决策权过渡到经营层手中。<br> CEO与总经理,形式上都是企业的“一把手”,CEO既是行政一把手,又是股东 权益代言人————大多数情况下,CEO是作为董事会成员出现的,总经理则不一定 是董事会成员。从这个意义上讲,CEO代表着企业,并对企业经营负责。 由于国外没有类似的上级主管和来自四面八方的牵制,CEO的权威比国内的总经理们更绝对,但他们绝不会像总经理那样过多介入公司的具体事务。CEO作出总体决 策后,具体执行权力就会下放。所以有人说,CEO就像我国50%的董事长加上50%的总经理。<br> 一般来讲,CEO的主要职责有三方面:①对公司所有重大事务和人事任免进行决 策,决策后,权力就下放给具体主管,CEO具体干预的较少;②营造一种促使员工愿 意为公司服务的企业文化;③把公司的整体形象推销出去。</p>
<h2 id="CTO"><a href="#CTO" class="headerlink" title="CTO"></a>CTO</h2><p> CTO是个技术主管,CIO是个具有技术背景或对技术有些了解地公司高层。</p>
<p> 通常CIO向CEO汇报,或向CFO汇报。CIO不需要是个技术大拿,但对技术必须非常敏感,并能发掘技术带给公司地潜力。随着IT在各公司地重要性日渐提高,CIO地地位也渐高,有时能进入公司地最高决策层。CIO是个桥梁,把公司地商业模式和技术连接起来。基本上CTO就是一个技术大拿,熟练掌握公司地核心技术,并可以带领团队开发或使用新技术来帮助公司达到目标。基本上CTO不会是公司地最高层。<br>企业CIO和CTO有着明显地区别,但是最大地区别不是在于他们对技术地掌握程度和深度,而是在于他们对企业战略地驾驭能力和适应能力。<br> CTO有时候也会成为公司地最高层,特别是一些以技术为核心竞争力地企业来说。首先,我们来解读一下什么是CTO。其实,CTO(首席技术官)作为一个外来名词,在中国还不多见,随着网络热潮传进中国地CXO系列中地一员,CTO给人留下地印象只是技术人员所能达到地最高职位。“但当技术日益成为影响企业发展地决定因素时,CTO也就成为对企业发展起着决定性作用地人群之一。<br> 在美国,CTO除了负责技术支持和技术改良等日常工作外,其主要职责是设计公司地未来工作。从某种意义上说,CTO地首要工作是提出公司未来两三年内地产品和服务地技术发展方向。<br> 尽管CTO这个名词是引进来了,但在角色职能定义方面同国外还存在一定差距。作为一个高科技公司地CTO,其更多地工作应该是前瞻性地,也就是制定下一代产品地策略和进行研究工作,属于技术战略地重要执行者。<br>在国内来看,大部分地企业里地“CTO”都是过去地“工程师”摇身一变而成地,因此带着很强地技术色彩。在一些通过技术安身立命地高科技企业,这些工程师出身地CTO也往往能够占据核心领导地位。但是在其他地行业中,例如一些传统地行业,一些把市场营销能力作为核心竞争力地企业,CTO地作用就大打折扣,CIO就逐渐浮出水面了。<br> “CIO”即信息总监,他通过组织和利用企业地IT资源,为企业创造效益。通过信息化掌握了企业地业务命脉以及战略方向地CIO,很可能向决策管理层地地位继续上升,直到达到权力地顶峰—CEO。<br>一家美国主导企业地首席执行官和一群首席信息官进行了一次谈话,讨论首席信息官在现代公司中地作用。在谈话进行到一半地时候,他直截了当地说:“首席信息官也许是我最重要地经理人。没有他们,我不知道我地公司会是怎样。”由此可见CIO在企业中地重要作用了。<br> 在CIO成功地基本素质中,其中有一项是要精通企业以及相关行业地知识。要搞信息化,一个CIO至少要熟悉企业地研发、生产、计划、营销、市场、物流等核心业务流程,熟悉企业地财务管理、组织结构、行政程序、人力资源管理等基础资源,以及企业发展地远景、价值观等企业地文化范畴。在这基础上,CIO才能对企业地IT建设和信息资源做出正确地规划。<br> 因此如果你想成为一个成功地CIO,那么最好远离电脑,去积极培养作为企业管理者应该具备地各种能力。对500名CIO所做地调查发现,70%地人认为通往成功地关键是有效地沟通;58%地人选择谙熟商业流程和运作;而46%地人则认为战略性地思想和计划能力很重要。而此前被认为很重要地IT技能,只获得了10%地认可。这不能不说是一个巨大地观念改变。</p>
<h2 id="CFO"><a href="#CFO" class="headerlink" title="CFO"></a>CFO</h2><p> CFO(Chief Financial Officer)意指公司首席财政官或财务总监,是现代公司中最重要、最有价值的顶尖管理职位之一,是掌握着企业的神经系统(财务信息)和血液系统(现金资源)灵魂人物。</p>
<p> 做一名成功的CFO需要具备丰富的金融理论知识和实务经验。公司理财与金融市场交互、项目估价、风险管理、产品研发、战略规划、企业核心竞争力的识别与建立以及洞悉信息技术及电子商务对企业的冲击等自然都是CFO职责范围内的事。</p>
<p> 在一个大型公司运作中,CFO是一个穿插在金融市场操作和公司内部财务管理之间的角色。担当CFO的人才大多是拥有多年在金融市场驰骋经验的人。在美国,优秀的CFO常常在华尔街做过成功的基金经理人。</p>
<h2 id="COO"><a href="#COO" class="headerlink" title="COO"></a>COO</h2><p> COO( chief Operation officer) 的职责主要是负责公司的日常营运,辅助CEO的工作。<br> 一般来讲,COO负责公司职能管理组织体系的建设,并代表CEO处理企业的日常职能事务。如果公司未设有总裁职务,则COO还要承担整体业务管理的职能,主管企业营销与综合业务拓展,负责建立公司整个的销售策略与政策,组织生产经营,协助CEO制定公司的业务发展计划,并对公司的经营绩效进行考核。</p>
<h2 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h2><table>
<thead>
<tr>
<th align="left">中文解释</th>
<th align="left">英文缩写</th>
</tr>
</thead>
<tbody><tr>
<td align="left">计算机/互联网/通讯</td>
<td align="left">Technology/Internet</td>
</tr>
<tr>
<td align="left">首席技术执行官</td>
<td align="left">CTO/VP Engineering</td>
</tr>
<tr>
<td align="left">技术总监/经理</td>
<td align="left">Technical Director/Manager</td>
</tr>
<tr>
<td align="left">信息技术经理</td>
<td align="left">IT Manager</td>
</tr>
<tr>
<td align="left">信息技术主管</td>
<td align="left">IT Supervisor</td>
</tr>
<tr>
<td align="left">信息技术专员</td>
<td align="left">IT Specialist</td>
</tr>
<tr>
<td align="left">项目经理/主管</td>
<td align="left">Project Manager/Supervisor</td>
</tr>
<tr>
<td align="left">项目执行/协调人员</td>
<td align="left">Project Specialist / Coordinator</td>
</tr>
<tr>
<td align="left">系统分析员</td>
<td align="left">System Analyst</td>
</tr>
<tr>
<td align="left">高级软件工程师</td>
<td align="left">Senior Software Engineer</td>
</tr>
<tr>
<td align="left">软件工程师</td>
<td align="left">Software Engineer</td>
</tr>
<tr>
<td align="left">系统工程师</td>
<td align="left">System Engineer</td>
</tr>
<tr>
<td align="left">高级硬件工程师</td>
<td align="left">Senior Hardware Engineer</td>
</tr>
<tr>
<td align="left">硬件工程师</td>
<td align="left">Hardware Engineer</td>
</tr>
<tr>
<td align="left">通信技术工程师</td>
<td align="left">Communications Engineer</td>
</tr>
<tr>
<td align="left">ERP技术/应用顾问</td>
<td align="left">ERP Technical/Application Consultant</td>
</tr>
<tr>
<td align="left">数据库工程师</td>
<td align="left">Database Engineer</td>
</tr>
<tr>
<td align="left">技术支持经理</td>
<td align="left">Technical Support Manager</td>
</tr>
<tr>
<td align="left">技术支持工程师</td>
<td align="left">Technical Support Engineer</td>
</tr>
<tr>
<td align="left">品质经理</td>
<td align="left">QA Manager</td>
</tr>
<tr>
<td align="left">信息安全工程师</td>
<td align="left">Information Security Engineer</td>
</tr>
<tr>
<td align="left">软件测试工程师</td>
<td align="left">Software QA Engineer</td>
</tr>
<tr>
<td align="left">硬件测试工程师</td>
<td align="left">Hardware QA Engineer</td>
</tr>
<tr>
<td align="left">测试员</td>
<td align="left">Test Engineer</td>
</tr>
<tr>
<td align="left">网站营运经理/主管</td>
<td align="left">Web Operations Manager/Supervisor</td>
</tr>
<tr>
<td align="left">网络工程师</td>
<td align="left">Network Engineer</td>
</tr>
<tr>
<td align="left">系统管理员/网管</td>
<td align="left">System Manager/Webmaster</td>
</tr>
<tr>
<td align="left">网页设计/制作</td>
<td align="left">Web Designer/Production</td>
</tr>
<tr>
<td align="left">技术文员/助理</td>
<td align="left">Technical Clerk/Assistant</td>
</tr>
</tbody></table>
<h2 id="其他岗位较常见简称"><a href="#其他岗位较常见简称" class="headerlink" title="其他岗位较常见简称"></a>其他岗位较常见简称</h2><p>首席品牌官【CBO】 chief brand officer</p>
<p>首席文化官【CCO】 Chief Cultural Officer</p>
<p>开发总监【CDO】 chief Development officer</p>
<p>人事总监 【CHO】 Chief Human resource officer</p>
<p>首席知识官【CKO】 chief knowledge officer</p>
<p>首席市场官【CMO】 chief Marketing officer</p>
<p>首席谈判官【CNO】 chief Negotiation officer</p>
<p>公关总监【CPO】 chief Public relation officer</p>
<p>质量总监【CQO】 chief Quality officer</p>
<p>销售总监【CSO】 chief Sales officer</p>
<p>评估总监【CVO】 chief Valuation officer </p>
<p>CAO:Answerer 首席答辩人,专门负责解答媒体、债权人和用户等有关网站倒闭问题的询问。</p>
<p>CBO:Business Plan 首席商业计划官,是首席财务官的助理之一,专门针对不同的投资人制订相应的BP。</p>
<p>CCO:Cost Control 首席成本控制官,凡超过100元以上的支出必须由CC0批准。</p>
<p>CDO:Domain name 首席域名官,负责公司域名注册、网站清盘时域名的拍卖、域名法律纠纷等相关问题。</p>
<p>CGO:Guideline 首席方针制订官,规划公司的宏伟蓝图,一般是5年以后的目标。</p>
<p>CHO:Harmony 首席协调官,调解投资者和经营者之间的冲突,并确保公司内部矛盾不要泄露。</p>
<p>CJO:Judge 首席执法官,解决内部劳资纠纷,包括员工对降薪、辞退补偿等所引起的问题。</p>
<p>CKO:Keep connecting,网络连接专员,最繁忙的岗位之一,当中国电信的网络连接中断时及时向员工通报。</p>
<p>CLO:Lawer 首席律师,负责公司被控侵权时的应诉以及各种合同文本的审核。</p>
<p>CMO:Media 首席媒体官,保持和媒体之间的友好关系,为公司随时发布新闻做准备。</p>
<p>CNO:News 首席新闻官,向媒体披露公司网站被黑、裁员、被收购等重大新闻。</p>
<p>CPO:Privacy 首席隐私官,负责公司内部员工Email、ICQ、OICQ等通信内容的监控。</p>
<p>CQO:Quantity Making,数量指标编造专家,负责注册用户数量、页面浏览、营业收入等指标的编造。</p>
<p>CRO:Reduce the stafftrimmer 首席裁员官,负责所有与裁员有关的事务,直接向股东大会负责,包括董事长在内都不得干预其工作。</p>
<p>CSO:Strategy 首席战略官,由已经退位的公司主要创建人担任,在政府机关一般称为调研员或顾问。</p>
<p>CTO:Testing 首席测试官,是公司唯一负责网站建设的专家,由于技术开发不成熟,需要一直测试下去。</p>
<p>CUO:Union 首席联盟官,以战略联盟的名义,专门寻找有收购自己意向的网站。</p>
<p>CVO:VC reception 风险投资商接待专员,首席财务官的另一重要助理。</p>
<p>CWO:Writer 首席网络写手,负责将小事扩大化,通过炒作达到扩大网站知名度的目的,其下属为COO。</p>
<p>CXO:Xingxiang(因为中国特有,所以只能用汉语拼音表示) 网站形象代言人,一般由学历不高且没有任何网络知识的年轻人担任。</p>
<p>CYO:Yearly 公司元老,这是一个荣誉称号,授予在同一网站工作满一年的员工(这个职位通常空缺)。</p>
<p>CZO:Zero 最后离开公司的一个人,负责关好门窗,将公司大门钥匙交给物业管理处,可以由CAO兼任。</p>
<table>
<thead>
<tr>
<th align="left">中文解释</th>
<th align="left">英文缩写</th>
</tr>
</thead>
<tbody><tr>
<td align="left">销售总监</td>
<td align="left">Sales Director</td>
</tr>
<tr>
<td align="left">销售经理</td>
<td align="left">Sales Manager</td>
</tr>
<tr>
<td align="left">区域销售经理</td>
<td align="left">Regional Sales Manager</td>
</tr>
<tr>
<td align="left">客户经理</td>
<td align="left">Sales Account Manager</td>
</tr>
<tr>
<td align="left">渠道/分销经理</td>
<td align="left">Channel/Distribution Manager</td>
</tr>
<tr>
<td align="left">渠道主管</td>
<td align="left">Channel Supervisor</td>
</tr>
<tr>
<td align="left">销售主管</td>
<td align="left">Sales Supervisor</td>
</tr>
<tr>
<td align="left">销售代表</td>
<td align="left">Sales Representative / Executive</td>
</tr>
<tr>
<td align="left">销售工程师</td>
<td align="left">Sales Engineer</td>
</tr>
<tr>
<td align="left">医药代表</td>
<td align="left">Pharmaceutical Sales Representative</td>
</tr>
<tr>
<td align="left">保险代理</td>
<td align="left">Insurance Agent</td>
</tr>
<tr>
<td align="left">销售助理</td>
<td align="left">Sales Assistant / Trainee</td>
</tr>
<tr>
<td align="left">商务经理</td>
<td align="left">Business Manager</td>
</tr>
<tr>
<td align="left">商务专员/助理</td>
<td align="left">Business Executive/Assistant</td>
</tr>
<tr>
<td align="left">销售行政经理</td>
<td align="left">Sales Admin. Manager</td>
</tr>
<tr>
<td align="left">销售行政主管</td>
<td align="left">Sales Admin. Supervisor</td>
</tr>
<tr>
<td align="left">售前/售后技术服务经理</td>
<td align="left">Technical Service Manager</td>
</tr>
<tr>
<td align="left">售前/售后技术服务主管</td>
<td align="left">Technical Service Supervisor</td>
</tr>
<tr>
<td align="left">售前/售后技术服务工程师</td>
<td align="left">Technical Service Engineer</td>
</tr>
<tr>
<td align="left">售后/客户服务(非技术)经理</td>
<td align="left">Customer Service Manager</td>
</tr>
<tr>
<td align="left">售后/客户服务(非技术)主管</td>
<td align="left">Customer Service Supervisor</td>
</tr>
<tr>
<td align="left">售后/客户服务(非技术)专员</td>
<td align="left">Customer Service Executive</td>
</tr>
<tr>
<td align="left">经销商</td>
<td align="left">Distributor</td>
</tr>
<tr>
<td align="left">市场/公关/广告</td>
<td align="left">Marketing/PR/Advertising</td>
</tr>
<tr>
<td align="left">市场/广告总监</td>
<td align="left">Marketing/Advertising Director/VP</td>
</tr>
<tr>
<td align="left">市场/营销经理</td>
<td align="left">Marketing Manager</td>
</tr>
<tr>
<td align="left">市场/营销主管</td>
<td align="left">Marketing Supervisor</td>
</tr>
<tr>
<td align="left">市场/营销专员</td>
<td align="left">Marketing Executive/Communication</td>
</tr>
<tr>
<td align="left">市场助理</td>
<td align="left">Marketing Assistant / Trainee</td>
</tr>
<tr>
<td align="left">产品/品牌经理</td>
<td align="left">Product/Brand Manager</td>
</tr>
<tr>
<td align="left">产品/品牌主管</td>
<td align="left">Product/Brand Supervisor</td>
</tr>
<tr>
<td align="left">市场通路经理</td>
<td align="left">Trade Marketing Manager</td>
</tr>
<tr>
<td align="left">市场通路主管</td>
<td align="left">Trade Marketing Supervisor</td>
</tr>
<tr>
<td align="left">促销经理</td>
<td align="left">Promotions Manager</td>
</tr>
<tr>
<td align="left">促销主管</td>
<td align="left">Promotions Supervisor</td>
</tr>
<tr>
<td align="left">促销员</td>
<td align="left">Promotions Specialist</td>
</tr>
<tr>
<td align="left">市场分析/调研人员</td>
<td align="left">Market Analyst/ Research Analyst</td>
</tr>
<tr>
<td align="left">公关/会务经理</td>
<td align="left">Public Relations Manager</td>
</tr>
<tr>
<td align="left">公关/会务主管</td>
<td align="left">Public Relations Supervisor</td>
</tr>
<tr>
<td align="left">公关/会务专员</td>
<td align="left">Public Relations Executive</td>
</tr>
<tr>
<td align="left">媒介经理</td>
<td align="left">Media Manager</td>
</tr>
<tr>
<td align="left">媒介人员</td>
<td align="left">Media Specialist</td>
</tr>
<tr>
<td align="left">企业/业务发展经理</td>
<td align="left">Business Development Manager</td>
</tr>
<tr>
<td align="left">企业策划人员</td>
<td align="left">Corporate Planning</td>
</tr>
<tr>
<td align="left">广告策划/设计/文案</td>
<td align="left">Advertising Creative/Design/Copy writer</td>
</tr>
<tr>
<td align="left">总经理</td>
<td align="left">GM(General Manager)</td>
</tr>
<tr>
<td align="left">副总裁</td>
<td align="left">VP(Vice President)</td>
</tr>
<tr>
<td align="left">第一副总裁</td>
<td align="left">FVP(First Vice President)</td>
</tr>
<tr>
<td align="left">副总裁助理</td>
<td align="left">AVP(Assistant Vice President)</td>
</tr>
<tr>
<td align="left">人力资源总监</td>
<td align="left">HRD(Human Resource Director)</td>
</tr>
<tr>
<td align="left">运营总监</td>
<td align="left">OD(Operations Director)</td>
</tr>
<tr>
<td align="left">市场总监</td>
<td align="left">MD(Marketing Director)</td>
</tr>
<tr>
<td align="left">运作经理</td>
<td align="left">OM(Operations Manager)</td>
</tr>
<tr>
<td align="left">部门经理</td>
<td align="left">BM(Branch Manager)</td>
</tr>
<tr>
<td align="left">区域经理</td>
<td align="left">(District
什么是Docker?
https://fe32.top/articles/do0228ck/
2021-06-28T08:38:48.000Z
2023-05-17T10:01:12.000Z
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/28/7f84e198802cb.png"/></div></div>
<h1 id="容器技术的起源"><a href="#容器技术的起源" class="headerlink" title="容器技术的起源"></a>容器技术的起源</h1><p>假设你们公司正在秘密研发下一个“今日头条”APP,我们姑且称为明日头条,程序员自己<b>从头到尾搭建了一套环境</b>开始写代码,写完代码后程序员要把代码交给测试同学测试,这时测试同学开始<b>从头到尾搭建这套环境</b>,测试过程中出现问题程序员也不用担心,大可以一脸无辜的撒娇,“明明在人家的环境上可以运行的”。</p>
<p>测试同学测完后终于可以上线了,这时运维同学又要重新<b>从头到尾搭建这套环境</b>,费了九牛二虎之力搭建好环境开始上线,糟糕,上线系统就崩溃了,这时心理素质好的程序员又可以施展演技了,“明明在人家的环境上可以运行的”。</p>
<p>从整个过程可以看到,不但我们重复搭建了三套环境还要迫使程序员转行演员浪费表演才华,典型的浪费时间和效率,聪明的程序员是永远不会满足现状的,因此又到了程序员改变世界的时候了,容器技术应运而生。</p>
<p>有的同学可能会说:“等等,先别改变世界,我们有虚拟机啊,VMware好用的飞起,先搭好一套虚拟机环境然后给测试和运维clone出来不就可以了吗?”</p>
<p>在没有容器技术之前,这确实是一个好办法,只不过这个办法还没有那么好。</p>
<p>先科普一下,现在云计算其底层的基石就是虚拟机技术,云计算厂商买回来一堆硬件搭建好数据中心后使用虚拟机技术就可以将硬件资源进行切分了,比如可以切分出100台虚拟机,这样就可以卖给很多用户了。</p>
<p>你可能会想这个办法为什么不好呢?</p>
<h1 id="容器技术-vs-虚拟机"><a href="#容器技术-vs-虚拟机" class="headerlink" title="容器技术 vs 虚拟机"></a>容器技术 vs 虚拟机</h1><p>我们知道和一个单纯的应用程序相比,<b>操作系统是一个很重而且很笨的程序</b>,简称笨重,有多笨重呢?</p>
<p>我们知道操作系统运行起来是需要占用很多资源的,大家对此肯定深有体会,刚装好的系统还什么都没有部署,单纯的操作系统其磁盘占用至少几十G起步,内存要几个G起步。</p>
<p>假设我有一台机器,16G内存,需要部署三个应用,那么使用虚拟机技术可以这样划分:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/28/6e308d43bb076.png"/></div></div>
<p>在这台机器上开启三个虚拟机,每个虚拟机上部署一个应用,其中VM1占用2G内存,VM2占用1G内存,VM3占用了4G内存。</p>
<p>我们可以看到虚拟本身就占据了总共7G内存,因此<b>我们没有办法划分出更多虚拟机从而部署更多的应用程序</b>,可是我们部署的是应用程序,要用的也是应用程序而<b>不是操作系统</b>。</p>
<p>如果有一种技术可以让我们避免把内存浪费在“无用”的操作系统上岂不是太香?这是问题一,主要原因在于操作系统太重了。</p>
<p>还有另一个问题,那就是启动时间问题,我们知道操作系统重启是非常慢的,因为操作系统要从头到尾把该检测的都检测了该加载的都加载上,这个过程非常缓慢,动辄数分钟,因此操作系统还是太笨了。</p>
<p>那么有没有一种技术可以让我们获得虚拟机的好处又能克服这些缺点从而一举实现鱼和熊掌的兼得呢?</p>
<p>答案是肯定的,这就是容器技术。</p>
<h1 id="什么是容器"><a href="#什么是容器" class="headerlink" title="什么是容器"></a>什么是容器</h1><p>容器一词的英文是container,其实container还有集装箱的意思,集装箱绝对是商业史上了不起的一项发明,大大降低了海洋贸易运输成本。让我们来看看集装箱的好处:</p>
<ul>
<li><p>集装箱之间相互隔离</p>
</li>
<li><p>长期反复使用</p>
</li>
<li><p>快速装载和卸载</p>
</li>
<li><p>规格标准,在港口和船上都可以摆放</p>
</li>
</ul>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/28/d826239097558.png"/></div></div>
<p>回到软件中的容器,其实容器和集装箱在概念上是很相似的。</p>
<p>现代软件开发的一大目的就是隔离,应用程序在运行时相互独立互不干扰,这种隔离实现起来是很不容易的,其中一种解决方案就是上面提到的虚拟机技术,通过将应用程序部署在不同的虚拟机中从而实现隔离。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/28/21d11fcd87686.png"/></div></div>
<p>但是虚拟机技术有上述提到的各种缺点,那么容器技术又怎么样呢?</p>
<p>与虚拟机通过操作系统实现隔离不同,容器技术<b>只隔离应用程序的运行时环境但容器之间可以共享同一个操作系统</b>,这里的运行时环境指的是程序运行依赖的各种库以及配置。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/28/116fac1ee37df.png"/></div></div>
<p>从图中我们可以看到容器更加的<b>轻量级且占用的资源更少</b>,与操作系统动辄几G的内存占用相比,容器技术只需数M空间,因此我们可以在同样规格的硬件上<b>大量部署容器</b>,这是虚拟机所不能比拟的,而且不同于操作系统数分钟的启动时间容器几乎瞬时启动,容器技术为<b>打包服务栈</b>提供了一种更加高效的方式,So cool。</p>
<p>那么我们该怎么使用容器呢?这就要讲到docker了。</p>
<p>注意,容器是一种通用技术,docker只是其中的一种实现。</p>
<h1 id="什么是docker"><a href="#什么是docker" class="headerlink" title="什么是docker"></a>什么是docker</h1><p>docker是一个用Go语言实现的开源项目,可以让我们方便的创建和使用容器,docker将程序以及程序所有的依赖都打包到docker container,这样你的程序可以在任何环境都会有一致的表现,这里程序运行的依赖也就是容器就好比集装箱,容器所处的操作系统环境就好比货船或港口,<b>程序的表现只和集装箱有关系(容器),和集装箱放在哪个货船或者哪个港口(操作系统)没有关系</b>。</p>
<p>因此我们可以看到docker可以屏蔽环境差异,也就是说,只要你的程序打包到了docker中,那么无论运行在什么环境下程序的行为都是一致的,程序员再也无法施展表演才华了,<b>不会再有“在我的环境上可以运行”</b>,真正实现“build once, run everywhere”。</p>
<p>此外docker的另一个好处就是<b>快速部署</b>,这是当前互联网公司最常见的一个应用场景,一个原因在于容器启动速度非常快,另一个原因在于只要确保一个容器中的程序正确运行,那么你就能确信无论在生产环境部署多少都能正确运行。</p>
<h1 id="如何使用docker"><a href="#如何使用docker" class="headerlink" title="如何使用docker"></a>如何使用docker</h1><p>docker中有这样几个概念:</p>
<ul>
<li><p>dockerfile</p>
</li>
<li><p>image</p>
</li>
<li><p>container</p>
</li>
</ul>
<p>实际上你可以简单的把image理解为可执行程序,container就是运行起来的进程。</p>
<p>那么写程序需要源代码,那么“写”image就需要dockerfile,dockerfile就是image的源代码,docker就是”编译器”。</p>
<p>因此我们只需要在dockerfile中指定需要哪些程序、依赖什么样的配置,之后把dockerfile交给“编译器”docker进行“编译”,也就是docker build命令,生成的可执行程序就是image,之后就可以运行这个image了,这就是docker run命令,image运行起来后就是docker container。</p>
<p>具体的使用方法就不再这里赘述了,大家可以参考<a href="https://www.docker.com/">docker的官方文档</a>,那里有详细的讲解。</p>
<h1 id="docker是如何工作的"><a href="#docker是如何工作的" class="headerlink" title="docker是如何工作的"></a>docker是如何工作的</h1><p>实际上docker使用了常见的CS架构,也就是client-server模式,docker client负责处理用户输入的各种命令,比如docker build、docker run,真正工作的其实是server,也就是docker demon,值得注意的是,docker client和docker demon可以运行在同一台机器上。</p>
<p>接下来我们用几个命令来讲解一下docker的工作流程:</p>
<ol>
<li><p>docker build<br>当我们写完dockerfile交给docker“编译”时使用这个命令,那么client在接收到请求后转发给docker daemon,接着docker daemon根据dockerfile创建出“可执行程序”image。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/28/23cf69ed3533b.png"/></div></div></li>
<li><p>docker run<br>有了 “可执行程序” image 后就可以运行程序了,接下来使用命令<code>docker run</code>,docker daemon 接收到该命令后找到具体的image,然后加载到内存开始执行,image 执行起来就是所谓的 container。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/28/aa10e883300e0.png"/></div></div></li>
<li><p>docker pull<br>其实<code>docker build</code>和<code>docker run</code>是两个最核心的命令,会用这两个命令基本上 docker 就可以用起来了,剩下的就是一些补充。</p>
<p> 那么docker pull是什么意思呢?</p>
<p> 我们之前说过,docker中image的概念就类似于“可执行程序”,我们可以从哪里下载到别人写好的应用程序呢?很简单,那就是APP Store,即应用商店。与之类似,既然image也是一种“可执行程序”,那么有没有”Docker Image Store”呢?答案是肯定的,这就是 Docker Hub,docker官方的“应用商店”,你可以在这里下载到别人编写好的image,这样你就不用自己编写dockerfile了。</p>
<p> docker registry 可以用来存放各种image,公共的可以供任何人下载image的仓库就是 docker Hub。那么该怎么从 Docker Hub 中下载image呢,就是这里的 docker pull命令了。</p>
<p> 因此,这个命令的实现也很简单,那就是用户通过docker client发送命令,docker daemon 接收到命令后向 docker registry 发送 image下载请求,下载后存放在本地,这样我们就可以使用image了。</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/28/e636fa0771dc2.png"/></div></div></li>
</ol>
<p>最后,让我们来看一下docker的底层实现。</p>
<h1 id="docker的底层实现"><a href="#docker的底层实现" class="headerlink" title="docker的底层实现"></a>docker的底层实现</h1><p>docker 基于 Linux内核 提供这样几项功能实现的:</p>
<ul>
<li><p>NameSpace</p>
<p> 我们知道 Linux 中的 PID、IPC、网络等资源是全局的,而 NameSpace 机制是一种资源隔离方案,在该机制下这些资源就不再是全局的了,而是属于某个特定的 NameSpace,各个 NameSpace 下的资源互不干扰,这就使得每个 NameSpace 看上去就像一个独立的操作系统一样,但是只有NameSpace是不够。</p>
</li>
<li><p>Control groups</p>
<p> 虽然有了 NameSpace 技术可以实现资源隔离,但进程还是可以不受控的访问系统资源,比如 CPU、内存、磁盘、网络等,为了控制容器中进程对资源的访问,Docker 采用 control groups技术(也就是cgroup),有了 cgroup 就可以控制容器中进程对系统资源的消耗了,比如你可以限制某个容器使用内存的上限、可以在哪些CPU上运行等等。</p>
</li>
</ul>
<h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>docker是目前非常流行的技术,很多公司都在生产环境中使用,但是docker依赖的底层技术实际上很早就已经出现了,现在以docker的形式重新焕发活力,并且能很好的解决面临的问题,希望本文能对大家理解docker有所帮助。</p>
<div class="note info flat"><p>本文摘自:<a
巧用 CSS3 filter(滤镜) 属性
https://fe32.top/articles/fi5487lt/
2021-06-25T09:04:09.000Z
2023-05-17T10:01:12.000Z
<h1 id="效果预览"><a href="#效果预览" class="headerlink" title="效果预览"></a>效果预览</h1><p>这里仅展示黑白效果,更多效果移步<a href='https://fe32.top/articles/fi5487lt/#%E5%AE%9E%E4%BE%8B'>实例</a>。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">filter</span>: <span class="built_in">grayscale</span>(<span class="number">100%</span>);</span><br></pre></td></tr></table></figure>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/25/48f6531cbf285.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/25/eb5903ddd67e2.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/25/47c36ad3de185.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/25/d94a376192b34.jpg"/></div></div>
<h1 id="定义和使用"><a href="#定义和使用" class="headerlink" title="定义和使用"></a>定义和使用</h1><p>filter 属性定义了元素(通常是<code><img></code>)的可视效果(例如:模糊与饱和度)。</p>
<table>
<thead>
<tr>
<th align="left">默认值</th>
<th align="left">none</th>
</tr>
</thead>
<tbody><tr>
<td align="left">继承</td>
<td align="left">no</td>
</tr>
<tr>
<td align="left">动画支持</td>
<td align="left">是。详细可查阅 <a href="https://www.runoob.com/cssref/css-animatable.html">CSS 动画</a></td>
</tr>
<tr>
<td align="left">版本</td>
<td align="left">CSS3</td>
</tr>
</tbody></table>
<h1 id="浏览器支持"><a href="#浏览器支持" class="headerlink" title="浏览器支持"></a>浏览器支持</h1><p>表格中的数字表示支持该方法的第一个浏览器的版本号。</p>
<p>紧跟在数字后面的 -webkit- 为指定浏览器的前缀。</p>
<table>
<thead>
<tr>
<th align="center">属性</th>
<th align="center">filter</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/25/0696c251966a3.gif"></td>
<td align="center">18.0 -webkit-</td>
</tr>
<tr>
<td align="center"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/25/4131d95989914.gif"></td>
<td align="center">不支持</td>
</tr>
<tr>
<td align="center"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/25/080cb4436d0cc.gif"></td>
<td align="center">35.0</td>
</tr>
<tr>
<td align="center"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/25/9d1558e08bc3d.gif"></td>
<td align="center">6.0 -webkit-</td>
</tr>
<tr>
<td align="center"><img src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/25/3ec81173fd5b7.gif"></td>
<td align="center">15.0 -webkit-</td>
</tr>
</tbody></table>
<p>注意: 旧版 Internet Explorer 浏览器(4.0 to 8.0) 支持的非标准 “filter” 属性已被废弃。 IE8 及更低版本浏览器通常使用 opacity 属性。</p>
<h1 id="CSS-语法"><a href="#CSS-语法" class="headerlink" title="CSS 语法"></a>CSS 语法</h1><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">filter</span>: none | <span class="built_in">blur</span>() | <span class="built_in">brightness</span>() | <span class="built_in">contrast</span>() | <span class="built_in">drop-shadow</span>() | <span class="built_in">grayscale</span>() | <span class="built_in">hue-rotate</span>() | <span class="built_in">invert</span>() | <span class="built_in">opacity</span>() | <span class="built_in">saturate</span>() | <span class="built_in">sepia</span>() | <span class="built_in">url</span>();</span><br></pre></td></tr></table></figure>
<h1 id="Filter-函数"><a href="#Filter-函数" class="headerlink" title="Filter 函数"></a>Filter 函数</h1><div class="tip "><p>注意: 滤镜通常使用百分比 (如:75%), 当然也可以使用小数来表示 (如:0.75)。</p>
</div>
<table>
<thead>
<tr>
<th align="left">Filter</th>
<th align="left">描述</th>
</tr>
</thead>
<tbody><tr>
<td align="left">none</td>
<td align="left">默认值,没有效果。</td>
</tr>
<tr>
<td align="left">blur(px)</td>
<td align="left">给图像设置高斯模糊。”radius”一值设定高斯函数的标准差,或者是屏幕上以多少像素融在一起, 所以值越大越模糊;<br>如果没有设定值,则默认是0;这个参数可设置css长度值,但不接受百分比值。</td>
</tr>
<tr>
<td align="left">brightness(%)</td>
<td align="left">给图片应用一种线性乘法,使其看起来更亮或更暗。如果值是0%,图像会全黑。值是100%,则图像无变化。 <br>其他的值对应线性乘数效果。值超过100%也是可以的,图像会比原来更亮。如果没有设定值,默认是1。</td>
</tr>
<tr>
<td align="left">contrast(%)</td>
<td align="left">调整图像的对比度。值是0%的话,图像会全黑。值是100%,图像不变。值可以超过100%,意味着会运用更低的对比。若没有设置值,默认是1。</td>
</tr>
<tr>
<td align="left">drop-shadow(h-shadow v-shadow blur spread color)</td>
<td align="left">给图像设置一个阴影效果。阴影是合成在图像下面,可以有模糊度的,可以以特定颜色画出的遮罩图的偏移版本。 函数接受<code><shadow></code>(在CSS3背景中定义)类型的值,除了 “inset” 关键字是不允许的。该函数与已有的 <code>box-shadow box-shadow</code> 属性很相似;不同之处在于,通过滤镜,一些浏览器为了更好的性能会提供硬件加速。<code><shadow></code>参数如下:<br> <code><offset-x> <offset-y></code>(必须) <br> 这是设置阴影偏移量的两个 <code><length></code>值。 <code><offset-x></code>设定水平方向距离, 负值会使阴影出现在元素左边。 <code><offset-y></code>设定垂直距,负值会使阴影出现在元素上方,查看<code><length></code>可能的单位,如果两个值都是0, 则阴影出现在元素正后面 (如果设置了 <code><blur-radius></code> and/or <code><spread-radius></code>,会有模糊效果)。<br><code><blur-radius></code> (可选)<br>这是第三个code <code><length></code>值。 值越大,越模糊,则阴影会变得更大更淡。不允许负值 若未设定,默认是0 (则阴影的边界很锐利)。<br><code><spread-radius></code> (可选)<br>这是第四个 <length>值。 正值会使阴影扩张和变大,负值会是阴影缩小。若未设定,默认是0 (阴影会与元素一样大小)。 <br>注意: Webkit , 以及一些其他浏览器 不支持第四个长度,如果加了也不会渲染。<br><code><color></code> (可选)<br>查看 <code><color></code>该值可能的关键字和标记。若未设定,颜色值基于浏览器。在Gecko (Firefox), Presto (Opera)和Trident (Internet Explorer)中, 会应用colorcolor属性的值。另外, 如果颜色值省略,WebKit中阴影是透明的。</td>
</tr>
<tr>
<td align="left">grayscale(%)</td>
<td align="left">将图像转换为灰度图像。值定义转换的比例。值为100%则完全转为灰度图像,值为0%图像无变化。值在0%到100%之间,则是效果的线性乘子。若未设置,值默认是0。</td>
</tr>
<tr>
<td align="left">hue-rotate(deg)</td>
<td align="left">给图像应用色相旋转。”angle”一值设定图像会被调整的色环角度值。值为0deg,则图像无变化。若值未设置,默认值是0deg。该值虽然没有最大值,超过360deg的值相当于又绕一圈。</td>
</tr>
<tr>
<td align="left">invert(%)</td>
<td align="left">反转输入图像。值定义转换的比例。100%的价值是完全反转。值为0%则图像无变化。值在0%和100%之间,则是效果的线性乘子。 若值未设置,值默认是0。</td>
</tr>
<tr>
<td align="left">opacity(%)</td>
<td align="left">转化图像的透明程度。值定义转换的比例。值为0%则是完全透明,值为100%则图像无变化。值在0%和100%之间,则是效果的线性乘子,也相当于图像样本乘以数量。 若值未设置,值默认是1。该函数与已有的opacity属性很相似,不同之处在于通过filter,一些浏览器为了提升性能会提供硬件加速。</td>
</tr>
<tr>
<td align="left">saturate(%)</td>
<td align="left">转换图像饱和度。值定义转换的比例。值为0%则是完全不饱和,值为100%则图像无变化。其他值,则是效果的线性乘子。超过100%的值是允许的,则有更高的饱和度。 若值未设置,值默认是1。</td>
</tr>
<tr>
<td align="left">sepia(%)</td>
<td align="left">将图像转换为深褐色。值定义转换的比例。值为100%则完全是深褐色的,值为0%图像无变化。值在0%到100%之间,则是效果的线性乘子。若未设置,值默认是0;</td>
</tr>
<tr>
<td align="left">url()</td>
<td align="left">URL函数接受一个XML文件,该文件设置了 一个SVG滤镜,且可以包含一个锚点来指定一个具体的滤镜元素。<br> 例如:<br><code>filter: url(svg-url#element-id)</code></td>
</tr>
<tr>
<td align="left">initial</td>
<td align="left">设置属性为默认值,可参阅: <a href="https://www.runoob.com/cssref/css-initial.html">CSS initial 关键字</a></td>
</tr>
<tr>
<td align="left">inherit</td>
<td align="left">设置属性为默认值,可参阅: <a href="https://www.runoob.com/cssref/css-inherit.html">CSS inherit 关键字</a></td>
</tr>
</tbody></table>
<h1 id="实例"><a href="#实例" class="headerlink" title="实例"></a>实例</h1><p>初始图片如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/25/193f0b161f865.jpg"/></div></div>
<h2 id="模糊实例"><a href="#模糊实例" class="headerlink" title="模糊实例"></a>模糊实例</h2><p>图片使用高斯模糊效果:</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></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">img</span> {</span><br><span class="line"> -webkit-<span class="attribute">filter</span>: <span class="built_in">blur</span>(<span class="number">5px</span>); <span class="comment">/* Chrome, Safari, Opera */</span></span><br><span class="line"> <span class="attribute">filter</span>: <span class="built_in">blur</span>(<span class="number">5px</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>效果如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/25/e81c905329dac.png"/></div></div>
<div class="note red no-icon flat"><p>注意: Internet Explorer 不支持 filter 属性。</p>
</div>
<h2 id="Brightness-函数实例"><a href="#Brightness-函数实例" class="headerlink" title="Brightness 函数实例"></a>Brightness 函数实例</h2><p>使图片变亮:</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></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">img</span> {</span><br><span class="line"> -webkit-<span class="attribute">filter</span>: <span class="built_in">brightness</span>(<span class="number">200%</span>); <span class="comment">/* Chrome, Safari, Opera */</span></span><br><span class="line"> <span class="attribute">filter</span>: <span class="built_in">brightness</span>(<span class="number">200%</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>效果如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/25/c8078af0c5b43.jpg"/></div></div>
<h2 id="Contrast-函数实例"><a href="#Contrast-函数实例" class="headerlink" title="Contrast 函数实例"></a>Contrast 函数实例</h2><p>调整图像的对比度:</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></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">img</span> {</span><br><span class="line"> -webkit-<span class="attribute">filter</span>: <span class="built_in">contrast</span>(<span class="number">200%</span>); <span class="comment">/* Chrome, Safari, Opera */</span></span><br><span class="line"> <span class="attribute">filter</span>: <span class="built_in">contrast</span>(<span class="number">200%</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>效果如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/25/112e0228e4c73.jpg"/></div></div>
<h2 id="drop-shadow-函数实例"><a href="#drop-shadow-函数实例" class="headerlink" title="drop-shadow 函数实例"></a>drop-shadow 函数实例</h2><p>给图像设置一个阴影效果:</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></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">img</span> {</span><br><span class="line"> -webkit-<span class="attribute">filter</span>: <span class="built_in">drop-shadow</span>(<span class="number">8px</span> <span class="number">8px</span> <span class="number">10px</span> red); <span class="comment">/* Chrome, Safari, Opera */</span></span><br><span class="line"> <span class="attribute">filter</span>: <span class="built_in">drop-shadow</span>(<span class="number">8px</span> <span class="number">8px</span> <span class="number">10px</span> red);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>效果如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/25/11e098987e2a8.jpg"/></div></div>
<h2 id="Grayscale-函数实例"><a href="#Grayscale-函数实例" class="headerlink" title="Grayscale 函数实例"></a>Grayscale 函数实例</h2><p>将图像转换为灰度图像:</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></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">img</span> {</span><br><span class="line"> -webkit-<span class="attribute">filter</span>: <span class="built_in">grayscale</span>(<span class="number">50%</span>); <span class="comment">/* Chrome, Safari, Opera */</span></span><br><span class="line"> <span class="attribute">filter</span>: <span class="built_in">grayscale</span>(<span class="number">50%</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>效果如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/25/9a251a70957b6.jpg"/></div></div>
<h2 id="hue-rotate-函数实例"><a href="#hue-rotate-函数实例" class="headerlink" title="hue-rotate() 函数实例"></a>hue-rotate() 函数实例</h2><p>给图像应用色相旋转:</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></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">img</span> {</span><br><span class="line"> -webkit-<span class="attribute">filter</span>: <span class="built_in">hue-rotate</span>(<span class="number">90deg</span>); <span class="comment">/* Chrome, Safari, Opera */</span></span><br><span class="line"> <span class="attribute">filter</span>: <span class="built_in">hue-rotate</span>(<span class="number">90deg</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>效果如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/25/4e59982490ed6.jpg"/></div></div>
<h2 id="Invert-函数实例"><a href="#Invert-函数实例" class="headerlink" title="Invert 函数实例"></a>Invert 函数实例</h2><p>反转输入图像:</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></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">img</span> {</span><br><span class="line"> -webkit-<span class="attribute">filter</span>: <span class="built_in">invert</span>(<span class="number">100%</span>); <span class="comment">/* Chrome, Safari, Opera */</span></span><br><span class="line"> <span class="attribute">filter</span>: <span class="built_in">invert</span>(<span class="number">100%</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>效果如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/25/ee3d5a4de13e6.jpg"/></div></div>
<h2 id="Opacity-函数实例"><a href="#Opacity-函数实例" class="headerlink" title="Opacity 函数实例"></a>Opacity 函数实例</h2><p>转化图像的透明程度:</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></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">img</span> {</span><br><span class="line"> -webkit-<span class="attribute">filter</span>: <span class="built_in">opacity</span>(<span class="number">30%</span>); <span class="comment">/* Chrome, Safari, Opera */</span></span><br><span class="line"> <span class="attribute">filter</span>: <span class="built_in">opacity</span>(<span class="number">30%</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>效果如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/25/298c30334aea4.jpg"/></div></div>
<h2 id="Saturate-函数实例"><a href="#Saturate-函数实例" class="headerlink" title="Saturate 函数实例"></a>Saturate 函数实例</h2><p>转换图像饱和度:</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></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">img</span> {</span><br><span class="line"> -webkit-<span class="attribute">filter</span>: <span class="built_in">saturate</span>(<span class="number">800%</span>); <span class="comment">/* Chrome, Safari, Opera */</span></span><br><span class="line"> <span class="attribute">filter</span>: <span class="built_in">saturate</span>(<span class="number">800%</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>效果如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/25/6af7c9ea945f4.jpg"/></div></div>
<h2 id="Sepia-函数实例"><a href="#Sepia-函数实例" class="headerlink" title="Sepia 函数实例"></a>Sepia 函数实例</h2><p>将图像转换为深褐色:</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></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">img</span> {</span><br><span class="line"> -webkit-<span class="attribute">filter</span>: <span class="built_in">sepia</span>(<span class="number">100%</span>); <span class="comment">/* Chrome, Safari, Opera */</span></span><br><span class="line"> <span class="attribute">filter</span>: <span class="built_in">sepia</span>(<span class="number">100%</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>效果如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/25/4cd1f7012db40.jpg"/></div></div>
<h2 id="复合函数"><a href="#复合函数" class="headerlink" title="复合函数"></a>复合函数</h2><p>使用多个滤镜,每个滤镜使用空格分隔。</p>
<div class="note red no-icon flat"><p>注意: 顺序是非常重要的 (例如使用 grayscale() 后再使用 sepia()将产生一个完整的灰度图片)。</p>
</div>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">img</span> {</span><br><span class="line"> -webkit-<span class="attribute">filter</span>: <span class="built_in">contrast</span>(<span class="number">200%</span>) <span class="built_in">brightness</span>(<span class="number">150%</span>); <span class="comment">/* Chrome, Safari, Opera */</span></span><br><span class="line"> <span class="attribute">filter</span>: <span class="built_in">contrast</span>(<span class="number">200%</span>) <span class="built_in">brightness</span>(<span class="number">150%</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>效果如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/25/4895c7bd99db2.jpg"/></div></div>
<h2 id="滤镜效果整合"><a href="#滤镜效果整合" class="headerlink" title="滤镜效果整合"></a>滤镜效果整合</h2><p>使用多个滤镜,每个滤镜使用空格分隔。</p>
<div class="note red no-icon flat"><p>注意: 顺序是非常重要的 (例如使用 grayscale() 后再使用 sepia()将产生一个完整的灰度图片)。</p>
</div>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE <span class="keyword">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">style</span>></span><span class="language-css"></span></span><br><span class="line"><span class="language-css"><span class="selector-tag">img</span> {</span></span><br><span class="line"><span class="language-css"> <span class="attribute">width</span>: <span class="number">300.666px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">height</span>: <span class="number">240px</span>;</span></span><br><span class="line"><span class="language-css">}</span></span><br><span class="line"><span class="language-css"><span class="selector-class">.blur</span> {-webkit-<span class="attribute">filter</span>: <span class="built_in">blur</span>(<span class="number">4px</span>);<span class="attribute">filter</span>: <span class="built_in">blur</span>(<span class="number">4px</span>);}</span></span><br><span class="line"><span class="language-css"><span class="selector-class">.brightness</span> {-webkit-<span class="attribute">filter</span>: <span class="built_in">brightness</span>(<span class="number">0.30</span>);<span class="attribute">filter</span>: <span class="built_in">brightness</span>(<span class="number">0.30</span>);}</span></span><br><span class="line"><span class="language-css"><span class="selector-class">.contrast</span> {-webkit-<span class="attribute">filter</span>: <span class="built_in">contrast</span>(<span class="number">180%</span>);<span class="attribute">filter</span>: <span class="built_in">contrast</span>(<span class="number">180%</span>);}</span></span><br><span class="line"><span class="language-css"><span class="selector-class">.grayscale</span> {-webkit-<span class="attribute">filter</span>: <span class="built_in">grayscale</span>(<span class="number">100%</span>);<span class="attribute">filter</span>: <span class="built_in">grayscale</span>(<span class="number">100%</span>);}</span></span><br><span class="line"><span class="language-css"><span class="selector-class">.huerotate</span> {-webkit-<span class="attribute">filter</span>: <span class="built_in">hue-rotate</span>(<span class="number">180deg</span>);<span class="attribute">filter</span>: <span class="built_in">hue-rotate</span>(<span class="number">180deg</span>);}</span></span><br><span class="line"><span class="language-css"><span class="selector-class">.invert</span> {-webkit-<span class="attribute">filter</span>: <span class="built_in">invert</span>(<span class="number">100%</span>);<span class="attribute">filter</span>: <span class="built_in">invert</span>(<span class="number">100%</span>);}</span></span><br><span class="line"><span class="language-css"><span class="selector-class">.opacity</span> {-webkit-<span class="attribute">filter</span>: <span class="built_in">opacity</span>(<span class="number">50%</span>);<span class="attribute">filter</span>: <span class="built_in">opacity</span>(<span class="number">50%</span>);}</span></span><br><span class="line"><span class="language-css"><span class="selector-class">.saturate</span> {-webkit-<span class="attribute">filter</span>: <span class="built_in">saturate</span>(<span class="number">7</span>); <span class="attribute">filter</span>: <span class="built_in">saturate</span>(<span class="number">7</span>);}</span></span><br><span class="line"><span class="language-css"><span class="selector-class">.sepia</span> {-webkit-<span class="attribute">filter</span>: <span class="built_in">sepia</span>(<span class="number">100%</span>);<span class="attribute">filter</span>: <span class="built_in">sepia</span>(<span class="number">100%</span>);}</span></span><br><span class="line"><span class="language-css"><span class="selector-class">.shadow</span> {-webkit-<span class="attribute">filter</span>: <span class="built_in">drop-shadow</span>(<span class="number">8px</span> <span class="number">8px</span> <span class="number">10px</span> green);<span class="attribute">filter</span>: <span class="built_in">drop-shadow</span>(<span class="number">8px</span> <span class="number">8px</span> <span class="number">10px</span> green);}</span></span><br><span class="line"><span class="language-css"></span><span class="tag"></<span class="name">style</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">p</span>></span><span class="tag"><<span class="name">strong</span>></span>注意:<span class="tag"></<span class="name">strong</span>></span> Internet Explorer 不支持 filter 属性。<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">img</span> <span class="attr">src</span>=<span class="string">"https://bu.dusays.com/2021/06/25/193f0b161f865.jpg"</span> <span class="attr">alt</span>=<span class="string">"Pineapple"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">img</span> <span class="attr">class</span>=<span class="string">"blur"</span> <span class="attr">src</span>=<span class="string">"https://bu.dusays.com/2021/06/25/193f0b161f865.jpg"</span> <span class="attr">alt</span>=<span class="string">"Pineapple"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">img</span> <span class="attr">class</span>=<span class="string">"brightness"</span> <span class="attr">src</span>=<span class="string">"https://bu.dusays.com/2021/06/25/193f0b161f865.jpg"</span> <span class="attr">alt</span>=<span class="string">"Pineapple"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">img</span> <span class="attr">class</span>=<span class="string">"contrast"</span> <span class="attr">src</span>=<span class="string">"https://bu.dusays.com/2021/06/25/193f0b161f865.jpg"</span> <span class="attr">alt</span>=<span class="string">"Pineapple"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">img</span> <span class="attr">class</span>=<span class="string">"grayscale"</span> <span class="attr">src</span>=<span class="string">"https://bu.dusays.com/2021/06/25/193f0b161f865.jpg"</span> <span class="attr">alt</span>=<span class="string">"Pineapple"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">img</span> <span class="attr">class</span>=<span class="string">"huerotate"</span> <span class="attr">src</span>=<span class="string">"https://bu.dusays.com/2021/06/25/193f0b161f865.jpg"</span> <span class="attr">alt</span>=<span class="string">"Pineapple"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">img</span> <span class="attr">class</span>=<span class="string">"invert"</span> <span class="attr">src</span>=<span class="string">"https://bu.dusays.com/2021/06/25/193f0b161f865.jpg"</span> <span class="attr">alt</span>=<span class="string">"Pineapple"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">img</span> <span class="attr">class</span>=<span class="string">"opacity"</span> <span class="attr">src</span>=<span class="string">"https://bu.dusays.com/2021/06/25/193f0b161f865.jpg"</span> <span class="attr">alt</span>=<span class="string">"Pineapple"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">img</span> <span class="attr">class</span>=<span class="string">"saturate"</span> <span class="attr">src</span>=<span class="string">"https://bu.dusays.com/2021/06/25/193f0b161f865.jpg"</span> <span class="attr">alt</span>=<span class="string">"Pineapple"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">img</span> <span class="attr">class</span>=<span class="string">"sepia"</span> <span class="attr">src</span>=<span class="string">"https://bu.dusays.com/2021/06/25/193f0b161f865.jpg"</span> <span class="attr">alt</span>=<span class="string">"Pineapple"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">img</span> <span class="attr">class</span>=<span class="string">"shadow"</span> <span class="attr">src</span>=<span class="string">"https://bu.dusays.com/2021/06/25/193f0b161f865.jpg"</span> <span class="attr">alt</span>=<span class="string">"Pineapple"</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure>
<p>效果如下:</p>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/06/25/1406a17f76011.jpg"/></div></div>
<h2 id="一些常见效果"><a href="#一些常见效果" class="headerlink" title="一些常见效果"></a>一些常见效果</h2><ol>
<li>电影效果<br>滤镜中的<code>brightness</code>用于调整图像的明暗度。默认值是1;小于1时图像变暗,为0时显示为全黑图像;大于1时图像显示比原图更明亮。<br>我们可以通过调整 背景图的明暗度 和 文字的透明度 ,来模拟电影谢幕的效果。<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"container"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"ethan"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"p-t"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>如果生活中有什么使你感到快乐,那就去做吧<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">br</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>不要管别人说什么<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></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><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.ethan</span>{</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">100%</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">background</span>: <span class="built_in">url</span>(<span class="string">'./images/movie.webp'</span>) no-repeat;</span><br><span class="line"> <span class="attribute">background-size</span>: cover;</span><br><span class="line"> <span class="comment">/* forwards当动画完成后,保持最后一帧的状态 */</span></span><br><span class="line"> <span class="attribute">animation</span>: fade-away <span class="number">2.5s</span> linear forwards; </span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.p-t</span>{</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">line-height</span>: <span class="number">55px</span>;</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#fff</span>;</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">36px</span>;</span><br><span class="line"> <span class="attribute">text-align</span>: center;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">50%</span>;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">50%</span>;</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">translate</span>(-<span class="number">50%</span>,-<span class="number">50%</span>);</span><br><span class="line"> <span class="attribute">opacity</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">animation</span>: show <span class="number">2s</span> <span class="built_in">cubic-bezier</span>(.<span class="number">74</span>,-<span class="number">0.1</span>,.<span class="number">86</span>,.<span class="number">83</span>) forwards;</span><br><span class="line">}</span><br><span class="line"><span class="comment">/* 背景图的明暗度动画 */</span></span><br><span class="line"><span class="keyword">@keyframes</span> fade-away { </span><br><span class="line"> <span class="number">30%</span>{</span><br><span class="line"> <span class="attribute">filter</span>: <span class="built_in">brightness</span>(<span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="number">100%</span>{</span><br><span class="line"> <span class="attribute">filter</span>: <span class="built_in">brightness</span>(<span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">/* 文字的透明度动画 */</span></span><br><span class="line"><span class="keyword">@keyframes</span> show{ </span><br><span class="line"> <span class="number">20%</span>{</span><br><span class="line"> <span class="attribute">opacity</span>: <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="number">100%</span>{</span><br><span class="line"> <span class="attribute">opacity</span>: <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
效果如下:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b843c216405b47938613fe576026b199~tplv-k3u1fbpfcp-watermark.awebp"/></div></div></li>
<li>模糊效果<br>在下面的单词卡片中,当鼠标<code>hover</code>到某一张卡片上时,其他卡片背景模糊,使用户焦点集中到当前卡片。<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">ul</span> <span class="attr">class</span>=<span class="string">"cards"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span> <span class="attr">class</span>=<span class="string">"card"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span> <span class="attr">class</span>=<span class="string">"title"</span>></span>Flower<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span> <span class="attr">class</span>=<span class="string">"content"</span>></span>The flowers mingle to form a blaze of color.<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span> <span class="attr">class</span>=<span class="string">"card"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span> <span class="attr">class</span>=<span class="string">"title"</span>></span>Sunset<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span> <span class="attr">class</span>=<span class="string">"content"</span>></span>The sunset glow tinted the sky red.<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span> <span class="attr">class</span>=<span class="string">"card"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span> <span class="attr">class</span>=<span class="string">"title"</span>></span>Plain<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span> <span class="attr">class</span>=<span class="string">"content"</span>></span>The winds came from the north, across the plains, funnelling down the valley. <span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">ul</span>></span></span><br></pre></td></tr></table></figure>
实现的方式,是将背景加在 <code>.card</code> 元素的伪类上,当元素不是焦点时,为该元素的伪类加上滤镜。<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.card</span><span class="selector-pseudo">:before</span>{</span><br><span class="line"> <span class="attribute">z-index</span>: -<span class="number">1</span>;</span><br><span class="line"> <span class="attribute">content</span>: <span class="string">''</span>;</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">bottom</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">right</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">20px</span>;</span><br><span class="line"> <span class="attribute">filter</span>: <span class="built_in">blur</span>(<span class="number">0px</span>) <span class="built_in">opacity</span>(<span class="number">1</span>);</span><br><span class="line"> <span class="attribute">transition</span>: filter <span class="number">200ms</span> linear, transform <span class="number">200ms</span> linear;</span><br><span class="line">}</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> 这里不能将滤镜直接加在.card元素,而是将背景和滤镜都加在伪类上。</span></span><br><span class="line"><span class="comment"> 因为,父元素加了滤镜,它的子元素都会一起由该滤镜改变。</span></span><br><span class="line"><span class="comment"> 如果滤镜直接加在.card元素上,会导致上面的文字也变模糊。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="comment">/* 通过css选择器选出非hover的.card元素,给其伪类添加模糊、透明度和明暗度的滤镜 */</span></span><br><span class="line"><span class="selector-class">.cards</span><span class="selector-pseudo">:hover</span> > <span class="selector-class">.card</span><span class="selector-pseudo">:not</span>(<span class="selector-pseudo">:hover</span>)<span class="selector-pseudo">:before</span>{ </span><br><span class="line"> <span class="attribute">filter</span>: <span class="built_in">blur</span>(<span class="number">5px</span>) <span class="built_in">opacity</span>(<span class="number">0.8</span>) <span class="built_in">brightness</span>(<span class="number">0.8</span>);</span><br><span class="line">}</span><br><span class="line"><span class="comment">/* 对于hover的元素,其伪类增强饱和度,尺寸放大 */</span></span><br><span class="line"><span class="selector-class">.card</span><span class="selector-pseudo">:hover</span><span class="selector-pseudo">:before</span>{</span><br><span class="line"> <span class="attribute">filter</span>: <span class="built_in">saturate</span>(<span class="number">1.2</span>); </span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">scale</span>(<span class="number">1.05</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
效果如下:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b580f7a573d9497dbc6497b6cf66364c~tplv-k3u1fbpfcp-watermark.awebp"/></div></div></li>
<li>褪色效果<br>褪色效果可以打造出一种怀旧的风格。下面这组照片墙,我们通过sepia滤镜将图像基调转换为深褐色,再通过降低 饱和度 <code>saturate</code> 和 色相旋转 <code>hue-rotate</code> 微调,模拟老照片的效果。<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-class">.pic</span>{</span><br><span class="line"> <span class="attribute">border</span>: <span class="number">3px</span> solid <span class="number">#fff</span>;</span><br><span class="line"> <span class="attribute">box-shadow</span>: <span class="number">0</span> <span class="number">10px</span> <span class="number">50px</span> <span class="number">#5f2f1182</span>;</span><br><span class="line"> <span class="attribute">filter</span>: <span class="built_in">sepia</span>(<span class="number">30%</span>) <span class="built_in">saturate</span>(<span class="number">40%</span>) <span class="built_in">hue-rotate</span>(<span class="number">5deg</span>);</span><br><span class="line"> <span class="attribute">transition</span>: transform <span class="number">1s</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.pic</span><span class="selector-pseudo">:hover</span>{</span><br><span class="line"> <span class="attribute">filter</span>: none;</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">scale</span>(<span class="number">1.2</span>) <span class="built_in">translateX</span>(<span class="number">10px</span>);</span><br><span class="line"> <span class="attribute">z-index</span>: <span class="number">1</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
效果如下:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/59281a5ea70544e1bd75414762e38aba~tplv-k3u1fbpfcp-watermark.awebp"/></div></div></li>
<li>水波效果<br>filter还可以通过 URL 链接到 SVG 滤镜元素,<a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/filter">SVG滤镜元素MDN</a>。<br>下面的水波纹效果就是基于 SVG 的feTurbulence滤镜实现的,原理参考了 <a href="https://zhuanlan.zhihu.com/p/366438535">说说SVG的feTurbulence滤镜</a> 和<a href="https://www.zhangxinxu.com/wordpress/2020/10/svg-feturbulence">SVG feTurbulence滤镜深入介绍</a> ,有兴趣的朋友可以深入阅读。<br><code>feTurbulence滤镜</code> 借助 <code>Perlin</code> 噪声算法模拟自然界真实事物那样的随机样式。它接收下面5个属性:<br><code>baseFrequency</code> 表示噪声的基本频率参数,频率越高,噪声越密集。<br><code>numOctaves</code> 就表示倍频的数量,倍频的数量越多,噪声看起来越自然。<br><code>seed</code>属性表示<code>feTurbulence</code>滤镜效果中伪随机数生成的起始值,不同数量的<code>seed</code>不会改变噪声的频率和密度,改变的是噪声的形状和位置。<br><code>stitchTiles</code>定义了Perlin噪声在边框处的行为表现。<br><code>type</code>属性值有<code>fractalNoise</code>和<code>turbulence</code>,模拟随机样式使用<code>turbulence</code>。<br>在这个例子,两个<code>img标签</code>使用同一张图片,将第二个<code>img标签</code>使用<code>scaleY(-1)</code>实现垂直方向的镜像翻转,模拟倒影。<br>并且,对倒影图片使用<code>feTurbulence</code>滤镜,通过动画不断改变<code>feTurbulence</code>滤镜的<code>baseFrequency</code>值实现水纹波动的效果。<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"container"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">img</span> <span class="attr">src</span>=<span class="string">"images/moon.jpg"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">img</span> <span class="attr">src</span>=<span class="string">"images/moon.jpg"</span> <span class="attr">class</span>=<span class="string">"reflect"</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="comment"><!--定义svg滤镜,这里使用的是feTurbulence滤镜--></span></span><br><span class="line"><span class="tag"><<span class="name">svg</span> <span class="attr">width</span>=<span class="string">"0"</span> <span class="attr">height</span>=<span class="string">"0"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">filter</span> <span class="attr">id</span>=<span class="string">"displacement-wave-filter"</span>></span></span><br><span class="line"> <span class="comment"><!--baseFrequency设置0.01 0.09两个值,代表x轴和y轴的噪声频率--></span> </span><br><span class="line"> <span class="tag"><<span class="name">feTurbulence</span> <span class="attr">baseFrequency</span>=<span class="string">"0.01 0.09"</span>></span></span><br><span class="line"> <span class="comment"><!--这是svg动画的定义方式,通过动画不断改变baseFrequency的值,从而形成波动效果--></span></span><br><span class="line"> <span class="tag"><<span class="name">animate</span> <span class="attr">attributeName</span>=<span class="string">"baseFrequency"</span> <span class="attr">dur</span>=<span class="string">"20s"</span> <span class="attr">keyTimes</span>=<span class="string">"0;0.5;1"</span> <span class="attr">values</span>=<span class="string">"0.01 0.09;0.02 0.13;0.01 0.09"</span> <span class="attr">repeatCount</span>=<span class="string">"indefinite"</span> ></span><span class="tag"></<span class="name">animate</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">feTurbulence</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">feDisplacementMap</span> <span class="attr">in</span>=<span class="string">"SourceGraphic"</span> <span class="attr">scale</span>=<span class="string">"10"</span> /></span> </span><br><span class="line"> <span class="tag"></<span class="name">filter</span>></span></span><br><span class="line"><span class="tag"></<span class="name">svg</span>></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><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="selector-class">.container</span>{</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">520px</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">400px</span>;</span><br><span class="line"> <span class="attribute">display</span>: flex;</span><br><span class="line"> <span class="attribute">clip-path</span>: <span class="built_in">inset</span>(<span class="number">10px</span>);</span><br><span class="line"> <span class="attribute">flex-direction</span>: column;</span><br><span class="line">}</span><br><span class="line"><span class="selector-tag">img</span>{</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">50%</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.reflect</span> {</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">translateY</span>(-<span class="number">2px</span>) <span class="built_in">scaleY</span>(-<span class="number">1</span>);</span><br><span class="line"> <span class="comment">/* 对模拟倒影的元素应用svg filter</span></span><br><span class="line"><span class="comment"> url中对应的是上面svg filter的id */</span></span><br><span class="line"> <span class="attribute">filter</span>: <span class="built_in">url</span>(<span class="string">#displacement-wave-filter</span>); </span><br><span class="line">}</span><br></pre></td></tr></table></figure>
效果如下:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif"
四月末
https://fe32.top/articles/si4575mo/
2021-06-18T11:06:30.000Z
2023-05-17T10:01:12.000Z
不以物喜 不以己悲
Vue中使用zTree插件实现文件多选
https://fe32.top/articles/zt1442re/
2021-05-12T10:22:01.000Z
2023-05-17T10:01:12.000Z
vue中使用zTree插件实现多文件选择
Hexo-悬挂灯笼
https://fe32.top/articles/Ha5487ng/
2021-05-06T12:21:44.000Z
2023-05-17T10:01:12.000Z
<h2 id="推荐阅读"><a href="#推荐阅读" class="headerlink" title="推荐阅读"></a>推荐阅读</h2><ul>
<li><a href="https://fe32.top/articles/hexo1601/">基于 Hexo 从零开始搭建个人博客(一)</a></li>
<li><a href="https://fe32.top/articles/hexo1602/">基于 Hexo 从零开始搭建个人博客(二)</a></li>
<li><a href="https://fe32.top/articles/hexo1603/">基于 Hexo 从零开始搭建个人博客(三)</a></li>
<li><a href="https://fe32.top/articles/hexo1604/">基于 Hexo 从零开始搭建个人博客(四)</a></li>
<li><a href="https://fe32.top/articles/hexo1605/">基于 Hexo 从零开始搭建个人博客(五)</a></li>
<li><a href="https://fe32.top/articles/hexo1606/">基于 Hexo 从零开始搭建个人博客(六)</a></li>
<li><a href="https://fe32.top/articles/hexo1607/">基于 Hexo 键入搜索功能</a></li>
<li><a href="https://fe32.top/articles/hexo1609/">基于 Hexo 键入分享功能</a></li>
<li><a href="https://fe32.top/articles/hexo1608/">Hexo + Butterfly 自定义右键菜单</a></li>
<li><a href="https://fe32.top/articles/hexo1612/">Hexo + Butterfly 一些常见问题</a></li>
<li><a href="https://fe32.top/articles/hexo1613/">请收下这只可爱的猫咪吧</a></li>
<li><a href="https://fe32.top/articles/hexo1614/">关于Vercel被墙导致获取Twikoo评论失败的解决方案</a></li>
<li><a href="https://fe32.top/articles/hexo1615/">飞只因太美,给你的首页装上吧!</a></li>
<li><a href="https://fe32.top/articles/hexo1617/">Hexo + Butterfly 自定义页脚</a></li>
<li><a href="https://fe32.top/articles/hexo1618/">Hexo + Butterfly 侧边栏公众号</a></li>
</ul>
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><div class="tip cogs faa-horizontal animated"><p>之前有小伙伴留言问我博客悬挂灯笼如何实现的? 那现在写一篇简单的教程。</p>
</div>
<h2 id="悬挂灯笼效果"><a href="#悬挂灯笼效果" class="headerlink" title="悬挂灯笼效果"></a>悬挂灯笼效果</h2><blockquote>
<p>还是先看效果,分别展示PC端和移动端,具体效果如下图:</p>
</blockquote>
<div class="tabs" id="text-"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#text--1">PC</button></li><li class="tab"><button type="button" data-href="#text--2">Mobile</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="text--1"><div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/05/06/d639262f95b85.jpg"/></div></div>
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/05/06/d304534177d69.jpg"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="text--2"><div class="gallery">
<div class="fj-gallery data" data-rowHeight="220" data-limit="10">
<span class="gallery-data">[{"url":"https://bu.dusays.com/2021/05/06/2b5a9214222ff.jpg","alt":""},{"url":"https://bu.dusays.com/2021/05/06/44bdb2b03f327.jpg","alt":""},{"url":"https://bu.dusays.com/2021/05/06/848c6b4dbb123.jpg","alt":""},{"url":"https://bu.dusays.com/2021/05/06/2da209dcf5e72.jpg","alt":""}]</span>
</div>
<button class="gallery-load-more"><span>加载更多</span><i class="fa-solid fa-arrow-down"></i></button>
</div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div>
<h2 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h2><ol>
<li>新建CSS样式<br>在<code>BlogRoot/themes/butterfly/source/css</code>文件下新建 CSS 文件,并命名为 <code>lantern.css</code>( 当然名字随便取,只要在主题配置文件中引入对应的<code>CSS</code>文件即可 ),将以下代码复制到新建的<code>lantern.css</code>中。<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><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><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 灯笼 Start */</span></span><br><span class="line"></span><br><span class="line">* {</span><br><span class="line"> <span class="attribute">box-sizing</span>: border-box;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 移动端显示/隐藏 /none/block,可自定义显示一个 */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">@media</span> screen <span class="keyword">and</span> (<span class="attribute">max-width</span>: <span class="number">970px</span>) {</span><br><span class="line"> <span class="selector-class">.d-box1</span> {</span><br><span class="line"> <span class="attribute">display</span>: none;</span><br><span class="line"> }</span><br><span class="line"> <span class="selector-class">.dengl</span> <span class="selector-class">.d-box</span> {</span><br><span class="line"> <span class="attribute">right</span>: <span class="number">0px</span>;</span><br><span class="line"> <span class="attribute">top</span>: -<span class="number">40px</span>;</span><br><span class="line"> <span class="comment">/* 自定义灯笼大小 */</span></span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">scale</span>(<span class="number">0.4</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.dengl</span> {</span><br><span class="line"> <span class="attribute">position</span>: fixed;</span><br><span class="line"> <span class="attribute">z-index</span>: <span class="number">9</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/* .d-box,.d-box1{</span></span><br><span class="line"><span class="comment"> z-index: 9;</span></span><br><span class="line"><span class="comment"> } */</span></span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d-box</span> {</span><br><span class="line"> <span class="attribute">position</span>: fixed;</span><br><span class="line"> <span class="comment">/* 自定义灯笼的位置 */</span></span><br><span class="line"> <span class="attribute">right</span>: <span class="number">85px</span>;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="comment">/* 自定义灯笼大小 */</span></span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">scale</span>(<span class="number">0.8</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d-box1</span> {</span><br><span class="line"> <span class="attribute">position</span>: fixed;</span><br><span class="line"> <span class="comment">/* 自定义灯笼的位置 */</span></span><br><span class="line"> <span class="attribute">left</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="comment">/* 自定义灯笼大小 */</span></span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">scale</span>(<span class="number">0.8</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 修改灯笼的字体 */</span></span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d-box</span> <span class="selector-class">.d1</span><span class="selector-pseudo">::after</span> {</span><br><span class="line"> <span class="attribute">content</span>: <span class="string">'虎年大吉'</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d-box1</span> <span class="selector-class">.d1</span><span class="selector-pseudo">::after</span> {</span><br><span class="line"> <span class="attribute">content</span>: <span class="string">'万事顺遂'</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d-box1</span> <span class="selector-class">.d1</span>,</span><br><span class="line"><span class="selector-class">.d-box1</span> <span class="selector-class">.d2</span>,</span><br><span class="line"><span class="selector-class">.d-box1</span> <span class="selector-class">.d1</span><span class="selector-pseudo">::after</span>,</span><br><span class="line"><span class="selector-class">.d-box1</span> <span class="selector-class">.d1</span><span class="selector-pseudo">::before</span>,</span><br><span class="line"><span class="selector-class">.d-box1</span> <span class="selector-class">.d2</span><span class="selector-pseudo">::after</span>,</span><br><span class="line"><span class="selector-class">.d-box1</span> <span class="selector-class">.d2</span><span class="selector-pseudo">::before</span>,</span><br><span class="line"><span class="selector-class">.d-box1</span> <span class="selector-class">.d2</span> <span class="selector-tag">ul</span> <span class="selector-tag">li</span> <span class="selector-tag">span</span>,</span><br><span class="line"><span class="selector-class">.d-box1</span> <span class="selector-class">.d1</span> <span class="selector-tag">ul</span> <span class="selector-tag">li</span> <span class="selector-tag">span</span> {</span><br><span class="line"> <span class="attribute">background-color</span>: <span class="number">#f01f1a</span>;</span><br><span class="line"> <span class="attribute">border</span>: <span class="number">5px</span> solid <span class="number">#5c1713</span>;</span><br><span class="line"> <span class="comment">/* 自定义灯笼的阴影 */</span></span><br><span class="line"> <span class="comment">/* box-shadow: 0 5px 61px rgba(255, 240, 29, 0.88); */</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d1</span>,</span><br><span class="line"><span class="selector-class">.d2</span>,</span><br><span class="line"><span class="selector-class">.d1</span><span class="selector-pseudo">::after</span>,</span><br><span class="line"><span class="selector-class">.d1</span><span class="selector-pseudo">::before</span>,</span><br><span class="line"><span class="selector-class">.d2</span><span class="selector-pseudo">::after</span>,</span><br><span class="line"><span class="selector-class">.d2</span><span class="selector-pseudo">::before</span>,</span><br><span class="line"><span class="selector-class">.d2</span> <span class="selector-tag">ul</span> <span class="selector-tag">li</span> <span class="selector-tag">span</span>,</span><br><span class="line"><span class="selector-class">.d1</span> <span class="selector-tag">ul</span> <span class="selector-tag">li</span> <span class="selector-tag">span</span> {</span><br><span class="line"> <span class="attribute">background-color</span>: <span class="number">#f01f1a</span>;</span><br><span class="line"> <span class="attribute">border</span>: <span class="number">5px</span> solid <span class="number">#5c1713</span>;</span><br><span class="line"> <span class="comment">/* 自定义灯笼的阴影 */</span></span><br><span class="line"> <span class="attribute">box-shadow</span>: <span class="number">0</span> <span class="number">5px</span> <span class="number">61px</span> <span class="number">#ff1d1d</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d1</span><span class="selector-pseudo">::after</span>,</span><br><span class="line"><span class="selector-class">.d1</span><span class="selector-pseudo">::before</span> {</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">82px</span>;</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">content</span>: <span class="string">''</span>;</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">17px</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d1</span>,</span><br><span class="line"><span class="selector-class">.d2</span> {</span><br><span class="line"> <span class="attribute">position</span>: relative;</span><br><span class="line"> <span class="attribute">animation</span>: swing <span class="number">4s</span> linear infinite;</span><br><span class="line"> <span class="attribute">transform-origin</span>: top center;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d1</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">160px</span>;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">100px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">90px</span>;</span><br><span class="line"> <span class="attribute">right</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">80px</span>/<span class="number">49px</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d1</span><span class="selector-pseudo">::before</span> {</span><br><span class="line"> <span class="attribute">top</span>: -<span class="number">5px</span>;</span><br><span class="line"> <span class="attribute">right</span>: <span class="number">7px</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">123px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">62px</span>/<span class="number">52px</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d1</span><span class="selector-pseudo">::after</span> {</span><br><span class="line"> <span class="attribute">text-align</span>: center;</span><br><span class="line"> <span class="attribute">line-height</span>: <span class="number">90px</span>;</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#ffe31d</span>;</span><br><span class="line"> <span class="attribute">font-weight</span>: <span class="number">600</span>;</span><br><span class="line"> <span class="attribute">top</span>: -<span class="number">5px</span>;</span><br><span class="line"> <span class="attribute">right</span>: <span class="number">35px</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">69px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">41px</span>/<span class="number">49px</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d1</span> <span class="selector-tag">span</span> {</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">84px</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">49px</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">50px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">16px</span>;</span><br><span class="line"> <span class="attribute">z-index</span>: <span class="number">2</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">0</span> <span class="number">0</span> <span class="number">10px</span> <span class="number">10px</span>;</span><br><span class="line"> <span class="attribute">background-color</span>: <span class="number">#ffe31d</span>;</span><br><span class="line"> <span class="attribute">border</span>: <span class="number">5px</span> solid <span class="number">#5c1713</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d1</span> <span class="selector-tag">span</span><span class="selector-pseudo">:nth-child</span>(<span class="number">2</span>) {</span><br><span class="line"> <span class="attribute">top</span>: -<span class="number">17px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">10px</span> <span class="number">10px</span> <span class="number">0</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d1</span> <span class="selector-tag">p</span> {</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">top</span>: -<span class="number">31px</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">13px</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">16px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">13px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">25px</span>;</span><br><span class="line"> <span class="attribute">border</span>: <span class="number">5px</span> solid <span class="number">#5c1713</span>;</span><br><span class="line"> <span class="attribute">border-bottom</span>: <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d1</span> <span class="selector-tag">ul</span> {</span><br><span class="line"> <span class="attribute">position</span>: relative;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">80px</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">13px</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">54px</span>;</span><br><span class="line"> <span class="attribute">display</span>: flex;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d1</span> <span class="selector-tag">li</span> {</span><br><span class="line"> <span class="attribute">flex</span>: <span class="number">1</span>;</span><br><span class="line"> <span class="attribute">list-style</span>: none;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">24px</span>;</span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">0px</span> <span class="number">2.5px</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">5px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">5px</span>;</span><br><span class="line"> <span class="attribute">border-right</span>: <span class="number">3.5px</span> solid <span class="number">#5c1713</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d1</span> <span class="selector-tag">ul</span> <span class="selector-tag">li</span><span class="selector-pseudo">:nth-child</span>(<span class="number">3</span>) {</span><br><span class="line"> <span class="attribute">content</span>: <span class="string">''</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">50px</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d1</span> <span class="selector-tag">ul</span> <span class="selector-tag">li</span><span class="selector-pseudo">:nth-child</span>(<span class="number">3</span>)<span class="selector-pseudo">::before</span> {</span><br><span class="line"> <span class="attribute">content</span>: <span class="string">''</span>;</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">47px</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">54px</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">5px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">5px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">5px</span> <span class="number">5px</span> <span class="number">10px</span> <span class="number">10px</span>;</span><br><span class="line"> <span class="attribute">background-color</span>: <span class="number">#ffe31d</span>;</span><br><span class="line"> <span class="attribute">border</span>: <span class="number">5px</span> solid <span class="number">#5c1713</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d1</span> <span class="selector-tag">ul</span> <span class="selector-tag">li</span> <span class="selector-tag">span</span> {</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">20px</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">55px</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">13px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">19px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">14px</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d2</span><span class="selector-pseudo">::after</span>,</span><br><span class="line"><span class="selector-class">.d2</span><span class="selector-pseudo">::before</span> {</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">128px</span>;</span><br><span class="line"> <span class="attribute">top</span>: -<span class="number">3px</span>;</span><br><span class="line"> <span class="attribute">content</span>: <span class="string">''</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d2</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">199px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">128px</span>;</span><br><span class="line"> <span class="attribute">top</span>: -<span class="number">61px</span>;</span><br><span class="line"> <span class="attribute">right</span>: -<span class="number">122px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">98px</span>/<span class="number">70px</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d2</span><span class="selector-pseudo">::before</span> {</span><br><span class="line"> <span class="attribute">top</span>: -<span class="number">8px</span>;</span><br><span class="line"> <span class="attribute">right</span>: <span class="number">18px</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">143px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">69px</span>/<span class="number">67px</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 自定义背景图片 */</span></span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d2</span><span class="selector-pseudo">::after</span> {</span><br><span class="line"> <span class="attribute">top</span>: -<span class="number">8px</span>;</span><br><span class="line"> <span class="attribute">right</span>: <span class="number">51px</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">75px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">57px</span>/<span class="number">89px</span>;</span><br><span class="line"> <span class="attribute">background</span>: <span class="built_in">url</span>(<span class="string">https://bu.dusays.com/2021/02/03/7e1a77cf800cf.png</span>) no-repeat;</span><br><span class="line"> <span class="attribute">background-position</span>: center;</span><br><span class="line"> <span class="attribute">background-size</span>: <span class="number">92px</span> auto;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d2</span> <span class="selector-tag">span</span> {</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">123px</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">68px</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">55px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">14px</span>;</span><br><span class="line"> <span class="attribute">z-index</span>: <span class="number">2</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">0</span> <span class="number">0</span> <span class="number">10px</span> <span class="number">10px</span>;</span><br><span class="line"> <span class="attribute">background-color</span>: <span class="number">#ffe31d</span>;</span><br><span class="line"> <span class="attribute">border</span>: <span class="number">5px</span> solid <span class="number">#5c1713</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d2</span> <span class="selector-tag">span</span><span class="selector-pseudo">:nth-child</span>(<span class="number">2</span>) {</span><br><span class="line"> <span class="attribute">top</span>: -<span class="number">16px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">10px</span> <span class="number">10px</span> <span class="number">0</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d2</span> <span class="selector-tag">p</span> {</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">top</span>: -<span class="number">32px</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">13px</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">19px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">13px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">25px</span>;</span><br><span class="line"> <span class="attribute">border</span>: <span class="number">5px</span> solid <span class="number">#5c1713</span>;</span><br><span class="line"> <span class="attribute">border-bottom</span>: <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d2</span> <span class="selector-tag">ul</span> {</span><br><span class="line"> <span class="attribute">position</span>: relative;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">121px</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">32px</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">53px</span>;</span><br><span class="line"> <span class="attribute">display</span>: flex;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d2</span> <span class="selector-tag">li</span> {</span><br><span class="line"> <span class="attribute">flex</span>: <span class="number">1</span>;</span><br><span class="line"> <span class="attribute">list-style</span>: none;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">24px</span>;</span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">0px</span> <span class="number">3px</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">4px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">7px</span>;</span><br><span class="line"> <span class="attribute">border-right</span>: <span class="number">3px</span> solid <span class="number">#5c1713</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d2</span> <span class="selector-tag">ul</span> <span class="selector-tag">li</span><span class="selector-pseudo">:nth-child</span>(<span class="number">3</span>) {</span><br><span class="line"> <span class="attribute">content</span>: <span class="string">''</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">60px</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d2</span> <span class="selector-tag">ul</span> <span class="selector-tag">li</span><span class="selector-pseudo">:nth-child</span>(<span class="number">3</span>)<span class="selector-pseudo">::before</span> {</span><br><span class="line"> <span class="attribute">content</span>: <span class="string">''</span>;</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">59px</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">53px</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">9px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">6px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">5px</span> <span class="number">5px</span> <span class="number">10px</span> <span class="number">10px</span>;</span><br><span class="line"> <span class="attribute">background-color</span>: <span class="number">#ffe31d</span>;</span><br><span class="line"> <span class="attribute">border</span>: <span class="number">5px</span> solid <span class="number">#5c1713</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.d2</span> <span class="selector-tag">ul</span> <span class="selector-tag">li</span> <span class="selector-tag">span</span> {</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">21px</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">54px</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">18px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">17px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">20px</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">@keyframes</span> swing {</span><br><span class="line"> <span class="number">0%</span> {</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">rotate</span>(<span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="number">25%</span> {</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">rotate</span>(-<span class="number">13deg</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="number">50%</span> {</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">rotate</span>(<span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="number">75%</span> {</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">rotate</span>(<span class="number">13deg</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="number">100%</span> {</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">rotate</span>(<span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 灯笼 END */</span></span><br><span class="line"></span><br></pre></td></tr></table></figure></li>
<li>新建PUG<br>在<code>BlogRoot/themes/butterfly/layout/includes</code>文件夹下新建<code>lantern.pug</code>,文件内容如下:<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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></pre></td><td class="code"><pre><span class="line">.dengl</span><br><span class="line"> .d-box</span><br><span class="line"> .d1</span><br><span class="line"> span</span><br><span class="line"> span</span><br><span class="line"> p</span><br><span class="line"> ul</span><br><span class="line"> li</span><br><span class="line"> li </span><br><span class="line"> li</span><br><span class="line"> span</span><br><span class="line"> li </span><br><span class="line"> li </span><br><span class="line"> .d2</span><br><span class="line"> span</span><br><span class="line"> span</span><br><span class="line"> p</span><br><span class="line"> ul</span><br><span class="line"> li</span><br><span class="line"> li </span><br><span class="line"> li</span><br><span class="line"> span</span><br><span class="line"> li </span><br><span class="line"> li </span><br><span class="line"> .d-box1</span><br><span class="line"> .d1</span><br><span class="line"> span</span><br><span class="line"> span</span><br><span class="line"> p</span><br><span class="line"> ul</span><br><span class="line"> li</span><br><span class="line"> li </span><br><span class="line"> li</span><br><span class="line"> span</span><br><span class="line"> li </span><br><span class="line"> li </span><br><span class="line"> .d2</span><br><span class="line"> span</span><br><span class="line"> span</span><br><span class="line"> p</span><br><span class="line"> ul</span><br><span class="line"> li</span><br><span class="line"> li </span><br><span class="line"> li</span><br><span class="line"> span</span><br><span class="line"> li </span><br><span class="line"> li </span><br></pre></td></tr></table></figure></li>
<li>在<code>BlogRoot/themes/butterfly/layout/includes/layout.pug</code>中引入<code>lantern.pug</code>。<br>具体位置请参考下图:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/05/06/f9738ff037914.jpg"/></div></div></li>
<li>在主题配置文件<code>_config.butterfly.yml</code>中的<code>inject.head</code>中引入<code>lantern.css</code>。<br>具体位置请参考下图:<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/05/06/eb76fbff1da60.jpg"/></div></div>
5.重新编译文件再运行,即可看到效果。</li>
</ol>
<h2 id="遇到问题"><a href="#遇到问题" class="headerlink" title="遇到问题"></a>遇到问题</h2><div class="note icon-padding modern"><i class="note-icon fas fa-car-crash"></i><p>如果在阅读过程中遇到什么问题 ,请在 <span class='p red'>评论区</span> 留言 ,我会在第一时间内帮助您解决问题
百度分享插件的使用
https://fe32.top/articles/bd1314sh/
2021-03-18T02:38:59.000Z
2023-05-17T10:01:10.000Z
百度分享插件的使用
Hexo + Butterfly 主题美化
https://fe32.top/articles/hexo541u/
2021-03-03T09:06:47.000Z
2023-05-17T10:01:12.000Z
<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2022/01/09/3a2053f3f8799.jpg" style="width:500px;"/></div></div>
<br>
<blockquote>
<p>本篇美化教程不再维护,请移步文章 <a href="https://fe32.top/articles/hexo1606/">基于 Hexo
一种自动化生成骨架屏的方案
https://fe32.top/articles/e13t14zy/
2021-01-25T12:28:15.000Z
2023-05-17T10:01:12.000Z
今天分享的主题是:「一种自动化生成骨架屏的方案」。
Vue-Cli3.0打包时如何去掉console打印信息
https://fe32.top/articles/p520xyz8/
2021-01-23T10:28:07.000Z
2023-05-17T10:01:12.000Z
<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><div class="tip cogs faa-horizontal animated"><p>我们在进行项目开发过程中,一般会用到console.log打印某些信息。但是如何生产环境打包时去掉所有consloe打印信息呢?以下是解决办法,也是自己一直使用的方法。</p>
</div>
<h1 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h1><ol>
<li>安装插件 babel-plugin-transform-remove-console<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install babel-plugin-transform-remove-console --save</span><br></pre></td></tr></table></figure></li>
<li>修改 babel.config.js(如果没有自己创建)<div class="img-wrap"><div class="img-bg"><img class="img" src= "https://bu.dusays.com/2022/01/14/1ff50a00bd75c.gif" data-lazy-src="https://bu.dusays.com/2021/12/31/bfef12a4dda96.jpg" alt="请在根目录下创建babel.config.js"/></div><span class="image-caption">请在根目录下创建babel.config.js</span></div>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> plugins = []</span><br><span class="line"><span class="keyword">if</span> (process.<span class="property">env</span>.<span class="property">NODE_ENV</span> === <span class="string">'production'</span>) {</span><br><span class="line"> <span class="comment">//exclude(忽略)</span></span><br><span class="line"> plugins.<span class="title function_">push</span>([<span class="string">"transform-remove-console"</span>, { <span class="string">"exclude"</span>: [<span class="string">"error"</span>, <span class="string">"warn"</span>] }])</span><br><span class="line">}</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = {</span><br><span class="line"> <span class="attr">presets</span>: [</span><br><span class="line"> <span class="string">'@vue/app'</span></span><br><span class="line"> ],</span><br><span class="line"> plugins</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<div class="note red icon-padding modern"><i class="note-icon fas
添加Github徽标
https://fe32.top/articles/kfwr2gpa/
2021-01-07T10:36:00.000Z
2023-05-17T10:01:10.000Z
添加Github徽标
基于Butterfly的外挂标签引入
https://fe32.top/articles/0xiipgum/
2021-01-05T08:14:02.000Z
2024-03-26T07:54:18.277Z
基于Butterfly的外挂标签引入
nvm,npm与nrm
https://fe32.top/articles/9r95s1wt/
2020-12-30T07:55:35.000Z
2023-05-17T10:01:12.000Z
Node必备:nvm、npm与nrm
Markdown语法支持的Emoji表情
https://fe32.top/articles/hyeccjmu/
2020-12-28T08:28:37.000Z
2023-05-17T10:01:12.000Z
Markdown语法支持添加 emoji 表情,输入不同的符号码(两个冒号包围的字符)可以显示出不同的表情。
Vue后台管理系统模板推荐
https://fe32.top/articles/xi2mpxmd/
2020-12-21T12:15:33.000Z
2023-11-14T08:46:08.000Z
在我们进行网站管理时一个好的后台管理模板是非常重要的,推荐几款后台模板给大家,也是自己常用的模板。