<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>磨蹭先生</title>
  
  <subtitle>猫系小孩</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://gatings.cn/"/>
  <updated>2021-10-13T01:56:29.703Z</updated>
  <id>https://gatings.cn/</id>
  
  <author>
    <name>gating</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>vue2+vite初体验</title>
    <link href="https://gatings.cn/2021-09-05/vue2+vite%E5%88%9D%E4%BD%93%E9%AA%8C/"/>
    <id>https://gatings.cn/2021-09-05/vue2+vite%E5%88%9D%E4%BD%93%E9%AA%8C/</id>
    <published>2021-09-04T16:00:00.000Z</published>
    <updated>2021-10-13T01:56:29.703Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>自从 <a href="https://cn.vitejs.dev/" target="_blank" rel="noopener">vite</a> 发布之后，社区赞誉无数，而我也一直心水 <a href="https://cn.vitejs.dev/" target="_blank" rel="noopener">vite</a> 的轻量快速的热重载的特性，特别是公司的项目巨大，已经严重拖慢了热重载的速度了，每次热重载都要等上一小会，所以急需寻找一个解决方案。也发现自己很久没更新博客了，顺手更新一篇下 😢</p><p>虽然，我们通过 <a href="https://www.webpackjs.com/" target="_blank" rel="noopener">webpack</a> 配置，指定了在本地加载的路由，使得热更新更加迅速一些，但是仍然是远远不够的。所以就想着使用 <a href="https://cn.vitejs.dev/" target="_blank" rel="noopener">vite</a> 进行尝试了。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">"fs"</span>);</span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">"path"</span>);</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">resolve</span>(<span class="params">dir</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> path.join(__dirname, dir);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">const</span> isLocal = process.env.LOCAL === <span class="string">"true"</span>;</span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  chainWebpack: <span class="function">(<span class="params">config</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (isLocal &amp;&amp; fs.existsSync(resolve(<span class="string">"/src/mainDev.js"</span>))) &#123;</span><br><span class="line">      config.entry(<span class="string">"app"</span>).clear().add(<span class="string">"./src/mainDev.js"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><blockquote><p>ps: 我的理想的方案是：<code>webpack</code> 仍然作为打包工具，<code>vite</code> 作为开发工具。因为我仍然觉得 <code>webpack</code> 还是当下构建 <code>webapp</code> 的最佳实践(带有代码拆分，旧浏览器的 Legecy-build)。所以，我会尽量在 <code>vite</code> 和 <code>webpack</code> 环境下维护一份配置。</p></blockquote><blockquote><p>ps: 为了更加无缝的迁移 Vite，这里使用了 vue-cli 插件，即 vue-cli-plugin-vite</p></blockquote><p>本次教程可能过于啰嗦，可以先到<a href="https://gitee.com/gating/demo/tree/master/vite" target="_blank" rel="noopener">gitee</a>、<a href="https://github.com/GATING/demo/tree/master/vite" target="_blank" rel="noopener">github</a>下载体验，也可到文末直接下载代码先自行体验。。。</p><blockquote><p>特别说明：项目使用的 Node 版本为 14.17.6，Node10 项目的版本为 10.15.3，皆为 Node 稳定版本</p></blockquote><h1 id="初步体验"><a href="#初步体验" class="headerlink" title="初步体验"></a>初步体验</h1><p>有了这个想法，当然就打开官网直接开干呀，打开<a href="https://cn.vitejs.dev/guide/#scaffolding-your-first-vite-project" target="_blank" rel="noopener">搭建第一个 Vite 项目</a>，发现 Vite 需要 <a href="https://nodejs.org/en/" target="_blank" rel="noopener">Node.js</a> 版本 &gt;= 12.0.0，而我公司用的是 <code>Node10</code> 稳定版。</p><p>哦豁 😢！！看到这里，本以为本次迁移就到此结束了~~。</p><h1 id="Node10-尝试（可选）"><a href="#Node10-尝试（可选）" class="headerlink" title="Node10 尝试（可选）"></a>Node10 尝试（可选）</h1><p>当然，我抱着尝着一试的心态，在 Node10 中运行 Vite，然后出现报错了，具体如下：</p><p><font color="red">Error: Cannot find module ‘worker_threads’</font></p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2021-09-05/vue2-vite-demo-icon01.jpg" data-fancybox="group" data-caption="vue2-vite-demo-icon01.jpg" class="fancybox"><img alt="vue2-vite-demo-icon01.jpg" title="vue2-vite-demo-icon01.jpg" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2021-09-05/vue2-vite-demo-icon01.jpg" class="lazyload"></a></p><p>所以我 <code>google</code> 搜索了下 <a href="https://stackoverflow.com/questions/62280966/cannot-find-module-worker-threads-error-in-production" target="_blank" rel="noopener">答案</a>，发现 Node10.5 就支持了 workers，不过 Node12 是自动开启，而 Node10 是需要手动开启，所以这边做了如下修改(伪代码):</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">json</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight json"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"scripts"</span>: &#123;</span><br><span class="line">    <span class="attr">"vite"</span>: <span class="string">"node --experimental-worker ./bin/vite"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>然后- -，Vite 底层出现了新的报错，因为 Vite 的使用了数组的 flat 方法。</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2021-09-05/vue2-vite-demo-icon02.jpg" data-fancybox="group" data-caption="vue2-vite-demo-icon02.jpg" class="fancybox"><img alt="vue2-vite-demo-icon02.jpg" title="vue2-vite-demo-icon02.jpg" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2021-09-05/vue2-vite-demo-icon02.jpg" class="lazyload"></a></p><p>所以我们需要对 Vite 进行 Babel 的编译，所以我们需要安装一下 @babel/node，<code>npm i @babel/node -D</code>，伪代码:</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">json</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight json"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"scripts"</span>: &#123;</span><br><span class="line">    <span class="attr">"vite"</span>: <span class="string">"babel-node --experimental-worker ./bin/vite"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>然后就可以愉快的运行啦</p><blockquote><p>ps: 因为这里使用的是 vue-cli-plugin-vite，他是使用 cross-spawn 执行脚本的，所以这里的 babel-node –experimental-worker 在 scripts 无效，需要在 ./bin/vite 文件里编写，具体参考<a href="https://gitee.com/gating/demo/tree/master/vite/vue2-vite-demo-node10" target="_blank" rel="noopener">这个链接-GITEE</a>、<a href="https://github.com/GATING/demo/tree/master/vite/vue2-vite-demo-node10" target="_blank" rel="noopener">这个链接-GITHUB</a></p></blockquote><h1 id="开始搭建"><a href="#开始搭建" class="headerlink" title="开始搭建"></a>开始搭建</h1><p>为了大家尽可能的少改 <code>webpack</code>，我的案例中也覆盖了相对多的常用配置，比如：</p><ul><li>scss 变量注入</li><li>环境变量的使用</li><li>使用别名 alias</li><li>配置 resolve externals</li><li>使用 jsx</li><li>require 语法</li><li>devServer</li><li>require.context 语法兼容</li></ul><blockquote><p>ps: 兼容这些虽然多数都是 vue-cli-plugin-vite 做的事，但是就是想着大家可以拿来即用 😂，更多兼容参考<a href="https://github.com/IndexXuan/vue-cli-plugin-vite" target="_blank" rel="noopener">vue-cli-plugin-vite</a></p></blockquote><p>为了更好的编写体验，这里提供一个基础的 <code>vue-cli</code> 的<a href="https://gitee.com/gating/demo/tree/master/vite/vue2-vite-demo-template" target="_blank" rel="noopener">demo</a>，可以 download 下来一起尝试编写一下。</p><h2 id="安装-vue-cli-plugin-vite"><a href="#安装-vue-cli-plugin-vite" class="headerlink" title="安装 vue-cli-plugin-vite"></a>安装 vue-cli-plugin-vite</h2><p>在当前项目打开终端，运行：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">shell</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">vue add vite</span><br></pre></td></tr></table></figure></div><h3 id="忽略-vue-拓展名"><a href="#忽略-vue-拓展名" class="headerlink" title="忽略 .vue 拓展名"></a>忽略 .vue 拓展名</h3><p>这里后你会发现项目里多了 <code>bin/vite</code> 文件，<code>package.json</code> 的 <code>scripts</code> 也多少了一个 <code>vite</code> 的命令，运行:</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">shell</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">npm run vite</span><br></pre></td></tr></table></figure></div><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2021-09-05/vue2-vite-demo-icon03.jpg" data-fancybox="group" data-caption="vue2-vite-demo-icon03.jpg" class="fancybox"><img alt="vue2-vite-demo-icon03.jpg" title="vue2-vite-demo-icon03.jpg" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2021-09-05/vue2-vite-demo-icon03.jpg" class="lazyload"></a></p><p><code>Unrestricted file system access to &quot;/src/layout&quot;</code>，这个报错说明找不到这个文件，可是我们看，我们明明有<code>layout/index.vue</code>，但是却报找不到，这是为什么呢？这是因为 Vite 的 resolve.extensions 默认的 .vue 的后缀名，官方也不推荐自定义导入类型的扩展名,因为它会影响 IDE 和类型支持。(<a href="https://cn.vitejs.dev/config/#resolve-extensions" target="_blank" rel="noopener">查看链接</a>)</p><p>当然，我们为了兼容以前的旧项目，还是需要配置的，所以我们需要更新下我们的配置，在<code>vue.config.js</code>中补上 resolve.extensions 的配置，代码如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  configureWebpack: &#123;</span><br><span class="line">    resolve: &#123;</span><br><span class="line">      extensions: [<span class="string">".mjs"</span>, <span class="string">".js"</span>, <span class="string">".ts"</span>, <span class="string">".jsx"</span>, <span class="string">".tsx"</span>, <span class="string">".json"</span>, <span class="string">".vue"</span>],</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><blockquote><p>ps: 小插曲，之前测试的时候发现配了 resolve.extensions 也没有效果，然后翻阅 Vite 文档，发现 Vite 是支持的，但是 vue-cli-plugin-vite 不支持，所以我给作者提了个 <a href="https://github.com/IndexXuan/vue-cli-plugin-vite/issues/45" target="_blank" rel="noopener">Issue</a>，现在也支持了，感谢作者~~</p></blockquote><blockquote><p>ps: 以后一定要写后缀名<del>~</del> 相关 Issues <a href="https://github.com/vitejs/vite/issues/178#issuecomment-630138450" target="_blank" rel="noopener">178</a>、<a href="https://github.com/vitejs/vite/issues/2163" target="_blank" rel="noopener">2163</a>、</p></blockquote><h3 id="JSX-语法处理"><a href="#JSX-语法处理" class="headerlink" title="JSX 语法处理"></a>JSX 语法处理</h3><p>添加完后，再次运行:</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">shell</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">npm run vite</span><br></pre></td></tr></table></figure></div><p>发现又报了如下错误：</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2021-09-05/vue2-vite-demo-icon04.jpg" data-fancybox="group" data-caption="vue2-vite-demo-icon04.jpg" class="fancybox"><img alt="vue2-vite-demo-icon04.jpg" title="vue2-vite-demo-icon04.jpg" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2021-09-05/vue2-vite-demo-icon04.jpg" class="lazyload"></a></p><p>翻译来说就是说你在 .vue 文件中用了无效的 js 语法(即 JSX)，这里就就需要我们在 vue 的 sfc 组件中还得加上 jsx 标识，即(<code>src/components/HelloWorld.vue</code>)：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">html</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">lang</span>=<span class="string">"jsx"</span>&gt;</span></span><br><span class="line"><span class="javascript">  <span class="keyword">import</span> Test <span class="keyword">from</span> <span class="string">"./Test"</span>;</span></span><br><span class="line"><span class="javascript">  <span class="keyword">export</span> <span class="keyword">default</span> &#123;</span></span><br><span class="line"><span class="actionscript">    name: <span class="string">"HelloWorld"</span>,</span></span><br><span class="line">    components: &#123;</span><br><span class="line">      Test,</span><br><span class="line">      TestJsx: &#123;</span><br><span class="line">        render() &#123;</span><br><span class="line"><span class="javascript">          <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span>我是vue文件的JSX渲染的<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span>;</span></span><br><span class="line">        &#125;,</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">    props: &#123;</span><br><span class="line"><span class="javascript">      msg: <span class="built_in">String</span>,</span></span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure></div><p>修改完后再次运行，发现又报错了，而且这个错误和上面的还很类似。不过只是说我们在 .js 文件中用了无效的 js 语法(即 JSX)，如果您使用的是 JSX 请确保将文件命名为.JSX 或.tsx 扩展名。</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2021-09-05/vue2-vite-demo-icon05.jpg" data-fancybox="group" data-caption="vue2-vite-demo-icon05.jpg" class="fancybox"><img alt="vue2-vite-demo-icon05.jpg" title="vue2-vite-demo-icon05.jpg" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2021-09-05/vue2-vite-demo-icon05.jpg" class="lazyload"></a></p><p>js 中不支持 jsx 的原因，尤大也在 issue 有过说明，具体参考<a href="https://github.com/vitejs/vite/issues/769#issuecomment-780593283" target="_blank" rel="noopener">这个链接</a></p><p>所以，我们只需要把 .js 文件的后缀名修改为 .jsx 即可</p><p>修改完后，再次运行:</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">shell</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">npm run vite</span><br></pre></td></tr></table></figure></div><p>这里会发现，浏览器报 require is not defined，这里我们先把 Home.vue 文件的 require 注释掉先(require 的问题下面会讲到)，代码如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">html</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="actionscript">  <span class="comment">// @ is an alias to /src</span></span></span><br><span class="line"><span class="javascript">  <span class="keyword">import</span> HelloWorld <span class="keyword">from</span> <span class="string">"@comp/HelloWorld"</span>;</span></span><br><span class="line"><span class="actionscript">  <span class="comment">// const &#123; sum &#125; = require('../utils/index')</span></span></span><br><span class="line"></span><br><span class="line"><span class="javascript">  <span class="keyword">export</span> <span class="keyword">default</span> &#123;</span></span><br><span class="line"><span class="actionscript">    name: <span class="string">"Home"</span>,</span></span><br><span class="line">    components: &#123;</span><br><span class="line">      HelloWorld,</span><br><span class="line">    &#125;,</span><br><span class="line">    methods: &#123;</span><br><span class="line">      handleClick() &#123;</span><br><span class="line"><span class="actionscript">        <span class="comment">// console.log(sum(1, 32))</span></span></span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure></div><p>出现如下报错：</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2021-09-05/vue2-vite-demo-icon06.jpg" data-fancybox="group" data-caption="vue2-vite-demo-icon06.jpg" class="fancybox"><img alt="vue2-vite-demo-icon06.jpg" title="vue2-vite-demo-icon06.jpg" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2021-09-05/vue2-vite-demo-icon06.jpg" class="lazyload"></a></p><p>因为我们虽然设置了一堆使用 jsx 的配置，但是没有在插件上配置开启 jsx(即不设置 vitePluginVue2Options: { jsx: true })，所以需要在 vue.config.js 编写下 vite 的配置啦(终于开始配置 vite 了)，<a href="https://github.com/IndexXuan/vue-cli-plugin-vite/issues/9" target="_blank" rel="noopener">相关 issue</a></p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  pluginOptions: &#123;</span><br><span class="line">    vite: &#123;</span><br><span class="line">      <span class="comment">/**</span></span><br><span class="line"><span class="comment">       * Plugin[]</span></span><br><span class="line"><span class="comment">       * <span class="doctag">@default </span>[]</span></span><br><span class="line"><span class="comment">       */</span></span><br><span class="line">      plugins: [], <span class="comment">// other vite plugins list, will be merge into this plugin\'s underlying vite.config.ts</span></span><br><span class="line">      <span class="comment">/**</span></span><br><span class="line"><span class="comment">       * Vite UserConfig.optimizeDeps options</span></span><br><span class="line"><span class="comment">       * recommended set `include` for speedup page-loaded time, e.g. include: ['vue', 'vue-router', '<span class="doctag">@scope</span>/xxx']</span></span><br><span class="line"><span class="comment">       * <span class="doctag">@default <span class="type">&#123;&#125;</span></span></span></span><br><span class="line"><span class="comment">       */</span></span><br><span class="line">      optimizeDeps: &#123;&#125;,</span><br><span class="line">      <span class="comment">/**</span></span><br><span class="line"><span class="comment">       * type-checker, recommended disabled for large-scale old project.</span></span><br><span class="line"><span class="comment">       * <span class="doctag">@default <span class="variable">false</span></span></span></span><br><span class="line"><span class="comment">       */</span></span><br><span class="line">      disabledTypeChecker: <span class="literal">true</span>,</span><br><span class="line">      <span class="comment">/**</span></span><br><span class="line"><span class="comment">       * lint code by eslint</span></span><br><span class="line"><span class="comment">       * <span class="doctag">@default <span class="variable">false</span></span></span></span><br><span class="line"><span class="comment">       */</span></span><br><span class="line">      disabledLint: <span class="literal">false</span>,</span><br><span class="line">      <span class="comment">/**</span></span><br><span class="line"><span class="comment">       * enable css-loader url resolve compat</span></span><br><span class="line"><span class="comment">       * disabled it if you do not use `~@/assets/logo.png` for better performance.</span></span><br><span class="line"><span class="comment">       * <span class="doctag">@default <span class="variable">true</span></span></span></span><br><span class="line"><span class="comment">       */</span></span><br><span class="line">      cssLoaderCompat: <span class="literal">true</span>,</span><br><span class="line">      vitePluginVue2Options: &#123;</span><br><span class="line">        jsx: <span class="literal">true</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><p>再次运行，发现可以打开页面了</p><blockquote><p>总结：在 vite 中使用 jsx 还是稍微有点麻烦的，一是使用到 jsx 语法的 js 文件都必须改成使用 jsx 后缀名，二是在 vue 的 sfc 组件中还得加上 jsx 标识(仅仅引入一个 .jsx 文件 不需要加上)</p></blockquote><h3 id="require-语法处理"><a href="#require-语法处理" class="headerlink" title="require 语法处理"></a>require 语法处理</h3><p>把 require 的注释打开，再次运行，f12 打开控制台，出现如下错误：</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2021-09-05/vue2-vite-demo-icon07.jpg" data-fancybox="group" data-caption="vue2-vite-demo-icon07.jpg" class="fancybox"><img alt="vue2-vite-demo-icon07.jpg" title="vue2-vite-demo-icon07.jpg" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2021-09-05/vue2-vite-demo-icon07.jpg" class="lazyload"></a></p><p>因为 vite 不支持 require 的，那么怎么解决呢？这时候就需要使用 vite 插件了。</p><p>这里说说我是怎么找这些插件的吧，通常不知道怎么办的时候，就去 npm 搜索一下关键字 vite commonjs，然后看下这些插件的下载量，率先选择最高的那个使用，这里发现 <a href="https://www.npmjs.com/package/@originjs/vite-plugin-commonjs" target="_blank" rel="noopener">@originjs/vite-plugin-commonjs</a> 这个周下载量有 2000+。所以这里就尝试使用这个了，发现一试还真成了。</p><p>所以，接下来就跟着我一起安装并且配置一下吧。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">shell</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">npm install @originjs/vite-plugin-commonjs -D</span><br></pre></td></tr></table></figure></div><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> &#123; viteCommonjs &#125; = <span class="built_in">require</span>(<span class="string">"@originjs/vite-plugin-commonjs"</span>);</span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  pluginOptions: &#123;</span><br><span class="line">    vite: &#123;</span><br><span class="line">      plugins: [</span><br><span class="line">        viteCommonjs(&#123;</span><br><span class="line">          <span class="comment">// lodash不需要进行转换</span></span><br><span class="line">          exclude: [<span class="string">"lodash"</span>],</span><br><span class="line">        &#125;),</span><br><span class="line">      ],</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><blockquote><p>ps: 但是标签上的 require 并不支持，所以建议全面拥抱 ES Module</p></blockquote><blockquote><p>ps: 路由使用 <code>resolve =&gt; require([&#39;../components/views/Home.vue&#39;], resolve)</code> 导入的，可以通过 vscode 使用下面的正则全局替换<br><br>搜索：<code>\(?resolve\)?\s*=&gt;\s*require\(\[(.\*)\], resolve\)</code><br><br>替换：<code>() =&gt; import($1)</code></p></blockquote><h3 id="scss-变量注入"><a href="#scss-变量注入" class="headerlink" title="scss 变量注入"></a>scss 变量注入</h3><p>重新运行一下，发现啥问题都没有，看着一切正常，这时候我觉得 HelloWorld 组件缺点样式，我想美化一样，比如修改下字体颜色、文字大小啥的。</p><p>所以我对 HelloWorld 组件添加了样式，进行了如下修改：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">html</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"hello"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">h1</span> <span class="attr">class</span>=<span class="string">"h1"</span>&gt;</span>&#123;&#123; msg &#125;&#125;<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">test</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">test-jsx</span> /&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">lang</span>=<span class="string">"jsx"</span>&gt;</span></span><br><span class="line"><span class="javascript">  <span class="keyword">import</span> Test <span class="keyword">from</span> <span class="string">"./Test"</span>;</span></span><br><span class="line"><span class="javascript">  <span class="keyword">export</span> <span class="keyword">default</span> &#123;</span></span><br><span class="line"><span class="actionscript">    name: <span class="string">"HelloWorld"</span>,</span></span><br><span class="line">    components: &#123;</span><br><span class="line">      Test,</span><br><span class="line">      TestJsx: &#123;</span><br><span class="line">        render() &#123;</span><br><span class="line"><span class="javascript">          <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span>我是vue文件的JSX渲染的<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span>;</span></span><br><span class="line">        &#125;,</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">    props: &#123;</span><br><span class="line"><span class="javascript">      msg: <span class="built_in">String</span>,</span></span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">style</span> <span class="attr">lang</span>=<span class="string">"scss"</span> <span class="attr">scoped</span>&gt;</span></span><br><span class="line"><span class="css">  <span class="selector-class">.h1</span> &#123;</span></span><br><span class="line">    font-size: 30px;</span><br><span class="line">    color: skyblue;</span><br><span class="line">  &#125;</span><br><span class="line"><span class="css">  <span class="selector-class">.hello</span> &#123;</span></span><br><span class="line"><span class="css">    <span class="keyword">@include</span> bgCover(<span class="string">"@/assets/logo.png"</span>);</span></span><br><span class="line">  &#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br></pre></td></tr></table></figure></div><p>还没开始写呢，控制台就一堆报错：</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2021-09-05/vue2-vite-demo-icon08.jpg" data-fancybox="group" data-caption="vue2-vite-demo-icon08.jpg" class="fancybox"><img alt="vue2-vite-demo-icon08.jpg" title="vue2-vite-demo-icon08.jpg" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2021-09-05/vue2-vite-demo-icon08.jpg" class="lazyload"></a></p><p>猜测是使用了别名导入 scss 后，识别到 url() 后就会输出相对路径，所以这边在 vite 环境时候，使用 src/styles 导入即可，具体 vue.config.js 修改如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// npm 正在执行哪个 script,npm_lifecycle_event 就返回当前正在运行的脚本名称。</span></span><br><span class="line"><span class="keyword">const</span> isVite = process.env.npm_lifecycle_event.startsWith(<span class="string">"vite"</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 兼容vite</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getAdditionalData</span>(<span class="params">str</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (isVite) &#123;</span><br><span class="line">    <span class="keyword">return</span> str.replace(<span class="regexp">/@style\//</span>, <span class="string">"src/styles/"</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> str;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  css: &#123;</span><br><span class="line">    requireModuleExtension: <span class="literal">true</span>,</span><br><span class="line">    loaderOptions: &#123;</span><br><span class="line">      scss: &#123;</span><br><span class="line">        <span class="comment">// 注意：在 sass-loader v7 中，这个选项名是 "data" 官网文档还是prependData   此项目用的7+版本</span></span><br><span class="line">        <span class="comment">// 注意：在 sass-loader v10 使用 additionalData，这里为了兼容vite，所以升级了sass-loader@10</span></span><br><span class="line">        additionalData: getAdditionalData(<span class="string">`@import '@style/variables.scss';`</span>),</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><blockquote><p>ps: 这里也有个小知识点，我们可以通过 npm_lifecycle_event 来获取我们执行了的脚本名称，通过 npm_lifecycle_script 获取执行了什么命令</p></blockquote><h3 id="script-指定环境"><a href="#script-指定环境" class="headerlink" title="script 指定环境"></a>script 指定环境</h3><p>通常我们会有 beta、pre、dev 好几个环境，在 vue-cli 开发的时候我们通过会通过 <code>--mode env</code> 指定我们本地的开发环境，现在我们也尝试在 scripts 中的 vite 指定 staging 环境，发现并没有效果：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">json</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight json"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"scripts"</span>: &#123;</span><br><span class="line">    <span class="attr">"vite"</span>: <span class="string">"node ./bin/vite --mode staging"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>这是为什么呢？打开 bin/vite 文件一看，发现 使用 cross-spawn 执行脚本的，所以 <code>--mode staging</code> 这个参数根本就没有获取，那么我们怎么可以获取呢？</p><p>其实我们可以通过 process.argv 获取我们执行的命令的参数，打印一下发现 argv 是个数组，而我们需要的是最后那两个，所以这里需要进行如下修改(<code>bin/vite</code>)：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="meta">#!/usr/bin/env node</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">"path"</span>);</span><br><span class="line"><span class="keyword">const</span> spawn = <span class="built_in">require</span>(<span class="string">"cross-spawn"</span>);</span><br><span class="line"><span class="keyword">const</span> configPath = <span class="built_in">require</span>.resolve(<span class="string">"vue-cli-plugin-vite/config/index.ts"</span>);</span><br><span class="line"><span class="keyword">const</span> cwd = path.resolve(__dirname, <span class="string">"../"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> params = [</span><br><span class="line">  <span class="string">`<span class="subst">$&#123;process.env.BUILD ? <span class="string">"build"</span> : <span class="string">""</span>&#125;</span>`</span>,</span><br><span class="line">  process.env.VITE_DEBUG ? <span class="string">"--debug"</span> : <span class="string">""</span>,</span><br><span class="line">  <span class="string">"--config"</span>,</span><br><span class="line">  <span class="string">`<span class="subst">$&#123;configPath&#125;</span>`</span>,</span><br><span class="line">  ...process.argv.slice(<span class="number">2</span>),</span><br><span class="line">].filter(<span class="built_in">Boolean</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">`running: vite <span class="subst">$&#123;params.join(<span class="string">" "</span>)&#125;</span>`</span>);</span><br><span class="line"><span class="keyword">const</span> serveService = spawn(<span class="string">"vite"</span>, params, &#123;</span><br><span class="line">  cwd,</span><br><span class="line">  stdio: <span class="string">"inherit"</span>,</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">serveService.on(<span class="string">"close"</span>, (code) =&gt; &#123;</span><br><span class="line">  process.exit(code);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></div><p>至此，我们的 vite 命令也可以指定开发环境啦 😉</p><h1 id="额外知识点-keep-alive-使用动态-key-时，热更新无效"><a href="#额外知识点-keep-alive-使用动态-key-时，热更新无效" class="headerlink" title="额外知识点 - keep-alive 使用动态 key 时，热更新无效"></a>额外知识点 - keep-alive 使用动态 key 时，热更新无效</h1><p>一般的后台管理肯定需要 keep-alive 这个组件，比如我们 layout 组件上就是用了 keep-alive，但是你会发现在你使用 keep-alive 的时候，页面却没有热更新，这个不是 vite 的问题，也不是 webpack 的问题，这是 Vue 的问题(当然也有<a href="https://github.com/vuejs/vue/pull/12092" target="_blank" rel="noopener">相关 issue</a>)，而且这个 issue 已经从 18 年就开始有了，且现在仍然是 open 状态(<a href="https://github.com/vuejs/vue-loader/issues/1332" target="_blank" rel="noopener">相关 issue</a>)</p><p>参考评论和 issue，我们也可以编写一个只在开发环境中使用的 keep-alive 组件了。</p><p>创建 plugins/keep-alive.js 文件，编写如下代码：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; isArray, isRegExp &#125; <span class="keyword">from</span> <span class="string">"lodash"</span>;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">remove</span>(<span class="params">arr, item</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (arr.length) &#123;</span><br><span class="line">    <span class="keyword">var</span> index = arr.indexOf(item);</span><br><span class="line">    <span class="keyword">if</span> (index &gt; <span class="number">-1</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> arr.splice(index, <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">isDef</span>(<span class="params">v</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> v !== <span class="literal">undefined</span> &amp;&amp; v !== <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">isAsyncPlaceholder</span>(<span class="params">node</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> node.isComment &amp;&amp; node.asyncFactory;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getFirstComponentChild</span>(<span class="params">children</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (isArray(children)) &#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; children.length; i++) &#123;</span><br><span class="line">      <span class="keyword">let</span> c = children[i];</span><br><span class="line">      <span class="keyword">if</span> (isDef(c) &amp;&amp; (isDef(c.componentOptions) || isAsyncPlaceholder(c))) &#123;</span><br><span class="line">        <span class="keyword">return</span> c;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getComponentName</span>(<span class="params">opts</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> opts &amp;&amp; (opts.Ctor.options.name || opts.tag);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">matches</span>(<span class="params">pattern</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (isArray(pattern)) &#123;</span><br><span class="line">    <span class="keyword">return</span> pattern.indexOf(name) &gt; <span class="number">-1</span>;</span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="keyword">typeof</span> pattern === <span class="string">"string"</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> pattern.split(<span class="string">","</span>).indexOf(name) &gt; <span class="number">-1</span>;</span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (isRegExp(pattern)) &#123;</span><br><span class="line">    <span class="keyword">return</span> pattern.test(name);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">/* istanbul ignore next */</span></span><br><span class="line">  <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">pruneCache</span>(<span class="params">keepAliveInstance, filter</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; cache, keys, _vnode &#125; = keepAliveInstance;</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">const</span> key <span class="keyword">in</span> cache) &#123;</span><br><span class="line">    <span class="keyword">const</span> entry = cache[key];</span><br><span class="line">    <span class="keyword">if</span> (entry) &#123;</span><br><span class="line">      <span class="keyword">const</span> name = entry.name;</span><br><span class="line">      <span class="keyword">if</span> (name &amp;&amp; !filter(name)) &#123;</span><br><span class="line">        pruneCacheEntry(cache, key, keys, _vnode);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">pruneCacheEntry</span>(<span class="params">cache, key, keys, current</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> entry = cache[key];</span><br><span class="line">  <span class="keyword">if</span> (entry &amp;&amp; (!current || entry.tag !== current.tag)) &#123;</span><br><span class="line">    entry.componentInstance.$destroy();</span><br><span class="line">  &#125;</span><br><span class="line">  cache[key] = <span class="literal">null</span>;</span><br><span class="line">  remove(keys, key);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">  install(app) &#123;</span><br><span class="line">    <span class="comment">//只在开发模式下生效</span></span><br><span class="line">    <span class="keyword">if</span> (process.env.NODE_ENV === <span class="string">"development"</span>) &#123;</span><br><span class="line">      <span class="comment">/**</span></span><br><span class="line"><span class="comment">       * Remove an item from an array.</span></span><br><span class="line"><span class="comment">       */</span></span><br><span class="line"></span><br><span class="line">      <span class="keyword">const</span> patternTypes = [<span class="built_in">String</span>, <span class="built_in">RegExp</span>, <span class="built_in">Array</span>];</span><br><span class="line"></span><br><span class="line">      <span class="keyword">const</span> KeepAlive = &#123;</span><br><span class="line">        name: <span class="string">"keep-alive"</span>,</span><br><span class="line">        abstract: <span class="literal">true</span>,</span><br><span class="line"></span><br><span class="line">        props: &#123;</span><br><span class="line">          include: patternTypes,</span><br><span class="line">          exclude: patternTypes,</span><br><span class="line">          max: [<span class="built_in">String</span>, <span class="built_in">Number</span>],</span><br><span class="line">        &#125;,</span><br><span class="line"></span><br><span class="line">        methods: &#123;</span><br><span class="line">          cacheVNode() &#123;</span><br><span class="line">            <span class="keyword">const</span> &#123; cache, keys, vnodeToCache, keyToCache &#125; = <span class="keyword">this</span>;</span><br><span class="line">            <span class="keyword">if</span> (vnodeToCache) &#123;</span><br><span class="line">              <span class="keyword">const</span> &#123; tag, componentInstance, componentOptions &#125; = vnodeToCache;</span><br><span class="line">              cache[keyToCache] = &#123;</span><br><span class="line">                name: getComponentName(componentOptions),</span><br><span class="line">                tag,</span><br><span class="line">                componentInstance,</span><br><span class="line">                cid: vnodeToCache.cid,</span><br><span class="line">              &#125;;</span><br><span class="line">              keys.push(keyToCache);</span><br><span class="line">              <span class="comment">// prune oldest entry</span></span><br><span class="line">              <span class="keyword">if</span> (<span class="keyword">this</span>.max &amp;&amp; keys.length &gt; <span class="built_in">parseInt</span>(<span class="keyword">this</span>.max)) &#123;</span><br><span class="line">                pruneCacheEntry(cache, keys[<span class="number">0</span>], keys, <span class="keyword">this</span>._vnode);</span><br><span class="line">              &#125;</span><br><span class="line">              <span class="keyword">this</span>.vnodeToCache = <span class="literal">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line">          &#125;,</span><br><span class="line">        &#125;,</span><br><span class="line"></span><br><span class="line">        created() &#123;</span><br><span class="line">          <span class="keyword">this</span>.cache = <span class="built_in">Object</span>.create(<span class="literal">null</span>);</span><br><span class="line">          <span class="keyword">this</span>.keys = [];</span><br><span class="line">        &#125;,</span><br><span class="line"></span><br><span class="line">        destroyed() &#123;</span><br><span class="line">          <span class="keyword">for</span> (<span class="keyword">const</span> key <span class="keyword">in</span> <span class="keyword">this</span>.cache) &#123;</span><br><span class="line">            pruneCacheEntry(<span class="keyword">this</span>.cache, key, <span class="keyword">this</span>.keys);</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;,</span><br><span class="line"></span><br><span class="line">        mounted() &#123;</span><br><span class="line">          <span class="keyword">this</span>.cacheVNode();</span><br><span class="line">          <span class="keyword">this</span>.$watch(<span class="string">"include"</span>, (val) =&gt; &#123;</span><br><span class="line">            pruneCache(<span class="keyword">this</span>, (name) =&gt; matches(val, name));</span><br><span class="line">          &#125;);</span><br><span class="line">          <span class="keyword">this</span>.$watch(<span class="string">"exclude"</span>, (val) =&gt; &#123;</span><br><span class="line">            pruneCache(<span class="keyword">this</span>, (name) =&gt; !matches(val, name));</span><br><span class="line">          &#125;);</span><br><span class="line">        &#125;,</span><br><span class="line"></span><br><span class="line">        updated() &#123;</span><br><span class="line">          <span class="keyword">this</span>.cacheVNode();</span><br><span class="line">        &#125;,</span><br><span class="line"></span><br><span class="line">        render() &#123;</span><br><span class="line">          <span class="keyword">const</span> slot = <span class="keyword">this</span>.$slots.default;</span><br><span class="line">          <span class="keyword">const</span> vnode = getFirstComponentChild(slot);</span><br><span class="line">          <span class="keyword">const</span> componentOptions = vnode &amp;&amp; vnode.componentOptions;</span><br><span class="line">          <span class="keyword">if</span> (componentOptions) &#123;</span><br><span class="line">            vnode.cid = componentOptions.Ctor.cid;</span><br><span class="line">            <span class="comment">// check pattern</span></span><br><span class="line">            <span class="keyword">const</span> name = getComponentName(componentOptions);</span><br><span class="line">            <span class="keyword">const</span> &#123; include, exclude &#125; = <span class="keyword">this</span>;</span><br><span class="line">            <span class="keyword">if</span> (</span><br><span class="line">              <span class="comment">// not included</span></span><br><span class="line">              (include &amp;&amp; (!name || !matches(include, name))) ||</span><br><span class="line">              <span class="comment">// excluded</span></span><br><span class="line">              (exclude &amp;&amp; name &amp;&amp; matches(exclude, name))</span><br><span class="line">            ) &#123;</span><br><span class="line">              <span class="keyword">return</span> vnode;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">const</span> &#123; cache, keys &#125; = <span class="keyword">this</span>;</span><br><span class="line">            <span class="keyword">const</span> key =</span><br><span class="line">              vnode.key == <span class="literal">null</span></span><br><span class="line">                ? <span class="comment">// same constructor may get registered as different local components</span></span><br><span class="line">                  <span class="comment">// so cid alone is not enough (#3269)</span></span><br><span class="line">                  componentOptions.Ctor.cid +</span><br><span class="line">                  (componentOptions.tag ? <span class="string">`::<span class="subst">$&#123;componentOptions.tag&#125;</span>`</span> : <span class="string">""</span>)</span><br><span class="line">                : vnode.key;</span><br><span class="line">            <span class="keyword">if</span> (cache[key]) &#123;</span><br><span class="line">              <span class="keyword">if</span> (vnode.cid === cache[key].cid) &#123;</span><br><span class="line">                vnode.componentInstance = cache[key].componentInstance;</span><br><span class="line">                <span class="comment">// make current key freshest</span></span><br><span class="line">                remove(keys, key);</span><br><span class="line">                keys.push(key);</span><br><span class="line">              &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                cache[key].componentInstance.$destroy();</span><br><span class="line">                cache[key] = vnode;</span><br><span class="line">              &#125;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">              <span class="comment">// delay setting the cache until update</span></span><br><span class="line">              <span class="keyword">this</span>.vnodeToCache = vnode;</span><br><span class="line">              <span class="keyword">this</span>.keyToCache = key;</span><br><span class="line">            &#125;</span><br><span class="line">            vnode.data.keepAlive = <span class="literal">true</span>;</span><br><span class="line">          &#125;</span><br><span class="line">          <span class="keyword">return</span> vnode || (slot &amp;&amp; slot[<span class="number">0</span>]);</span><br><span class="line">        &#125;,</span><br><span class="line">      &#125;;</span><br><span class="line"></span><br><span class="line">      app.component(<span class="string">"keep-alive"</span>, KeepAlive);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><p>在 main.js 引入：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> KeepAlive <span class="keyword">from</span> <span class="string">"./plugins/keep-alive"</span>;</span><br><span class="line">Vue.use(KeepAlive);</span><br></pre></td></tr></table></figure></div><p>这样子，我们的 keep-alive 就具有热更新功能啦ヾ(≧▽≦*)</p><h1 id="未解决的问题"><a href="#未解决的问题" class="headerlink" title="未解决的问题"></a>未解决的问题</h1><ul><li><p>含有 jsx 标识的 vue 文件热更新失效，.jsx 文件有效，<a href="https://github.com/vitejs/vite/issues/1486" target="_blank" rel="noopener">相关 issue</a></p><ul><li>但是有<a href="https://github.com/vitejs/vite/pull/4563" target="_blank" rel="noopener">相关 pr</a>实现了 jsx in sfc 的热更新，但是我在 vue2 中使用并未热更新</li></ul></li></ul><blockquote><p>ps: vue-cli-plugin-vite 插件中的 vite 是锁定 <a href="mailto:vite@2.5.1">vite@2.5.1</a> 版本的<a href="https://github.com/IndexXuan/vue-cli-plugin-vite/issues/46" target="_blank" rel="noopener">相关 issue</a>，而这个 issue 的 <a href="https://github.com/vitejs/vite/pull/4563" target="_blank" rel="noopener">相关 pr</a> 是 2.5.3 版本才 merge，不过我尝试使用 <a href="mailto:vite@2.5.3">vite@2.5.3</a> 也没有成功</p></blockquote><blockquote><p>ps: 看了下源代码，<a href="https://github.com/vitejs/vite" target="_blank" rel="noopener">github</a>上的源码已经 merge 了，但是 npm 上部分包仍然没有发布，比如<a href="https://www.npmjs.com/package/@vitejs/plugin-vue" target="_blank" rel="noopener">@vitejs/plugin-vue</a>、<a href="https://www.npmjs.com/package/@vitejs/plugin-vue-jsx" target="_blank" rel="noopener">@vitejs/plugin-vue-jsx</a>，猜测下个版本应该就能实现 jsx in sfc 的热更新了 😍。<br>不过我们也可以将 <a href="https://github.com/vitejs/vite/pull/4563" target="_blank" rel="noopener">pr</a> 的源码复制到 node_modules 里也可提前体验 jsx in sfc 的热更新🤞</p></blockquote><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>虽然- -这里没有用实际项目对比，也没有实际的数据对比，但是大家可以 download 那个配置在自己项目体验一下，迁移起来还是比较简单的。如果有什么问题欢迎大家留言进行交流~~</p><p>最后再强调，在 vite 中使用 jsx 语法的话，一是使用到 jsx 语法的 js 文件都必须改成使用 jsx 后缀名，二是在 vue 的 sfc 组件中还得加上 jsx 标识(仅仅引入一个 .jsx 文件 不需要加上)</p><p>仓库代码链接如下：</p><ul><li><p><a href="https://gitee.com/gating/demo/tree/master/vite" target="_blank" rel="noopener">gitee 地址</a></p><ul><li><p><a href="https://gitee.com/gating/demo/tree/master/vite/vue2-vite-demo-template" target="_blank" rel="noopener">vue2-vite-demo-template</a></p></li><li><p><a href="https://gitee.com/gating/demo/tree/master/vite/vue2-vite-demo" target="_blank" rel="noopener">vue2-vite-demo</a></p></li><li><p><a href="https://gitee.com/gating/demo/tree/master/vite/vue2-vite-demo-node10" target="_blank" rel="noopener">vue2-vite-demo-node10</a></p></li></ul></li><li><p><a href="https://github.com/GATING/demo/tree/master/vite" target="_blank" rel="noopener">github 地址</a></p><ul><li><p><a href="https://github.com/GATING/demo/tree/master/vite/vue2-vite-demo-template" target="_blank" rel="noopener">vue2-vite-demo-template</a></p></li><li><p><a href="https://github.com/GATING/demo/tree/master/vite/vue2-vite-demo" target="_blank" rel="noopener">vue2-vite-demo</a></p></li><li><p><a href="https://github.com/GATING/demo/tree/master/vite/vue2-vite-demo-node10" target="_blank" rel="noopener">vue2-vite-demo-node10</a></p></li></ul></li></ul><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>虽然本文罗嗦了点，但还是感谢各位观众老爷的能看到最后 O(∩_∩)O 希望你能有所收获 😁</p>]]></content>
    
    <summary type="html">
    
      自从 vite 发布之后，社区赞誉无数，而我也一直心水 vite 的轻量快速的热重载的特性，特别是公司的项目巨大，已经严重拖慢了热重载的速度了，每次热重载都要等上一小会，所以急需寻找一个解决方案。
    
    </summary>
    
    
      <category term="小工具" scheme="https://gatings.cn/categories/%E5%B0%8F%E5%B7%A5%E5%85%B7/"/>
    
    
      <category term="vite" scheme="https://gatings.cn/tags/vite/"/>
    
  </entry>
  
  <entry>
    <title>Vue+Antd搭配百度地图实现搜索定位等功能</title>
    <link href="https://gatings.cn/2020-11-03/Vue+Antd%E6%90%AD%E9%85%8D%E7%99%BE%E5%BA%A6%E5%9C%B0%E5%9B%BE%E5%AE%9E%E7%8E%B0%E6%90%9C%E7%B4%A2%E5%AE%9A%E4%BD%8D%E7%AD%89%E5%8A%9F%E8%83%BD/"/>
    <id>https://gatings.cn/2020-11-03/Vue+Antd%E6%90%AD%E9%85%8D%E7%99%BE%E5%BA%A6%E5%9C%B0%E5%9B%BE%E5%AE%9E%E7%8E%B0%E6%90%9C%E7%B4%A2%E5%AE%9A%E4%BD%8D%E7%AD%89%E5%8A%9F%E8%83%BD/</id>
    <published>2020-11-02T16:00:00.000Z</published>
    <updated>2021-10-13T01:56:29.703Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>最近，在做<code>vue</code>项目的时候有做到选择地址功能，而原项目中又引入了百度地图，所以我就打算通过使用百度地图来实现地址搜索功能啦。</p><p>本次教程可能过于啰嗦，所以这里先放上预览地址供大家预览——<a href="https://gating.gitee.io/demo/baidu-map-demo/" target="_blank" rel="noopener">点我预览</a>，也可到文末直接下载代码先自行体验。。。</p><blockquote><p>ps: 又因为百度地图 1.2 以上需要 AK 密钥，所以这里我直接使用 1.2 版本实现<br>ps: 😐1.x版本是不能支持https的，所以使用时请注意</p></blockquote><h1 id="简单的说下实现的效果"><a href="#简单的说下实现的效果" class="headerlink" title="简单的说下实现的效果"></a>简单的说下实现的效果</h1><p>因为我这边做的是打卡的地址选择，那么肯定要有搜索提示来选取地址啦，又因为是打卡，肯定的打卡的范围选择。为了用户体验，我们也要添加点击地图任意位置生辰对应的地址，也要可以拖拽标注来生成对应地址。</p><p>既然知道了功能点，那么我们就上效果图吧 😁</p><p><a href="https://cdn.jsdelivr.net/gh/GATING/blog_imgs/2020-11-03/renderings.jpg" data-fancybox="group" data-caption="baidu-map-demo效果图" class="fancybox"><img alt="baidu-map-demo效果图" title="baidu-map-demo效果图" data-src="https://cdn.jsdelivr.net/gh/GATING/blog_imgs/2020-11-03/renderings.jpg" class="lazyload"></a></p><p>看到这，我们大概知道的功能点有：</p><ul><li>设置图像标注并绑定拖拽标注结束后事件</li><li>绑定点击地图任意点事件</li><li>封装逆地址解析函数，用于通过坐标点获取详细地址</li><li>添加输入提示来选取地址</li><li>添加地图覆盖物(圆)，用于标识我们选择的范围</li></ul><p>看到这里，是不是也想跃跃欲试啦，所以，我们就开始写我们的代码吧</p><h1 id="搭建项目"><a href="#搭建项目" class="headerlink" title="搭建项目"></a>搭建项目</h1><p>因为，用到了<code>vue</code>，所以我们肯定安装<code>vue-cli</code>这个脚手架啦，又因为<code>Vue3</code>发布了正式版，所以这次我们的教程当然是使用<code>Vue3</code>进行开发啦，所以我们脚手架可能需要更新一下。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm install -g @vue/cli</span><br><span class="line"><span class="comment"># OR</span></span><br><span class="line">yarn global add @vue/cli</span><br></pre></td></tr></table></figure></div><blockquote><p>ps: 建议都更新下咯，避免无法创建 vue3 的项目</p></blockquote><p>这里我们选择默认的配置就好了，如图:</p><p><a href="https://cdn.jsdelivr.net/gh/GATING/blog_imgs/2020-11-03/default-options.jpg" data-fancybox="group" data-caption="vue3默认配置" class="fancybox"><img alt="vue3默认配置" title="vue3默认配置" data-src="https://cdn.jsdelivr.net/gh/GATING/blog_imgs/2020-11-03/default-options.jpg" class="lazyload"></a></p><blockquote><p>若安装缓慢报错，可尝试用 yarn 或别的镜像源自行安装：rm -rf node_modules &amp;&amp; yarn install。</p></blockquote><p>在漫长的等他，他安装了我们的模板，从标题我们也知道，这里我们使用<code>ant-design-vue</code>啦，因为<code>element-ui</code>现在还没有支持<code>Vue3</code>，而<code>element-plus</code>的文档还是<code>element-ui</code>的，对我们十分不友好，支持的也不完善，所以我们这里直接使用<code>ant-design-vue@2.x</code>啦。</p><p>所以废话不多说了，直接安装依赖：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm i --save ant-design-vue@next</span><br></pre></td></tr></table></figure></div><p>安装完后我们就可以在<code>main.js</code>配置下我们的<code>ant-design-vue</code>了</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; createApp &#125; <span class="keyword">from</span> <span class="string">"vue"</span>;</span><br><span class="line"><span class="keyword">import</span> App <span class="keyword">from</span> <span class="string">"./App.vue"</span>;</span><br><span class="line"><span class="keyword">import</span> Antd <span class="keyword">from</span> <span class="string">"ant-design-vue"</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">"ant-design-vue/dist/antd.css"</span>;</span><br><span class="line">createApp(App).use(Antd).mount(<span class="string">"#app"</span>);</span><br></pre></td></tr></table></figure></div><blockquote><p>ps：因为这里我们只是做个例子，所以我为了方便直接使用全局了</p></blockquote><p>既然我们用了<code>Vue3</code>，我们就说说 <code>Vue3</code> 对比 <code>Vue2</code> 有什么更爽的点</p><h2 id="Vue2-与-Vue3-的对比"><a href="#Vue2-与-Vue3-的对比" class="headerlink" title="Vue2 与 Vue3 的对比"></a>Vue2 与 Vue3 的对比</h2><ul><li><p>对 <code>TypeScript</code> 支持更友好了，因为 <code>Vue2</code> 所有属性都放在了 <code>this</code> 对象上，难以推倒组件的数据类型。</p></li><li><p>同第一点，所有属性都放在了 <code>this</code> 对象上，难以实现 <code>TreeShaking</code>。</p></li><li><p><code>Template</code> 终于支持多个根标签了，不需要每次写模板的时候都加上多余的根元素。</p></li><li><p><code>Composition Api</code>，也是我们最听到的新功能(如果你用过<code>React Hooks</code>，那一定对它不陌生，因为它和<code>React Hooks</code>十分类似)，很多人也建议优先使用<code>Composition Api</code>来替代<code>Mixins</code>的写法，好处如下：</p><ol><li>相关逻辑可以集中，且更容易复用</li><li>不会因为莫名的变数或方法名找半天，然后发现在<code>Mixins</code></li><li>减少<code>this</code>指向问题</li><li>解决组件内的命名冲突</li><li>隐式依赖得到解决，你可以直观的看到消费组件所需要的变量</li><li>其它等等…</li></ol></li><li><p>其它等等…</p></li></ul><h2 id="组合式-API"><a href="#组合式-API" class="headerlink" title="组合式 API"></a>组合式 API</h2><p>既然我们说了这么多 <code>Composition Api</code> 的优点，那么我们该怎么使用他呢？在 <code>Vue</code> 组件中，提供了一个<code>setup</code>的组件选项，并充当合成 API 的入口点。</p><blockquote><p>ps: 由于在执行 setup 时尚未创建组件实例，即在 created 之前，因此在 setup 选项中没有 this。这意味着，除了 props 之外，你将无法访问组件中声明的任何属性——本地状态、计算属性或方法。</p></blockquote><p>使用<code>setup</code>函数是，他将接受两个参数，分别是<code>props</code>和<code>context</code></p><h3 id="Props"><a href="#Props" class="headerlink" title="Props"></a>Props</h3><p><code>setup</code> 函数中的第一个参数是 <code>props</code>。正如在一个标准组件中所期望的那样，setup 函数中的 props 是响应式的，当传入新的 prop 时，它将被更新。</p><blockquote><p>ps: 因为 props 是响应式的，你不能使用 ES6 解构，因为它会消除 prop 的响应性</p></blockquote><h3 id="上下文"><a href="#上下文" class="headerlink" title="上下文"></a>上下文</h3><p><code>context</code>是一个普通的<code>JavaScript</code>对象，它暴露三个组件的 property：<code>attrs</code>、<code>slots</code>、<code>emit</code></p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">  setup(props, context) &#123;</span><br><span class="line">    <span class="comment">// Attribute (非响应式对象)</span></span><br><span class="line">    <span class="built_in">console</span>.log(context.attrs);</span><br><span class="line">    <span class="comment">// 插槽 (非响应式对象)</span></span><br><span class="line">    <span class="built_in">console</span>.log(context.slots);</span><br><span class="line">    <span class="comment">// 触发事件 (方法) 同以前的 this.$emit()</span></span><br><span class="line">    <span class="built_in">console</span>.log(context.emit);</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><p><code>context</code>是一个普通的<code>JavaScript</code>对象，也就是说，它不是响应式的，这意味着你可以安全地对<code>context</code>使用<code>ES6</code>解构。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">  setup(props, &#123; attrs, slots, emit &#125;) &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><p>😢 因为我们不是<code>Vue3</code>基础入门，所以我这里就只讲用到的几个 API，另<code>Vue3</code>支持大多数<code>Vue2</code>的特性，所以我们用<code>Vue2</code>语法开发<code>Vue3</code>也是完全没问题的(🤣 开玩笑的)</p><h3 id="ref-函数"><a href="#ref-函数" class="headerlink" title="ref 函数"></a>ref 函数</h3><p>闲话就不多说了，先来了解以下<code>Composition Api</code>的魅力吧。</p><p>在 Vue 3.0 中，我们可以通过一个新的<code>ref</code>函数使任何响应式变量在任何地方起作用。</p><p>并且<code>ref</code>返回的是一个对象值，该对像只包含一个 <code>value</code> 属性，且只有我们在<code>setup</code>函数进行访问/修改的时候需要加.value，接下来我们就修改下<code>HelloWorld</code>组件，来实现一下<code>选择最喜爱的水果</code>的小程序吧。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">html</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span>&gt;</span>请选择你最喜欢的水果<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">button</span> <span class="attr">v-for</span>=<span class="string">"(fruit, idx) in fruits"</span> <span class="attr">:key</span>=<span class="string">"fruit"</span> @<span class="attr">click</span>=<span class="string">"handleSelect(idx)"</span>&gt;</span></span><br><span class="line">      &#123;&#123; fruit &#125;&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span>&gt;</span>你最喜欢的是【&#123;&#123; select &#125;&#125;】<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="javascript"><span class="keyword">import</span> &#123; ref &#125; <span class="keyword">from</span> <span class="string">"vue"</span>;</span></span><br><span class="line"><span class="javascript"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span></span><br><span class="line">  setup() &#123;</span><br><span class="line"><span class="actionscript">    <span class="keyword">const</span> fruits = ref([<span class="string">"芒果"</span>, <span class="string">"榴莲"</span>, <span class="string">"菠萝"</span>]);</span></span><br><span class="line"><span class="actionscript">    <span class="keyword">const</span> select = ref(<span class="string">""</span>);</span></span><br><span class="line"><span class="javascript">    <span class="keyword">const</span> handleSelect = <span class="function">(<span class="params">idx</span>) =&gt;</span> &#123;</span></span><br><span class="line">      select.value = fruits.value[idx];</span><br><span class="line">    &#125;;</span><br><span class="line"><span class="actionscript">    <span class="keyword">return</span> &#123;</span></span><br><span class="line">      fruits,</span><br><span class="line">      select,</span><br><span class="line">      handleSelect,</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure></div><p>这样子，我们的这个小 demo 就是实现啦。看下我们的代码，有发现了什么吗？没错，我们使用<code>setup</code>之后，可以完全不需要 data 和 methods 属性，并且我们可以在组件模板中使用多个根节点。</p><h3 id="reactive-函数"><a href="#reactive-函数" class="headerlink" title="reactive 函数"></a>reactive 函数</h3><p>看了上面的代码，可以说没什么章法可言，所有的变量和方法都混淆在一起，最不能忍受的就是在 <code>setup</code> 中要改变和读取一个值的时候，还要加上 value。那么这里，我们就引入一个新的 Api <code>reactive</code>来优化我们的代码吧。</p><p><code>reactive</code>函数接收一个普通对象，返回一个响应式的数据对象。既然是普通对象，那么无论是变量、还是方法，都可以作为对象中的一个属性来使用啦，那么我们就能优雅的修改我们的值，不用再通过<code>.value</code>修改我们的值啦，那么就通过<code>reactive</code>修改下我们的代码吧。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">html</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span>&gt;</span>请选择你最喜欢的水果<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">button</span> <span class="attr">v-for</span>=<span class="string">"(fruit, idx) in data.fruits"</span> <span class="attr">:key</span>=<span class="string">"fruit"</span> @<span class="attr">click</span>=<span class="string">"data.handleSelect(idx)"</span>&gt;</span></span><br><span class="line">      &#123;&#123; fruit &#125;&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span>&gt;</span>你最喜欢的是【&#123;&#123; data.select &#125;&#125;】<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="javascript"><span class="keyword">import</span> &#123; reactive &#125; <span class="keyword">from</span> <span class="string">"vue"</span>;</span></span><br><span class="line"><span class="javascript"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span></span><br><span class="line">  setup() &#123;</span><br><span class="line"><span class="actionscript">    <span class="keyword">const</span> data = reactive(&#123;</span></span><br><span class="line"><span class="actionscript">      fruits: [<span class="string">"芒果"</span>, <span class="string">"榴莲"</span>, <span class="string">"菠萝"</span>],</span></span><br><span class="line"><span class="actionscript">      select: <span class="string">""</span>,</span></span><br><span class="line">      handleSelect(idx) &#123;</span><br><span class="line">        data.select = data.fruits[idx];</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;);</span><br><span class="line"><span class="actionscript">    <span class="keyword">return</span> &#123;</span></span><br><span class="line">      data,</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure></div><h3 id="toRefs-函数"><a href="#toRefs-函数" class="headerlink" title="toRefs 函数"></a>toRefs 函数</h3><p>虽然我们通过<code>reactive</code>优化了代码，但是看着都需要<code>data.</code>也不是事啊，那么有没有什么方法优化这个点呢？实际是有的，Vue3 提供了 <code>toRefs()</code>，将响应式对象转换为普通对象，其中结果对象的每个 property 都是指向原始对象相应 property 的 <code>ref</code>。</p><p>那么我们继续优化我们的代码吧。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">html</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span>&gt;</span>请选择你最喜欢的水果<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">button</span> <span class="attr">v-for</span>=<span class="string">"(fruit, idx) in fruits"</span> <span class="attr">:key</span>=<span class="string">"fruit"</span> @<span class="attr">click</span>=<span class="string">"handleSelect(idx)"</span>&gt;</span></span><br><span class="line">      &#123;&#123; fruit &#125;&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span>&gt;</span>你最喜欢的是【&#123;&#123; select &#125;&#125;】<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="javascript"><span class="keyword">import</span> &#123; reactive, toRefs &#125; <span class="keyword">from</span> <span class="string">"vue"</span>;</span></span><br><span class="line"><span class="javascript"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span></span><br><span class="line">  setup() &#123;</span><br><span class="line"><span class="actionscript">    <span class="keyword">const</span> data = reactive(&#123;</span></span><br><span class="line"><span class="actionscript">      fruits: [<span class="string">"芒果"</span>, <span class="string">"榴莲"</span>, <span class="string">"菠萝"</span>],</span></span><br><span class="line"><span class="actionscript">      select: <span class="string">""</span>,</span></span><br><span class="line">      handleSelect(idx) &#123;</span><br><span class="line">        data.select = data.fruits[idx];</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;);</span><br><span class="line"><span class="actionscript">    <span class="keyword">return</span> &#123;</span></span><br><span class="line">      ...toRefs(data),</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure></div><h3 id="watch-函数"><a href="#watch-函数" class="headerlink" title="watch 函数"></a>watch 函数</h3><p><code>watch</code>函数与选项式 API<code>this.$watch</code>(以及相应的 <code>watch</code> 选项) 完全等效。<code>watch</code>需要侦听特定的<code>data</code>源，并在单独的回调函数中副作用。默认情况下，它是懒执行，即回调是仅在侦听源发生更改时调用。</p><p>虽然这里的自己不需要使用<code>watch</code>和获取真实的<code>DOM</code>，但我这里也讲一下，便于后面例子的代码编写(生硬的转折 🤣)。</p><p>Vue3 获取真实 dom 元素也比较简单，基本和往常一样，大概分为三步：</p><ol><li>和以前一样，在标签上写上 ref 名称</li><li>在 setup 中定义一个和标签上 ref 名称一样的 <code>Ref</code> 的示例，并返回</li><li>onMounted 就可以得到 ref 的 RefImpl 的对象，并通过.value 获取</li></ol><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">html</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span>&gt;</span>请选择你最喜欢的水果<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">button</span> <span class="attr">v-for</span>=<span class="string">"(fruit, idx) in fruits"</span> <span class="attr">:key</span>=<span class="string">"fruit"</span> @<span class="attr">click</span>=<span class="string">"handleSelect(idx)"</span>&gt;</span></span><br><span class="line">      &#123;&#123; fruit &#125;&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="comment">&lt;!-- 1.和以前一样，在标签上写上 ref 名称--&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">ref</span>=<span class="string">"selectRef"</span>&gt;</span>你最喜欢的是【&#123;&#123; select &#125;&#125;】<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="javascript"><span class="keyword">import</span> &#123; ref, reactive, toRefs, watch &#125; <span class="keyword">from</span> <span class="string">"vue"</span>;</span></span><br><span class="line"><span class="javascript"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span></span><br><span class="line">  setup() &#123;</span><br><span class="line"><span class="actionscript">    <span class="comment">// 2. 定义一个和标签上 ref 名称一样的 Ref 实例</span></span></span><br><span class="line"><span class="actionscript">    <span class="keyword">const</span> selectRef = ref(<span class="literal">null</span>);</span></span><br><span class="line"><span class="actionscript">    <span class="keyword">const</span> data = reactive(&#123;</span></span><br><span class="line"><span class="actionscript">      fruits: [<span class="string">"芒果"</span>, <span class="string">"榴莲"</span>, <span class="string">"菠萝"</span>],</span></span><br><span class="line"><span class="actionscript">      select: <span class="string">""</span>,</span></span><br><span class="line">      handleSelect(idx) &#123;</span><br><span class="line">        data.select = data.fruits[idx];</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;);</span><br><span class="line">    watch(</span><br><span class="line">      () =&gt; data.select,</span><br><span class="line">      (val, preVal) =&gt; &#123;</span><br><span class="line"><span class="actionscript">        <span class="comment">// 得到一个 RefImpl 的对象, 通过 .value 访问到真实DOM</span></span></span><br><span class="line"><span class="javascript">        <span class="built_in">console</span>.log(selectRef.value);</span></span><br><span class="line"><span class="javascript">        <span class="built_in">console</span>.log(val, preVal);</span></span><br><span class="line">      &#125;</span><br><span class="line">    );</span><br><span class="line"><span class="actionscript">    <span class="keyword">return</span> &#123;</span></span><br><span class="line">      ...toRefs(data),</span><br><span class="line">      selectRef,</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure></div><p>当然，<code>watch</code>还可以监听多个源：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) =&gt; &#123;</span><br><span class="line">  <span class="comment">/* ... */</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></div><p>到这里，基本上前置知识都过得差不多了，可以开始编写我们的代码了</p><h1 id="正式编写代码"><a href="#正式编写代码" class="headerlink" title="正式编写代码"></a>正式编写代码</h1><p>通过前面学习的知识点我们大概了解了 Vue3 最基本的用法，那么就可以编写我们的代码了</p><h2 id="清理下无用的代码"><a href="#清理下无用的代码" class="headerlink" title="清理下无用的代码"></a>清理下无用的代码</h2><p>用 <code>vue-cli</code> 生产的 Vue3 项目中，我们修改了<code>HelloWorld</code>用于学习了 Vue3 的基本 Api，实际上我们接下来的案例是不需要这些代码的，所以我们打开<code>App.vue</code>，去掉部分无关代码，并在<code>components</code>目录新建<code>MapDialog.vue</code>文件，内容如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">html</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span>&gt;</span>这是地图弹窗<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="javascript"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span></span><br><span class="line"><span class="actionscript">  name: <span class="string">"MapDialog"</span>,</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure></div><p>清理无用代码后并导入<code>MapDialog</code>组件</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">html</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">map-dialog</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="javascript"><span class="keyword">import</span> MapDialog <span class="keyword">from</span> <span class="string">"./components/MapDialog.vue"</span>;</span></span><br><span class="line"><span class="javascript"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span></span><br><span class="line"><span class="actionscript">  name: <span class="string">"App"</span>,</span></span><br><span class="line">  components: &#123;</span><br><span class="line">    MapDialog,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure></div><h2 id="百度地图基本使用"><a href="#百度地图基本使用" class="headerlink" title="百度地图基本使用"></a>百度地图基本使用</h2><p>前文也说了，我之前项目是通过<code>script</code>标签引入的，所以这里我们也是直接引入 js 库</p><blockquote><p>ps: 也可以通过 npm 安装 vue-baidu-map 引入<a href="https://dafrok.github.io/vue-baidu-map/#/" target="_blank" rel="noopener">vue-baidu-map</a>这个百度地图组件</p></blockquote><ol><li>引入 js 库</li></ol><p>打开<code>public/index.html</code>，引入 js</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">html</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">type</span>=<span class="string">"text/javascript"</span> <span class="attr">src</span>=<span class="string">"https://api.map.baidu.com/api?v=1.2"</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure></div><ol start="2"><li>编写代码</li></ol><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">html</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">"map"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="javascript"><span class="keyword">import</span> &#123; onMounted &#125; <span class="keyword">from</span> <span class="string">"vue"</span>;</span></span><br><span class="line"><span class="javascript"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span></span><br><span class="line"><span class="actionscript">  name: <span class="string">"MapDialog"</span>,</span></span><br><span class="line">  setup() &#123;</span><br><span class="line"><span class="javascript">    onMounted(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span></span><br><span class="line"><span class="javascript">      <span class="keyword">const</span> &#123; <span class="built_in">Map</span>, Point &#125; = BMap;</span></span><br><span class="line"><span class="javascript">      <span class="keyword">const</span> map = <span class="keyword">new</span> <span class="built_in">Map</span>(<span class="string">"map"</span>);</span></span><br><span class="line"><span class="actionscript">      <span class="keyword">const</span> point = <span class="keyword">new</span> Point(<span class="number">116.404</span>, <span class="number">39.915</span>);</span></span><br><span class="line">      map.centerAndZoom(point, 16);</span><br><span class="line">      map.enableScrollWheelZoom();</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">style</span> <span class="attr">scoped</span>&gt;</span></span><br><span class="line"><span class="css"><span class="selector-id">#map</span> &#123;</span></span><br><span class="line">  height: 400px;</span><br><span class="line">&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br></pre></td></tr></table></figure></div><p>写到这里可能会出现下图的一个错误：</p><p><a href="https://cdn.jsdelivr.net/gh/GATING/blog_imgs/2020-11-03/eslint.jpg" data-fancybox="group" data-caption="eslint" class="fancybox"><img alt="eslint" title="eslint" data-src="https://cdn.jsdelivr.net/gh/GATING/blog_imgs/2020-11-03/eslint.jpg" class="lazyload"></a></p><p>因为我们选择了默认模板，里面又包括了<code>eslint</code>而我们又引入了一个<code>BMap</code>的全局变量，<code>eslint</code>不认识它，所以会报<code>BMap is not defined.</code>这个错误。怎么解决呢？我们只需要告诉<code>eslint</code>，这是全局变量即可，打开<code>package.json</code>，添加如下配置：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">json</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight json"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  <span class="attr">"eslintConfig"</span>: &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="attr">"globals"</span>: &#123;</span><br><span class="line">      <span class="attr">"BMap"</span>: <span class="literal">true</span>,</span><br><span class="line">      <span class="attr">"BMAP_STATUS_SUCCESS"</span>: <span class="literal">true</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>值得注意的点是：</p><ul><li>容器 div 需要使用 id</li><li>容器 div 需要指定宽高</li></ul><p>其余用法与 html 中编码无异</p><p>编写完这个代码后，我们就可以在页面看到百度地图的雏形并且不会报错了，接下来就可以开始书写其他功能的代码啦 O(∩_∩)O~~</p><h2 id="先从简单的开始入手"><a href="#先从简单的开始入手" class="headerlink" title="先从简单的开始入手"></a>先从简单的开始入手</h2><p>从前文的效果图可以知道，我们是通过点击<code>选择位置</code>按钮来弹出地图的，这里我就不一步步编写基本的<code>ui</code>了，直接上基础代码了</p><p><code>App.vue</code>代码如下</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">html</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  您选择的位置是：&#123;&#123; place.address &#125;&#125;</span><br><span class="line">  <span class="tag">&lt;<span class="name">a-button</span> @<span class="attr">click</span>=<span class="string">"toggleVisible"</span>&gt;</span>选择位置<span class="tag">&lt;/<span class="name">a-button</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">map-dialog</span> <span class="attr">v-model:visible</span>=<span class="string">"visible"</span> <span class="attr">:point</span>=<span class="string">"place.point"</span> <span class="attr">:range</span>=<span class="string">"place.range"</span> @<span class="attr">confirm</span>=<span class="string">"handleConfirm"</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="javascript"><span class="keyword">import</span> &#123; reactive, toRefs &#125; <span class="keyword">from</span> <span class="string">"vue"</span>;</span></span><br><span class="line"><span class="javascript"><span class="keyword">import</span> MapDialog <span class="keyword">from</span> <span class="string">"./components/MapDialog.vue"</span>;</span></span><br><span class="line"><span class="javascript"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span></span><br><span class="line"><span class="actionscript">  name: <span class="string">"App"</span>,</span></span><br><span class="line">  components: &#123;</span><br><span class="line">    MapDialog,</span><br><span class="line">  &#125;,</span><br><span class="line">  setup() &#123;</span><br><span class="line"><span class="actionscript">    <span class="keyword">const</span> data = reactive(&#123;</span></span><br><span class="line">      place: &#123;&#125;,</span><br><span class="line"><span class="actionscript">      visible: <span class="literal">false</span>,</span></span><br><span class="line">      toggleVisible() &#123;</span><br><span class="line">        data.visible = !data.visible;</span><br><span class="line">      &#125;,</span><br><span class="line">      handleConfirm(place) &#123;</span><br><span class="line">        data.place = place;</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;);</span><br><span class="line"><span class="actionscript">    <span class="keyword">return</span> &#123;</span></span><br><span class="line">      ...toRefs(data),</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure></div><p>这里用了我们<code>v-mode:visible</code>对<code>visible</code>对这个<code>props</code>进行了双向绑定，实际上在 Vue2.x 的写法中是通过<code>:visible.sync</code>修饰符来实现的</p><p>详细了解，请参考<a href="https://vue3js.cn/docs/zh/guide/migration/v-model.html" target="_blank" rel="noopener">这个链接</a></p><p><code>MapDialog.vue</code> 基础代码如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">html</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">a-modal</span></span></span><br><span class="line"><span class="tag">    <span class="attr">:visible</span>=<span class="string">"visible"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">centered</span></span></span><br><span class="line"><span class="tag">    <span class="attr">title</span>=<span class="string">"请选择地址"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">cancelText</span>=<span class="string">"取消"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">okText</span>=<span class="string">"确定"</span></span></span><br><span class="line"><span class="tag">    @<span class="attr">cancel</span>=<span class="string">"close"</span></span></span><br><span class="line"><span class="tag">    @<span class="attr">ok</span>=<span class="string">"handleOk"</span></span></span><br><span class="line"><span class="tag">  &gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">a-form</span> <span class="attr">class</span>=<span class="string">"form"</span> <span class="attr">layout</span>=<span class="string">"inline"</span> <span class="attr">ref</span>=<span class="string">"mapForm"</span> <span class="attr">:model</span>=<span class="string">"form"</span> <span class="attr">:rules</span>=<span class="string">"rules"</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">a-form-item</span> <span class="attr">name</span>=<span class="string">"address"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">a-auto-complete</span></span></span><br><span class="line"><span class="tag">          <span class="attr">v-model:value</span>=<span class="string">"form.address"</span></span></span><br><span class="line"><span class="tag">          <span class="attr">:options</span>=<span class="string">"addressSource"</span></span></span><br><span class="line"><span class="tag">          <span class="attr">placeholder</span>=<span class="string">"请输入你要搜索的地点"</span></span></span><br><span class="line"><span class="tag">          @<span class="attr">search</span>=<span class="string">"handleQuery"</span></span></span><br><span class="line"><span class="tag">          @<span class="attr">select</span>=<span class="string">"handleSelect"</span></span></span><br><span class="line"><span class="tag">          <span class="attr">style</span>=<span class="string">"width: 360px"</span></span></span><br><span class="line"><span class="tag">        /&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">a-form-item</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">a-form-item</span> <span class="attr">name</span>=<span class="string">"range"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">a-select</span> <span class="attr">v-model:value</span>=<span class="string">"form.range"</span> <span class="attr">placeholder</span>=<span class="string">"请选择范围"</span> @<span class="attr">change</span>=<span class="string">"setRadius"</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">a-select-option</span> <span class="attr">v-for</span>=<span class="string">"range in ranges"</span> <span class="attr">:key</span>=<span class="string">"range"</span>&gt;</span></span><br><span class="line">            &#123;&#123; range &#125;&#125;</span><br><span class="line">          <span class="tag">&lt;/<span class="name">a-select-option</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">a-select</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">a-form-item</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">a-form</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">"map"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">a-modal</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="javascript"><span class="keyword">import</span> &#123; ref, reactive, toRefs, watch, nextTick &#125; <span class="keyword">from</span> <span class="string">"vue"</span>;</span></span><br><span class="line"><span class="javascript"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span></span><br><span class="line"><span class="actionscript">  name: <span class="string">"MapDialog"</span>,</span></span><br><span class="line">  props: &#123;</span><br><span class="line">    visible: &#123;</span><br><span class="line"><span class="javascript">      type: <span class="built_in">Boolean</span>,</span></span><br><span class="line"><span class="actionscript">      <span class="keyword">default</span>: <span class="literal">false</span>,</span></span><br><span class="line">    &#125;,</span><br><span class="line">    range: &#123;</span><br><span class="line"><span class="javascript">      type: <span class="built_in">String</span>,</span></span><br><span class="line"><span class="actionscript">      <span class="keyword">default</span>: <span class="string">"300米"</span>,</span></span><br><span class="line">    &#125;,</span><br><span class="line">    point: &#123;</span><br><span class="line"><span class="javascript">      type: <span class="built_in">Object</span>,</span></span><br><span class="line"><span class="javascript">      <span class="keyword">default</span>: <span class="function"><span class="params">()</span> =&gt;</span> (&#123; <span class="attr">lng</span>: <span class="number">113.271429</span>, <span class="attr">lat</span>: <span class="number">23.135336</span> &#125;),</span></span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">  setup(props, &#123; emit &#125;) &#123;</span><br><span class="line"><span class="actionscript">    <span class="keyword">const</span> mapForm = ref(<span class="literal">null</span>);</span></span><br><span class="line"><span class="actionscript">    <span class="keyword">const</span> formData = reactive(&#123;</span></span><br><span class="line">      form: &#123;</span><br><span class="line"><span class="actionscript">        address: <span class="string">""</span>,</span></span><br><span class="line">        range: props.range,</span><br><span class="line">      &#125;,</span><br><span class="line">      rules: &#123;</span><br><span class="line">        address: [</span><br><span class="line">          &#123;</span><br><span class="line"><span class="actionscript">            required: <span class="literal">true</span>,</span></span><br><span class="line"><span class="actionscript">            message: <span class="string">"请输入你要搜索的地点"</span>,</span></span><br><span class="line"><span class="actionscript">            trigger: <span class="string">"blur"</span>,</span></span><br><span class="line">          &#125;,</span><br><span class="line">        ],</span><br><span class="line">      &#125;,</span><br><span class="line"><span class="actionscript">      ranges: [<span class="string">"100米"</span>, <span class="string">"300米"</span>, <span class="string">"500米"</span>],</span></span><br><span class="line">      addressPoint: props.point,</span><br><span class="line">      addressSource: [],</span><br><span class="line">      setRadius() &#123;&#125;,</span><br><span class="line">      handleQuery() &#123;&#125;,</span><br><span class="line">      handleSelect() &#123;&#125;,</span><br><span class="line">      close() &#123;</span><br><span class="line"><span class="actionscript">        emit(<span class="string">"update:visible"</span>, <span class="literal">false</span>);</span></span><br><span class="line">        mapForm.value.resetFields();</span><br><span class="line">      &#125;,</span><br><span class="line">      handleOk() &#123;</span><br><span class="line"><span class="javascript">        mapForm.value.validate().then(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span></span><br><span class="line"><span class="actionscript">          emit(<span class="string">"confirm"</span>, &#123;</span></span><br><span class="line">            address: formData.form.address,</span><br><span class="line">            point: formData.addressPoint,</span><br><span class="line">            range: formData.form.range,</span><br><span class="line">          &#125;);</span><br><span class="line"><span class="actionscript">          emit(<span class="string">"update:visible"</span>, <span class="literal">false</span>);</span></span><br><span class="line">        &#125;);</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line"><span class="javascript">    <span class="keyword">const</span> &#123; <span class="built_in">Map</span>, Point &#125; = BMap;</span></span><br><span class="line"></span><br><span class="line"><span class="actionscript">    <span class="comment">// 地图相关元素，因为可能在别的方法使用</span></span></span><br><span class="line"><span class="javascript">    <span class="keyword">let</span> map = <span class="literal">null</span>;</span></span><br><span class="line"></span><br><span class="line"><span class="actionscript">    <span class="comment">// 初始化地图</span></span></span><br><span class="line"><span class="actionscript">    <span class="function"><span class="keyword">function</span> <span class="title">initMap</span><span class="params">()</span> </span>&#123;</span></span><br><span class="line"><span class="actionscript">      <span class="comment">// 防止dom还未渲染</span></span></span><br><span class="line"><span class="javascript">      nextTick(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span></span><br><span class="line"><span class="actionscript">        <span class="comment">// 禁用地图默认点击弹框</span></span></span><br><span class="line"><span class="javascript">        map = <span class="keyword">new</span> <span class="built_in">Map</span>(<span class="string">"map"</span>, &#123; <span class="attr">enableMapClick</span>: <span class="literal">false</span> &#125;);</span></span><br><span class="line"><span class="actionscript">        <span class="keyword">const</span> &#123; lng, lat &#125; = formData.addressPoint;</span></span><br><span class="line"><span class="actionscript">        <span class="keyword">const</span> point = <span class="keyword">new</span> Point(lng, lat);</span></span><br><span class="line">        map.centerAndZoom(point, 16);</span><br><span class="line">        map.enableScrollWheelZoom();</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">    watch(</span><br><span class="line">      () =&gt; props.visible,</span><br><span class="line">      (visible) =&gt; &#123;</span><br><span class="line">        visible &amp;&amp; initMap();</span><br><span class="line">      &#125;</span><br><span class="line">    );</span><br><span class="line"><span class="actionscript">    <span class="keyword">return</span> &#123;</span></span><br><span class="line">      mapForm,</span><br><span class="line">      ...toRefs(formData),</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">style</span> <span class="attr">scoped</span>&gt;</span></span><br><span class="line"><span class="css"><span class="selector-id">#map</span> &#123;</span></span><br><span class="line">  height: 400px;</span><br><span class="line">&#125;</span><br><span class="line"><span class="css"><span class="selector-class">.form</span> &#123;</span></span><br><span class="line">  height: 66px;</span><br><span class="line">&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br></pre></td></tr></table></figure></div><p>复制进去，基本上整个模子就出来了，接下来就是实现我们的功能了</p><h3 id="设置图像标注并绑定拖拽标注结束后事件"><a href="#设置图像标注并绑定拖拽标注结束后事件" class="headerlink" title="设置图像标注并绑定拖拽标注结束后事件"></a>设置图像标注并绑定拖拽标注结束后事件</h3><p>百度地图提供了很多覆盖物供我们很多覆盖物的类，而我们这里使用<code>Marker</code>标注点，也就是我们效果图所看到的小红点，因为它可以比较形象的标注用户看到的兴趣点(就比如我们选中的地址)。</p><p>当然，它也可以自定义新的图标，不过这不是我们这篇案例的重点，有兴趣的可以参考<a href="http://lbsyun.baidu.com/index.php?title=jspopular3.0/guide/mark" target="_blank" rel="noopener">标注</a>、(自定义 Marker 图标)[<a href="http://lbsyun.baidu.com/jsdemo.htm#eChangeMarkerIcon]" target="_blank" rel="noopener">http://lbsyun.baidu.com/jsdemo.htm#eChangeMarkerIcon]</a></p><p>设置图像标注并并绑定拖拽事件非常简单，只需要下面几行代码：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 导入Marker类</span></span><br><span class="line"><span class="keyword">const</span> &#123; <span class="built_in">Map</span>, Point, Marker &#125; = BMap;</span><br><span class="line"><span class="comment">// 地图相关元素，因为可能在别的方法使用</span></span><br><span class="line"><span class="keyword">let</span> map = <span class="literal">null</span>,</span><br><span class="line">  marker = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 初始化地图</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">initMap</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 防止dom还未渲染</span></span><br><span class="line">  nextTick(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="comment">// 创建一个图像标注实例，允许启用拖拽Marker</span></span><br><span class="line">    marker = <span class="keyword">new</span> Marker(point, &#123; <span class="attr">enableDragging</span>: <span class="literal">true</span> &#125;);</span><br><span class="line">    map.addOverlay(marker);</span><br><span class="line">    <span class="comment">// 标注拖拽</span></span><br><span class="line">    marker.addEventListener(<span class="string">"dragend"</span>, (&#123; point &#125;) =&gt; &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(point);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>这样你就可以在地图上看到小红点，并且可以拖拽小红点啦，拖拽释放后还会在浏览器打印出坐标点。</p><h3 id="绑定点击地图任意点事件"><a href="#绑定点击地图任意点事件" class="headerlink" title="绑定点击地图任意点事件"></a>绑定点击地图任意点事件</h3><p>既然实现拖拽标注结束后获取坐标点，当然在地图上选取任意点，我们也需要获取该点的地址信息啦。</p><p>实现也十分简单，代码如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 初始化地图</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">initMap</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 防止dom还未渲染</span></span><br><span class="line">  nextTick(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="comment">// 地图点击</span></span><br><span class="line">    map.addEventListener(<span class="string">"click"</span>, (&#123; point &#125;) =&gt; &#123;</span><br><span class="line">      getAddrByPoint(point);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><h3 id="添加地图覆盖物-圆"><a href="#添加地图覆盖物-圆" class="headerlink" title="添加地图覆盖物(圆)"></a>添加地图覆盖物(圆)</h3><p>因为我们需要选中返回，那么覆盖物-圆就最符合我们的需求了，所以我们接下来添加一下吧</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 因为默认的圆太难看了，所以我修改了下样式</span></span><br><span class="line"><span class="keyword">const</span> circleOptions = &#123;</span><br><span class="line">  strokeColor: <span class="string">"#18A65E"</span>,</span><br><span class="line">  strokeWeight: <span class="number">2</span>,</span><br><span class="line">  fillColor: <span class="string">"#18A65E"</span>,</span><br><span class="line">  fillOpacity: <span class="string">"0.1"</span>,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  setup(props, &#123; emit &#125;) &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="keyword">const</span> &#123; <span class="built_in">Map</span>, Point, Marker, Circle &#125; = BMap;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 初始化地图</span></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">initMap</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">      <span class="comment">// 防止dom还未渲染</span></span><br><span class="line">      nextTick(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="comment">// ...</span></span><br><span class="line">        <span class="comment">// 创建一个覆盖物——圆</span></span><br><span class="line">        circle = <span class="keyword">new</span> BMap.Circle(point, <span class="built_in">parseInt</span>(formData.form.range), circleOptions);</span><br><span class="line">        <span class="comment">// 添加覆盖物</span></span><br><span class="line">        map.addOverlay(circle);</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">      mapForm,</span><br><span class="line">      ...toRefs(formData),</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><p>既然已经添加了圆，那么当我们改变了范围的时候这个圆肯定也要跟着改变啦</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> formData = reactive(&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  setRadius() &#123;</span><br><span class="line">    circle.setCenter(formData.addressPoint);</span><br><span class="line">    circle.setRadius(<span class="built_in">parseInt</span>(formData.form.range));</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></div><p>切换一下，看我们的圆是不是会变大和变小啦？</p><h3 id="封装逆地址解析函数，用于通过坐标点获取详细地址"><a href="#封装逆地址解析函数，用于通过坐标点获取详细地址" class="headerlink" title="封装逆地址解析函数，用于通过坐标点获取详细地址"></a>封装逆地址解析函数，用于通过坐标点获取详细地址</h3><p>写到这里，我们已经获取可以点击地图和拖拽获取坐标点了，那么我们缺少什么呢？没错，就是缺少了个可以解析坐标点的方法。</p><p>参考<a href="http://lbsyun.baidu.com/jsdemo.htm#xRevAddressParseSingle" target="_blank" rel="noopener">地址逆解析</a>，我们就可以封装一个根据坐标点可以获取到距离位置的方法了，同时也可以给地图设置默认的地址了。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> &#123; <span class="built_in">Map</span>, Point, Marker, Circle, Geocoder &#125; = BMap;</span><br><span class="line"><span class="keyword">const</span> geco = <span class="keyword">new</span> Geocoder();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 逆地址解析函数</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getAddrByPoint</span>(<span class="params">point</span>) </span>&#123;</span><br><span class="line">  geco.getLocation(point, (res) =&gt; &#123;</span><br><span class="line">    formData.addressPoint = point;</span><br><span class="line">    formData.form.address = res.address;</span><br><span class="line">    formData.setRadius();</span><br><span class="line">    map.panTo(point);</span><br><span class="line">    marker.setPosition(point);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 初始化地图</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">initMap</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 防止dom还未渲染</span></span><br><span class="line">  nextTick(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="comment">// 标注拖拽</span></span><br><span class="line">    marker.addEventListener(<span class="string">"dragend"</span>, (&#123; point &#125;) =&gt; &#123;</span><br><span class="line">      getAddrByPoint(point);</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 地图点击</span></span><br><span class="line">    map.addEventListener(<span class="string">"click"</span>, (&#123; point &#125;) =&gt; &#123;</span><br><span class="line">      getAddrByPoint(point);</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="comment">// 设置默认地址</span></span><br><span class="line">    geco.getLocation(point, (res) =&gt; &#123;</span><br><span class="line">      formData.form.address = res.address;</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><h3 id="添加输入提示来选取地址"><a href="#添加输入提示来选取地址" class="headerlink" title="添加输入提示来选取地址"></a>添加输入提示来选取地址</h3><p>实现到现在，其实基本上功能都已经写完了，就差一个搜索功能。而百度地图提供的检索功能有很多，这里我采用的是<a href="http://lbsyun.baidu.com/jsdemo.htm#i1_4" target="_blank" rel="noopener">本地检索</a>，感兴趣的可以看看他其他的检索功能。</p><p>Antdd 的 AutoComplete 可以参考<a href="https://2x.antdv.com/components/auto-complete-cn/" target="_blank" rel="noopener">这个链接</a>，这里就不做进一步地讲解了。</p><p>主要用到了<code>search</code>和<code>select</code>两个事件回调。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> formData = reactive(&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  handleQuery(query) &#123;</span><br><span class="line">    <span class="keyword">if</span> (!query) &#123;</span><br><span class="line">      formData.addressSource = [];</span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    local.search(query);</span><br><span class="line">  &#125;,</span><br><span class="line">  handleSelect(item) &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; point &#125; = formData.addressSource.find(<span class="function">(<span class="params">&#123; value &#125;</span>) =&gt;</span> value === item);</span><br><span class="line">    formData.addressPoint = point;</span><br><span class="line">    formData.setRadius();</span><br><span class="line">    marker.setPosition(point);</span><br><span class="line">    map.panTo(point);</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> &#123; <span class="built_in">Map</span>, Point, Marker, Geocoder, LocalSearch &#125; = BMap;</span><br><span class="line"><span class="comment">// 地图相关元素，因为可能在别的方法使用</span></span><br><span class="line"><span class="keyword">let</span> map = <span class="literal">null</span>,</span><br><span class="line">  marker = <span class="literal">null</span>,</span><br><span class="line">  circle = <span class="literal">null</span>,</span><br><span class="line">  local = <span class="literal">null</span>;</span><br><span class="line"><span class="comment">// 初始化地图</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">initMap</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 防止dom还未渲染</span></span><br><span class="line">  nextTick(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="comment">// 创建本地检索实例供search回调使用</span></span><br><span class="line">    local = <span class="keyword">new</span> LocalSearch(map, &#123;</span><br><span class="line">      onSearchComplete: <span class="function">(<span class="params">results</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (local.getStatus() == BMAP_STATUS_SUCCESS) &#123;</span><br><span class="line">          <span class="keyword">const</span> res = [];</span><br><span class="line">          <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; results.getCurrentNumPois(); i++) &#123;</span><br><span class="line">            <span class="keyword">const</span> &#123; title, address &#125; = results.getPoi(i);</span><br><span class="line">            res.push(&#123;</span><br><span class="line">              ...results.getPoi(i),</span><br><span class="line">              value: <span class="string">`<span class="subst">$&#123;title&#125;</span>(<span class="subst">$&#123;address&#125;</span>)`</span>,</span><br><span class="line">            &#125;);</span><br><span class="line">          &#125;</span><br><span class="line">          formData.addressSource = res;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>至此，我们就完成了所有的功能点啦 φ(*￣ 0 ￣) 当然，其实好多没有完善的点，就等着各位之后完善咯</p><blockquote><p><a href="https://gitee.com/gating/demo/tree/master/baidu-map-demo" target="_blank" rel="noopener">gitee 地址</a>,<a href="https://github.com/GATING/demo/tree/master/baidu-map-demo" target="_blank" rel="noopener">github 地址</a></p></blockquote><h1 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h1><p><a href="https://2x.antdv.com/docs/vue/introduce-cn/" target="_blank" rel="noopener">Ant Design of Vue</a></p><p><a href="https://vue3js.cn/docs/zh/guide/composition-api-introduction.html#%E4%BB%80%E4%B9%88%E6%98%AF%E7%BB%84%E5%90%88%E5%BC%8F-api" target="_blank" rel="noopener">什么是组合式 API？</a></p><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>虽然本文罗嗦了点，但还是感谢各位观众老爷的能看到最后 O(∩_∩)O 希望你能有所收获 😁</p>]]></content>
    
    <summary type="html">
    
      最近，在做 vue 项目的时候有做到选择地址功能，而原项目中又引入了百度地图，所以我就打算通过使用百度地图来实现地址搜索功能啦。
    
    </summary>
    
    
      <category term="记录" scheme="https://gatings.cn/categories/%E8%AE%B0%E5%BD%95/"/>
    
    
      <category term="vue" scheme="https://gatings.cn/tags/vue/"/>
    
      <category term="ant-design-vue" scheme="https://gatings.cn/tags/ant-design-vue/"/>
    
      <category term="baidu-map" scheme="https://gatings.cn/tags/baidu-map/"/>
    
  </entry>
  
  <entry>
    <title>JS中有趣的内置对象-JSON</title>
    <link href="https://gatings.cn/2020-09-08/JS%E4%B8%AD%E6%9C%89%E8%B6%A3%E7%9A%84%E5%86%85%E7%BD%AE%E5%AF%B9%E8%B1%A1-JSON/"/>
    <id>https://gatings.cn/2020-09-08/JS%E4%B8%AD%E6%9C%89%E8%B6%A3%E7%9A%84%E5%86%85%E7%BD%AE%E5%AF%B9%E8%B1%A1-JSON/</id>
    <published>2020-09-07T16:00:00.000Z</published>
    <updated>2021-10-13T01:56:29.703Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>在以前的<code>web开发中</code>，我们多数选择<code>纯文本</code>或<code>XML</code>作为我们的提交的数据格式，大多数是<code>XML</code>，少数<code>纯文本</code>。其实从<code>AJAX(Asynchronous JavaScript and XML)</code>的命名我们也知道，数据交换是大多数通过<code>XML</code>格式进行的。但是<code>XML</code>格式有一个缺点，就是文档构造复杂，需要传输比较多的字节数，并且解析起来也比较麻烦。所以就创建了<code>JSON</code>这种数据描述格式，可以很简单的就描述很复杂的数据。同时独立于语言，这样就可以在多种语言内使用。也正是因为这个，<code>JSON</code>的轻便性逐渐得到重视，后来替代<code>XML</code>成为最主要的数据传输格式。</p><blockquote><p>ps: 当然，虽然说了很多<code>JSON</code>的好处，你就以为不用学习<code>XML</code>了吗？肯定不是啦，微信公共平台接口中的有是有相当一部分接口使用了<code>XML</code>的。</p></blockquote><p><code>JSON</code>对象包含两个方法: 用于解析 <a href="https://www.json.org/json-en.html" target="_blank" rel="noopener">JavaScript Object Notation</a> (<a href="https://developer.mozilla.org/zh-CN/docs/Glossary/JSON" target="_blank" rel="noopener">JSON</a>) 的 parse() 方法，以及将对象/值转换为 JSON 字符串的 stringify() 方法。除了这两个方法, JSON 这个对象本身并没有其他作用，也不能被调用或者作为构造函数调用。</p><blockquote><p>ps: <a href="http://timelessrepo.com/json-isnt-a-javascript-subset" target="_blank" rel="noopener">JSON：并不是 JavaScript 的子集。</a>尽管不是严格意义上的子集，JSON 非常接近 JavaScript 语法的子集。</p></blockquote><h1 id="JavaScript-与-JSON-的区别"><a href="#JavaScript-与-JSON-的区别" class="headerlink" title="JavaScript 与 JSON 的区别"></a>JavaScript 与 JSON 的区别</h1><table><thead><tr><th>JavaScript类型</th><th>JSON 的不同点</th></tr></thead><tbody><tr><td>对象和数组</td><td>属性名称必须是双引号括起来的字符串；最后一个属性后不能有逗号。</td></tr><tr><td>数值</td><td>禁止出现前导零（JSON.stringify 方法自动忽略前导零，而在 JSON.parse 方法中将会抛出 SyntaxError）；如果有小数点, 则后面至少跟着一位数字。</td></tr></tbody></table><p>针对于这两点，我们举个例子，对于对象和数组类型：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 属性名称必须是双引号括起来的字符串</span></span><br><span class="line"><span class="keyword">const</span> test1 = <span class="string">"['1','1']"</span></span><br><span class="line"><span class="built_in">JSON</span>.parse(test1) <span class="comment">// Uncaught SyntaxError: Unexpected token ' in JSON at position 1</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> test2 = <span class="string">"&#123;'name':'gating'&#125;"</span></span><br><span class="line"><span class="built_in">JSON</span>.parse(test2) <span class="comment">// Uncaught SyntaxError: Unexpected token o in JSON at position 1</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> test3 = <span class="string">'["2","1"]'</span></span><br><span class="line"><span class="built_in">JSON</span>.parse(test3) <span class="comment">// ["2","1"]</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> test4 = <span class="string">'&#123;"name":"gating"&#125;'</span></span><br><span class="line"><span class="built_in">JSON</span>.parse(test3) <span class="comment">// &#123;name: "gating"&#125;</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> test5 = <span class="string">'["2","1",]'</span></span><br><span class="line"><span class="built_in">JSON</span>.parse(test5) <span class="comment">// Uncaught SyntaxError: Unexpected token ] in JSON at position 9</span></span><br><span class="line"><span class="keyword">const</span> test6 = <span class="string">'&#123;"name":"gating",&#125;'</span></span><br><span class="line"><span class="built_in">JSON</span>.parse(test6) <span class="comment">// Uncaught SyntaxError: Unexpected token &#125; in JSON at position 17</span></span><br></pre></td></tr></table></figure></div><p>对于数值类型：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 禁止出现前导零</span></span><br><span class="line"><span class="keyword">const</span> test7 = [<span class="number">00010</span>, <span class="number">1</span>]</span><br><span class="line"><span class="built_in">JSON</span>.stringify(test7) <span class="comment">// "[8,1]"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> test8 = <span class="string">"[00010, 1]"</span></span><br><span class="line"><span class="built_in">JSON</span>.parse(test8) <span class="comment">// SyntaxError: Unexpected number in JSON at position 2**</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> test9 = [<span class="number">1</span>, <span class="number">1.</span>]</span><br><span class="line"><span class="built_in">JSON</span>.stringify(test9) <span class="comment">// "[1,1]"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> test10 = <span class="string">"[1, 1.]"</span></span><br><span class="line"><span class="built_in">JSON</span>.parse(test10) <span class="comment">// Uncaught SyntaxError: Unexpected token ] in JSON at position 6</span></span><br></pre></td></tr></table></figure></div><blockquote><p>ps: <strong>0+数字</strong>表示8进制，- -所以上面打印出来的是8，同样的有：<strong>0b+数字(二进制)</strong>、<strong>0x+数字(16进制)</strong>，其实<strong>0o+数字</strong>也可以表示8进制</p></blockquote><p>其实还有一个字符串类型，<strong>JavaScript</strong>和<strong>JSON</strong>处理是不一致的，这里为啥我只写两点呢？是因为还有现在在新版的<strong>Chrome</strong>已经可以解析正常，但是想具体可以查看下面<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON" target="_blank" rel="noopener">参考链接</a>自行了解，这里就不展开了</p><blockquote><p>参考：<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON" target="_blank" rel="noopener">JavaScript 与 JSON 的区别</a>，字符串那一点我在<strong>Chrome 85.0.4183.83</strong>以及可以正常解析</p></blockquote><h1 id="stringify"><a href="#stringify" class="headerlink" title="stringify"></a>stringify</h1><p>stringify() 方法用于将<code>JavaScript</code>值转换为<code>JSON</code>字符串。</p><p>虽然我们经常用这个方法，但我想估计有不少人不知道它居然有三个参数之多吧？分别是<code>value</code>、<code>replacer</code>、<code>space</code>。</p><blockquote><p>JSON.stringify(value[, replacer [, space]])</p></blockquote><ul><li>value<ul><li>将要序列化成 一个 JSON 字符串的值。</li></ul></li><li>replacer <code>可选</code><ul><li>如果该参数是一个<strong>函数</strong>，则在序列化过程中，被序列化的值的每个属性都会经过该函数的转换和处理；如果该参数是一个<strong>数组</strong>，则只有包含在这个数组中的属性名才会被序列化到最终的 JSON 字符串中；如果该参数为 <strong>null</strong> 或者未提供，则对象所有的属性都会被序列化。</li></ul></li><li>space <code>可选</code><ul><li>指定缩进用的空白字符串，用于美化输出（pretty-print）；如果参数是个数字，它代表有多少的空格；上限为10。该值若小于1，则意味着没有空格；如果该参数为字符串（当字符串长度超过10个字母，取其前10个字母），该字符串将被作为空格；如果该参数没有提供（或者为 null），将没有空格。</li></ul></li></ul><h2 id="value参数"><a href="#value参数" class="headerlink" title="value参数"></a>value参数</h2><p>通常我们最常用的就是第一个参数，也就是value参数，但是你知道它的运算规则是怎么样的吗？闲话不多说，直接上例子</p><h3 id="NaN-和-Infinity-格式的数值及-null-都会被当做-null"><a href="#NaN-和-Infinity-格式的数值及-null-都会被当做-null" class="headerlink" title="NaN 和 Infinity 格式的数值及 null 都会被当做 null"></a>NaN 和 Infinity 格式的数值及 null 都会被当做 null</h3><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">JSON</span>.stringify([<span class="literal">NaN</span>, <span class="literal">Infinity</span>, <span class="literal">null</span>]) <span class="comment">// [null,null,null]</span></span><br></pre></td></tr></table></figure></div><h3 id="undefined、任意的函数以及-symbol-值的处理"><a href="#undefined、任意的函数以及-symbol-值的处理" class="headerlink" title="undefined、任意的函数以及 symbol 值的处理"></a>undefined、任意的函数以及 symbol 值的处理</h3><h4 id="出现在非数组对象的属性值中时，在序列化过程中会被忽略"><a href="#出现在非数组对象的属性值中时，在序列化过程中会被忽略" class="headerlink" title="出现在非数组对象的属性值中时，在序列化过程中会被忽略"></a>出现在非数组对象的属性值中时，在序列化过程中会被忽略</h4><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">JSON</span>.stringify(&#123;<span class="attr">x</span>: <span class="literal">undefined</span>, <span class="attr">y</span>: <span class="built_in">Object</span>, <span class="attr">z</span>: <span class="built_in">Symbol</span>(<span class="string">""</span>)&#125;) <span class="comment">// "&#123;&#125;"</span></span><br></pre></td></tr></table></figure></div><h4 id="出现在数组中时被转换成null"><a href="#出现在数组中时被转换成null" class="headerlink" title="出现在数组中时被转换成null"></a>出现在数组中时被转换成null</h4><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">JSON</span>.stringify([<span class="literal">undefined</span>, <span class="built_in">Object</span>, <span class="built_in">Symbol</span>(<span class="string">""</span>)])  <span class="comment">// "[null,null,null]"</span></span><br></pre></td></tr></table></figure></div><h4 id="单独出现则会返回undefined"><a href="#单独出现则会返回undefined" class="headerlink" title="单独出现则会返回undefined"></a>单独出现则会返回undefined</h4><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">JSON</span>.stringify(<span class="literal">undefined</span>)  <span class="comment">// undefined</span></span><br><span class="line"><span class="built_in">JSON</span>.stringify(<span class="built_in">Object</span>)  <span class="comment">// undefined</span></span><br><span class="line"><span class="built_in">JSON</span>.stringify(<span class="built_in">Symbol</span>(<span class="string">""</span>))  <span class="comment">// undefined</span></span><br></pre></td></tr></table></figure></div><h3 id="所有以-symbol-为属性键的属性都会被完全忽略掉，即便-replacer-参数中强制指定包含了它们。"><a href="#所有以-symbol-为属性键的属性都会被完全忽略掉，即便-replacer-参数中强制指定包含了它们。" class="headerlink" title="所有以 symbol 为属性键的属性都会被完全忽略掉，即便 replacer 参数中强制指定包含了它们。"></a>所有以 symbol 为属性键的属性都会被完全忽略掉，即便 replacer 参数中强制指定包含了它们。</h3><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">JSON</span>.stringify(&#123;[<span class="built_in">Symbol</span>(<span class="string">"foo"</span>)]: <span class="string">"foo"</span>&#125;) <span class="comment">// "&#123;&#125;"</span></span><br><span class="line"><span class="built_in">JSON</span>.stringify(&#123;[<span class="built_in">Symbol</span>.for(<span class="string">"foo"</span>)]: <span class="string">"foo"</span>&#125;, [<span class="built_in">Symbol</span>.for(<span class="string">"foo"</span>)]) <span class="comment">// "&#123;&#125;"</span></span><br></pre></td></tr></table></figure></div><h3 id="转换值如果有-toJSON-方法，该方法定义什么值将被序列化。"><a href="#转换值如果有-toJSON-方法，该方法定义什么值将被序列化。" class="headerlink" title="转换值如果有 toJSON() 方法，该方法定义什么值将被序列化。"></a>转换值如果有 toJSON() 方法，该方法定义什么值将被序列化。</h3><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">JSON</span>.stringify(&#123;</span><br><span class="line">  toJSON: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">'gating'</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;) <span class="comment">// "gating"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> name = &#123;</span><br><span class="line">  age: <span class="number">18</span>,</span><br><span class="line">  toJSON: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">'gating'</span></span><br><span class="line">  &#125;,</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">JSON</span>.stringify(&#123; name &#125;) <span class="comment">// &#123;"name":"gating"&#125;</span></span><br></pre></td></tr></table></figure></div><p>又因为<strong>Date</strong>对象上挂载了一个<strong>toJSON</strong>方法，所以针对于<strong>Date</strong>类型，它默认就会调用<strong>Date</strong>类型上的<strong>toJSON</strong>方法(同Date.toISOString())</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">JSON</span>.stringify(<span class="keyword">new</span> <span class="built_in">Date</span>(<span class="string">"2020/01/01"</span>)) <span class="comment">// "2019-12-31T16:00:00.000Z" 因为中国在东八区，所以相差了8小时</span></span><br></pre></td></tr></table></figure></div><blockquote><p>ps: 东八区（UTC/GMT+08:00）是比世界协调时间（UTC）/格林尼治时间（GMT）快8小时的时区</p></blockquote><h3 id="其他类型的对象，仅会序列化对象可枚举的属性，包括-Map-Set-WeakMap-WeakSet"><a href="#其他类型的对象，仅会序列化对象可枚举的属性，包括-Map-Set-WeakMap-WeakSet" class="headerlink" title="其他类型的对象，仅会序列化对象可枚举的属性，包括 Map/Set/WeakMap/WeakSet"></a>其他类型的对象，仅会序列化对象可枚举的属性，包括 Map/Set/WeakMap/WeakSet</h3><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">JSON</span>.stringify(</span><br><span class="line">  <span class="built_in">Object</span>.create(<span class="literal">null</span>, &#123;</span><br><span class="line">    x: &#123; <span class="attr">value</span>: <span class="string">'x'</span>&#125;, <span class="comment">// enumerable 默认为不可枚举</span></span><br><span class="line">    y: &#123; <span class="attr">value</span>: <span class="string">'y'</span>, <span class="attr">enumerable</span>: <span class="literal">true</span> &#125;,</span><br><span class="line">  &#125;)</span><br><span class="line">) <span class="comment">// "&#123;"y":"y"&#125;"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> map = <span class="keyword">new</span> <span class="built_in">Map</span>()</span><br><span class="line"><span class="built_in">Object</span>.defineProperty(map, <span class="string">'name'</span>, &#123;</span><br><span class="line">  value: <span class="string">'gating'</span>,</span><br><span class="line">  enumerable: <span class="literal">true</span></span><br><span class="line">&#125;)</span><br><span class="line"><span class="built_in">Object</span>.defineProperty(map, <span class="string">'age'</span>, &#123;</span><br><span class="line">  value: <span class="number">18</span></span><br><span class="line">&#125;)</span><br><span class="line"><span class="built_in">JSON</span>.stringify(map) <span class="comment">// &#123;"name":"gating"&#125;</span></span><br><span class="line"><span class="built_in">JSON</span>.stringify(<span class="keyword">new</span> <span class="built_in">Set</span>) <span class="comment">// &#123;&#125;</span></span><br><span class="line"><span class="built_in">JSON</span>.stringify(<span class="keyword">new</span> <span class="built_in">RegExp</span>) <span class="comment">// &#123;&#125;</span></span><br></pre></td></tr></table></figure></div><p>看到这里，你基本已经知道运算规则是怎么样滴了，也就知道了为什么<code>JSON.stringify + JSON.parse</code>不能转换函数、正则、Error等对象了吧？</p><p>那么接下来就要了解下更有意思的东西啦，就是<code>stringify</code>第二个参数<code>replacer</code>啦</p><h2 id="replacer参数"><a href="#replacer参数" class="headerlink" title="replacer参数"></a>replacer参数</h2><p>replacer 参数可以用来来更改默认的字符串化的行为。它可以是一个函数或者一个数组。如果该参数为 null 或者未提供，则对象所有的属性都会被序列化。</p><h3 id="作为函数"><a href="#作为函数" class="headerlink" title="作为函数"></a>作为函数</h3><p>在开始时, replacer 函数会被传入一个空字符串作为 key 值，代表着要被 stringify 的这个对象。随后每个对象或数组上的属性会被依次传入。</p><p>这句话看不太懂？不要紧，举个例子你就懂了</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">replacer</span>(<span class="params">key, value</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'['</span> + key + <span class="string">']:'</span> + value)</span><br><span class="line">  <span class="keyword">return</span> value</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">JSON</span>.stringify(&#123; <span class="attr">name</span>: <span class="string">'gating'</span>, <span class="attr">age</span>: <span class="number">18</span> &#125;, replacer)</span><br><span class="line"><span class="comment">// []:[object Object]</span></span><br><span class="line"><span class="comment">// [name]:gating</span></span><br><span class="line"><span class="comment">// [age]:18</span></span><br></pre></td></tr></table></figure></div><p>上面例子中，他会执行三次，也就是一开始他会默认传一个空字符串作为键，而键值是整个对象；第二次键为<code>name</code>，键值为<code>gating</code>，以此类推。</p><p>当然，这里也有个特别需要注意的点，就是返回的是一个对象的时候，该对象递归地序列化成 JSON 字符串，对每个属性调用<code>replacer</code>方法。除非该对象是一个函数，这种情况将不会被序列化成 JSON 字符串。</p><p>比如：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">replacer</span>(<span class="params">key, value</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;<span class="attr">a</span>:<span class="number">1</span>&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">JSON</span>.stringify(&#123;&#125;, replacer)</span><br></pre></td></tr></table></figure></div><p>因为我们每次返回的都是对象，那么每次都会调用<code>replacer</code>，所以会造成堆栈溢出，那么我们举个不会溢出的小栗子例子吧🤣：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">replacer</span>(<span class="params">key, value</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(<span class="keyword">typeof</span> value === <span class="string">'object'</span>)&#123;</span><br><span class="line">    <span class="keyword">return</span> &#123; <span class="attr">age</span>: <span class="number">9</span> &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> value * <span class="number">2</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">const</span> name = &#123;&#125;</span><br><span class="line"><span class="built_in">JSON</span>.stringify(name, replacer) <span class="comment">// &#123;"age":18&#125;</span></span><br></pre></td></tr></table></figure></div><p>在这里，每一次处理的对象，都是前一次返回的值。因为我们<code>replacer</code>修改了<code>name</code>对象，接着就要递归<code>replacer</code>处理修改后的对象。</p><blockquote><p>总结：递归处理中，每一次处理的对象，都是前一次返回的值。</p></blockquote><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">f</span>(<span class="params">key, value</span>)</span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(value)</span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">typeof</span> value === <span class="string">"object"</span>)&#123;</span><br><span class="line">    <span class="keyword">return</span> &#123;<span class="attr">b</span>: <span class="number">2</span>&#125;;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> value*<span class="number">2</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">JSON</span>.stringify(o,f)</span><br></pre></td></tr></table></figure></div><p>那么，既然是更改默认的字符串化的行为，那么我就尝试下更改下吧</p><h4 id="replacer——数字Double"><a href="#replacer——数字Double" class="headerlink" title="replacer——数字Double"></a>replacer——数字Double</h4><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">replacer</span>(<span class="params">key, value</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">typeof</span> value === <span class="string">"number"</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> value * <span class="number">2</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> value;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">const</span> me = &#123;</span><br><span class="line">  name: <span class="string">'gating'</span>,</span><br><span class="line">  age: <span class="number">9</span>,</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">JSON</span>.stringify(me, replacer) <span class="comment">// &#123;"name":"gating","age":18&#125;</span></span><br></pre></td></tr></table></figure></div><h4 id="replacer——剔除不要的属性"><a href="#replacer——剔除不要的属性" class="headerlink" title="replacer——剔除不要的属性"></a>replacer——剔除不要的属性</h4><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">replacer</span>(<span class="params">key, value</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">typeof</span> value === <span class="string">"string"</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">undefined</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> value;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">const</span> me = &#123;</span><br><span class="line">  name: <span class="string">'gating'</span>,</span><br><span class="line">  age: <span class="number">18</span>,</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">JSON</span>.stringify(me, replacer) <span class="comment">// &#123;"age":18&#125; 剔除了name属性</span></span><br><span class="line"><span class="built_in">JSON</span>.stringify([<span class="number">1</span>,<span class="string">"2"</span>], replacer) <span class="comment">// "[1,null]"</span></span><br></pre></td></tr></table></figure></div><blockquote><p>注意: 不能用 replacer 方法，从数组中移除值（values），如若返回 undefined 或者一个函数，将会被 null 取代(其实从 value 的运算规则我们也知道会出现这个结果了)。</p></blockquote><h3 id="作为数组"><a href="#作为数组" class="headerlink" title="作为数组"></a>作为数组</h3><p>数组的值代表将被序列化成<code>JSON</code>字符串的属性名</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> me = &#123;</span><br><span class="line">  name: <span class="string">'gating'</span>,</span><br><span class="line">  age: <span class="number">18</span>,</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">JSON</span>.stringify(me, [<span class="string">'name'</span>]) <span class="comment">// &#123;"name":"gating"&#125; 只保留了name的属性值</span></span><br><span class="line"><span class="built_in">JSON</span>.stringify([<span class="string">'a'</span>, <span class="string">'b'</span>], [<span class="string">'0'</span>]) <span class="comment">// ['a', 'b']</span></span><br></pre></td></tr></table></figure></div><blockquote><p>这个类似白名单的数组，只对对象的属性有效，对数组无效。</p></blockquote><h2 id="space参数"><a href="#space参数" class="headerlink" title="space参数"></a>space参数</h2><p>space 参数用来控制结果字符串里面的间距，增加返回的 JSON 字符串的可读性。如果是一个数字, 则在字符串化时每一级别会比上一级别缩进多这个数字值的空格（最多10个空格）；如果是一个字符串，则每一级别会比上一级别多缩进该字符串（或该字符串的前10个字符）。</p><h3 id="使用数字"><a href="#使用数字" class="headerlink" title="使用数字"></a>使用数字</h3><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">JSON</span>.stringify(&#123; <span class="attr">name</span>: <span class="string">"gating"</span>, <span class="attr">age</span>: <span class="number">18</span> &#125;, <span class="literal">null</span>, <span class="number">6</span>)</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">"&#123;</span></span><br><span class="line"><span class="comment">      "name": "gating",</span></span><br><span class="line"><span class="comment">      "age": 18</span></span><br><span class="line"><span class="comment">&#125;"</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure></div><h3 id="使用字符串"><a href="#使用字符串" class="headerlink" title="使用字符串"></a>使用字符串</h3><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">JSON</span>.stringify(&#123; <span class="attr">name</span>: <span class="string">"gating"</span>, <span class="attr">age</span>: <span class="number">18</span> &#125;, <span class="literal">null</span>, <span class="string">'|-'</span>)</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">"&#123;</span></span><br><span class="line"><span class="comment">|-"name": "gating",</span></span><br><span class="line"><span class="comment">|-"age": 18</span></span><br><span class="line"><span class="comment">&#125;"</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure></div><h3 id="使用制表符（-t）来缩进"><a href="#使用制表符（-t）来缩进" class="headerlink" title="使用制表符（\t）来缩进"></a>使用制表符（\t）来缩进</h3><p>因为最用用到要美化JSON的场景，所以顺便把制表符也写出来了 😊</p><p>就是提交的时候是json，返回的时候美化json显示到输入框内</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">JSON</span>.stringify(&#123; <span class="attr">name</span>: <span class="string">"gating"</span>, <span class="attr">age</span>: <span class="number">18</span> &#125;, <span class="literal">null</span>, <span class="string">'\t'</span>)</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">"&#123;</span></span><br><span class="line"><span class="comment">"name": "gating",</span></span><br><span class="line"><span class="comment">"age": 18</span></span><br><span class="line"><span class="comment">&#125;"</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure></div><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ul><li>JSON.stringify()将值转换为相应的JSON格式：<ul><li>转换值如果有 toJSON() 方法，该方法定义什么值将被序列化。</li><li>非数组对象的属性不能保证以特定的顺序出现在序列化后的字符串中。</li><li>布尔值、数字、字符串的包装对象在序列化过程中会自动转换成对应的原始值。</li><li>undefined、任意的函数以及 symbol 值，在序列化过程中会被忽略（出现在非数组对象的属性值中时）或者被转换成 null（出现在数组中时）。函数、undefined 被单独转换时，会返回 undefined，如JSON.stringify(function(){}) or JSON.stringify(undefined).</li><li>对包含循环引用的对象（对象之间相互引用，形成无限循环）执行此方法，会抛出错误。</li><li>所有以 symbol 为属性键的属性都会被完全忽略掉，即便 replacer 参数中强制指定包含了它们。</li><li>Date 日期调用了 toJSON() 将其转换为了 string 字符串（同Date.toISOString()），因此会被当做字符串处理。</li><li>NaN 和 Infinity 格式的数值及 null 都会被当做 null。</li><li>其他类型的对象，包括 Map/Set/WeakMap/WeakSet，仅会序列化可枚举的属性。</li></ul></li></ul><h1 id="parse"><a href="#parse" class="headerlink" title="parse"></a>parse</h1><p>parse() 方法用于将一个 JSON 字符串转换为对象。</p><blockquote><p>JSON.parse(text[, reviver])</p></blockquote><h2 id="text参数"><a href="#text参数" class="headerlink" title="text参数"></a>text参数</h2><p>text参数没啥可说的，就是要被解析成 JavaScript 值的字符串，必须符合<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON" target="_blank" rel="noopener">JSON</a>的语法格式</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">JSON</span>.parse(<span class="string">'&#123;&#125;'</span>) <span class="comment">// &#123;&#125;</span></span><br><span class="line"><span class="comment">// 如果传入的字符串不是有效的 JSON 格式, JSON.parse方法将报错。</span></span><br><span class="line"><span class="built_in">JSON</span>.parse(<span class="string">"'String'"</span>) <span class="comment">// Uncaught SyntaxError: Unexpected token ' in JSON at position 0</span></span><br></pre></td></tr></table></figure></div><h2 id="reviver参数"><a href="#reviver参数" class="headerlink" title="reviver参数"></a>reviver参数</h2><p>reviver参数和JSON.stringify方法的reviver参数类似，唯一不同的是如果有深层对象，他的遍历顺序依照：</p><p>从最内层开始，按照层级顺序，依次向外遍历，而JSON.stringify的遍历顺序刚好相反，从外向内</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">JSON</span>.parse(<span class="string">'&#123;"1": 1, "2": 2,"3": &#123;"4": 4, "5": &#123;"6": 6&#125;&#125;&#125;'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">k, v</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(k); <span class="comment">// 输出当前的属性名，从而得知遍历顺序是从内向外的，</span></span><br><span class="line">                    <span class="comment">// 最后一个属性名会是个空字符串。</span></span><br><span class="line">    <span class="keyword">return</span> v;       <span class="comment">// 返回原始属性值，相当于没有传递 reviver 参数。</span></span><br><span class="line">&#125;) <span class="comment">// 1 2 4 6 5 3 ""</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">JSON</span>.stringify(&#123;<span class="string">"1"</span>: <span class="number">1</span>, <span class="string">"2"</span>: <span class="number">2</span>,<span class="string">"3"</span>: &#123;<span class="string">"4"</span>: <span class="number">4</span>, <span class="string">"5"</span>: &#123;<span class="string">"6"</span>: <span class="number">6</span>&#125;&#125;&#125;, <span class="function"><span class="keyword">function</span> (<span class="params">k, v</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(k); </span><br><span class="line">    <span class="keyword">return</span> v;      </span><br><span class="line">&#125;) <span class="comment">// "" 1 2 3 4 5 6</span></span><br></pre></td></tr></table></figure></div><h1 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h1><ul><li><a href="https://javascript.ruanyifeng.com/stdlib/json.html" target="_blank" rel="noopener">JSON对象</a></li><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON" target="_blank" rel="noopener">JSON</a></li></ul><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>虽然本文罗嗦了点，但还是感谢各位观众老爷的能看到最后 O(∩_∩)O 希望你能有所收获 😁</p>]]></content>
    
    <summary type="html">
    
      在以前的web开发中，我们多数选择纯文本或XML作为我们的提交的数据格式，大多数是XML，少数纯文本。其实从AJAX(Asynchronous JavaScript and XML)的命名我们也知道，数据交换是大多数通过XML格式进行的。
    
    </summary>
    
    
      <category term="记录" scheme="https://gatings.cn/categories/%E8%AE%B0%E5%BD%95/"/>
    
    
      <category term="javascript" scheme="https://gatings.cn/tags/javascript/"/>
    
  </entry>
  
  <entry>
    <title>node实现文件属性批量修改(文件名)</title>
    <link href="https://gatings.cn/2020-07-05/node%E5%AE%9E%E7%8E%B0%E6%96%87%E4%BB%B6%E5%B1%9E%E6%80%A7%E6%89%B9%E9%87%8F%E4%BF%AE%E6%94%B9(%E6%96%87%E4%BB%B6%E5%90%8D)/"/>
    <id>https://gatings.cn/2020-07-05/node%E5%AE%9E%E7%8E%B0%E6%96%87%E4%BB%B6%E5%B1%9E%E6%80%A7%E6%89%B9%E9%87%8F%E4%BF%AE%E6%94%B9(%E6%96%87%E4%BB%B6%E5%90%8D)/</id>
    <published>2020-07-04T16:00:00.000Z</published>
    <updated>2021-10-13T01:56:29.703Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>书接上回，我们实现了<code>批量修改文件的时间</code>，但是却没有实现<code>文件名称的批量修改</code>，是因为我也说过，没有界面的话直接在命令行实现显得有点繁琐，所以我们就通过<code>接口+界面</code>的方式来实现我们这个小需求吧。所以，闲话不多说啦，开始写我们的代码啦~~</p><p>本次教程过于啰嗦，所以这里先放上预览地址供大家预览——<a href="https://gating.gitee.io/demo/batch-modify-filenames/" target="_blank" rel="noopener">点我预览</a>，也可到文末直接下载代码先自行体验。。。</p><h1 id="简单的说下实现的效果"><a href="#简单的说下实现的效果" class="headerlink" title="简单的说下实现的效果"></a>简单的说下实现的效果</h1><p>通常我们在<code>蓝湖</code>上下载的切图是和<code>UI小姐姐</code>定义的图层名相关的，一般下载下来之后我们就需要修改名称，但是一个个修改又显得十分傻逼 😆，所以我们就自己写一下代码自己修改，具体效果如图：</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-07-05/need.png" data-fancybox="group" data-caption="产品效果" class="fancybox"><img alt="产品效果" title="产品效果" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-07-05/need.png" class="lazyload"></a></p><p>看到这里，是不是也想跃跃欲试啦，所以，我们就开始写我们的代码吧</p><h1 id="简单的搭建一下"><a href="#简单的搭建一下" class="headerlink" title="简单的搭建一下"></a>简单的搭建一下</h1><ul><li><p>新建一个 <code>batch-modify-filenames</code> 目录</p></li><li><p>初始化一个<code>node</code>项目工程</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm init -y</span><br></pre></td></tr></table></figure></div></li><li><p>安装依赖，这里依赖比较多，所以下面我会讲一下他们大概是干嘛的</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm i archiver glob koa koa-body koa-router koa-static uuid -S</span><br><span class="line">npm i nodemon -D</span><br></pre></td></tr></table></figure></div><ul><li>koa <code>Nodejs的Web框架</code></li><li>koa-body <code>解析 post 请求，支持文件上传</code></li><li>koa-router <code>处理路由(接口)相关</code></li><li>koa-static <code>处理静态文件</code></li><li>glob <code>批量处理文件</code></li><li>uuid <code>生成不重复的文件名</code></li><li>nodemon <code>监听文件变化，自动重启项目</code></li><li>archiver <code>压缩成 zip 文件</code></li></ul><blockquote><p>ps：nodemon 是用于我们调试的，所以他是开发依赖，所以我们需要<code>-D</code>。其他的都是主要依赖，所以<code>-S</code></p></blockquote></li><li><p>配置一下我们的启动命令</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  ...</span><br><span class="line">  <span class="string">"scripts"</span>: &#123;</span><br><span class="line">      <span class="string">"dev"</span>: <span class="string">"nodemon app.js"</span></span><br><span class="line">  &#125;,</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div></li></ul><h2 id="Koa-是什么"><a href="#Koa-是什么" class="headerlink" title="Koa 是什么"></a>Koa 是什么</h2><p>既然用到了<code>Koa</code>，那么我们就了解一下他是什么？</p><p><code>Koa</code> 是由 <code>Express</code> 原班人马打造的，致力于成为一个更小、更富有表现力、更健壮的 Web 框架，采用了 <code>async</code> 和 <code>await</code> 的方式执行异步操作。 <code>Koa</code> 并没有捆绑任何中间件， 而是提供了一套优雅的方法，帮助您快速而愉快地编写服务端应用程序。也正是因为没有捆绑任何中间件，<code>Koa</code> 保持着一个很小的体积。</p><p>通俗点来讲，就是常说的后端框架，处理我们前端发送过去的请求。</p><h3 id="上下文（Context）"><a href="#上下文（Context）" class="headerlink" title="上下文（Context）"></a>上下文（Context）</h3><p><code>Koa Context</code> 将 <code>node</code> 的 <code>request</code> 和 <code>response</code> 对象封装到单个对象中，为编写 Web 应用程序和 API 提供了许多有用的方法。 这些操作在 <code>HTTP</code> 服务器开发中频繁使用，它们被添加到此级别而不是更高级别的框架，这将强制中间件重新实现此通用功能。</p><p><code>Context</code>这里我们主要用到了<code>state</code>、<code>request</code>、<code>response</code>这几个常用的对象，这里我大概讲讲他们的作用。</p><ul><li>state <code>推荐的命名空间，用于通过中间件传递信息和你的前端视图。</code></li><li>req <code>Node 的 Request 对象.</code></li><li>request <code>Koa 的 Request 对象.</code></li><li>res <code>Node 的 Response 对象.</code></li><li>response <code>Koa 的 Response 对象.</code></li></ul><h4 id="ctx-req-和-ctx-request-的区别"><a href="#ctx-req-和-ctx-request-的区别" class="headerlink" title="ctx.req 和 ctx.request 的区别"></a>ctx.req 和 ctx.request 的区别</h4><p>通常刚学<code>Koa</code>的时候，估计有不少人弄混这两个的区别，这里就说说他们两有什么区别吧。</p><p>最主要的区别是，<code>ctx.request</code> 是 <code>context</code> 经过封装的请求对象，<code>ctx.req</code> 是 <code>context</code> 提供的 <code>node.js</code> 原生 <code>HTTP</code> 请求对象，同理 <code>ctx.response</code> 是 <code>context</code> 经过封装的响应对象，<code>ctx.res</code> 是 <code>context</code> 提供的 <code>node.js</code> 原生 <code>HTTP</code> 响应对象。</p><p>所以，通常我们是通过<code>ctx.request</code>获取请求参数，通过<code>ctx.response</code>设置返回值，不要弄混了哦 (⊙o⊙)</p><h4 id="ctx-body-和-ctx-request-body-傻傻分不清"><a href="#ctx-body-和-ctx-request-body-傻傻分不清" class="headerlink" title="ctx.body 和 ctx.request.body 傻傻分不清"></a>ctx.body 和 ctx.request.body 傻傻分不清</h4><p>以为通常<code>get</code>请求我们可以直接通过<code>ctx.query(ctx.request.query的别名)</code>就可以获得提交过来的数据，<code>post</code>请求的话这是通过<code>body</code>来获取，所以通常我们会通过猜想，以为<code>ctx.body</code>也是<code>ctx.request.body</code>的别名，其实- -这个是不对的。因为我们不仅要接受数据，最重要还要响应数据给前端，所以<code>ctx.body</code>是<code>ctx.response.body</code>的别名。而<code>ctx.request.body</code>为了区分，是没有设置别名的，即只能通过<code>ctx.request.body</code>获取<code>post</code>提交过来的数据。</p><blockquote><p>总结：<code>ctx.body</code>是<code>ctx.response.body</code>的别名，而<code>ctx.request.body</code>是<code>post</code>提交过来的数据</p></blockquote><h3 id="Koa-中间件"><a href="#Koa-中间件" class="headerlink" title="Koa 中间件"></a>Koa 中间件</h3><p><code>Koa</code> 的最大特色，也是最重要的一个设计，就是<code>中间件（middleware）</code>。<code>Koa</code> 应用程序是一个包含一组中间件函数的对象，它是按照类似堆栈的方式组织和执行的。<code>Koa</code> 中使用 <code>app.use()</code>用来加载中间件，基本上 <code>Koa</code> 所有的功能都是通过中间件实现的。每个中间件默认接受两个参数，第一个参数是 <code>Context</code> 对象，第二个参数是 <code>next</code> 函数。只要调用 <code>next</code> 函数，就可以把执行权转交给下一个中间件。</p><p>下面两张图很清晰的表明了一个请求是如何经过中间件最后生成响应的，这种模式中开发和使用中间件都是非常方便的：</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-07-05/onion-model01.png" data-fancybox="group" data-caption="洋葱模型1" class="fancybox"><img alt="洋葱模型1" title="洋葱模型1" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-07-05/onion-model01.png" class="lazyload"></a></p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-07-05/onion-model02.png" data-fancybox="group" data-caption="洋葱模型2" class="fancybox"><img alt="洋葱模型2" title="洋葱模型2" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-07-05/onion-model02.png" class="lazyload"></a></p><p>再来看下 <code>Koa</code> 的洋葱模型实例代码：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> Koa = <span class="built_in">require</span>(<span class="string">"koa"</span>);</span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Koa();</span><br><span class="line">app.use(<span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="number">1</span>);</span><br><span class="line">  <span class="keyword">await</span> next();</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="number">6</span>);</span><br><span class="line">&#125;);</span><br><span class="line">app.use(<span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="number">2</span>);</span><br><span class="line">  <span class="keyword">await</span> next();</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="number">5</span>);</span><br><span class="line">&#125;);</span><br><span class="line">app.use(<span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="number">3</span>);</span><br><span class="line">  <span class="keyword">await</span> next();</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="number">4</span>);</span><br><span class="line">&#125;);</span><br><span class="line">app.listen(<span class="number">8000</span>);</span><br></pre></td></tr></table></figure></div><p>怎么样，是不是有一点点感觉了。当程序运行到 <code>await next()</code>的时候就会暂停当前程序，进入下一个中间件，处理完之后才会回过头来继续处理。</p><p>理解完这些后就可以开始写我们的代码啦！！！ (lll ￢ ω ￢)，好像写了好多和这次教程主题没关的东西，见怪莫怪啦</p><h1 id="简单的搭建前端项目"><a href="#简单的搭建前端项目" class="headerlink" title="简单的搭建前端项目"></a>简单的搭建前端项目</h1><p>既然说到了写界面，这里我们技术栈就采用<code>vue</code>吧，然后<code>UI</code>库的话，大家都用惯了<code>ElementUI</code>，我想大家都特别熟悉了，所以我们这里就采用<code>Ant Design Vue</code>吧，也方便大家对<code>Antd</code>熟悉一下，也没什么坏处</p><p>所以，我们就简单的创建一下我们的项目，在我们<code>batch-modify-filenames</code>文件夹下运行<code>vue create batch-front-end</code>，如图所示:</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-07-05/batch-modify-filenames01.png" data-fancybox="group" data-caption="简单的编写界面" class="fancybox"><img alt="简单的编写界面" title="简单的编写界面" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-07-05/batch-modify-filenames01.png" class="lazyload"></a></p><p>基本上都是无脑下一步，只不过是<code>ant-design-vue</code>用了<code>less</code>，我们为了符合它的写法，我们配置上也采用<code>less</code>。当然，采用<code>sass</code>也是可以的，没什么强制要求。</p><p>创建完项目后就是安装依赖了，因为其实我们用到的组件不多，所以这里我们使用按需加载，即需要安装<code>babel-plugin-import</code>，这里<code>babel-plugin-import</code>也是开发依赖，生产环境是不需要的，所以安装的时候需要<code>-D</code></p><p>这里我们用到了一个常用的<code>工具库（类库）—— lodash</code>，我们不一定用到他所有的方法，所以我们也需要安装个<code>babel</code>插件进行按需加载，即<code>babel-plugin-transform-imports</code>，同样也是<code>-D</code></p><p>最后，既然是与后端做交互，我们肯定需要用到一个<code>http</code>库啦，既然官方推荐我们用<code>axios</code>，所以这里我们也要把<code>axios</code>装上，不过<code>axios</code>不是<code>vue</code>的插件,所以不能直接用<code>use</code>方法。所以，这里我为了方便，也把<code>vue-axios</code>装上了。在之后，因为我又不想把最终的<code>zip</code>文件留在服务器上，毕竟会占用空间，所以我以<strong>流(Stream)</strong>的方式返回给前端，让前端自己下载，那么这里我就采用一个成熟第三方库实现，也就是<code>file-saver</code>，所以最终我们的依赖项就是：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm install ant-design-vue lodash axios vue-axios file-saver -S</span><br><span class="line">npm install babel-plugin-import babel-plugin-transform-imports -D</span><br></pre></td></tr></table></figure></div><p>配置<code>babel.config.js</code>:</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  presets: [<span class="string">"@vue/cli-plugin-babel/preset"</span>],</span><br><span class="line">  plugins: [</span><br><span class="line">    [</span><br><span class="line">      <span class="string">"import"</span>,</span><br><span class="line">      &#123; <span class="attr">libraryName</span>: <span class="string">"ant-design-vue"</span>, <span class="attr">libraryDirectory</span>: <span class="string">"es"</span>, <span class="attr">style</span>: <span class="literal">true</span> &#125;,</span><br><span class="line">    ], <span class="comment">// `style: true` 会加载 less 文件,</span></span><br><span class="line">    [</span><br><span class="line">      <span class="string">"transform-imports"</span>,</span><br><span class="line">      &#123;</span><br><span class="line">        lodash: &#123;</span><br><span class="line">          transform: <span class="string">"lodash/$&#123;member&#125;"</span>,</span><br><span class="line">          preventFullImport: <span class="literal">true</span>,</span><br><span class="line">        &#125;,</span><br><span class="line">      &#125;,</span><br><span class="line">    ],</span><br><span class="line">  ],</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><p>因为我们这里改成了<code>style: true</code>，按需引入的时候大概会报下面的这个错误：</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-07-05/batch-modify-filenames02.png" data-fancybox="group" data-caption="按需加载报错" class="fancybox"><img alt="按需加载报错" title="按需加载报错" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-07-05/batch-modify-filenames02.png" class="lazyload"></a></p><p>解决方案这里也说的很清楚了，在<a href="https://github.com/ant-design/ant-motion/issues/44" target="_blank" rel="noopener">https://github.com/ant-design/ant-motion/issues/44</a>这个链接，也有说明<code>Inline JavaScript is not enabled. Is it set in your options?</code>，告诉我们<code>less</code>没开启<code>JavaScript</code>功能，我们需要修改下 l<code>less-loader</code>的配置即可</p><p>因为<code>vue-cli4</code>的<code>webpack</code>不像<code>vue-cli2.x</code>，他对外屏蔽了<code>webpack</code>的细节，如果想修改必须创建<code>vue.config.js</code>来修改配置，所以我们创建一个<code>vue.config.js</code>文件，书写下面配置：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  css: &#123;</span><br><span class="line">    loaderOptions: &#123;</span><br><span class="line">      less: &#123;</span><br><span class="line">        javascriptEnabled: <span class="literal">true</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><p>关掉服务，再重新跑一下<code>npm run serve</code>，看是不是没有报错了？这样子我就可以书写我们的代码了。</p><h2 id="编写布局"><a href="#编写布局" class="headerlink" title="编写布局"></a>编写布局</h2><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-07-05/batch-modify-filenames03.png" data-fancybox="group" data-caption="批处理界面" class="fancybox"><img alt="批处理界面" title="批处理界面" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-07-05/batch-modify-filenames03.png" class="lazyload"></a></p><p>界面大概长这样子，我想大家写界面应该比我厉害多了，都是直接套用<code>Antd</code>的组件，所以这里我主要分析我们怎么拆分这个页面的组件比较好，怎么定义我们的数据比较好~~</p><p>从这个图我们可以看出<strong>新文件列表</strong>是基于<strong>原文件列表+各种设置</strong>得出来的，所以<strong>新文件列表</strong>我们就可以采用<code>计算属性(computed)</code>来实现啦，那么接下来就是拆分我们页面的时候啦。。。</p><blockquote><p>ps：这里我不会详细讲怎么写界面，只会把我觉得对开发有用的讲出来，不然文章就太多冗长了。虽然现在也十分冗长了（；´д ｀）ゞ</p></blockquote><h3 id="拆分页面"><a href="#拆分页面" class="headerlink" title="拆分页面"></a>拆分页面</h3><p>其实从页面的分割线我们大概就可以看出，他是分成 3 个大的子组件了，还有<strong>文件列表</strong>可以单独划分为孙子组件，所以基本上如图所示：</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-07-05/batch-modify-filenames04.png" data-fancybox="group" data-caption="拆分页面" class="fancybox"><img alt="拆分页面" title="拆分页面" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-07-05/batch-modify-filenames04.png" class="lazyload"></a></p><p>代码结构如图：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Code</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">|-- batch-front-end</span><br><span class="line">    ├─.browserslistrc</span><br><span class="line">    ├─.eslintrc.js</span><br><span class="line">    ├─.gitignore</span><br><span class="line">    ├─babel.config.js</span><br><span class="line">    ├─package-lock.json</span><br><span class="line">    ├─package.json</span><br><span class="line">    ├─README.md</span><br><span class="line">    ├─vue.config.js</span><br><span class="line">    ├─src</span><br><span class="line">    |  ├─App.vue</span><br><span class="line">    |  ├─main.js</span><br><span class="line">    |  ├─utils</span><br><span class="line">    |  |   ├─helpers.js</span><br><span class="line">    |  |   ├─index.js</span><br><span class="line">    |  |   └regexp.js</span><br><span class="line">    |  ├─components</span><br><span class="line">    |  |     ├─ModifyFilename2.vue</span><br><span class="line">    |  |     ├─ModifyFilename</span><br><span class="line">    |  |     |       ├─FileList.vue</span><br><span class="line">    |  |     |       ├─FileListItem.vue</span><br><span class="line">    |  |     |       ├─FileOutput.vue</span><br><span class="line">    |  |     |       ├─FileSetting.vue</span><br><span class="line">    |  |     |       └index.vue</span><br><span class="line">    |  ├─assets</span><br><span class="line">    |  |   └logo.png</span><br><span class="line">    ├─public</span><br><span class="line">    |   ├─favicon.ico</span><br><span class="line">    |   └index.html</span><br></pre></td></tr></table></figure></div><p>看到这里，基本知道我是怎么拆分的吧？没错，一共用了四个组件分别是<code>FileSetting(文件名设置)</code>、<code>FileOutput(输出设置)</code>、<code>FileList(输出结果)</code>和<code>FileListItem(列表组件)</code>这么四大块</p><p>当然知道怎么拆分了还远远不够的，虽然现在我们只有 4 个组件，所以写起来问题不是那么的大，但是呢。。。写起页面来其实也是比较麻烦的，一般正常的写法是：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">html</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"content"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">divider</span> <span class="attr">orientation</span>=<span class="string">"left"</span>&gt;</span>文件名设置<span class="tag">&lt;/<span class="name">divider</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">FileSetting</span> <span class="attr">:fileSettings</span>=<span class="string">"fileSettings"</span> <span class="attr">:diyForm</span>=<span class="string">"diyForm"</span> /&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">divider</span> <span class="attr">orientation</span>=<span class="string">"left"</span>&gt;</span>输出设置<span class="tag">&lt;/<span class="name">divider</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">FileOutput</span> <span class="attr">:ext</span>=<span class="string">"ext"</span> <span class="attr">:enable</span>=<span class="string">"enable"</span> /&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">divider</span> <span class="attr">orientation</span>=<span class="string">"left"</span>&gt;</span>输出结果<span class="tag">&lt;/<span class="name">divider</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">FileList</span> <span class="attr">:oldFiles</span>=<span class="string">"oldFiles"</span> <span class="attr">:newFiles</span>=<span class="string">"newFiles"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="javascript">  <span class="keyword">import</span> &#123; Divider &#125; <span class="keyword">from</span> <span class="string">"ant-design-vue"</span>;</span></span><br><span class="line"><span class="javascript">  <span class="keyword">import</span> FileList <span class="keyword">from</span> <span class="string">"./FileList"</span>;</span></span><br><span class="line"><span class="javascript">  <span class="keyword">import</span> FileOutput <span class="keyword">from</span> <span class="string">"./FileOutput"</span>;</span></span><br><span class="line"><span class="javascript">  <span class="keyword">import</span> FileSetting <span class="keyword">from</span> <span class="string">"./FileSetting"</span>;</span></span><br><span class="line"><span class="javascript">  <span class="keyword">export</span> <span class="keyword">default</span> &#123;</span></span><br><span class="line"><span class="actionscript">    name: <span class="string">"ModifyFilename"</span>,</span></span><br><span class="line">    components: &#123;</span><br><span class="line">      Divider,</span><br><span class="line">      FileList,</span><br><span class="line">      FileOutput,</span><br><span class="line">      FileSetting,</span><br><span class="line">    &#125;,</span><br><span class="line">    computed: &#123;</span><br><span class="line"><span class="actionscript">      <span class="comment">// 新文件列表</span></span></span><br><span class="line">      newFiles() &#123;</span><br><span class="line"><span class="actionscript">        <span class="keyword">return</span> <span class="keyword">this</span>.oldFiles;</span></span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">    data() &#123;</span><br><span class="line"><span class="actionscript">      <span class="keyword">return</span> &#123;</span></span><br><span class="line"><span class="actionscript">        <span class="comment">// 存放这文件名设置的数据</span></span></span><br><span class="line">        fileSettings: &#123;&#125;,</span><br><span class="line"><span class="actionscript">        <span class="comment">// 存放自定义序号数组</span></span></span><br><span class="line">        diyForm: &#123;&#125;,</span><br><span class="line"><span class="actionscript">        <span class="comment">// 启用输出设置</span></span></span><br><span class="line"><span class="actionscript">        enable: <span class="literal">false</span>,</span></span><br><span class="line"><span class="actionscript">        <span class="comment">// 输出设置后缀名</span></span></span><br><span class="line">        ext: [],</span><br><span class="line"><span class="actionscript">        <span class="comment">// 原文件列表</span></span></span><br><span class="line">        oldFiles: [],</span><br><span class="line">      &#125;;</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">style</span> <span class="attr">lang</span>=<span class="string">"less"</span> <span class="attr">scoped</span>&gt;</span></span><br><span class="line"><span class="css">  <span class="selector-class">.content</span> &#123;</span></span><br><span class="line">    width: 1366px;</span><br><span class="line">    box-sizing: border-box;</span><br><span class="line">    padding: 0 15px;</span><br><span class="line">    margin: 0 auto;</span><br><span class="line">    overflow-x: hidden;</span><br><span class="line">  &#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br></pre></td></tr></table></figure></div><h3 id="思考一下"><a href="#思考一下" class="headerlink" title="思考一下"></a>思考一下</h3><p>但是有没有发现，我们写了三个<code>divider</code>组件，要绑定的数据也是相当之多，虽然我都整合在<code>fileSettings</code>了。如果我们要单独拿出来的话，岂不是要累死个人？所以我们思考一下，怎么可以更加方便的书写我们的这个页面。所以我引申出了下面三个问题：</p><ol><li><p>有没有办法可以用一个组件来标识我们导入的另外三个子组件呢？</p></li><li><p>有没有办法一次性绑定我们要的数据，而不是一个个的绑定呢？</p></li><li><p>一次性绑定之后，组件间怎么通信呢（因为这里涵盖了子孙组件）？</p></li></ol><p>针对于这三个问题，我分别使用了<code>动态组件</code>、<code>v-bind</code>和<code>provide</code>实现的，接下来我们就讲讲怎么实现它，先上代码：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">html</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"content"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">v-for</span>=<span class="string">"item in components"</span> <span class="attr">:key</span>=<span class="string">"item.name"</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">divider</span> <span class="attr">orientation</span>=<span class="string">"left"</span>&gt;</span>&#123;&#123; item.label &#125;&#125;<span class="tag">&lt;/<span class="name">divider</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">component</span></span></span><br><span class="line"><span class="tag">        <span class="attr">:is</span>=<span class="string">"item.name"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">v-bind</span>=<span class="string">"&#123; ...getProps(item.props) &#125;"</span></span></span><br><span class="line"><span class="tag">        @<span class="attr">update</span>=<span class="string">"</span></span></span><br><span class="line"><span class="tag"><span class="string">          (key, val) =&gt; &#123;</span></span></span><br><span class="line"><span class="tag"><span class="string">            update(item.props, key, val);</span></span></span><br><span class="line"><span class="tag"><span class="string">          &#125;</span></span></span><br><span class="line"><span class="tag"><span class="string">        "</span></span></span><br><span class="line"><span class="tag">      /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="javascript">  <span class="keyword">import</span> getNewFileList <span class="keyword">from</span> <span class="string">"@/utils/"</span>;</span></span><br><span class="line"><span class="javascript">  <span class="keyword">import</span> &#123; Divider &#125; <span class="keyword">from</span> <span class="string">"ant-design-vue"</span>;</span></span><br><span class="line"><span class="javascript">  <span class="keyword">import</span> FileList <span class="keyword">from</span> <span class="string">"./FileList"</span>;</span></span><br><span class="line"><span class="javascript">  <span class="keyword">import</span> FileOutput <span class="keyword">from</span> <span class="string">"./FileOutput"</span>;</span></span><br><span class="line"><span class="javascript">  <span class="keyword">import</span> FileSetting <span class="keyword">from</span> <span class="string">"./FileSetting"</span>;</span></span><br><span class="line"><span class="javascript">  <span class="keyword">export</span> <span class="keyword">default</span> &#123;</span></span><br><span class="line"><span class="actionscript">    name: <span class="string">"ModifyFilename"</span>,</span></span><br><span class="line">    components: &#123;</span><br><span class="line">      Divider,</span><br><span class="line">      FileList,</span><br><span class="line">      FileOutput,</span><br><span class="line">      FileSetting,</span><br><span class="line">    &#125;,</span><br><span class="line"><span class="actionscript">    <span class="comment">// 传递给深层级子组件</span></span></span><br><span class="line">    provide() &#123;</span><br><span class="line"><span class="actionscript">      <span class="keyword">return</span> &#123;</span></span><br><span class="line"><span class="actionscript">        parent: <span class="keyword">this</span>,</span></span><br><span class="line">      &#125;;</span><br><span class="line">    &#125;,</span><br><span class="line">    data() &#123;</span><br><span class="line"><span class="actionscript">      <span class="keyword">return</span> &#123;</span></span><br><span class="line">        components: [</span><br><span class="line">          &#123;</span><br><span class="line"><span class="actionscript">            label: <span class="string">"文件名设置"</span>,</span></span><br><span class="line"><span class="actionscript">            name: <span class="string">"FileSetting"</span>,</span></span><br><span class="line"><span class="actionscript">            props: <span class="string">"fileSettingsProps"</span>,</span></span><br><span class="line">          &#125;,</span><br><span class="line">          &#123;</span><br><span class="line"><span class="actionscript">            label: <span class="string">"输出设置"</span>,</span></span><br><span class="line"><span class="actionscript">            name: <span class="string">"FileOutput"</span>,</span></span><br><span class="line"><span class="actionscript">            props: <span class="string">"fileOutputProps"</span>,</span></span><br><span class="line">          &#125;,</span><br><span class="line">          &#123;</span><br><span class="line"><span class="actionscript">            label: <span class="string">"输出结果"</span>,</span></span><br><span class="line"><span class="actionscript">            name: <span class="string">"FileList"</span>,</span></span><br><span class="line"><span class="actionscript">            props: <span class="string">"fileListProps"</span>,</span></span><br><span class="line">          &#125;,</span><br><span class="line">        ],</span><br><span class="line">        fileSettingsProps: &#123;</span><br><span class="line">          fileSettings: &#123;</span><br><span class="line">            filename: &#123;</span><br><span class="line"><span class="actionscript">              value: <span class="string">""</span>,</span></span><br><span class="line">              span: 6,</span><br><span class="line"><span class="actionscript">              type: <span class="string">"file"</span>,</span></span><br><span class="line"><span class="actionscript">              placeholder: <span class="string">"请输入新的文件名"</span>,</span></span><br><span class="line">            &#125;,</span><br><span class="line">            serialNum: &#123;</span><br><span class="line"><span class="actionscript">              value: <span class="string">""</span>,</span></span><br><span class="line">              span: 6,</span><br><span class="line"><span class="actionscript">              type: <span class="string">"sort-descending"</span>,</span></span><br><span class="line"><span class="actionscript">              placeholder: <span class="string">"起始序号(默认支持纯数字或纯字母)"</span>,</span></span><br><span class="line">            &#125;,</span><br><span class="line">            increment: &#123;</span><br><span class="line">              value: 1,</span><br><span class="line">              span: 2,</span><br><span class="line"><span class="actionscript">              placeholder: <span class="string">"增量"</span>,</span></span><br><span class="line"><span class="actionscript">              isNum: <span class="literal">true</span>,</span></span><br><span class="line">            &#125;,</span><br><span class="line">            preReplaceWord: &#123;</span><br><span class="line"><span class="actionscript">              value: <span class="string">""</span>,</span></span><br><span class="line">              span: 3,</span><br><span class="line"><span class="actionscript">              type: <span class="string">"file"</span>,</span></span><br><span class="line"><span class="actionscript">              placeholder: <span class="string">"替换前的字符"</span>,</span></span><br><span class="line">            &#125;,</span><br><span class="line">            replaceWord: &#123;</span><br><span class="line"><span class="actionscript">              value: <span class="string">""</span>,</span></span><br><span class="line">              span: 3,</span><br><span class="line"><span class="actionscript">              type: <span class="string">"file"</span>,</span></span><br><span class="line"><span class="actionscript">              placeholder: <span class="string">"替换后的字符"</span>,</span></span><br><span class="line">            &#125;,</span><br><span class="line">          &#125;,</span><br><span class="line">          diyForm: &#123;</span><br><span class="line"><span class="actionscript">            diySerial: <span class="string">""</span>,</span></span><br><span class="line"><span class="actionscript">            separator: <span class="string">""</span>,</span></span><br><span class="line"><span class="actionscript">            diyEnable: <span class="literal">false</span>,</span></span><br><span class="line">          &#125;,</span><br><span class="line">        &#125;,</span><br><span class="line">        fileOutputProps: &#123;</span><br><span class="line"><span class="actionscript">          enable: <span class="literal">false</span>,</span></span><br><span class="line"><span class="actionscript">          ext: [<span class="string">""</span>, <span class="string">""</span>],</span></span><br><span class="line">        &#125;,</span><br><span class="line">        oldFiles: [],</span><br><span class="line">      &#125;;</span><br><span class="line">    &#125;,</span><br><span class="line">    computed: &#123;</span><br><span class="line">      newFiles() &#123;</span><br><span class="line"><span class="actionscript">        <span class="keyword">const</span> &#123; fileSettings, diyForm &#125; = <span class="keyword">this</span>.fileSettingsProps;</span></span><br><span class="line"><span class="actionscript">        <span class="keyword">const</span> &#123; ext, enable &#125; = <span class="keyword">this</span>.fileOutputProps;</span></span><br><span class="line"><span class="actionscript">        <span class="keyword">const</span> &#123; diySerial, separator, diyEnable &#125; = diyForm;</span></span><br><span class="line"><span class="actionscript">        <span class="keyword">return</span> getNewFileList(</span></span><br><span class="line"><span class="actionscript">          <span class="keyword">this</span>.oldFiles,</span></span><br><span class="line">          fileSettings,</span><br><span class="line">          ext,</span><br><span class="line">          enable,</span><br><span class="line"><span class="actionscript">          <span class="keyword">this</span>.getRange(diySerial, separator, diyEnable)</span></span><br><span class="line">        );</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">    watch: &#123;</span><br><span class="line"><span class="actionscript">      <span class="string">"fileSettingsProps.diyForm.diySerial"</span>(val) &#123;</span></span><br><span class="line">        if (!val) &#123;</span><br><span class="line"><span class="actionscript">          <span class="keyword">this</span>.fileSettingsProps.diyForm.diyEnable = !<span class="number">1</span>;</span></span><br><span class="line">        &#125;</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">    methods: &#123;</span><br><span class="line">      getRange(diySerial, separator, enable) &#123;</span><br><span class="line"><span class="actionscript">        <span class="keyword">if</span> (!enable) <span class="keyword">return</span> <span class="literal">null</span>;</span></span><br><span class="line"><span class="actionscript">        !separator ? (separator = <span class="string">","</span>) : <span class="literal">null</span>;</span></span><br><span class="line"><span class="actionscript">        <span class="keyword">return</span> diySerial.split(separator);</span></span><br><span class="line">      &#125;,</span><br><span class="line">      getProps(key) &#123;</span><br><span class="line"><span class="actionscript">        <span class="keyword">if</span> (key === <span class="string">"fileListProps"</span>) &#123;</span></span><br><span class="line"><span class="actionscript">          <span class="keyword">return</span> &#123;</span></span><br><span class="line"><span class="actionscript">            oldFiles: <span class="keyword">this</span>.oldFiles,</span></span><br><span class="line"><span class="actionscript">            newFiles: <span class="keyword">this</span>.newFiles,</span></span><br><span class="line">          &#125;;</span><br><span class="line">        &#125;</span><br><span class="line"><span class="actionscript">        <span class="keyword">return</span> <span class="keyword">this</span>[key] || &#123;&#125;;</span></span><br><span class="line">      &#125;,</span><br><span class="line">      update(props, key, val) &#123;</span><br><span class="line"><span class="actionscript">        <span class="keyword">if</span> (props === <span class="string">"fileListProps"</span>) &#123;</span></span><br><span class="line"><span class="actionscript">          <span class="keyword">return</span> (<span class="keyword">this</span>[key] = val);</span></span><br><span class="line">        &#125;</span><br><span class="line"><span class="actionscript">        <span class="keyword">this</span>[props][key] = val;</span></span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">style</span> <span class="attr">lang</span>=<span class="string">"less"</span> <span class="attr">scoped</span>&gt;</span></span><br><span class="line"><span class="css">  <span class="selector-class">.content</span> &#123;</span></span><br><span class="line">    width: 1366px;</span><br><span class="line">    box-sizing: border-box;</span><br><span class="line">    padding: 0 15px;</span><br><span class="line">    margin: 0 auto;</span><br><span class="line">    overflow-x: hidden;</span><br><span class="line">  &#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br></pre></td></tr></table></figure></div><p>代码里，我通过<code>component</code>的<code>is</code>来标识我们导入的组件，这样就解决了我们的第一个问题。第二个问题，从<a href="https://cn.vuejs.org/v2/api/#v-bind" target="_blank" rel="noopener">文档</a>可知，<code>v-bind</code>是可以绑定多个属性值，所以我们直接通过<code>v-bind</code>就可以实现了。<br>】<br>但是，解决第二个问题后，就引发了第三个问题，因为通常我们可以通过<code>.sync</code>修饰符来进行<code>props</code>的双向绑定，但是<a href="https://cn.vuejs.org/v2/guide/components-custom-events.html#sync-%E4%BF%AE%E9%A5%B0%E7%AC%A6" target="_blank" rel="noopener">文档</a>有说，在解析一个复杂表达式时是无法正常工作的，所以我们无法通过<code>this.$emit(&#39;update:props&#39;,newVal)</code>更新我们的值。</p><p>所以这里我自定义了一个<code>update</code>方法，通过<code>props</code>的方式传递给子组件，通过子组件触发父组件的方法实现状态的更新。当然，也通过<code>provide</code>把自身传递下去共子组件使用，这里提供<code>FileListItem(列表组件)</code>的代码供大家参考：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">html</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">a-list</span> <span class="attr">bordered</span> <span class="attr">:dataSource</span>=<span class="string">"fileList"</span> <span class="attr">:pagination</span>=<span class="string">"pagination"</span> <span class="attr">ref</span>=<span class="string">"list"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">slot</span>=<span class="string">"header"</span> <span class="attr">class</span>=<span class="string">"list-header"</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">strong</span>&gt;</span></span><br><span class="line">        &#123;&#123; filename &#125;&#125;</span><br><span class="line">      <span class="tag">&lt;/<span class="name">strong</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">a-button</span> <span class="attr">type</span>=<span class="string">"danger"</span> <span class="attr">size</span>=<span class="string">"small"</span> @<span class="attr">click</span>=<span class="string">"clearFiles"</span>&gt;</span></span><br><span class="line">        清空</span><br><span class="line">      <span class="tag">&lt;/<span class="name">a-button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">a-list-item</span> <span class="attr">slot</span>=<span class="string">"renderItem"</span> <span class="attr">slot-scope</span>=<span class="string">"item, index"</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">a-list-item-meta</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">a-tooltip</span> <span class="attr">slot</span>=<span class="string">"title"</span> <span class="attr">:overlayStyle</span>=<span class="string">"&#123; maxWidth: '500px' &#125;"</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">template</span> <span class="attr">slot</span>=<span class="string">"title"</span>&gt;</span></span><br><span class="line">            &#123;&#123; item.name &#125;&#125;</span><br><span class="line">          <span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line">          &#123;&#123; item.name &#125;&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">a-tooltip</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">a-list-item-meta</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">a-button</span></span></span><br><span class="line"><span class="tag">        <span class="attr">ghost</span></span></span><br><span class="line"><span class="tag">        <span class="attr">type</span>=<span class="string">"danger"</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">click</span>=<span class="string">"</span></span></span><br><span class="line"><span class="tag"><span class="string">          () =&gt; &#123;</span></span></span><br><span class="line"><span class="tag"><span class="string">            delCurrent(index);</span></span></span><br><span class="line"><span class="tag"><span class="string">          &#125;</span></span></span><br><span class="line"><span class="tag"><span class="string">        "</span></span></span><br><span class="line"><span class="tag">      &gt;</span></span><br><span class="line">        删除</span><br><span class="line">      <span class="tag">&lt;/<span class="name">a-button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">a-list-item</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">a-list</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="javascript">  <span class="keyword">import</span> &#123; List, Button, Tooltip &#125; <span class="keyword">from</span> <span class="string">"ant-design-vue"</span>;</span></span><br><span class="line"><span class="actionscript">  <span class="keyword">const</span> &#123; Item &#125; = List;</span></span><br><span class="line"><span class="javascript">  <span class="keyword">export</span> <span class="keyword">default</span> &#123;</span></span><br><span class="line"><span class="actionscript">    name: <span class="string">"FileListItem"</span>,</span></span><br><span class="line">    props: &#123;</span><br><span class="line">      fileList: &#123;</span><br><span class="line"><span class="javascript">        type: <span class="built_in">Array</span>,</span></span><br><span class="line"><span class="actionscript">        required: <span class="literal">true</span>,</span></span><br><span class="line">      &#125;,</span><br><span class="line">      filename: &#123;</span><br><span class="line"><span class="javascript">        type: <span class="built_in">String</span>,</span></span><br><span class="line"><span class="actionscript">        required: <span class="literal">true</span>,</span></span><br><span class="line">      &#125;,</span><br><span class="line">      pagination: &#123;</span><br><span class="line"><span class="javascript">        type: <span class="built_in">Object</span>,</span></span><br><span class="line"><span class="javascript">        <span class="keyword">default</span>: <span class="function"><span class="params">()</span> =&gt;</span> (&#123;</span></span><br><span class="line">          pageSize: 10,</span><br><span class="line"><span class="actionscript">          showQuickJumper: <span class="literal">true</span>,</span></span><br><span class="line"><span class="actionscript">          hideOnSinglePage: <span class="literal">true</span>,</span></span><br><span class="line">        &#125;),</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line"><span class="actionscript">    inject: [<span class="string">"parent"</span>],</span></span><br><span class="line">    components: &#123;</span><br><span class="line"><span class="actionscript">      <span class="string">"a-list"</span>: List,</span></span><br><span class="line"><span class="actionscript">      <span class="string">"a-list-item"</span>: Item,</span></span><br><span class="line"><span class="actionscript">      <span class="string">"a-list-item-meta"</span>: Item.Meta,</span></span><br><span class="line"><span class="actionscript">      <span class="string">"a-button"</span>: Button,</span></span><br><span class="line"><span class="actionscript">      <span class="string">"a-tooltip"</span>: Tooltip,</span></span><br><span class="line">    &#125;,</span><br><span class="line">    methods: &#123;</span><br><span class="line">      delCurrent(current) &#123;</span><br><span class="line"><span class="actionscript">        <span class="keyword">this</span>.parent.oldFiles.splice(current, <span class="number">1</span>);</span></span><br><span class="line">      &#125;,</span><br><span class="line">      clearFiles() &#123;</span><br><span class="line"><span class="actionscript">        <span class="keyword">this</span>.parent.update(<span class="string">"fileListProps"</span>, <span class="string">"oldFiles"</span>, []);</span></span><br><span class="line">      &#125;,</span><br><span class="line">      drop(e) &#123;</span><br><span class="line">        e.preventDefault();</span><br><span class="line"><span class="actionscript">        <span class="keyword">this</span>.parent.update(<span class="string">"fileListProps"</span>, <span class="string">"oldFiles"</span>, [</span></span><br><span class="line">          ...this.parent.oldFiles,</span><br><span class="line">          ...e.dataTransfer.files,</span><br><span class="line">        ]);</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">    mounted() &#123;</span><br><span class="line"><span class="javascript">      <span class="keyword">let</span> $el = <span class="keyword">this</span>.$refs.list.$el;</span></span><br><span class="line"><span class="actionscript">      <span class="keyword">this</span>.$el = $el;</span></span><br><span class="line">      if ($el) &#123;</span><br><span class="line"><span class="javascript">        $el.ondragenter = $el.ondragover = $el.ondragleave = <span class="function"><span class="params">()</span> =&gt;</span> <span class="literal">false</span>;</span></span><br><span class="line"><span class="actionscript">        $el.addEventListener(<span class="string">"drop"</span>, <span class="keyword">this</span>.drop, <span class="literal">false</span>);</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    destroyed() &#123;</span><br><span class="line"><span class="actionscript">      <span class="keyword">this</span>.$el &amp;&amp; <span class="keyword">this</span>.$el.removeEventListener(<span class="string">"drop"</span>, <span class="keyword">this</span>.drop, <span class="literal">false</span>);</span></span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">style</span> <span class="attr">lang</span>=<span class="string">"less"</span> <span class="attr">scoped</span>&gt;</span></span><br><span class="line"><span class="css">  <span class="selector-class">.list-header</span> &#123;</span></span><br><span class="line">    display: flex;</span><br><span class="line">    justify-content: space-between;</span><br><span class="line">    align-items: center;</span><br><span class="line">  &#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br></pre></td></tr></table></figure></div><p>这里我们看到，可以直接调用<strong>父组件</strong>身上的方法来进行数据的更新（当然这里也可以用<code>splice</code>更新数据），这样也就解决了我们之前的三个问题啦。</p><blockquote><p><code>react</code>，<code>react</code>可是很常用扩展运算符传属性的哦，虽然是<code>jsx</code>都可以 😝 但是我们通过<code>v-bind</code>也可以绑定复杂属性，知识点哦(●’◡’●)</p></blockquote><h4 id="Antd-的坑"><a href="#Antd-的坑" class="headerlink" title="Antd 的坑"></a>Antd 的坑</h4><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-07-05/batch-modify-filenames05.png" data-fancybox="group" data-caption="自定义序号" class="fancybox"><img alt="自定义序号" title="自定义序号" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-07-05/batch-modify-filenames05.png" class="lazyload"></a></p><p>因为我自定义序号采用的事弹窗，又因为我们采用的是按需加载，在<code>ant-design-vue@1.6.2</code>版本中会报<strong>Failed to resolve directive: ant-portal</strong>无法解析指令的错误，所以我们需要在<code>main.js</code>中全局注册他，然后因为我们请求可能会用<code>message</code>，所以我顺便也把<code>message</code>放到<code>vue</code>的原型链上了，即：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> Vue <span class="keyword">from</span> <span class="string">"vue"</span>;</span><br><span class="line"><span class="keyword">import</span> App <span class="keyword">from</span> <span class="string">"./App.vue"</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; Message, Modal &#125; <span class="keyword">from</span> <span class="string">"ant-design-vue"</span>;</span><br><span class="line"><span class="keyword">import</span> axios <span class="keyword">from</span> <span class="string">"axios"</span>;</span><br><span class="line"><span class="keyword">import</span> VueAxios <span class="keyword">from</span> <span class="string">"vue-axios"</span>;</span><br><span class="line">Vue.config.productionTip = <span class="literal">false</span>;</span><br><span class="line">Vue.use(VueAxios, axios);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Failed to resolve directive: ant-portal</span></span><br><span class="line"><span class="comment">// https://github.com/vueComponent/ant-design-vue/issues/2261</span></span><br><span class="line">Vue.use(Modal);</span><br><span class="line">Vue.prototype.$message = Message;</span><br><span class="line"><span class="keyword">new</span> Vue(&#123;</span><br><span class="line">  render: <span class="function">(<span class="params">h</span>) =&gt;</span> h(App),</span><br><span class="line">&#125;).$mount(<span class="string">"#app"</span>);</span><br></pre></td></tr></table></figure></div><p>这样子就可以愉快的使用我们的<code>Modal</code>了，然后到了组件选型上了，一开始我选的组件时<code>Form</code>组件，但是写着写着发现我们有个<code>自定义序号</code>和<code>是否启用自定义</code>相关联，而且<code>Form</code>组件如果是必选的话，只能通过<code>v-decorator</code>指令的<code>rules</code>实现绑定数据和必选，不能通过<code>v-model</code>进行数据的双向绑定(不能偷懒)。</p><p>因为我们的<code>ant-design-vue</code>版本已经是<code>1.5.0+</code>，而<code>FormModel</code>组件也支持支持<code>v-model</code>检验，那么就更符合我们的需求啦，所以我这里改了下我的代码，使用<code>FormModel</code>组件实现我们的需求了：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">html</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">a-modal</span></span></span><br><span class="line"><span class="tag">    <span class="attr">title</span>=<span class="string">"自定义序号"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">:visible</span>=<span class="string">"serialNumVisible"</span></span></span><br><span class="line"><span class="tag">    @<span class="attr">cancel</span>=<span class="string">"serialNumVisible = !1"</span></span></span><br><span class="line"><span class="tag">    @<span class="attr">ok</span>=<span class="string">"handleDiySerialNum"</span></span></span><br><span class="line"><span class="tag">  &gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">a-form-model</span></span></span><br><span class="line"><span class="tag">      <span class="attr">ref</span>=<span class="string">"diyForm"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">:model</span>=<span class="string">"diyForm"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">:rules</span>=<span class="string">"rules"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">labelAlign</span>=<span class="string">"left"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">:label-col</span>=<span class="string">"&#123; span: 6 &#125;"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">:wrapper-col</span>=<span class="string">"&#123; span: 18 &#125;"</span></span></span><br><span class="line"><span class="tag">    &gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">a-form-model-item</span> <span class="attr">label</span>=<span class="string">"自定义序号"</span> <span class="attr">prop</span>=<span class="string">"diySerial"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">a-input</span></span></span><br><span class="line"><span class="tag">          <span class="attr">v-model</span>=<span class="string">"diyForm.diySerial"</span></span></span><br><span class="line"><span class="tag">          <span class="attr">placeholder</span>=<span class="string">"请输入自定义序号"</span></span></span><br><span class="line"><span class="tag">          <span class="attr">aria-placeholder</span>=<span class="string">"请输入自定义序号"</span></span></span><br><span class="line"><span class="tag">        /&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">a-form-model-item</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">a-form-model-item</span> <span class="attr">label</span>=<span class="string">"自定义分隔符"</span> <span class="attr">prop</span>=<span class="string">"separator"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">a-input</span></span></span><br><span class="line"><span class="tag">          <span class="attr">v-model</span>=<span class="string">"diyForm.separator"</span></span></span><br><span class="line"><span class="tag">          <span class="attr">placeholder</span>=<span class="string">"请输入自定义序号分隔符(默认,)"</span></span></span><br><span class="line"><span class="tag">          <span class="attr">aria-placeholder</span>=<span class="string">"请输入自定义序号分隔符"</span></span></span><br><span class="line"><span class="tag">        /&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">a-form-model-item</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">a-form-model-item</span> <span class="attr">label</span>=<span class="string">"是否启用自定义"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">a-switch</span> <span class="attr">v-model</span>=<span class="string">"diyForm.diyEnable"</span> <span class="attr">:disabled</span>=<span class="string">"disabled"</span> /&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">a-form-model-item</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">a-form-model</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">a-modal</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br></pre></td></tr></table></figure></div><blockquote><p>ps：果然，懒人还是推动技术进步的最主要的动力啊 😂</p></blockquote><h4 id="post-请求下载文件"><a href="#post-请求下载文件" class="headerlink" title="post 请求下载文件"></a>post 请求下载文件</h4><p>因为我之前说过，我们后端不想保留返回的<code>zip</code>文件，所以我们是以<code>流(Stream)</code>传递给前端的，那我们怎么实现在个功能呢？</p><p>其实，还是挺简单的。主要是后端设置两个请求头，分别是<code>Content-Type</code>和<code>Content-Disposition</code>，一个告诉浏览器是什么类型，一个是告诉要以附件的形式下载，并指明默认文件名。</p><p><code>Content-Type</code>我想大家都很常见了吧，而且也不用我们处理了，所以这里我们讲讲怎么处理<code>Content-Disposition</code>，即获取默认的文件名，如图所示：</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-07-05/batch-modify-filenames06.png" data-fancybox="group" data-caption="Content-Disposition响应头" class="fancybox"><img alt="Content-Disposition响应头" title="Content-Disposition响应头" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-07-05/batch-modify-filenames06.png" class="lazyload"></a></p><p>从图可以看出，响应头信息为<code>content-disposition: attachment; filename=&quot;files.zip&quot;</code>。看到这个字符串，我们第一眼可能会想到通过<code>split</code>方法分割<code>=</code>然后下标取<code>1</code>就可以获取文件名了。但是发现了吗？我们获取的文件名是<code>&quot;files.zip&quot;</code>，与我们想要的结果不同，虽然我们可以通过切割来实现获取到<code>files.zip</code>，但是假设有一天服务器返回的不带<code>&quot;</code>就不通用了。</p><p>那怎么办呢？没错啦，就是通过正则并搭配字符串的<code>replace</code>方法来获取啦~~当然，正则不是本篇的重点，所以就不讲正则怎么写了，接下来书写我们的方法：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 获取content-disposition响应头的默认文件名</span></span><br><span class="line"><span class="keyword">const</span> getFileName = <span class="function">(<span class="params">str</span>) =&gt;</span> str.replace(<span class="regexp">/^.*filename="?([^"]+)"?.*$/</span>, <span class="string">"$1"</span>);</span><br><span class="line"><span class="keyword">const</span> str = <span class="string">`content-disposition: attachment; filename=files.zip`</span>;</span><br><span class="line"><span class="keyword">const</span> doubleStr = <span class="string">`content-disposition: attachment; filename="files.zip"`</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(getFileName(str)); <span class="comment">// files.zip</span></span><br><span class="line"><span class="built_in">console</span>.log(getFileName(doubleStr)); <span class="comment">// files.zip</span></span><br></pre></td></tr></table></figure></div><p>看输出的是不是和预期的一样？如果一样，这里就实现了我们的获取用户名的方法了，主要用到的就是<code>正则</code>和<code>replace</code>的特殊变量名</p><blockquote><p>ps：不知道<code>repalce</code>的<strong>搞基（高级）</strong>用法的请看<a href="https://gatings.cn/2019-01-09/%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E6%96%B9%E6%B3%95%E7%9A%84%E6%80%BB%E7%BB%93%E5%92%8C%E4%BD%BF%E7%94%A8/#newSubStr-%E7%9A%84%E7%89%B9%E6%AE%8A%E5%8F%98%E9%87%8F%E5%90%8D">请点击我</a>，这里就不阐述啦</p></blockquote><p>当然，写到这里其实如果是<code>get</code>请求，那么浏览器会默认就下载文件了，我们也不用获取文件名。但是我们是<code>post</code>请求，所以我们需要处理这一系列的东西，并且期待<code>responseType</code>是<code>blob</code>类型，所以我们就写一下前端怎么请求后端并下载文件的。</p><p>既然要写前端代码，那么就要先和后端约定接口是什么，这里因为后端也是自己写的，所以我们暂且把接口定义为<code>http://localhost:3000/upload</code>，又因为我们这里<code>vue</code>的端口在<code>8080</code>，肯定会和我们的后端跨域，所以我们需要在<code>vue.config.js</code>，配置一下我们的代理，即：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// 静态资源导入的路径</span></span><br><span class="line">  publicPath: <span class="string">"./"</span>,</span><br><span class="line">  <span class="comment">// 输出目录，因为这里我们Node设置本上级public为静态服务</span></span><br><span class="line">  <span class="comment">// 实际设置成koa-static设置batch-front-end/dist为静态目录的话就不要修改了</span></span><br><span class="line">  <span class="comment">// 具体看自己变通</span></span><br><span class="line">  outputDir: <span class="string">"../public"</span>,</span><br><span class="line">  <span class="comment">// 生产环境下不输出.map文件</span></span><br><span class="line">  productionSourceMap: <span class="literal">false</span>,</span><br><span class="line">  devServer: &#123;</span><br><span class="line">    <span class="comment">// 自动打开浏览器</span></span><br><span class="line">    open: <span class="literal">true</span>,</span><br><span class="line">    <span class="comment">// 配置代理</span></span><br><span class="line">    proxy: &#123;</span><br><span class="line">      <span class="string">"/upload"</span>: &#123;</span><br><span class="line">        target: <span class="string">"http://localhost:3000"</span>,</span><br><span class="line">        <span class="comment">// 发送请求头中host会设置成target</span></span><br><span class="line">        changeOrigin: <span class="literal">true</span>,</span><br><span class="line">        <span class="comment">// 路径重写</span></span><br><span class="line">        pathRewrite: &#123;</span><br><span class="line">          <span class="string">"^/upload"</span>: <span class="string">"/upload"</span>,</span><br><span class="line">        &#125;,</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">  css: &#123;</span><br><span class="line">    loaderOptions: &#123;</span><br><span class="line">      less: &#123;</span><br><span class="line">        javascriptEnabled: <span class="literal">true</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><p>这样子我们就可以跨域请求我们的接口啦，既然说到了下载文件，我们就简单看下<code>file-saver</code>是怎么使用的，从<a href="https://www.npmjs.com/package/file-saver" target="_blank" rel="noopener">官方实例</a>来看：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; saveAs &#125; <span class="keyword">from</span> <span class="string">"file-saver"</span>;</span><br><span class="line"><span class="keyword">const</span> blob = <span class="keyword">new</span> Blob([<span class="string">"Hello, world!"</span>], &#123; <span class="attr">type</span>: <span class="string">"text/plain;charset=utf-8"</span> &#125;);</span><br><span class="line">saveAs(blob, <span class="string">"hello world.txt"</span>);</span><br></pre></td></tr></table></figure></div><p>从示例来看，它可以直接保存<code>blob</code>，并指定文件名为<code>hello world.txt</code>，知道这个之后我们就可以书写我们的代码啦：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; saveAs &#125; <span class="keyword">from</span> <span class="string">"file-saver"</span>;</span><br><span class="line"><span class="keyword">this</span>.axios(&#123;</span><br><span class="line">  method: <span class="string">"post"</span>,</span><br><span class="line">  url: <span class="string">"/upload"</span>,</span><br><span class="line">  data,</span><br><span class="line">  <span class="comment">// 重要，告诉浏览器响应的类型为blob</span></span><br><span class="line">  responseType: <span class="string">"blob"</span>,</span><br><span class="line">&#125;)</span><br><span class="line">  .then(<span class="function">(<span class="params">res</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> disposition = res.headers[<span class="string">"content-disposition"</span>];</span><br><span class="line">    <span class="comment">// 转换为Blob对象</span></span><br><span class="line">    <span class="keyword">let</span> file = <span class="keyword">new</span> Blob([res.data], &#123;</span><br><span class="line">      type: <span class="string">"application/zip"</span>,</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="comment">// 下载文件</span></span><br><span class="line">    saveAs(file, getFileName(disposition));</span><br><span class="line">    <span class="keyword">this</span>.$message.success(<span class="string">"修改成功"</span>);</span><br><span class="line">  &#125;)</span><br><span class="line">  .catch(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">this</span>.$message.error(<span class="string">"发生错误"</span>);</span><br><span class="line">  &#125;);</span><br></pre></td></tr></table></figure></div><p>基本上，这样就可以实现下载文件啦，下面是<code>FileSetting.vue</code>源码，仅供参考：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">html</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">a-row</span> <span class="attr">type</span>=<span class="string">"flex"</span> <span class="attr">:gutter</span>=<span class="string">"16"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">a-col</span></span></span><br><span class="line"><span class="tag">      <span class="attr">:key</span>=<span class="string">"key"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">:span</span>=<span class="string">"setting.span"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">v-for</span>=<span class="string">"(setting, key) in fileSettings"</span></span></span><br><span class="line"><span class="tag">    &gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">template</span> <span class="attr">v-if</span>=<span class="string">"setting.isNum"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">a-input-number</span></span></span><br><span class="line"><span class="tag">          <span class="attr">style</span>=<span class="string">"width:100%"</span></span></span><br><span class="line"><span class="tag">          <span class="attr">:placeholder</span>=<span class="string">"setting.placeholder"</span></span></span><br><span class="line"><span class="tag">          <span class="attr">:min</span>=<span class="string">"1"</span></span></span><br><span class="line"><span class="tag">          <span class="attr">v-model</span>=<span class="string">"setting.value"</span></span></span><br><span class="line"><span class="tag">        /&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">template</span> <span class="attr">v-else</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">a-input</span></span></span><br><span class="line"><span class="tag">          <span class="attr">:placeholder</span>=<span class="string">"setting.placeholder"</span></span></span><br><span class="line"><span class="tag">          <span class="attr">v-model</span>=<span class="string">"setting.value"</span></span></span><br><span class="line"><span class="tag">          <span class="attr">allowClear</span></span></span><br><span class="line"><span class="tag">        &gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">a-icon</span></span></span><br><span class="line"><span class="tag">            <span class="attr">slot</span>=<span class="string">"prefix"</span></span></span><br><span class="line"><span class="tag">            <span class="attr">:type</span>=<span class="string">"setting.type"</span></span></span><br><span class="line"><span class="tag">            <span class="attr">style</span>=<span class="string">"color:rgba(0,0,0,.25)"</span></span></span><br><span class="line"><span class="tag">          /&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">a-input</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">a-col</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">a-col</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">a-button</span> @<span class="attr">click</span>=<span class="string">"serialNumVisible = !0"</span>&gt;</span></span><br><span class="line">        自定义序号</span><br><span class="line">      <span class="tag">&lt;/<span class="name">a-button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">a-col</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">a-col</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">a-button</span> <span class="attr">type</span>=<span class="string">"primary"</span> @<span class="attr">click</span>=<span class="string">"handleModify"</span>&gt;</span></span><br><span class="line">        确定修改</span><br><span class="line">      <span class="tag">&lt;/<span class="name">a-button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">a-col</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">a-modal</span></span></span><br><span class="line"><span class="tag">      <span class="attr">title</span>=<span class="string">"自定义序号"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">:visible</span>=<span class="string">"serialNumVisible"</span></span></span><br><span class="line"><span class="tag">      @<span class="attr">cancel</span>=<span class="string">"serialNumVisible = !1"</span></span></span><br><span class="line"><span class="tag">      @<span class="attr">ok</span>=<span class="string">"handleDiySerialNum"</span></span></span><br><span class="line"><span class="tag">    &gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">a-form-model</span></span></span><br><span class="line"><span class="tag">        <span class="attr">ref</span>=<span class="string">"diyForm"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">:model</span>=<span class="string">"diyForm"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">:rules</span>=<span class="string">"rules"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">labelAlign</span>=<span class="string">"left"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">:label-col</span>=<span class="string">"&#123; span: 6 &#125;"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">:wrapper-col</span>=<span class="string">"&#123; span: 18 &#125;"</span></span></span><br><span class="line"><span class="tag">      &gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">a-form-model-item</span> <span class="attr">label</span>=<span class="string">"自定义序号"</span> <span class="attr">prop</span>=<span class="string">"diySerial"</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">a-input</span></span></span><br><span class="line"><span class="tag">            <span class="attr">v-model</span>=<span class="string">"diyForm.diySerial"</span></span></span><br><span class="line"><span class="tag">            <span class="attr">placeholder</span>=<span class="string">"请输入自定义序号"</span></span></span><br><span class="line"><span class="tag">            <span class="attr">aria-placeholder</span>=<span class="string">"请输入自定义序号"</span></span></span><br><span class="line"><span class="tag">          /&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">a-form-model-item</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">a-form-model-item</span> <span class="attr">label</span>=<span class="string">"自定义分隔符"</span> <span class="attr">prop</span>=<span class="string">"separator"</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">a-input</span></span></span><br><span class="line"><span class="tag">            <span class="attr">v-model</span>=<span class="string">"diyForm.separator"</span></span></span><br><span class="line"><span class="tag">            <span class="attr">placeholder</span>=<span class="string">"请输入自定义序号分隔符(默认,)"</span></span></span><br><span class="line"><span class="tag">            <span class="attr">aria-placeholder</span>=<span class="string">"请输入自定义序号分隔符"</span></span></span><br><span class="line"><span class="tag">          /&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">a-form-model-item</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">a-form-model-item</span> <span class="attr">label</span>=<span class="string">"是否启用自定义"</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">a-switch</span> <span class="attr">v-model</span>=<span class="string">"diyForm.diyEnable"</span> <span class="attr">:disabled</span>=<span class="string">"disabled"</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">a-form-model-item</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">a-form-model</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">a-modal</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">a-row</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="actionscript">  <span class="meta"><span class="meta-keyword">import</span> &#123;</span></span></span><br><span class="line"><span class="actionscript">    Row <span class="keyword">as</span> ARow,</span></span><br><span class="line"><span class="actionscript">    Col <span class="keyword">as</span> ACol,</span></span><br><span class="line"><span class="actionscript">    Icon <span class="keyword">as</span> AIcon,</span></span><br><span class="line"><span class="actionscript">    Input <span class="keyword">as</span> AInput,</span></span><br><span class="line"><span class="actionscript">    Switch <span class="keyword">as</span> ASwitch,</span></span><br><span class="line"><span class="actionscript">    Button <span class="keyword">as</span> AButton,</span></span><br><span class="line"><span class="actionscript">    InputNumber <span class="keyword">as</span> AInputNumber,</span></span><br><span class="line"><span class="actionscript">    FormModel <span class="keyword">as</span> AFormModel,</span></span><br><span class="line"><span class="javascript">  &#125; <span class="keyword">from</span> <span class="string">"ant-design-vue"</span>;</span></span><br><span class="line"><span class="javascript">  <span class="keyword">import</span> &#123; saveAs &#125; <span class="keyword">from</span> <span class="string">"file-saver"</span>;</span></span><br><span class="line"><span class="actionscript">  <span class="comment">// 是否符合默认序号规范</span></span></span><br><span class="line"><span class="javascript">  <span class="keyword">import</span> &#123; isDefaultSerialNum &#125; <span class="keyword">from</span> <span class="string">"@/utils/regexp"</span>;</span></span><br><span class="line"><span class="actionscript">  <span class="keyword">const</span> AFormModelItem = AFormModel.Item;</span></span><br><span class="line"></span><br><span class="line"><span class="actionscript">  <span class="comment">// 获取content-disposition响应头的默认文件名</span></span></span><br><span class="line"><span class="javascript">  <span class="keyword">const</span> getFileName = <span class="function">(<span class="params">str</span>) =&gt;</span> str.replace(<span class="regexp">/^.*filename="?([^"]+)"?.*$/</span>, <span class="string">"$1"</span>);</span></span><br><span class="line"></span><br><span class="line"><span class="javascript">  <span class="keyword">export</span> <span class="keyword">default</span> &#123;</span></span><br><span class="line"><span class="actionscript">    name: <span class="string">"FileSetting"</span>,</span></span><br><span class="line">    props: &#123;</span><br><span class="line">      fileSettings: &#123;</span><br><span class="line"><span class="javascript">        type: <span class="built_in">Object</span>,</span></span><br><span class="line"><span class="actionscript">        required: <span class="literal">true</span>,</span></span><br><span class="line">      &#125;,</span><br><span class="line">      diyForm: &#123;</span><br><span class="line"><span class="javascript">        type: <span class="built_in">Object</span>,</span></span><br><span class="line"><span class="actionscript">        required: <span class="literal">true</span>,</span></span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">    components: &#123;</span><br><span class="line">      ARow,</span><br><span class="line">      ACol,</span><br><span class="line">      AIcon,</span><br><span class="line">      AInput,</span><br><span class="line">      ASwitch,</span><br><span class="line">      AButton,</span><br><span class="line">      AInputNumber,</span><br><span class="line">      AFormModel,</span><br><span class="line">      AFormModelItem,</span><br><span class="line">    &#125;,</span><br><span class="line"><span class="actionscript">    inject: [<span class="string">"parent"</span>],</span></span><br><span class="line"><span class="actionscript">    <span class="comment">// 没有自定义序号时不可操作</span></span></span><br><span class="line">    computed: &#123;</span><br><span class="line">      disabled() &#123;</span><br><span class="line"><span class="actionscript">        <span class="keyword">return</span> !<span class="keyword">this</span>.diyForm.diySerial;</span></span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">    data() &#123;</span><br><span class="line"><span class="actionscript">      <span class="keyword">return</span> &#123;</span></span><br><span class="line">        serialNumVisible: !1,</span><br><span class="line">        rules: &#123;</span><br><span class="line">          diySerial: [</span><br><span class="line">            &#123;</span><br><span class="line"><span class="actionscript">              required: <span class="literal">true</span>,</span></span><br><span class="line"><span class="actionscript">              message: <span class="string">"请输入自定义序号"</span>,</span></span><br><span class="line"><span class="actionscript">              trigger: <span class="string">"blur"</span>,</span></span><br><span class="line">            &#125;,</span><br><span class="line">          ],</span><br><span class="line">        &#125;,</span><br><span class="line">      &#125;;</span><br><span class="line">    &#125;,</span><br><span class="line">    methods: &#123;</span><br><span class="line">      handleModify() &#123;</span><br><span class="line"><span class="actionscript">        <span class="comment">// 获取填写的序号</span></span></span><br><span class="line"><span class="actionscript">        <span class="keyword">const</span> serialNum = <span class="keyword">this</span>.fileSettings.serialNum.value;</span></span><br><span class="line"><span class="actionscript">        <span class="comment">// 当没有启用自定义时，走默认规则</span></span></span><br><span class="line"><span class="actionscript">        <span class="keyword">if</span> (isDefaultSerialNum(serialNum) &amp;&amp; !<span class="keyword">this</span>.diyForm.enable) &#123;</span></span><br><span class="line"><span class="actionscript">          <span class="keyword">return</span> <span class="keyword">this</span>.$message.error(<span class="string">"请输入正确的序号，格式为纯数字或纯字母"</span>);</span></span><br><span class="line">        &#125;</span><br><span class="line"><span class="actionscript">        <span class="keyword">const</span> &#123; newFiles, oldFiles &#125; = <span class="keyword">this</span>.parent;</span></span><br><span class="line"><span class="actionscript">        <span class="keyword">const</span> data = <span class="keyword">new</span> FormData();</span></span><br><span class="line"><span class="javascript">        <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; oldFiles.length; i++) &#123;</span></span><br><span class="line"><span class="actionscript">          <span class="keyword">const</span> &#123; name &#125; = newFiles[i];</span></span><br><span class="line"><span class="actionscript">          data.append(<span class="string">"files"</span>, oldFiles[i]);</span></span><br><span class="line"><span class="actionscript">          data.append(<span class="string">"name"</span>, name);</span></span><br><span class="line">        &#125;</span><br><span class="line"><span class="actionscript">        <span class="keyword">this</span>.axios(&#123;</span></span><br><span class="line"><span class="actionscript">          method: <span class="string">"post"</span>,</span></span><br><span class="line"><span class="actionscript">          url: <span class="string">"/upload"</span>,</span></span><br><span class="line">          data,</span><br><span class="line"><span class="actionscript">          responseType: <span class="string">"blob"</span>,</span></span><br><span class="line">        &#125;)</span><br><span class="line"><span class="javascript">          .then(<span class="function">(<span class="params">res</span>) =&gt;</span> &#123;</span></span><br><span class="line"><span class="actionscript">            <span class="keyword">const</span> disposition = res.headers[<span class="string">"content-disposition"</span>];</span></span><br><span class="line"><span class="actionscript">            <span class="comment">// 转换为Blob对象</span></span></span><br><span class="line"><span class="javascript">            <span class="keyword">let</span> file = <span class="keyword">new</span> Blob([res.data], &#123;</span></span><br><span class="line"><span class="actionscript">              type: <span class="string">"application/zip"</span>,</span></span><br><span class="line">            &#125;);</span><br><span class="line"><span class="actionscript">            <span class="comment">// 下载文件</span></span></span><br><span class="line">            saveAs(file, getFileName(disposition));</span><br><span class="line"><span class="actionscript">            <span class="keyword">this</span>.$message.success(<span class="string">"修改成功"</span>);</span></span><br><span class="line">          &#125;)</span><br><span class="line"><span class="javascript">          .catch(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span></span><br><span class="line"><span class="actionscript">            <span class="keyword">this</span>.$message.error(<span class="string">"发生错误"</span>);</span></span><br><span class="line">          &#125;);</span><br><span class="line">      &#125;,</span><br><span class="line">      handleDiySerialNum() &#123;</span><br><span class="line"><span class="javascript">        <span class="keyword">this</span>.$refs.diyForm.validate(<span class="function">(<span class="params">valid</span>) =&gt;</span> &#123;</span></span><br><span class="line">          if (!valid) &#123;</span><br><span class="line"><span class="actionscript">            <span class="keyword">return</span> <span class="literal">false</span>;</span></span><br><span class="line">          &#125;</span><br><span class="line"><span class="actionscript">          <span class="keyword">this</span>.serialNumVisible = !<span class="number">1</span>;</span></span><br><span class="line">        &#125;);</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure></div><p>至此，和后端交互的逻辑基本上已经写完了，但是- -我们好像还没有写前端页面的实际逻辑，那么接下来就开始写前端逻辑啦，可能比较啰嗦- -So Sorry😥</p><blockquote><p>ps：webpack 是开发解决跨域问题，线上该跨域还是要跨域，最好的方法是<code>cors</code>或者<code>proxy</code>，再不然就是放在<code>node</code>的静态服务里。</p></blockquote><h2 id="书写前端逻辑"><a href="#书写前端逻辑" class="headerlink" title="书写前端逻辑"></a>书写前端逻辑</h2><p>从之前的图来看，很显然我们的逻辑就是处理<code>新文件列表</code>这个数据，而这个数据则是根据页面其他组件的值来实现的，所以我们之前用了<strong>计算属性</strong>来实现，但是我们逻辑却还没写，所以接下来就是处理这个最重要的逻辑啦。</p><p>不过好像写好了界面，却还没有说，我想实现什么东西，所以简单的说一下我们界面的交互，我们再开始写逻辑吧。</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-07-05/batch-modify-filenames07.png" data-fancybox="group" data-caption="前端交互" class="fancybox"><img alt="前端交互" title="前端交互" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-07-05/batch-modify-filenames07.png" class="lazyload"></a></p><p>通过图片，我们大概可以知道有这些操作：</p><ol><li><p>我们可以通过输入文件名，批量修改所有的文件名</p></li><li><p>通过序号，让文件名后面添加后缀，默认支持纯数字和纯字母，即输入<code>test1+001</code>则输出<code>test1001</code></p></li><li><p>通过增量，我们可以让文件的后缀加上增量的值，即加设增量为 2，这里的下一个就是<code>test1+00(1+2)</code>，为<code>test1003</code></p></li><li><p>通过输入<code>需要替换的字符-test</code>和<code>替换的字符-测试</code>，把所有文件的名称修改替换为<code>测试1+001</code>，即<code>测试1001</code></p></li><li><p>通过输入<code>需要修改的后缀名-png</code>和<code>替换的字符-txt</code>并打开修改开关，把所有符合<code>png</code>后缀名的都修改为<code>txt</code>，因为这里只有一个，所以修改为<code>测试1001.txt</code>，即<code>test1001.png-&gt;测试1001.png-&gt;测试1001.txt</code></p></li></ol><p>而启用自定义序号之后，还有后续操作，如图所示：</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-07-05/batch-modify-filenames07.png" data-fancybox="group" data-caption="启用自定义序号" class="fancybox"><img alt="启用自定义序号" title="启用自定义序号" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-07-05/batch-modify-filenames07.png" class="lazyload"></a></p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-07-05/batch-modify-filenames07.png" data-fancybox="group" data-caption="前端交互" class="fancybox"><img alt="前端交互" title="前端交互" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-07-05/batch-modify-filenames07.png" class="lazyload"></a></p><ol start="6"><li><p>输入<code>g,a,t,i,n,g</code>这个自定义序号化时，我们的序号的值就应该为<code>[&quot;g&quot;, &quot;a&quot;, &quot;t&quot;, &quot;i&quot;, &quot;n&quot;, &quot;g&quot;]</code>中的一个</p></li><li><p>当我们序号为<code>g</code>且增量为<code>2</code>时，第一个文件的后缀为<code>g</code>，第二个为<code>t</code>，因为<code>g</code>为列表的第一个，那么他下一个的就为<code>1+2</code>，即列表的第三个，也就是<code>t</code></p></li><li><p>纯字母分小写和大写，所以这里我们也需要处理一下</p></li><li><p>文件名+后缀名不能为<code>.</code>，因为在 pc 上是创建不了文件的</p></li></ol><p>知道这 9 点后，我们就可以开始写我们的代码啦。其实主要分为几大块：</p><ul><li><p>文件名的处理</p></li><li><p>后缀名的处理</p></li><li><p>自定义序号的处理</p></li></ul><h3 id="思考一下-1"><a href="#思考一下-1" class="headerlink" title="思考一下"></a>思考一下</h3><p>还记得我们之前的目录结构吗？里面有一个<code>utils(工具库)</code>的文件夹，我们就在这里书写我们的方法。</p><p>先思考一下，我们这些都是针对字符串的，那么用什么处理字符串最合适呢？肯定是正则啦。</p><p>首先当然是书写我们常用的正则啦，主要有那么几个：</p><ul><li><p>获取文件名和拓展名</p></li><li><p>判断是不是空字符串，为空不处理</p></li><li><p>文件名+后缀名不能是<code>.</code></p></li><li><p>在没有自定义序号的情况下，是否符合纯字母这种情况，主要用于区分纯数字和纯字母这两种情况</p></li><li><p>是否符合默认序号规范（纯数字或者纯字母）</p></li></ul><p>在<code>utils/regexp.js</code>中写上：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 匹配文件名和拓展名</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> extReg = <span class="regexp">/(.+)(\..+)$/</span>;</span><br><span class="line"><span class="comment">// 是否为空字符串</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> isEmpty = <span class="regexp">/^\s*$/</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 class="doctag">@param <span class="type">&#123; string &#125;</span></span>&#125; str 文件名</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> testDot = <span class="function">(<span class="params">str</span>) =&gt;</span> <span class="regexp">/^\s*\.+\s*$/</span>.test(str);</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"> * <span class="doctag">@param <span class="type">&#123; string &#125;</span></span>&#125; str 序号</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> testWord = <span class="function">(<span class="params">str</span>) =&gt;</span> <span class="regexp">/^[a-zA-Z]+$/</span>.test(str);</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"> * <span class="doctag">@param <span class="type">&#123; string &#125;</span> </span>str 序号</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123; object &#125;</span> </span>返回是否符合默认序号规范（纯字母/纯数字）</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> isDefaultSerialNum = <span class="function">(<span class="params">str</span>) =&gt;</span></span><br><span class="line">  !<span class="regexp">/(^\d+$)|(^[a-zA-Z]+$)/</span>.test(str) &amp;&amp; !isEmpty.test(str);</span><br></pre></td></tr></table></figure></div><p>书写完正则后，就到了我们的<code>utis/helpers.js</code>帮助函数了，帮助函数主要有三个，分别做了三件事：</p><ol><li><p>判断首字母是不是大写，用于区分<code>a</code>和<code>A</code>，因为<code>a</code>和<code>A</code>序号输出的内容完全不同</p></li><li><p>计算默认情况中字母序号和自定义序号的实际值</p></li><li><p>用于转换默认情况中字母序号和自定义序号的值</p></li></ol><p>针对第一点，其实大家应该都知道怎么写了吧，也比较简单，我们直接通过正则就好了：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 判断是不是大写字母</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; string &#125;</span> </span>word =&gt; 字母</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123; boolean &#125;</span> </span>返回是否大写字母</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> isUpper = <span class="function">(<span class="params">word</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="regexp">/^[A-Z]$/</span>.test(word[<span class="number">0</span>]);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><p><code>2、3</code>点的话，可能会觉得比较拗口，也比较难理解。不怕，我举个例子你就理解了。</p><p>假设我们是默认是输入的是纯字母的情况，如果输入<code>a</code>，那么输出是不是就是<code>1</code>，即是第一个字母；输入<code>z</code>，则是<code>26</code>。又因为我们最后得到的字符串，所以我们需要把<code>26</code>这个值转换成<code>z</code>，其实就是反着来。</p><p>乍一看，是不是很像<code>26进制</code>转<code>10进制</code>？对的，没错，其实就是<code>26进制转</code>换成<code>10进制</code>。那么我们怎么转换呢？</p><p>然后我们也说过，要把<strong>字母</strong>先转成实际的值，在转换成十进制在进行上面的操作。</p><p>那么怎么计算十进制的值呢？比如<code>baa</code>转成十进制是多少？它的运算规则是这样的<code>baa = 26**2*2 + 26**1*1 + 26**0*1</code>,即<code>1379</code>，知道规则之后，我们就可以写出以下的计算代码，</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 创建一个连续的整数数组</span></span><br><span class="line"><span class="keyword">import</span> &#123; range &#125; <span class="keyword">from</span> <span class="string">"lodash"</span>;</span><br><span class="line"><span class="comment">// 创建一个[0-25]的数组，并转换为[A-Z]数组供默认字母序号使用</span></span><br><span class="line"><span class="keyword">let</span> convertArr = range(<span class="number">26</span>).map(<span class="function">(<span class="params">i</span>) =&gt;</span> <span class="built_in">String</span>.fromCharCode(<span class="number">65</span> + i));</span><br><span class="line"><span class="keyword">const</span> serialNum = <span class="string">"baa"</span>,</span><br><span class="line">  complement = serialNum.length;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 计算第n位26进制数的十进制值</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>range =&gt; 26进制数组</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>val =&gt; 当前值</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>idx =&gt; 当前的位置</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@returns <span class="type">&#123; number &#125;</span> </span>第n位26进制数的十进制值</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> calculate = <span class="function">(<span class="params">range, val, idx</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> word = range.indexOf(val.toLocaleUpperCase());</span><br><span class="line">  <span class="keyword">return</span> word === <span class="number">-1</span> ? <span class="number">0</span> : (word + <span class="number">1</span>) * <span class="number">26</span> ** idx;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> sum = [...serialNum].reduce(</span><br><span class="line">  (res, val, idx) =&gt; res + calculate(convertArr, val, complement - <span class="number">1</span> - idx),</span><br><span class="line">  <span class="number">0</span></span><br><span class="line">);</span><br><span class="line"><span class="built_in">console</span>.log(sum); <span class="comment">// 1379</span></span><br></pre></td></tr></table></figure></div><p>这样子就计算好了我们的值，接下来就是对这个值进行转换为字母了。因为我们不是从 0 开始，而是从 1 开始，所以每一位的时候我们只需要前的位进行减 1 操作即可。</p><blockquote><p>ps: 不知道<code>**</code>幂运算符的，建议看看 es7，比如<code>26 ** 3</code>，它相当于 Math.pow(26,3)，即<code>26 * 26 * 26</code></p></blockquote><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 创建一个连续的整数数组</span></span><br><span class="line"><span class="keyword">import</span> &#123; range &#125; <span class="keyword">from</span> <span class="string">"lodash"</span>;</span><br><span class="line"><span class="comment">// 创建一个[0-25]的数组，并转换为[A-Z]数组供默认字母序号使用</span></span><br><span class="line"><span class="keyword">let</span> convertArr = range(<span class="number">26</span>).map(<span class="function">(<span class="params">i</span>) =&gt;</span> <span class="built_in">String</span>.fromCharCode(<span class="number">65</span> + i));</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 26进制转换</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; number &#125;</span> </span>num =&gt; 转换的值</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; array &#125;</span> </span>range =&gt; 转换的编码</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123; string &#125;</span> </span>返回转换后的字符串</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> convert = <span class="function">(<span class="params">num, range</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> word = <span class="string">""</span>,</span><br><span class="line">    len = range.length;</span><br><span class="line">  <span class="keyword">while</span> (num &gt; <span class="number">0</span>) &#123;</span><br><span class="line">    num--;</span><br><span class="line">    word = range[num % len] + word;</span><br><span class="line">    <span class="comment">// ~~位运算取整</span></span><br><span class="line">    num = ~~(num / len);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> word;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(convert(<span class="number">1379</span>, convertArr)); <span class="comment">// BAA</span></span><br></pre></td></tr></table></figure></div><p>所以最终的<code>utils/helpers.js</code>文件代码如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 判断是不是大写字母</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; string &#125;</span> </span>word =&gt; 字母</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123; boolean &#125;</span> </span>返回是否大写字母</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> isUpper = <span class="function">(<span class="params">word</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="regexp">/^[A-Z]$/</span>.test(word[<span class="number">0</span>]);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 进制转换</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; number &#125;</span> </span>num =&gt; 转换的值</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; array &#125;</span> </span>range =&gt; 转换的编码</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123; string &#125;</span> </span>返回转换后的字符串</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> convert = <span class="function">(<span class="params">num, range</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// 没有range的时候即为数字，数字我们不需要处理</span></span><br><span class="line">  <span class="keyword">if</span> (!range) <span class="keyword">return</span> num;</span><br><span class="line">  <span class="keyword">let</span> word = <span class="string">""</span>,</span><br><span class="line">    len = range.length;</span><br><span class="line">  <span class="keyword">while</span> (num &gt; <span class="number">0</span>) &#123;</span><br><span class="line">    num--;</span><br><span class="line">    word = range[num % len] + word;</span><br><span class="line">    num = ~~(num / len);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> word;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 计算第n位进制数的十进制值</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>range =&gt; 进制数组</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>val =&gt; 当前值</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>idx =&gt; 当前的位置</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@returns <span class="type">&#123; number &#125;</span> </span>第n位进制数的十进制值</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> calculate = <span class="function">(<span class="params">range, val, idx</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> word = range.indexOf(val);</span><br><span class="line">  <span class="keyword">const</span> len = range.length;</span><br><span class="line">  <span class="keyword">return</span> word === <span class="number">-1</span> ? <span class="number">0</span> : (word + <span class="number">1</span>) * len ** idx;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><p>这里我把<code>range</code>作为参数传过来我想大家应该能理解吧？因为其实<code>自定义序号</code>和<code>默认的字母序号</code>的处理是一样，所以这里我们直接传入<code>range</code>就可以处理<strong>自定义</strong>和<strong>纯字母</strong>这种情况了。</p><p>无非就是<code>n进制</code>转<code>十进制</code>的操作，计算规则也同理。。。</p><p>不过写到这里，可能要骂我了- -这不就是把<code>baa</code>转为<code>1379</code>，然后再把<code>1379</code>转回<code>BAA</code>，压根就没有做什么操作啊？━━(￣ー￣*|||━━</p><p>小傻瓜，其实不是的，这里我们只是用一个<code>baa</code>作为演示，假设我们有多个文件，不就是需要<strong>它实际的值+增量</strong>来计算了吗，大概就是：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 伪代码...</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getNewFileList</span>(<span class="params">fileList, serialNum, increment, range</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 起始序号</span></span><br><span class="line">  <span class="keyword">let</span> start = [...serialNum].reduce(</span><br><span class="line">    (res, val, idx) =&gt; res + calculate(convertArr, val, complement - <span class="number">1</span> - idx),</span><br><span class="line">    <span class="number">0</span></span><br><span class="line">  );</span><br><span class="line">  <span class="keyword">return</span> fileList.map(<span class="function">(<span class="params">file</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// 得出后缀</span></span><br><span class="line">    <span class="keyword">const</span> suffer = convert(start, range);</span><br><span class="line">    <span class="comment">// 根据increment增量自增</span></span><br><span class="line">    start += increment;</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">      ...file,</span><br><span class="line">      name: file.name + suffer,</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>到这里，我们基本上对序号处理已经完成了，剩下来就是比较简单的了，也就是对<strong>文件名</strong>和<strong>后缀名</strong>进行处理。还记得我们之前定义的正则，接下来我们就是使用它的时候了。</p><h3 id="处理文件名和后缀名"><a href="#处理文件名和后缀名" class="headerlink" title="处理文件名和后缀名"></a>处理文件名和后缀名</h3><p>先书写我们觉得比较容易处理的方法，比如<code>获取文件和文件后缀名</code>和<code>根据指定字符替换文件名</code>，在<code>utils/index.js</code>文件书写如下代码：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; extReg &#125; <span class="keyword">from</span> <span class="string">"./regexp"</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 class="doctag">@param <span class="type">&#123; string &#125;</span> </span>filename 原始文件名</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123; array &#125;</span> </span>返回的文件和文件后缀名</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> splitFilename = <span class="function">(<span class="params">filename</span>) =&gt;</span></span><br><span class="line">  filename.replace(extReg, <span class="string">"$1,$2"</span>).split(<span class="string">","</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 替换文件名</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; string &#125;</span> </span>filename 文件名</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; string &#125;</span> </span>preReplaceWord 需要替换的字符</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; string &#125;</span> </span>replaceWord 替换的字符</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123; string &#125;</span> </span>返回替换后的文件名</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> replaceFilename = <span class="function">(<span class="params">filename, preReplaceWord, replaceWord</span>) =&gt;</span></span><br><span class="line">  filename.replace(preReplaceWord, replaceWord);</span><br></pre></td></tr></table></figure></div><p>还记得我们之前的<code>fileSettings</code>这个配置吗？他有很多一个对象，而我们只需要获取到这对象下的<code>value</code>值，但是一个个<strong>解构赋值</strong>比较麻烦，所以我们也可以写一个方法在获取到它的<code>value</code>值在结构赋值，即：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 获取文件名设置</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; Object &#125;</span> </span>fileSettings 文件名设置</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> getFileSetting = <span class="function">(<span class="params">fileSettings</span>) =&gt;</span></span><br><span class="line">  <span class="built_in">Object</span>.values(fileSettings).map(<span class="function">(<span class="params">setting</span>) =&gt;</span> setting.value);</span><br><span class="line"><span class="keyword">const</span> fileSettings = &#123;</span><br><span class="line">  filename: &#123;</span><br><span class="line">    value: <span class="string">"test"</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">  serialNum: &#123;</span><br><span class="line">    value: <span class="string">"aaa"</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">const</span> [filename, serialNum] = getFileSetting(fileSettings);</span><br><span class="line"><span class="built_in">console</span>.log(filename, serialNum); <span class="comment">// test aaa</span></span><br></pre></td></tr></table></figure></div><p>这样子就能很方便的<strong>解构赋值</strong>了,再然后就是根据<strong>输入的后缀名获得新的后缀名</strong>，这里有个暴力的配置，所以单独拿出来讲讲。</p><p>因为我们需要对<code>*</code>这个字符串进行全局的替换，同时也需要对<code>后缀名</code>，比如<code>png</code>；或者<code>点+后缀名</code>，比如<code>.png</code>。这两种情况处理。所以代码是：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; startsWith &#125; <span class="keyword">from</span> <span class="string">"lodash"</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 class="doctag">@param <span class="type">&#123; array &#125;</span> </span>fileExt =&gt; 文件后缀名数组</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123; array &#125;</span> </span>[oldExt, newExt] =&gt; 返回文件的后缀名</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> getFileExt = <span class="function">(<span class="params">fileExt</span>) =&gt;</span></span><br><span class="line">  <span class="comment">// 如果 i 不存在，返回""</span></span><br><span class="line">  <span class="comment">// 如果 i 是以 "." 开头的返回i，即'.png'返回'.png'</span></span><br><span class="line">  <span class="comment">// 如果 i === "*" 的返回i，即'*'返回'*'</span></span><br><span class="line">  <span class="comment">// 如果是 i 是 'png'，则返回 '.png'</span></span><br><span class="line">  fileExt.map(<span class="function">(<span class="params">i</span>) =&gt;</span></span><br><span class="line">    i ? (startsWith(i, <span class="string">"."</span>) || i === <span class="string">"*"</span> ? i : <span class="string">"."</span> + i) : <span class="string">""</span></span><br><span class="line">  );</span><br></pre></td></tr></table></figure></div><blockquote><p>ps：提一点，如果不知道<code>startsWith</code>这个方法的，建议阅读<a href="https://gatings.cn/2019-01-09/%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E6%96%B9%E6%B3%95%E7%9A%84%E6%80%BB%E7%BB%93%E5%92%8C%E4%BD%BF%E7%94%A8/#startsWith">字符串的方法的总结和使用</a>，当然我这里用的是<code>lodash</code>的<code>startsWith</code>，但实际上一样的</p></blockquote><p>这里代码翻译成中文就是：</p><ol><li><p>如果<code>后缀名</code>不存在，返回<code>&quot;&quot;(空字符串)</code></p></li><li><p>如果<code>后缀名</code>是以<code>.</code>开头的返回<code>后缀名</code>，即<code>.png</code>返回<code>.png</code></p></li><li><p>如果<code>后缀名</code>是<code>*</code> 的返回<code>*</code>，即<code>*</code>返回<code>*</code></p></li><li><p>如果是<code>后缀名</code>不是以<code>.</code>开头的返回<code>png</code>，则返回<code>.png</code></p></li></ol><p>写完获取<code>后缀名</code>之后就是修改啦，这里单独拿出来谈主要也是因为有个小坑，因为有些文件比较奇葩，他是<code>.+名字</code>，比如我们常常见到的<code>.gitignore</code>文件</p><p>所以我们需要针对这种<code>.+名字</code>这类型的文件进行一个区分，即没有<code>后缀名</code>的文件的处理：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 获取修改后的后缀名</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; string &#125;</span> </span>fileExt =&gt; 匹配的文件后缀名</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; string &#125;</span> </span>oldExt =&gt; 所有文件后缀名</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; string &#125;</span> </span>newExt =&gt; 修改后文件后缀名</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; boolean &#125;</span> </span>enable =&gt; 是否启用修改后缀名</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123; string &#125;</span> </span>返回修改后的后缀名</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> getNewFileExt = <span class="function">(<span class="params">fileExt, oldExt, newExt, enable</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">if</span> ((oldExt === <span class="string">"*"</span> || fileExt === oldExt) &amp;&amp; enable) &#123;</span><br><span class="line">    <span class="keyword">return</span> newExt;</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="comment">// 避免没有后缀名的bug，比如 .gitignore</span></span><br><span class="line">    <span class="keyword">return</span> fileExt || <span class="string">""</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><p>写到这里，基本上逻辑要写完了，但是还有一个最最最小的问题，就是他可能会输入<code>001</code>，而我们之前的代码会把<code>001</code>转为数字，即会直接转为<code>1</code>。这不是我们想要的，那么我们怎么让他还是字符串形式，但是还是按照数字计算呢？</p><blockquote><p>ps：因为转换值需要+增量，不可能用字符串相加的，所以必须转成数字</p></blockquote><p>所以这里就要需要用到<code>es6</code>的<code>padStart</code>方法啦，通过他来进行<code>序号</code>的补位，然后把之前的方法整理下，定义为<code>getOptions</code>函数，获取通用的配置：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; range, padStart &#125; <span class="keyword">from</span> <span class="string">"lodash"</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; testWord &#125; <span class="keyword">from</span> <span class="string">"./regexp"</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; calculate, isUpper &#125; <span class="keyword">from</span> <span class="string">"./helpers"</span>;</span><br><span class="line"><span class="keyword">let</span> convertArr = range(<span class="number">26</span>).map(<span class="function">(<span class="params">i</span>) =&gt;</span> <span class="built_in">String</span>.fromCharCode(<span class="number">65</span> + i));</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 class="doctag">@param <span class="type">&#123; string &#125;</span> </span>serialNum 文件序号</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; number &#125;</span> </span>complement 需要补的位数</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; array &#125;</span> </span>range 自定义序号数组</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123; object &#125;</span> </span>返回起始位置、补位字符和自定义数组</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> getOptions = <span class="function">(<span class="params">serialNum, complement, range</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// 起始序号的值，补位序号</span></span><br><span class="line">  <span class="keyword">let</span> start, padNum;</span><br><span class="line">  <span class="comment">// 字母和自定义序号的情况</span></span><br><span class="line">  <span class="keyword">if</span> (testWord(serialNum) || range) &#123;</span><br><span class="line">    <span class="keyword">if</span> (!range) &#123;</span><br><span class="line">      <span class="comment">// 转换大小写</span></span><br><span class="line">      <span class="keyword">if</span> (!isUpper(serialNum[<span class="number">0</span>])) &#123;</span><br><span class="line">        convertArr = convertArr.map(<span class="function">(<span class="params">str</span>) =&gt;</span> str.toLocaleLowerCase());</span><br><span class="line">      &#125;</span><br><span class="line">      range = convertArr;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 补位字符</span></span><br><span class="line">    padNum = range[<span class="number">0</span>];</span><br><span class="line">    start = [...serialNum].reduce(</span><br><span class="line">      (res, val, idx) =&gt; res + calculate(range, val, complement - <span class="number">1</span> - idx),</span><br><span class="line">      <span class="number">0</span></span><br><span class="line">    );</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="comment">// 纯数字的情况</span></span><br><span class="line">    start = serialNum ? ~~serialNum : <span class="literal">NaN</span>;</span><br><span class="line">    <span class="comment">// 补位字符</span></span><br><span class="line">    padNum = <span class="string">"0"</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    start,</span><br><span class="line">    padNum,</span><br><span class="line">    convertArr: range,</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">let</span> &#123; start, padNum &#125; = getOptions(<span class="string">"001"</span>, <span class="number">3</span>);</span><br><span class="line"><span class="built_in">console</span>.log(padStart(convert(start) + <span class="string">""</span>, <span class="number">3</span>, padNum)); <span class="comment">// 001</span></span><br></pre></td></tr></table></figure></div><blockquote><p>如果不知道<code>padStart</code>这个方法的，建议阅读<a href="https://gatings.cn/2019-01-09/%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E6%96%B9%E6%B3%95%E7%9A%84%E6%80%BB%E7%BB%93%E5%92%8C%E4%BD%BF%E7%94%A8/#padStart">字符串的方法的总结和使用</a>，当然我这里用的是<code>lodash</code>的<code>padStart</code>，但实际上一样的</p></blockquote><p>写好这一对方法之后，我们就可以实现刚刚那个<strong>伪代码</strong>了，而我们最终<code>vue</code>里面也就需要这一个方法，所以直接导出就行了。</p><p><code>utils/index.js</code>最终代码如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123;</span><br><span class="line">  extReg,</span><br><span class="line">  testWord,</span><br><span class="line">  isDefaultSerialNum,</span><br><span class="line">  isEmpty,</span><br><span class="line">  testDot,</span><br><span class="line">&#125; <span class="keyword">from</span> <span class="string">"./regexp"</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; calculate, isUpper, convert &#125; <span class="keyword">from</span> <span class="string">"./helpers"</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; range, padStart, startsWith &#125; <span class="keyword">from</span> <span class="string">"lodash"</span>;</span><br><span class="line"><span class="comment">// 创建一个[0-25]的数组，并转换为[A-Z]数组供默认字母序号使用</span></span><br><span class="line"><span class="keyword">let</span> convertArr = range(<span class="number">26</span>).map(<span class="function">(<span class="params">i</span>) =&gt;</span> <span class="built_in">String</span>.fromCharCode(<span class="number">65</span> + i));</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 class="doctag">@param <span class="type">&#123; string &#125;</span> </span>fileExt =&gt; 匹配的文件后缀名</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; string &#125;</span> </span>oldExt =&gt; 所有文件后缀名</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; string &#125;</span> </span>newExt =&gt; 修改后文件后缀名</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; boolean &#125;</span> </span>enable =&gt; 是否启用修改后缀名</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123; string &#125;</span> </span>返回修改后的后缀名</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> getNewFileExt = <span class="function">(<span class="params">fileExt, oldExt, newExt, enable</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">if</span> ((oldExt === <span class="string">"*"</span> || fileExt === oldExt) &amp;&amp; enable) &#123;</span><br><span class="line">    <span class="keyword">return</span> newExt;</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="comment">// 避免没有后缀名的bug</span></span><br><span class="line">    <span class="keyword">return</span> fileExt || <span class="string">""</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 根据输入的后缀名，获取修改文件名的的后缀名</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; array &#125;</span> </span>fileExt =&gt; 文件后缀名数组</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123; array &#125;</span> </span>[oldExt, newExt] =&gt; 返回文件的后缀名</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> getFileExt = <span class="function">(<span class="params">fileExt</span>) =&gt;</span></span><br><span class="line">  <span class="comment">// 如果 i 不存在，返回""</span></span><br><span class="line">  <span class="comment">// 如果 i 是以 "." 开头的返回i，即'.png'返回'.png'</span></span><br><span class="line">  <span class="comment">// 如果 i === "*" 的返回i，即'*'返回'*'</span></span><br><span class="line">  <span class="comment">// 如果是 i 是 'png'，则返回 '.png'</span></span><br><span class="line">  fileExt.map(<span class="function">(<span class="params">i</span>) =&gt;</span></span><br><span class="line">    i ? (startsWith(i, <span class="string">"."</span>) || i === <span class="string">"*"</span> ? i : <span class="string">"."</span> + i) : <span class="string">""</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"> * <span class="doctag">@param <span class="type">&#123; string &#125;</span> </span>filename 原始文件名</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123; array &#125;</span> </span>返回的文件和文件后缀名</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> splitFilename = <span class="function">(<span class="params">filename</span>) =&gt;</span></span><br><span class="line">  filename.replace(extReg, <span class="string">"$1,$2"</span>).split(<span class="string">","</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 替换文件名</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; string &#125;</span> </span>filename 文件名</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; string &#125;</span> </span>preReplaceWord 需要替换的字符</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; string &#125;</span> </span>replaceWord 替换的字符</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123; string &#125;</span> </span>返回替换后的文件名</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> replaceFilename = <span class="function">(<span class="params">filename, preReplaceWord, replaceWord</span>) =&gt;</span></span><br><span class="line">  filename.replace(preReplaceWord, replaceWord);</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"> * <span class="doctag">@param <span class="type">&#123; Object &#125;</span> </span>fileSettings 文件名设置</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> getFileSetting = <span class="function">(<span class="params">fileSettings</span>) =&gt;</span></span><br><span class="line">  <span class="built_in">Object</span>.values(fileSettings).map(<span class="function">(<span class="params">setting</span>) =&gt;</span> setting.value);</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"> * <span class="doctag">@param <span class="type">&#123; string &#125;</span> </span>serialNum 文件序号</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; number &#125;</span> </span>complement 需要补的位数</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; array &#125;</span> </span>range 自定义序号数组</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123; object &#125;</span> </span>返回起始位置、补位字符和自定义数组</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> getOptions = <span class="function">(<span class="params">serialNum, complement, range</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// 起始序号的值，补位序号</span></span><br><span class="line">  <span class="keyword">let</span> start, padNum;</span><br><span class="line">  <span class="comment">// 字母和自定义序号的情况</span></span><br><span class="line">  <span class="keyword">if</span> (testWord(serialNum) || range) &#123;</span><br><span class="line">    <span class="keyword">if</span> (!range) &#123;</span><br><span class="line">      <span class="comment">// 转换大小写</span></span><br><span class="line">      <span class="keyword">if</span> (!isUpper(serialNum[<span class="number">0</span>])) &#123;</span><br><span class="line">        convertArr = convertArr.map(<span class="function">(<span class="params">str</span>) =&gt;</span> str.toLocaleLowerCase());</span><br><span class="line">      &#125;</span><br><span class="line">      range = convertArr;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 补位字符</span></span><br><span class="line">    padNum = range[<span class="number">0</span>];</span><br><span class="line">    start = [...serialNum].reduce(</span><br><span class="line">      (res, val, idx) =&gt; res + calculate(range, val, complement - <span class="number">1</span> - idx),</span><br><span class="line">      <span class="number">0</span></span><br><span class="line">    );</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="comment">// 纯数字的情况</span></span><br><span class="line">    start = serialNum ? ~~serialNum : <span class="literal">NaN</span>;</span><br><span class="line">    <span class="comment">// 补位字符</span></span><br><span class="line">    padNum = <span class="string">"0"</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    start,</span><br><span class="line">    padNum,</span><br><span class="line">    convertArr: range,</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 获取文件名</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; string &#125;</span> </span>filename 旧文件名</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; string &#125;</span> </span>newFilename 新文件名</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123; string &#125;</span> </span>返回最终的文件名</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> getFileName = <span class="function">(<span class="params">filename, newFilename</span>) =&gt;</span></span><br><span class="line">  isEmpty.test(newFilename) ? filename : newFilename;</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"> * <span class="doctag">@param <span class="type">&#123; array &#125;</span> </span>fileList 原文件</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; object &#125;</span> </span>fileSettings 文件名设置</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; array &#125;</span> </span>extArr 修改的后缀名</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123; boolean &#125;</span> </span>enable 是否启用修改后缀名</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123; array &#125;</span> </span>修改后的文件名</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> <span class="title">getNewFileList</span>(<span class="params"></span></span></span><br><span class="line"><span class="function"><span class="params">  fileList,</span></span></span><br><span class="line"><span class="function"><span class="params">  fileSettings,</span></span></span><br><span class="line"><span class="function"><span class="params">  extArr,</span></span></span><br><span class="line"><span class="function"><span class="params">  enable,</span></span></span><br><span class="line"><span class="function"><span class="params">  range</span></span></span><br><span class="line"><span class="function"><span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> [</span><br><span class="line">    newFilename,</span><br><span class="line">    serialNum,</span><br><span class="line">    increment,</span><br><span class="line">    preReplaceWord,</span><br><span class="line">    replaceWord,</span><br><span class="line">  ] = getFileSetting(fileSettings);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 如果不符合默认序号规则，则不改名</span></span><br><span class="line">  <span class="keyword">if</span> (isDefaultSerialNum(serialNum) &amp;&amp; !range) &#123;</span><br><span class="line">    <span class="keyword">return</span> fileList;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 获取文件修改的后缀名</span></span><br><span class="line">  <span class="keyword">const</span> [oldExt, newExt] = getFileExt(extArr);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 补位，比如输入的是001 补位就是00</span></span><br><span class="line">  <span class="keyword">const</span> padLen = serialNum.length;</span><br><span class="line">  <span class="comment">// 获取开始</span></span><br><span class="line">  <span class="keyword">let</span> &#123; start, padNum, convertArr &#125; = getOptions(serialNum, padLen, range);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> fileList.map(<span class="function">(<span class="params">item</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// 获取文件名和后缀名</span></span><br><span class="line">    <span class="keyword">let</span> [oldFileName, fileExt] = splitFilename(item.name);</span><br><span class="line">    <span class="comment">// 获取修改后的文件名</span></span><br><span class="line">    <span class="keyword">let</span> filename = replaceFilename(</span><br><span class="line">      getFileName(oldFileName, newFilename),</span><br><span class="line">      preReplaceWord,</span><br><span class="line">      replaceWord</span><br><span class="line">    );</span><br><span class="line">    <span class="comment">// 获取修改后的后缀名</span></span><br><span class="line">    fileExt = getNewFileExt(fileExt, oldExt, newExt, enable);</span><br><span class="line">    <span class="keyword">const</span> suffix =</span><br><span class="line">      (padLen &amp;&amp; padStart(convert(start, convertArr) + <span class="string">""</span>, padLen, padNum)) ||</span><br><span class="line">      <span class="string">""</span>;</span><br><span class="line">    filename += suffix;</span><br><span class="line">    start += increment;</span><br><span class="line">    <span class="comment">// 文件名+后缀名不能是.</span></span><br><span class="line">    <span class="keyword">let</span> name = testDot(filename + fileExt) ? item.name : filename + fileExt;</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">      ...item,</span><br><span class="line">      basename: filename,</span><br><span class="line">      name,</span><br><span class="line">      ext: fileExt,</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>看到这里，你会发现，我多数方法只做一件事，通常也建议只做一件事（单一职责原则），这样有利于降低代码复杂度和降低维护成本。希望大家也能养成这样的好习惯哦~~ 😁</p><blockquote><p>ps：函数应该做一件事，做好这件事，只做这一件事。 —代码整洁之道</p></blockquote><h2 id="优化相关"><a href="#优化相关" class="headerlink" title="优化相关"></a>优化相关</h2><h3 id="预加载-preload-和预处理-prefetch"><a href="#预加载-preload-和预处理-prefetch" class="headerlink" title="预加载(preload)和预处理(prefetch)"></a>预加载(preload)和预处理(prefetch)</h3><p><code>preload</code>与<code>prefetch</code>不同的地方就是它专注于当前的页面，并以高优先级加载资源，<code>prefetch</code>专注于下一个页面将要加载的资源并以低优先级加载。同时也要注意<code>preload</code>并不会阻塞<code>window</code>的<code>onload</code>事件。</p><p>即<code>preload</code>加载资源一般是当前页面需要的， <code>prefetch</code>一般是其它页面有可能用到的资源。</p><p>明白这点后，就是在<code>vue.config.js</code>写我们的配置了：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// ...一堆之前配置</span></span><br><span class="line">  chainWebpack(config) &#123;</span><br><span class="line">    <span class="comment">// 建议打开预加载，它可以提高第一屏的速度</span></span><br><span class="line">    config.plugin(<span class="string">"preload"</span>).tap(<span class="function"><span class="params">()</span> =&gt;</span> [</span><br><span class="line">      &#123;</span><br><span class="line">        rel: <span class="string">"preload"</span>,</span><br><span class="line">        <span class="comment">// to ignore runtime.js</span></span><br><span class="line">        <span class="comment">// https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/cli-service/lib/config/app.js#L171</span></span><br><span class="line">        fileBlacklist: [<span class="regexp">/\.map$/</span>, /hot-update\.js$/, /runtime\..*\.js$/],</span><br><span class="line">        include: <span class="string">"initial"</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">    ]);</span><br><span class="line">    <span class="comment">// 去除预读取，因为如果页面过多，会造成无意义的请求</span></span><br><span class="line">    config.plugins.delete(<span class="string">"prefetch"</span>);</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><blockquote><p>ps：优化网站性能的 <code>pre 家族</code>还有<code>dns-prefetch</code>、<code>prerender</code>和<code>preconnect</code>，有兴趣的可以进一步了解</p></blockquote><h3 id="提取-runtime-js"><a href="#提取-runtime-js" class="headerlink" title="提取 runtime.js"></a>提取 runtime.js</h3><p>因为打包生成的<code>runtime.js</code>非常的小，但这个文件又经常会改变，它的<code>http</code>耗时远大于它的执行时间了，所以建议不要将它单独拆包，而是将它内联到我们的<code>index.html</code>之中，那么则需要使用到<code>script-ext-html-webpack-plugin</code>这个插件，我们安装一下，同样是开发依赖<code>-D</code>：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm i script-ext-html-webpack-plugin -D</span><br></pre></td></tr></table></figure></div><p>接下来就是在<code>vue.config.js</code>写我们的配置了：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> isProduction = process.env.NODE_ENV !== <span class="string">"development"</span>;</span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// ...一堆之前配置</span></span><br><span class="line">  chainWebpack(config) &#123;</span><br><span class="line">     <span class="comment">// 只在生成环境使用</span></span><br><span class="line">     config.when(isProduction, (config) =&gt; &#123;</span><br><span class="line">      <span class="comment">// html-webpack-plugin的增强功能</span></span><br><span class="line">      <span class="comment">// 打包生成的 runtime.js非常的小，但这个文件又经常会改变，它的 http 耗时远大于它的执行时间了，所以建议不要将它单独拆包，而是将它内联到我们的 index.html 之中</span></span><br><span class="line">      <span class="comment">// inline 的name 和你 runtimeChunk 的 name保持一致</span></span><br><span class="line">      config</span><br><span class="line">        .plugin(<span class="string">"ScriptExtHtmlWebpackPlugin"</span>)</span><br><span class="line">        .after(<span class="string">"html"</span>)</span><br><span class="line">        .use(<span class="string">"script-ext-html-webpack-plugin"</span>, [</span><br><span class="line">          &#123;</span><br><span class="line">            inline: <span class="regexp">/runtime\..*\.js$/</span>,</span><br><span class="line">          &#125;,</span><br><span class="line">        ])</span><br><span class="line">        .end();</span><br><span class="line">      <span class="comment">// 单独打包runtime</span></span><br><span class="line">      config.optimization.runtimeChunk(<span class="string">"single"</span>);</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><h3 id="对第三方库进行拆包"><a href="#对第三方库进行拆包" class="headerlink" title="对第三方库进行拆包"></a>对第三方库进行拆包</h3><p>实际上默认我们会将所有的第三方包打包在一个文件上，这样的方式可行吗？实际上肯定是有问题的，因为将第三方库一块打包，只要有一个库我们升级或者引入一个新库，这个文件就会变动,那么这个文件的变动性会很高,并不适合长期缓存，还有一点，我们要提高首页加载速度，第一要务是减少首页加载依赖的代码量，所以我们需要第三方库进行拆包。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  publicPath: <span class="string">"./"</span>,</span><br><span class="line">  outputDir: <span class="string">"../public"</span>,</span><br><span class="line">  productionSourceMap: <span class="literal">false</span>,</span><br><span class="line">  devServer: &#123;</span><br><span class="line">    open: <span class="literal">true</span>,</span><br><span class="line">    proxy: &#123;</span><br><span class="line">      <span class="string">"/upload"</span>: &#123;</span><br><span class="line">        target: <span class="string">"http://localhost:3000"</span>,</span><br><span class="line">        changeOrigin: <span class="literal">true</span>,</span><br><span class="line">        pathRewrite: &#123;</span><br><span class="line">          <span class="string">"^/upload"</span>: <span class="string">"/upload"</span>,</span><br><span class="line">        &#125;,</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">  css: &#123;</span><br><span class="line">    loaderOptions: &#123;</span><br><span class="line">      less: &#123;</span><br><span class="line">        javascriptEnabled: <span class="literal">true</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">  chainWebpack(config) &#123;</span><br><span class="line">      <span class="comment">// 拆分模块</span></span><br><span class="line">      config.optimization.splitChunks(&#123;</span><br><span class="line">        chunks: <span class="string">"all"</span>,</span><br><span class="line">        cacheGroups: &#123;</span><br><span class="line">          libs: &#123;</span><br><span class="line">            name: <span class="string">"chunk-libs"</span>, <span class="comment">// 输出名字</span></span><br><span class="line">            test: <span class="regexp">/[\\/]node_modules[\\/]/</span>, <span class="comment">// 匹配目录</span></span><br><span class="line">            priority: <span class="number">10</span>, <span class="comment">// 优先级</span></span><br><span class="line">            chunks: <span class="string">"initial"</span>, <span class="comment">// 从入口模块进行拆分</span></span><br><span class="line">          &#125;,</span><br><span class="line">          antDesign: &#123;</span><br><span class="line">            name: <span class="string">"chunk-antd"</span>, <span class="comment">// 将antd拆分为单个包</span></span><br><span class="line">            priority: <span class="number">20</span>, <span class="comment">// 权重需要大于libs和app，否则将打包成libs或app</span></span><br><span class="line">            test: <span class="regexp">/[\\/]node_modules[\\/]_?ant-design-vue(.*)/</span>, <span class="comment">// 为了适应cnpm</span></span><br><span class="line">          &#125;,</span><br><span class="line">          commons: &#123;</span><br><span class="line">            name: <span class="string">"chunk-commons"</span>,</span><br><span class="line">            test: resolve(<span class="string">"src/components"</span>),</span><br><span class="line">            minChunks: <span class="number">3</span>,</span><br><span class="line">            priority: <span class="number">5</span>,</span><br><span class="line">            reuseExistingChunk: <span class="literal">true</span>, <span class="comment">// 复用其他chunk内已拥有的模块</span></span><br><span class="line">          &#125;,</span><br><span class="line">        &#125;,</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><h3 id="其他优化"><a href="#其他优化" class="headerlink" title="其他优化"></a>其他优化</h3><p>当然其实还有很多的优化方式，我们这里没有提及，比如：</p><ol><li><p>(伪)服务端渲染，通过<code>prerender-spa-plugin</code>在本地模拟浏览器环境,预先执行我们的打包文件,这样通过解析就可以获取首屏的 HTML,在正常环境中,我们就可以返回预先解析好的 HTML 了。</p></li><li><p>FMP(首次有意义绘制)，通过<code>vue-skeleton-webpack-plugin</code>制作一份<code>Skeleton</code>骨架屏</p></li><li><p>使用<code>cdn</code></p></li><li><p>其它等等…</p></li></ol><h1 id="编写后端"><a href="#编写后端" class="headerlink" title="编写后端"></a>编写后端</h1><p>最后，到了编写后端了，为了符合<code>MVC</code>的开发模式，这里我们创建了<code>controllers</code>文件夹处理我们的业务逻辑，具体目录结构如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Code</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">|-- batch-modify-filenames</span><br><span class="line">    ├─batch-front-end     # 前端页面</span><br><span class="line">    ├─utils               # 工具库</span><br><span class="line">    |   └index.js</span><br><span class="line">    ├─uploads             # 存放用户上传的文件</span><br><span class="line">    ├─routes              # 后端路由（接口）</span><br><span class="line">    |   ├─index.js        # 路由入口文件</span><br><span class="line">    |   └upload.js        # 上传接口路由</span><br><span class="line">    ├─controllers         # 接口控制器，处理据具体操作</span><br><span class="line">    |      └upload.js     # 上传接口控制器</span><br><span class="line">    ├─package.json        # 依赖文件</span><br><span class="line">    ├─package-lock.json   # 依赖文件版本锁</span><br><span class="line">    ├─app.js              # 启动文件</span><br></pre></td></tr></table></figure></div><p>因为这次我们的后端只有一个接口，而<code>koa-router</code>的使用也十分简单，所以我只会讲我觉得相对有用的东西 （；´д ｀）ゞ（因为再讲下去，篇幅就太长了）</p><h2 id="路由的使用"><a href="#路由的使用" class="headerlink" title="路由的使用"></a>路由的使用</h2><p><code>koa-router</code>使用非常简单，我们在<code>routes/upload.js</code>书写如下代码：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 导入控制器</span></span><br><span class="line"><span class="keyword">const</span> &#123; upload &#125; = <span class="built_in">require</span>(<span class="string">"../controllers/upload"</span>);</span><br><span class="line"><span class="comment">// 导入路由</span></span><br><span class="line"><span class="keyword">const</span> Router = <span class="built_in">require</span>(<span class="string">"koa-router"</span>);</span><br><span class="line"><span class="comment">// 设置路由前缀为 upload</span></span><br><span class="line"><span class="keyword">const</span> router = <span class="keyword">new</span> Router(&#123;</span><br><span class="line">  prefix: <span class="string">"/upload"</span>,</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">// post请求，请求地址为 ip + 前缀 + '/'，即'/upload/'</span></span><br><span class="line">router.post(<span class="string">"/"</span>, upload);</span><br><span class="line"><span class="comment">// 导出路由</span></span><br><span class="line"><span class="built_in">module</span>.exports = router;</span><br></pre></td></tr></table></figure></div><p>这样子就是写了一个接口了，你可以先将<code>upload</code>理解为一个空方法，什么都不做，只返回<code>请求成功</code>,即<code>ctx.body=&quot;请求成功&quot;</code></p><blockquote><p>上文中间件那里有说，<code>upload</code>的第一个参数为<code>上下文</code>，不理解的翻阅前面内容。</p></blockquote><p>为了方便以后我们导入接口，而不需要每个<code>route</code>都调用一次<code>app.use(route.routes()).use(route.allowedMethods())</code>，我在<code>routes/index.js</code>（即入口文件），书写了一个方法，让他可以自动引入除<code>index.js</code>的其他文件，之后我们只需要新建接口文件就可以而不需要我们手动导入了，代码如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> &#123; resolve &#125; = <span class="built_in">require</span>(<span class="string">"path"</span>);</span><br><span class="line"><span class="comment">// 用于获取文件</span></span><br><span class="line"><span class="keyword">const</span> glob = <span class="built_in">require</span>(<span class="string">"glob"</span>);</span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function">(<span class="params">app</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// 获取当前文件夹下的所有文件，除了自己</span></span><br><span class="line">  glob.sync(resolve(__dirname, <span class="string">"!(index).js"</span>)).forEach(<span class="function">(<span class="params">item</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// 添加路由</span></span><br><span class="line">    <span class="keyword">const</span> route = <span class="built_in">require</span>(item);</span><br><span class="line">    app.use(route.routes()).use(route.allowedMethods());</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><h2 id="文件上传"><a href="#文件上传" class="headerlink" title="文件上传"></a>文件上传</h2><h3 id="为啥使用-koa-body-而不是-koa-bodyparser？"><a href="#为啥使用-koa-body-而不是-koa-bodyparser？" class="headerlink" title="为啥使用 koa-body 而不是 koa-bodyparser？"></a>为啥使用 koa-body 而不是 koa-bodyparser？</h3><p>因为<code>koa-bodyparser</code>不支持文件上传，想要文件上传还必须安装<code>koa-multer</code>，所以我们这里直接使用<code>koa-body</code>一劳永逸。</p><h3 id="文件上传优化"><a href="#文件上传优化" class="headerlink" title="文件上传优化"></a>文件上传优化</h3><p>很显然，我们上传的文件都在<code>uploads</code>目录下，如果日积月累，这个目录文件会越来越多。但同一目录下文件数量过多的时候，就会影响文件读写性能，这样子是我们最不想看到的了。那么有没有什么方法可以优化这个问题呢？当然是有的，我们可以在文件上传前的进行一些操作，比如<strong>根据日期</strong>创建文件夹，然后把文件保存在<strong>当前日期</strong>的文件夹下。</p><p>这样既可以保证性能，又不会导致文件夹的层次过深。而<code>koa-body</code>刚到又有提供<code>onFileBegin</code>这个函数来实现我们的需求，闲话不多说了，开始写代码吧</p><blockquote><p>ps：不建议层次太深，如果层次过深也会影响性能的- -</p></blockquote><p>为了更好的实现我们的需求，我们需要封装了两个基本的工具方法。</p><ol><li><p>根据日期，生成文件夹名称</p></li><li><p>检查文件夹路径是否存在，如果不存在则创建文件夹</p></li></ol><p>即<code>utils/index.js</code>代码如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">"fs"</span>);</span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">"path"</span>);</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 生成文件夹名称</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> getUploadDirName = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> date = <span class="keyword">new</span> <span class="built_in">Date</span>();</span><br><span class="line">  <span class="keyword">let</span> month = date.getMonth() + <span class="number">1</span>;</span><br><span class="line">  <span class="keyword">return</span> <span class="string">`<span class="subst">$&#123;date.getFullYear()&#125;</span><span class="subst">$&#123;month&#125;</span><span class="subst">$&#123;date.getDate()&#125;</span>`</span>;</span><br><span class="line">&#125;;</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 class="doctag">@param <span class="type">&#123;String&#125;</span> </span>pathStr =&gt; 文件夹路径</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> confirmPath = <span class="function">(<span class="params">dirname</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">if</span> (!fs.existsSync(dirname)) &#123;</span><br><span class="line">    <span class="keyword">if</span> (confirmPath(path.dirname(dirname))) &#123;</span><br><span class="line">      fs.mkdirSync(dirname);</span><br><span class="line">      <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  getUploadDirName,</span><br><span class="line">  confirmPath,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><p>写完工具函数后，我们就可以处理我们的<code>koa-body</code>这个中间件啦，具体代码如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> Koa = <span class="built_in">require</span>(<span class="string">"koa"</span>);</span><br><span class="line"><span class="comment">// uuid，生成不重复的文件名</span></span><br><span class="line"><span class="keyword">const</span> &#123; <span class="attr">v4</span>: uuid &#125; = <span class="built_in">require</span>(<span class="string">"uuid"</span>);</span><br><span class="line"><span class="comment">// 工具函数</span></span><br><span class="line"><span class="keyword">const</span> &#123; getUploadDirName, confirmPath &#125; = <span class="built_in">require</span>(<span class="string">"./utils/"</span>);</span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Koa();</span><br><span class="line"><span class="comment">// 解析post请求，</span></span><br><span class="line"><span class="keyword">const</span> koaBody = <span class="built_in">require</span>(<span class="string">"koa-body"</span>);</span><br><span class="line"><span class="comment">// 处理post请求的中间件</span></span><br><span class="line">app.use(</span><br><span class="line">  koaBody(&#123;</span><br><span class="line">    multipart: <span class="literal">true</span>, <span class="comment">// 支持文件上传</span></span><br><span class="line">    formidable: &#123;</span><br><span class="line">      maxFieldsSize: <span class="number">10</span> * <span class="number">1024</span> * <span class="number">1024</span>, <span class="comment">// 设置上传文件大小最大限制，默认2M</span></span><br><span class="line">      keepExtensions: <span class="literal">true</span>, <span class="comment">// 保持拓展名</span></span><br><span class="line">      uploadDir: resolve(__dirname, <span class="string">`uploads`</span>),</span><br><span class="line">      <span class="comment">// 文件上传前的一些设置操作</span></span><br><span class="line">      onFileBegin(name, file) &#123;</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">const</span> dirName = getUploadDirName();</span><br><span class="line">        <span class="comment">// 生成文件名</span></span><br><span class="line">        <span class="keyword">const</span> fileName = uuid();</span><br><span class="line">        <span class="keyword">const</span> dir = resolve(__dirname, <span class="string">`uploads/<span class="subst">$&#123;dirName&#125;</span>`</span>);</span><br><span class="line">        <span class="comment">// 检查文件夹是否存在如果不存在则新建文件夹</span></span><br><span class="line">        confirmPath(dir);</span><br><span class="line">        <span class="comment">// 重新覆盖 file.path 属性</span></span><br><span class="line">        file.path = join(dir, fileName);</span><br><span class="line">        <span class="comment">// 便于后续中间件使用</span></span><br><span class="line">        <span class="comment">// app.context.uploadPath = `$&#123;dirName&#125;/$&#123;fileName&#125;`;</span></span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;)</span><br><span class="line">);</span><br></pre></td></tr></table></figure></div><blockquote><p>ps： 针对于<code>uuid</code>的版本问题，建议看：<a href="https://www.zhihu.com/question/34876910" target="_blank" rel="noopener">UUID 是如何保证唯一性的？</a>，这里我们使用的是<code>v4</code>版本，也是最常用的一个版本。。</p></blockquote><h2 id="文件下载"><a href="#文件下载" class="headerlink" title="文件下载"></a>文件下载</h2><p>和之前讲的一样，后端只需要设置<code>Content-Type</code>和<code>Content-Disposition</code>这两个响应头就可以实现下载了。但是<code>archiver</code>这个库搭配<code>Koa</code>返回流给前端，确实让我措手不及。</p><p>我参考了官方<a href="https://github.com/archiverjs/node-archiver/blob/master/examples/express.js" target="_blank" rel="noopener">Express 这个例子</a>，但是发现在<code>Koa</code>身上不顶用，于是我就- -一直翻<code>issue</code>，发现很多人和我有同样的问题，最后终于在<code>stackoverflow</code>找到了想要的答案。我们可以通过<code>new Stream.PassThrough()</code>创建一个双向流，让<code>archiver</code>通过<code>pipe</code>把数据流写入到<code>双向流</code>里，再通过<code>Koa</code>返回给前端即可，具体实现如下（<code>controllers/upload.js</code>）：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 压缩文件</span></span><br><span class="line"><span class="keyword">const</span> archiver = <span class="built_in">require</span>(<span class="string">"archiver"</span>);</span><br><span class="line"><span class="keyword">const</span> Stream = <span class="built_in">require</span>(<span class="string">"stream"</span>);</span><br><span class="line"><span class="comment">// 判断是否为数组，如果不是，则转为数组</span></span><br><span class="line"><span class="keyword">const</span> isArray = <span class="function">(<span class="params">arr</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">if</span> (!<span class="built_in">Array</span>.isArray(arr)) &#123;</span><br><span class="line">    arr = [arr];</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> arr;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">// 上传接口</span></span><br><span class="line">exports.upload = <span class="keyword">async</span> (ctx) =&gt; &#123;</span><br><span class="line">  <span class="comment">// 获取上传的文件</span></span><br><span class="line">  <span class="keyword">let</span> &#123; files &#125; = ctx.request.files;</span><br><span class="line">  <span class="comment">// 获取上传的文件名</span></span><br><span class="line">  <span class="keyword">let</span> filenames = isArray(ctx.request.body.name);</span><br><span class="line">  <span class="comment">// 将文件转为数组</span></span><br><span class="line">  files = isArray(files);</span><br><span class="line">  <span class="comment">// 设置响应头，告诉浏览器我要下载的文件叫做files.zip</span></span><br><span class="line">  <span class="comment">// attachment用于浏览器文件下载</span></span><br><span class="line">  ctx.attachment(<span class="string">"files.zip"</span>);</span><br><span class="line">  <span class="comment">// 设置响应头的类型</span></span><br><span class="line">  ctx.set(&#123; <span class="string">"Content-Type"</span>: <span class="string">"application/zip"</span> &#125;);</span><br><span class="line">  <span class="comment">// 定义一个双向流</span></span><br><span class="line">  <span class="keyword">const</span> stream = <span class="keyword">new</span> Stream.PassThrough();</span><br><span class="line">  <span class="comment">// 把流返回给前端</span></span><br><span class="line">  ctx.body = stream;</span><br><span class="line">  <span class="comment">// 压缩成zip</span></span><br><span class="line">  <span class="keyword">const</span> archive = archiver(<span class="string">"zip"</span>, &#123;</span><br><span class="line">    zlib: &#123; <span class="attr">level</span>: <span class="number">9</span> &#125;, <span class="comment">// Sets the compression level.</span></span><br><span class="line">  &#125;);</span><br><span class="line">  archive.pipe(stream);</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; files.length; i++) &#123;</span><br><span class="line">    <span class="keyword">const</span> path = files[i].path;</span><br><span class="line">    archive.file(path, &#123; <span class="attr">name</span>: filenames[i] &#125;);</span><br><span class="line">  &#125;</span><br><span class="line">  archive.finalize();</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><p>这个处理也特别简单，就是根据前端传过来的文件名，把文件重命名即可。最后我们整理一下，<code>app.js</code>的代码如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> &#123; resolve, join &#125; = <span class="built_in">require</span>(<span class="string">"path"</span>);</span><br><span class="line"><span class="keyword">const</span> Koa = <span class="built_in">require</span>(<span class="string">"koa"</span>);</span><br><span class="line"><span class="comment">// 解析post请求，</span></span><br><span class="line"><span class="keyword">const</span> koaBody = <span class="built_in">require</span>(<span class="string">"koa-body"</span>);</span><br><span class="line"><span class="comment">// 静态服务器</span></span><br><span class="line"><span class="keyword">const</span> serve = <span class="built_in">require</span>(<span class="string">"koa-static"</span>);</span><br><span class="line"><span class="comment">// uuid，生成不重复的文件名</span></span><br><span class="line"><span class="keyword">const</span> &#123; <span class="attr">v4</span>: uuid &#125; = <span class="built_in">require</span>(<span class="string">"uuid"</span>);</span><br><span class="line"><span class="comment">// 工具函数</span></span><br><span class="line"><span class="keyword">const</span> &#123; getUploadDirName, confirmPath &#125; = <span class="built_in">require</span>(<span class="string">"./utils/"</span>);</span><br><span class="line"><span class="comment">// 初始化路由</span></span><br><span class="line"><span class="keyword">const</span> initRoutes = <span class="built_in">require</span>(<span class="string">"./routes"</span>);</span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Koa();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 处理post请求的中间件</span></span><br><span class="line">app.use(</span><br><span class="line">  koaBody(&#123;</span><br><span class="line">    multipart: <span class="literal">true</span>, <span class="comment">// 支持文件上传</span></span><br><span class="line">    formidable: &#123;</span><br><span class="line">      maxFieldsSize: <span class="number">10</span> * <span class="number">1024</span> * <span class="number">1024</span>, <span class="comment">// 设置上传文件大小最大限制，默认2M</span></span><br><span class="line">      keepExtensions: <span class="literal">true</span>, <span class="comment">// 保持拓展名</span></span><br><span class="line">      uploadDir: resolve(__dirname, <span class="string">`uploads`</span>),</span><br><span class="line">      <span class="comment">// 文件上传前的一些设置操作</span></span><br><span class="line">      onFileBegin(name, file) &#123;</span><br><span class="line">        <span class="comment">// 最终要保存到的文件夹目录</span></span><br><span class="line">        <span class="keyword">const</span> dirName = getUploadDirName();</span><br><span class="line">        <span class="keyword">const</span> fileName = uuid();</span><br><span class="line">        <span class="keyword">const</span> dir = resolve(__dirname, <span class="string">`uploads/<span class="subst">$&#123;dirName&#125;</span>`</span>);</span><br><span class="line">        <span class="comment">// 检查文件夹是否存在如果不存在则新建文件夹</span></span><br><span class="line">        confirmPath(dir);</span><br><span class="line">        <span class="comment">// 重新覆盖 file.path 属性</span></span><br><span class="line">        file.path = join(dir, fileName);</span><br><span class="line">        <span class="comment">// 便于后续中间件使用</span></span><br><span class="line">        <span class="comment">// app.context.uploadPath = `$&#123;dirName&#125;/$&#123;fileName&#125;`;</span></span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;)</span><br><span class="line">);</span><br><span class="line"><span class="comment">// 静态服务器</span></span><br><span class="line">app.use(</span><br><span class="line">  serve(resolve(__dirname, <span class="string">"public"</span>), &#123;</span><br><span class="line">    maxage: <span class="number">60</span> * <span class="number">60</span> * <span class="number">1000</span>,</span><br><span class="line">  &#125;)</span><br><span class="line">);</span><br><span class="line"><span class="comment">// 初始化路由</span></span><br><span class="line">initRoutes(app);</span><br><span class="line">app.listen(<span class="number">3000</span>, () =&gt; &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">`listen successd`</span>);</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">`服务器运行于 http://localhost:<span class="subst">$&#123;<span class="number">3000</span>&#125;</span>`</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></div><p>到这里，基本上本次本章就结束了。当然，其实我们前端界面还可以做的更加可控一点的，比如我可以修改<code>新文件列表</code>的某个文件，使他可以单独自定义而不根据我们的配置走，而根据用户输入的自定义名称走。不过，这个就留给各位当作小作业啦<del>~</del></p><p>顺便提一嘴，因为我们是在浏览器上操作的，没有操作文件的权限，所以写起来会比较麻烦- -如果用<code>Electron</code>编写的话，就方便多了。😝</p><blockquote><p><a href="https://gitee.com/gating/demo/tree/master/batch-modify-filenames" target="_blank" rel="noopener">gitee 地址</a>,<a href="https://github.com/GATING/demo/tree/master/batch-modify-filenames" target="_blank" rel="noopener">github 地址</a></p></blockquote><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>感谢各位观众老爷的观看 O(∩_∩)O 希望你能有所收获 😁</p>]]></content>
    
    <summary type="html">
    
      书接上回，我们实现了批量修改文件的时间，但是却没有实现文件名称的批量修改，是因为我也说过，没有界面的话直接在命令行实现显得有点繁琐，所以我们就通过`接口+界面`的方式来实现我们这个小需求吧。所以，闲话不多说啦，开始写我们的代码啦~~
    
    </summary>
    
    
      <category term="小工具" scheme="https://gatings.cn/categories/%E5%B0%8F%E5%B7%A5%E5%85%B7/"/>
    
    
      <category term="vue" scheme="https://gatings.cn/tags/vue/"/>
    
      <category term="node" scheme="https://gatings.cn/tags/node/"/>
    
  </entry>
  
  <entry>
    <title>node实现文件属性批量修改(时间属性)</title>
    <link href="https://gatings.cn/2020-06-17/node%E5%AE%9E%E7%8E%B0%E6%96%87%E4%BB%B6%E5%B1%9E%E6%80%A7%E6%89%B9%E9%87%8F%E4%BF%AE%E6%94%B9/"/>
    <id>https://gatings.cn/2020-06-17/node%E5%AE%9E%E7%8E%B0%E6%96%87%E4%BB%B6%E5%B1%9E%E6%80%A7%E6%89%B9%E9%87%8F%E4%BF%AE%E6%94%B9/</id>
    <published>2020-06-16T16:00:00.000Z</published>
    <updated>2021-10-13T01:56:29.703Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>在默认情况下，一个文件的<code>创建时间</code>和<code>修改时间</code>是系统自己设定的，我们不能修改该的。但我们有时为了某种特殊需要，为了不让别人一眼看出文件已经给修改了，我们又需要修改文件的<code>创建时间</code>和<code>修改时间</code>。那么如何修改文件夹时间，如何修改文件的创建时间，如何批量修改文件的<code>创建时间</code>、<code>修改时间</code>和<code>访问时间</code>呢？别着急，接下来就带你自己修改他们。所以，闲话不多说啦，开始写我们的代码啦~~</p><blockquote><p>ps：小工具推荐<a href="https://wwa.lanzous.com/iNDjKdqzg9e" target="_blank" rel="noopener">NewFileTime</a>，以上简述摘抄于<code>NewFileTime</code></p></blockquote><h1 id="简单的搭建一下"><a href="#简单的搭建一下" class="headerlink" title="简单的搭建一下"></a>简单的搭建一下</h1><ul><li><p>新建一个 <code>files</code> 目录</p></li><li><p>初始化一个<code>node</code>项目工程</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm init -y</span><br></pre></td></tr></table></figure></div></li></ul><p>看到这里你会发现，其实我没有安装依赖，是因为原生的库有这个自带的功能吗？说是也行，说不是也行。原生的<code>utimes</code>目前支持修改文件的<code>修改时间</code>和<code>访问时间</code>，不支持修改文件的<code>创建时间</code>，所以我们需要借助一个第三方库来修改。</p><h2 id="为什么不直接安装这个第三方库呢？"><a href="#为什么不直接安装这个第三方库呢？" class="headerlink" title="为什么不直接安装这个第三方库呢？"></a>为什么不直接安装这个第三方库呢？</h2><p>因为这个库有些许特殊，分两种情况，一个是低版本<code>Node</code>可以直接安装，在我本机的<code>Node13</code>上运行则会失败。具体原因嘛，可以看看下方的链接</p><blockquote><p>ps: <a href="https://github.com/ronomon/utimes/pull/8" target="_blank" rel="noopener">原因 + 解决方案</a></p></blockquote><p>所以，在低版本的<code>Node</code>我们可以直接<code>npm install @ronomon/utimes</code>，而在版本相对较高的则需要<code>npm i https://github.com/Jule-/utimes.git#napi-migration</code>啦</p><p>这里也提一嘴，如果<code>@ronomon/utimes</code>安装失败的话，是因为这些<code>原生Node</code>拓展是需要编译的，所以我们可能需要安装<code>windows-build-tools</code>，即以管理员身份启动<code>PowerShell</code>并运行：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm install --global windows-build-tools</span><br></pre></td></tr></table></figure></div><p>安装完依赖之后就可以正式写我们的代码啦，其实这个代码相对简单，就是直接调用它的<code>api</code>就好了。</p><h1 id="简单的使用一下"><a href="#简单的使用一下" class="headerlink" title="简单的使用一下"></a>简单的使用一下</h1><ul><li><p>新建一个<code>test-files</code>文件夹</p></li><li><p>在<code>test-files</code>文件夹新建<code>1.txt</code>文件供我们测试</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-17/utimes02.jpg" data-fancybox="group" data-caption="1.txt文件" class="fancybox"><img alt="1.txt文件" title="1.txt文件" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-17/utimes02.jpg" class="lazyload"></a></p></li><li><p>编写如下代码：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 导入 utimes</span></span><br><span class="line"><span class="keyword">const</span> &#123; utimes &#125; = <span class="built_in">require</span>(<span class="string">"@ronomon/utimes"</span>);</span><br><span class="line">utimes(</span><br><span class="line">  <span class="string">"./test-files/1.txt"</span>,</span><br><span class="line">  <span class="comment">// 创建时间</span></span><br><span class="line">  +<span class="keyword">new</span> <span class="built_in">Date</span>(<span class="string">"2010/01/01"</span>),</span><br><span class="line">  <span class="comment">// 修改时间</span></span><br><span class="line">  +<span class="keyword">new</span> <span class="built_in">Date</span>(<span class="string">"2010/01/02"</span>),</span><br><span class="line">  <span class="comment">// 访问时间</span></span><br><span class="line">  +<span class="keyword">new</span> <span class="built_in">Date</span>(<span class="string">"2010/01/03"</span>),</span><br><span class="line">  (err) =&gt; &#123;</span><br><span class="line">    <span class="comment">//  修改成功的回调</span></span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">`success`</span>);</span><br><span class="line">  &#125;</span><br><span class="line">);</span><br></pre></td></tr></table></figure></div></li><li><p>运行代码，<code>node app.js</code>，是都发现日期发生了改变呢？</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-17/utimes02.jpg" data-fancybox="group" data-caption="1.txt文件" class="fancybox"><img alt="1.txt文件" title="1.txt文件" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-17/utimes02.jpg" class="lazyload"></a></p></li></ul><p>看到这里你以为是不是写完了，其实也差不多了 😝，不过我当然不会让你收获这么少的，至少我们可以看看我们这个最最最简单的例子的缺点，比如代码没有<code>Promise</code>化，那么我们就封装一下<code>utimes</code></p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String&#125;</span> </span>path =&gt; 路径</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>btime =&gt; 创建时间，不传即不修改</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>mtime =&gt; 修改时间，不传即不修改</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>atime =&gt; 访问时间，不传即不修改</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> utimesPromise = <span class="function">(<span class="params">path, btime, mtime, atime</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">    utimes(path, btime, mtime, atime, (err) =&gt; (err ? reject(err) : resolve()));</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><p>当然- -，因为我们使用的是<code>Node</code>，所以我们不需要常规的用<code>new Promise</code>封装，可以直接使用内置的<code>util</code>这个工具中的<code>promisify</code>方法封装即可</p><blockquote><p>util.promisify 是在 node.js 8.x 版本中新增的一个工具，用于将老式的 <code>Error first callback</code> 转换为 <code>Promise</code> 对象，让老项目改造变得更为轻松。在官方推出这个工具之前，民间已经有很多类似的工具了，比如 <code>es6-promisify</code>、<code>thenify</code>、<code>bluebird.promisify</code>。以及很多其他优秀的工具，都是实现了这样的功能，帮助我们在处理老项目的时候，不必费神将各种代码使用 <code>Promise</code> 再重新实现一遍。</p></blockquote><p>所以，我们的封装又变得更加简单了，代码如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> &#123; promisify &#125; = <span class="built_in">require</span>(<span class="string">"util"</span>);</span><br><span class="line"><span class="keyword">const</span> utimesPromise = promisify(utimes);</span><br></pre></td></tr></table></figure></div><p>之前的代码就可以改写成之前我们那样的自执行<code>Async Function</code>了，代码如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// ...</span></span><br><span class="line">(<span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line">  <span class="keyword">await</span> utimesPromise(</span><br><span class="line">    <span class="string">"./test-files/1.txt"</span>,</span><br><span class="line">    <span class="comment">// 创建事件</span></span><br><span class="line">    +<span class="keyword">new</span> <span class="built_in">Date</span>(<span class="string">"2010/01/01"</span>),</span><br><span class="line">    <span class="comment">// 修改时间</span></span><br><span class="line">    +<span class="keyword">new</span> <span class="built_in">Date</span>(<span class="string">"2010/01/02"</span>),</span><br><span class="line">    <span class="comment">// 访问时间</span></span><br><span class="line">    +<span class="keyword">new</span> <span class="built_in">Date</span>(<span class="string">"2010/01/03"</span>)</span><br><span class="line">  );</span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure></div><p>写到这里，你会发现其实我们根本没有做批量修改，是因为有了之前的经验，我们可以直接通过<code>glob</code>这个工具获取所有的路径，根本不要我们操心，写起来也十分简单，所以我打算最后再来写</p><ul><li><p>安装<code>glob</code></p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm i glob -S</span><br></pre></td></tr></table></figure></div></li><li><p>多建几个文件用于测试我们的代码</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-17/utimes03.jpg" data-fancybox="group" data-caption="创建文件" class="fancybox"><img alt="创建文件" title="创建文件" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-17/utimes03.jpg" class="lazyload"></a></p><p>得出下面列表：</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-17/utimes04.jpg" data-fancybox="group" data-caption="创建文件" class="fancybox"><img alt="创建文件" title="创建文件" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-17/utimes04.jpg" class="lazyload"></a></p></li><li><p>修改我们的代码：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> &#123; utimes &#125; = <span class="built_in">require</span>(<span class="string">"@ronomon/utimes"</span>);</span><br><span class="line"><span class="keyword">const</span> glob = <span class="built_in">require</span>(<span class="string">"glob"</span>);</span><br><span class="line"><span class="keyword">const</span> &#123; promisify &#125; = <span class="built_in">require</span>(<span class="string">"util"</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String&#125;</span> </span>path =&gt; 路径</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>btime =&gt; 创建时间，不传即不修改</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>mtime =&gt; 修改时间，不传即不修改</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>atime =&gt; 访问时间，不传即不修改</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> utimesPromise = promisify(utimes);</span><br><span class="line"></span><br><span class="line">(<span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line">  <span class="keyword">const</span> paths = glob.sync(<span class="string">"./test-files/**"</span>);</span><br><span class="line">  <span class="keyword">const</span> len = paths.length;</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; len; i++) &#123;</span><br><span class="line">    <span class="keyword">await</span> utimesPromise(</span><br><span class="line">      paths[i],</span><br><span class="line">      +<span class="keyword">new</span> <span class="built_in">Date</span>(<span class="string">"2010/01/01"</span>),</span><br><span class="line">      +<span class="keyword">new</span> <span class="built_in">Date</span>(<span class="string">"2010/01/02"</span>),</span><br><span class="line">      +<span class="keyword">new</span> <span class="built_in">Date</span>(<span class="string">"2010/01/04"</span>)</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure></div></li><li><p>得出结果</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-17/utimes05.jpg" data-fancybox="group" data-caption="运行结果" class="fancybox"><img alt="运行结果" title="运行结果" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-17/utimes05.jpg" class="lazyload"></a></p></li></ul><p>这样子就递归了我们所有的文件夹跟子文件了进行修改了，本来想着在加载名字修改的，但苦于- -没有界面，篇幅也过长，就留着过几天再写了。</p><blockquote><p><a href="https://gitee.com/gating/demo/tree/master/files" target="_blank" rel="noopener">gitee 地址</a>,<a href="https://github.com/GATING/demo/tree/master/files" target="_blank" rel="noopener">github 地址</a></p></blockquote><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>感谢各位观众老爷的观看 O(∩_∩)O 希望你能有所收获 😁</p>]]></content>
    
    <summary type="html">
    
      在默认情况下，一个文件的创建时间和修改时间是系统自己设定的，我们不能修改该的。但我们有时为了某种特殊需要，为了不让别人一眼看出文件已经给修改了，我们又需要修改文件的创建时间和修改时间。那么如何修改文件夹时间，如何修改文件的创建时间，如何批量修改文件的创建时间、修改时间和访问时间呢？
    
    </summary>
    
    
      <category term="小工具" scheme="https://gatings.cn/categories/%E5%B0%8F%E5%B7%A5%E5%85%B7/"/>
    
    
      <category term="node" scheme="https://gatings.cn/tags/node/"/>
    
  </entry>
  
  <entry>
    <title>node实现批量修改图片尺寸</title>
    <link href="https://gatings.cn/2020-06-12/node%E5%AE%9E%E7%8E%B0%E5%9B%BE%E7%89%87%E5%B0%BA%E5%AF%B8%E6%89%B9%E9%87%8F%E4%BF%AE%E6%94%B9/"/>
    <id>https://gatings.cn/2020-06-12/node%E5%AE%9E%E7%8E%B0%E5%9B%BE%E7%89%87%E5%B0%BA%E5%AF%B8%E6%89%B9%E9%87%8F%E4%BF%AE%E6%94%B9/</id>
    <published>2020-06-11T16:00:00.000Z</published>
    <updated>2021-10-13T01:56:29.703Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>大家在工作中肯定有没有遇到过图片尺寸和我们要求的尺寸不一致的情况吧？通常我们会在网上找一下找在线的或者下载一个小工具，再或者通过<code>ps</code>的批处理解决。但是，作为<code>程序猿</code>，当然还是通过代码来解决这种小问题啦。所以，闲话不多说啦，开始写我们的代码啦~~</p><h1 id="简单的搭建一下"><a href="#简单的搭建一下" class="headerlink" title="简单的搭建一下"></a>简单的搭建一下</h1><ul><li><p>新建一个 <code>canvas-image-resize</code> 目录</p></li><li><p>初始化一个<code>node</code>项目工程</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm init -y</span><br></pre></td></tr></table></figure></div></li><li><p>安装依赖，这里主要用到了三个依赖，分别是<code>处理图片</code>、<code>批量处理文件</code>、<code>压缩成zip文件</code></p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm i canvas glob archiver -S</span><br></pre></td></tr></table></figure></div><p>没错，这里我们又用到了<code>canvas</code>这个库，惊不惊喜，意不意外 😂</p></li></ul><h1 id="简单的使用一下"><a href="#简单的使用一下" class="headerlink" title="简单的使用一下"></a>简单的使用一下</h1><p>同样，有了前面我们使用<code>canvas</code>的经验，书写这个代码应该问题也不大，主要是对<code>api</code>的熟练问题</p><p>查看文档我们不难发现，<code>drawImage</code>的第四和第五个参数就是设置图片的宽高，知道这个之后，我们书写代码就简单不少了</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">drawImage(image: Canvas|Image, <span class="attr">dx</span>: number, <span class="attr">dy</span>: number, <span class="attr">dw</span>: number, <span class="attr">dh</span>: number): <span class="keyword">void</span></span><br></pre></td></tr></table></figure></div><p>所以，我们的代码大概如下，</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 创建写入流</span></span><br><span class="line"><span class="keyword">const</span> &#123; createWriteStream &#125; = <span class="built_in">require</span>(<span class="string">"fs"</span>);</span><br><span class="line"><span class="comment">// 获取文件名</span></span><br><span class="line"><span class="keyword">const</span> &#123; basename &#125; = <span class="built_in">require</span>(<span class="string">"path"</span>);</span><br><span class="line"><span class="comment">// 压缩文件</span></span><br><span class="line"><span class="keyword">const</span> archiver = <span class="built_in">require</span>(<span class="string">"archiver"</span>);</span><br><span class="line"><span class="comment">// 导入canvas库，用于裁剪图片</span></span><br><span class="line"><span class="keyword">const</span> &#123; createCanvas, loadImage &#125; = <span class="built_in">require</span>(<span class="string">"canvas"</span>);</span><br><span class="line"><span class="comment">// 批量获取路径</span></span><br><span class="line"><span class="keyword">const</span> glob = <span class="built_in">require</span>(<span class="string">"glob"</span>);</span><br><span class="line">!<span class="function">(<span class="params"><span class="keyword">async</span> (</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> paths = glob.sync(<span class="string">"./images/*"</span>);</span><br><span class="line">  <span class="comment">// 压缩成zip</span></span><br><span class="line">  <span class="keyword">const</span> archive = archiver(<span class="string">"zip"</span>, &#123;</span><br><span class="line">    zlib: &#123; <span class="attr">level</span>: <span class="number">9</span> &#125;, <span class="comment">// Sets the compression level.</span></span><br><span class="line">  &#125;);</span><br><span class="line">  <span class="comment">// 输出到当前文件夹下的 image-resize.zip</span></span><br><span class="line">  <span class="keyword">const</span> output = createWriteStream(__dirname + <span class="string">"/image-resize.zip"</span>);</span><br><span class="line">  archive.pipe(output);</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; paths.length; i++) &#123;</span><br><span class="line">    <span class="keyword">const</span> path = paths[i];</span><br><span class="line">    <span class="keyword">const</span> image = <span class="keyword">await</span> loadImage(path);</span><br><span class="line">    <span class="keyword">const</span> &#123; width, height &#125; = image;</span><br><span class="line">    <span class="keyword">const</span> options = [width, height].map(<span class="function">(<span class="params">item</span>) =&gt;</span> item / <span class="number">2</span>);</span><br><span class="line">    <span class="keyword">const</span> canvas = createCanvas(...options);</span><br><span class="line">    <span class="keyword">const</span> ctx = canvas.getContext(<span class="string">"2d"</span>);</span><br><span class="line">    ctx.drawImage(image, <span class="number">0</span>, <span class="number">0</span>, ...options);</span><br><span class="line">    archive.append(canvas.toBuffer(), &#123; <span class="attr">name</span>: <span class="string">`<span class="subst">$&#123;basename(path)&#125;</span>`</span> &#125;);</span><br><span class="line">  &#125;</span><br><span class="line">  archive.finalize();</span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure></div><p>从上面代码可以看出，这里我只是对宽高进行了缩放一倍，没有做更多的配置，为了代码的健壮性，我们修改下我们的<code>options</code>，使得整个程序可以自定义宽高、可以根据宽度进行缩放、根据高度进行缩放</p><p>定义一下我们可配置的参数，基本配置是这样的:</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// 自定义宽度，传一个根据宽度等比缩放</span></span><br><span class="line">  width: <span class="string">""</span>,</span><br><span class="line">  <span class="comment">// 自定义高度，传一个根据高度等比缩放</span></span><br><span class="line">  height: <span class="string">""</span>,</span><br><span class="line">  <span class="comment">// 根据宽度等比缩放，优先级更高</span></span><br><span class="line">  isWidth: <span class="literal">false</span>,</span><br><span class="line">  <span class="comment">// 根据高度等比缩放</span></span><br><span class="line">  isHeight: <span class="literal">false</span>,</span><br><span class="line">  <span class="comment">// 宽高整体缩放</span></span><br><span class="line">  scale: <span class="number">1</span>,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><blockquote><p>ps：因为我们暂时没有图形界面，所以就定义一个<code>config.js</code>来模拟我们的插件啦</p></blockquote><p>所以，在当前目录下，新建一个<code>config.js</code>，书写上我们那些配置，然后在<code>app.js</code>导入下，基本代码就变成了如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// ....</span></span><br><span class="line"><span class="comment">// 导入配置文件（用户传过来的配置）</span></span><br><span class="line"><span class="keyword">const</span> config = <span class="built_in">require</span>(<span class="string">"./config"</span>);</span><br><span class="line"><span class="comment">// 根据配置获取宽高</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getOptions</span>(<span class="params">options, config</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 书写配置相关的代码，默认缩放两倍</span></span><br><span class="line">  <span class="keyword">return</span> options.map(<span class="function">(<span class="params">item</span>) =&gt;</span> item / <span class="number">2</span>);</span><br><span class="line">&#125;</span><br><span class="line">!<span class="function">(<span class="params"><span class="keyword">async</span> (</span>) =&gt;</span> &#123;</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 &lt; paths.length; i++) &#123;</span><br><span class="line">    <span class="keyword">const</span> path = paths[i];</span><br><span class="line">    <span class="keyword">const</span> image = <span class="keyword">await</span> loadImage(path);</span><br><span class="line">    <span class="keyword">const</span> &#123; width, height &#125; = image;</span><br><span class="line">    <span class="keyword">const</span> options = getOptions(&#123; width, height &#125;, config);</span><br><span class="line">    <span class="keyword">const</span> canvas = createCanvas(...options);</span><br><span class="line">    <span class="keyword">const</span> ctx = canvas.getContext(<span class="string">"2d"</span>);</span><br><span class="line">    ctx.drawImage(image, <span class="number">0</span>, <span class="number">0</span>, ...options);</span><br><span class="line">    archive.append(canvas.toBuffer(), &#123; <span class="attr">name</span>: <span class="string">`<span class="subst">$&#123;basename(path)&#125;</span>`</span> &#125;);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">//  ....</span></span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure></div><p>然后根据我们的配置文件来写逻辑的话，大概会出现如下逻辑：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 根据配置获取宽高</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getOptions</span>(<span class="params">options, config</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> [sourceWidth, sourceHeight] = options;</span><br><span class="line">  <span class="keyword">const</span> &#123; width, height, isWidth, isHeight, scale &#125; = config;</span><br><span class="line">  <span class="keyword">if</span> (width === <span class="number">0</span> || height === <span class="number">0</span>) <span class="keyword">return</span> [<span class="number">0</span>, <span class="number">0</span>];</span><br><span class="line">  <span class="keyword">if</span> (width &amp;&amp; height) &#123;</span><br><span class="line">    <span class="keyword">if</span> (isWidth) &#123;</span><br><span class="line">      <span class="keyword">return</span> [width, (sourceHeight * width * scale) / sourceWidth];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (isHeight) &#123;</span><br><span class="line">      <span class="keyword">return</span> [(sourceWidth * height * scale) / sourceHeight, height];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> [width / scale, height / scale];</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (width &amp;&amp; !height) &#123;</span><br><span class="line">    <span class="keyword">return</span> [width, (sourceHeight * width * scale) / sourceWidth];</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (height &amp;&amp; !width) &#123;</span><br><span class="line">    <span class="keyword">return</span> [(sourceWidth * height * scale) / sourceHeight, height];</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> options.map(<span class="function">(<span class="params">item</span>) =&gt;</span> item / scale);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>发现了吗？是不是感觉很乱？就算我们把一些公有部分提取出来改写如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 根据配置获取宽高</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getOptions</span>(<span class="params">options, config</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> [sourceWidth, sourceHeight] = options;</span><br><span class="line">  <span class="keyword">const</span> &#123; width, height, isWidth, isHeight, scale &#125; = config;</span><br><span class="line">  <span class="keyword">if</span> (width === <span class="number">0</span> || height === <span class="number">0</span>) <span class="keyword">return</span> [<span class="number">0</span>, <span class="number">0</span>];</span><br><span class="line">  <span class="keyword">const</span> widthOfOptions = [</span><br><span class="line">    width * scale,</span><br><span class="line">    (sourceHeight * width * scale) / sourceWidth,</span><br><span class="line">  ];</span><br><span class="line">  <span class="keyword">const</span> heightOfOptions = [</span><br><span class="line">    (sourceWidth * height * scale) / sourceHeight,</span><br><span class="line">    height * scale,</span><br><span class="line">  ];</span><br><span class="line">  <span class="keyword">if</span> (width &amp;&amp; height) &#123;</span><br><span class="line">    <span class="keyword">if</span> (isWidth) &#123;</span><br><span class="line">      <span class="keyword">return</span> widthOfOptions;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (isHeight) &#123;</span><br><span class="line">      <span class="keyword">return</span> heightOfOptions;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> [width / scale, height / scale];</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (width &amp;&amp; !height) &#123;</span><br><span class="line">    <span class="keyword">return</span> widthOfOptions;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (height &amp;&amp; !width) &#123;</span><br><span class="line">    <span class="keyword">return</span> heightOfOptions;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> options.map(<span class="function">(<span class="params">item</span>) =&gt;</span> item / scale);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>其实就算经过我们这么优化，其实看起来也不是特别优雅，不知道大家是否还记得我之前的一篇文章 <a href="https://gatings.cn/2020-01-17/%E4%BB%8E%E9%9B%B6%E6%90%AD%E5%BB%BAWindow%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83/#%E4%BD%BF%E7%94%A8Map%E4%BB%A3%E6%9B%BFif-else">从零搭建 Window 前端开发环境</a>，这里说过，我们可以使用<code>使用 Map 代替 if/else</code>，让我们的代码变得更优雅，可读性也更好。所以，接下来我们就通过 <code>Map</code> 来改写我们的代码吧</p><blockquote><p>ps: 如果判断简单，其实用<code>{}</code>对象也可以，这里只是用<code>Map</code>做个延申</p></blockquote><h2 id="思考一下，为什么用-Map-更好呢？"><a href="#思考一下，为什么用-Map-更好呢？" class="headerlink" title="思考一下，为什么用 Map 更好呢？"></a>思考一下，为什么用 Map 更好呢？</h2><p>说到这个，就不得不说 Map 对象和 Object 的区别了，他两有不少语法上的区别，比如<code>Map</code>获取值需要<code>get(key)</code>,设置值需要<code>set(key,value)</code>，但是这些区别不在我们讨论的范围内，我们说说他两最主要也是最重要的区别：</p><ul><li>一个对象的键只能是字符串或者 Symbols，但一个 Map 的键可以是任意值。</li><li>Map 自身有 size 属性，可以自己维护自己的 size，而对象的键值对个数只能手动确认。</li></ul><h2 id="优化代码"><a href="#优化代码" class="headerlink" title="优化代码"></a>优化代码</h2><p>知道了他两的区别后，我们就可以边写代码啦~~刚刚说到，<code>Map</code>的<code>key</code>可是是任意值，所以我们就可以使用正则类型(<code>RegExp</code>)来作为我们的<code>key</code>了，而正是因为有了正则，那么我们的判断就有了无限可能，可以适应各种情况。</p><h3 id="思考一下怎么通过正则来实现我们的代码呢？"><a href="#思考一下怎么通过正则来实现我们的代码呢？" class="headerlink" title="思考一下怎么通过正则来实现我们的代码呢？"></a>思考一下怎么通过正则来实现我们的代码呢？</h3><p>首先我们可以先观察下我们之前<code>if/else</code>这个版本的代码，最先判断的是不是有没有宽高，即宽高是否为 0，所以我们就可以通过这个条件把我们的判断改为布尔值，因为<code>js</code>是弱类型的，所以我们就可以用<code>0</code>或者<code>1</code>来表示了，又因为这里存在不传则根据传的值缩放的情况，所以我们需要额外判断当他为<code>空字符串</code>时取<code>01</code>之外的数字，这里我取的是<code>2</code>。</p><p>这里可能有点绕口，我举两个例子大家可能就就懂了，假如我们传入的数据为默认数据：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// 自定义宽度，传一个根据宽度等比缩放</span></span><br><span class="line">  width: <span class="string">""</span>,</span><br><span class="line">  <span class="comment">// 自定义高度，传一个根据高度等比缩放</span></span><br><span class="line">  height: <span class="string">""</span>,</span><br><span class="line">  <span class="comment">// 根据宽度等比缩放，优先级更高</span></span><br><span class="line">  isWidth: <span class="literal">false</span>,</span><br><span class="line">  <span class="comment">// 根据高度等比缩放</span></span><br><span class="line">  isHeight: <span class="literal">false</span>,</span><br><span class="line">  <span class="comment">// 宽高整体缩放</span></span><br><span class="line">  scale: <span class="number">1</span>,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><p>那么得出的字符串就是<code>22001</code>，假设我们传入了宽度，即数据：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// 自定义宽度，传一个根据宽度等比缩放</span></span><br><span class="line">  width: <span class="number">1920</span>,</span><br><span class="line">  <span class="comment">// 自定义高度，传一个根据高度等比缩放</span></span><br><span class="line">  height: <span class="string">""</span>,</span><br><span class="line">  <span class="comment">// 根据宽度等比缩放，优先级更高</span></span><br><span class="line">  isWidth: <span class="literal">false</span>,</span><br><span class="line">  <span class="comment">// 根据高度等比缩放</span></span><br><span class="line">  isHeight: <span class="literal">false</span>,</span><br><span class="line">  <span class="comment">// 宽高整体缩放</span></span><br><span class="line">  scale: <span class="number">1</span>,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><p>那么得出的字符串就是<code>12001</code>，看到这里大家应该懂了吧？所以我们只需要判断<code>config</code>这个配置的<code>value</code>值来生成我们的字符串即可，即得出如下代码</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 获取config字符串，即传入了就是true，即1，没传就是0，为空字符串就是2</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getConfigStr</span>(<span class="params">config</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">Object</span>.values(config).map(<span class="function">(<span class="params">el</span>) =&gt;</span> (el === <span class="string">""</span> ? <span class="string">"2"</span> : <span class="built_in">Number</span>(!!el)));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><blockquote><p>ps：如果不懂，请评论说出来，我看到会第一时间回复的。。。</p></blockquote><h4 id="拓展阅读：object-属性的输出顺序是无序的问题了解"><a href="#拓展阅读：object-属性的输出顺序是无序的问题了解" class="headerlink" title="拓展阅读：object 属性的输出顺序是无序的问题了解"></a>拓展阅读：object 属性的输出顺序是无序的问题了解</h4><blockquote><p>拓展阅读：<a href="https://zhuanlan.zhihu.com/p/40601459" target="_blank" rel="noopener">5 分钟彻底理解 Object.keys</a></p></blockquote><h3 id="正式编写优化后的代码"><a href="#正式编写优化后的代码" class="headerlink" title="正式编写优化后的代码"></a>正式编写优化后的代码</h3><p>通过上面的思考，我们基本分析出了我们的代码需要怎么写，如何写，我想大家应该很容易就能书写出来了，这里还是贴一下我的（仅供参考）：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 获取config字符串</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getConfigStr</span>(<span class="params">config</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">Object</span>.values(config).map(<span class="function">(<span class="params">el</span>) =&gt;</span> (el === <span class="string">""</span> ? <span class="string">"2"</span> : <span class="built_in">Number</span>(!!el)));</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 根据配置获取宽高</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getOptions</span>(<span class="params">options, config</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> [sourceWidth, sourceHeight] = options;</span><br><span class="line">  <span class="keyword">const</span> &#123; width, height, scale &#125; = config;</span><br><span class="line">  <span class="keyword">const</span> widthOfOptions = [</span><br><span class="line">    width * scale,</span><br><span class="line">    (sourceHeight * width * scale) / sourceWidth,</span><br><span class="line">  ];</span><br><span class="line">  <span class="keyword">const</span> heightOfOptions = [</span><br><span class="line">    (sourceWidth * height * scale) / sourceHeight,</span><br><span class="line">    height * scale,</span><br><span class="line">  ];</span><br><span class="line">  <span class="keyword">const</span> configStr = getConfigStr(config);</span><br><span class="line">  <span class="keyword">const</span> map = <span class="keyword">new</span> <span class="built_in">Map</span>([</span><br><span class="line">    [<span class="regexp">/^0|^\d0/</span>, [<span class="number">0</span>, <span class="number">0</span>]],</span><br><span class="line">    [<span class="regexp">/^1\d1|^1[0|2]0/</span>, widthOfOptions],</span><br><span class="line">    [<span class="regexp">/^\d101|^210/</span>, heightOfOptions],</span><br><span class="line">    [<span class="regexp">/^1100/</span>, [width / scale, height / scale]],</span><br><span class="line">    [<span class="regexp">/^2&#123;2&#125;\d&#123;2&#125;1/</span>, options.map(<span class="function">(<span class="params">item</span>) =&gt;</span> item / scale)],</span><br><span class="line">  ]);</span><br><span class="line">  <span class="keyword">return</span> [...map].find(<span class="function">(<span class="params">[key]</span>) =&gt;</span> key.test(configStr.join(<span class="string">""</span>)))[<span class="number">1</span>] || options;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><blockquote><p>ps: 这里使用了正则，如果有对正则不太了解的，建议可以去看下正则，因为正则对字符串的处理有着极大的意义，以极大程度上方便了我们的开发</p></blockquote><p>也许你看到这里，你就会像，你这里写的不是比以前更复杂了吗？还用了这些看不太懂的<code>正则</code>，可读性就更差了。。。</p><p>但是其实我这里只是想引申出<code>使用 Map 代替 if/else</code>这个思想（思路），通过这个例子，我想以后我们写的代码也可以使用<code>Map</code>书写出让我们更好维护的代码了</p><blockquote><p><a href="https://gitee.com/gating/demo/tree/master/canvas-image-resize" target="_blank" rel="noopener">gitee 地址</a>,<a href="https://github.com/GATING/demo/tree/master/canvas-image-resize" target="_blank" rel="noopener">github 地址</a></p></blockquote><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>感谢各位观众老爷的观看 O(∩_∩)O 希望你能有所收获 😁</p>]]></content>
    
    <summary type="html">
    
      大家在工作中肯定有没有遇到过图片尺寸和我们要求的尺寸不一致的情况吧？通常我们会在网上找一下找在线的或者下载一个小工具，再或者通过ps的批处理解决。但是，作为程序猿，当然还是通过代码来解决这种小问题啦。所以，闲话不多说啦，开始写我们的代码啦~~
    
    </summary>
    
    
      <category term="小工具" scheme="https://gatings.cn/categories/%E5%B0%8F%E5%B7%A5%E5%85%B7/"/>
    
    
      <category term="node" scheme="https://gatings.cn/tags/node/"/>
    
      <category term="node-canvas" scheme="https://gatings.cn/tags/node-canvas/"/>
    
  </entry>
  
  <entry>
    <title>node实现图片四周填充透明区域</title>
    <link href="https://gatings.cn/2020-06-10/node%E5%9B%BE%E7%89%87%E5%9B%9B%E5%91%A8%E5%A1%AB%E5%85%85%E9%80%8F%E6%98%8E%E5%8C%BA%E5%9F%9F/"/>
    <id>https://gatings.cn/2020-06-10/node%E5%9B%BE%E7%89%87%E5%9B%9B%E5%91%A8%E5%A1%AB%E5%85%85%E9%80%8F%E6%98%8E%E5%8C%BA%E5%9F%9F/</id>
    <published>2020-06-09T16:00:00.000Z</published>
    <updated>2021-10-13T01:56:29.703Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>不知道你们有没有遇到这样的情况，写小程序的时候，文档里面推荐的 tabbar 图标是 <code>81 * 81</code>，但是实际效果图片又偏大，而且配置项也没有可调节的配置。那么怎么样解决这个问题呢？一般都是找 UI 小姐姐把 icon 四边添加透明填充来调整 icon 大小的。</p><p>但是如果没有 ui 小姐姐的情况下，我们要如何解决呢？虽然可以通过<code>ps</code>的批处理解决，但毕竟我们是<code>程序猿</code>，当然是通过代码来解决这种小问题啦。所以，闲话不多说啦，开始写我们的代码啦</p><h1 id="简单搭建一下"><a href="#简单搭建一下" class="headerlink" title="简单搭建一下"></a>简单搭建一下</h1><ul><li><p>新建一个 <code>canvas-blank-area</code> 目录</p></li><li><p>初始化一个<code>node</code>项目工程</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm init -y</span><br></pre></td></tr></table></figure></div></li><li><p>安装依赖，这里主要用到了三个依赖，分别是<code>处理图片</code>、<code>批量处理文件</code>、<code>压缩成zip文件</code></p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm i canvas glob archiver -S</span><br></pre></td></tr></table></figure></div></li></ul><p>是不是很眼熟，这依赖和我之前的那篇<a href="https://gatings.cn/2020-03-11/node%E5%AE%9E%E7%8E%B0%E5%9B%BE%E7%89%87%E5%88%86%E5%89%B2/">node 实现图片分割</a>博客一模一样吗？没错，就是一摸一样，我们再次通过 <code>canvas</code>这个库来处理我们的小需求。</p><blockquote><p>不得不说这个<code>canvas</code>库真的是挺好用的。</p></blockquote><h1 id="简单的使用一下"><a href="#简单的使用一下" class="headerlink" title="简单的使用一下"></a>简单的使用一下</h1><p>有了上一篇我们使用<code>canvas</code>的经验，其实书写代码对于我们来说应该是不难了，我们看看我实现的效果：</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-10/blank-area.png" data-fancybox="group" data-caption="node图片四周填充透明区域" class="fancybox"><img alt="node图片四周填充透明区域" title="node图片四周填充透明区域" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-10/blank-area.png" class="lazyload"></a></p><p>看到这里，是不是想跃跃欲试啦，所以，我们尝试写一下我们的代码</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 创建写入流</span></span><br><span class="line"><span class="keyword">const</span> &#123; createWriteStream &#125; = <span class="built_in">require</span>(<span class="string">"fs"</span>);</span><br><span class="line"><span class="comment">// 获取文件名</span></span><br><span class="line"><span class="keyword">const</span> &#123; basename &#125; = <span class="built_in">require</span>(<span class="string">"path"</span>);</span><br><span class="line"><span class="comment">// 压缩文件</span></span><br><span class="line"><span class="keyword">const</span> archiver = <span class="built_in">require</span>(<span class="string">"archiver"</span>);</span><br><span class="line"><span class="comment">// 导入canvas库，用于裁剪图片</span></span><br><span class="line"><span class="keyword">const</span> &#123; createCanvas, loadImage &#125; = <span class="built_in">require</span>(<span class="string">"canvas"</span>);</span><br><span class="line"><span class="comment">// 批量获取路径</span></span><br><span class="line"><span class="keyword">const</span> glob = <span class="built_in">require</span>(<span class="string">"glob"</span>);</span><br><span class="line">!<span class="function">(<span class="params"><span class="keyword">async</span> (</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> paths = glob.sync(<span class="string">"./images/*"</span>);</span><br><span class="line">  <span class="comment">// 压缩成zip</span></span><br><span class="line">  <span class="keyword">const</span> archive = archiver(<span class="string">"zip"</span>, &#123;</span><br><span class="line">    zlib: &#123; <span class="attr">level</span>: <span class="number">9</span> &#125;, <span class="comment">// Sets the compression level.</span></span><br><span class="line">  &#125;);</span><br><span class="line">  <span class="comment">// 输出到当前文件夹下的 blank-area.zip</span></span><br><span class="line">  <span class="keyword">const</span> output = createWriteStream(__dirname + <span class="string">"/blank-area.zip"</span>);</span><br><span class="line">  archive.pipe(output);</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; paths.length; i++) &#123;</span><br><span class="line">    <span class="keyword">const</span> path = paths[i];</span><br><span class="line">    <span class="keyword">const</span> image = <span class="keyword">await</span> loadImage(path);</span><br><span class="line">    <span class="keyword">const</span> &#123; width, height &#125; = image;</span><br><span class="line">    <span class="keyword">const</span> canvas = createCanvas(<span class="number">81</span>, <span class="number">81</span>);</span><br><span class="line">    <span class="keyword">const</span> ctx = canvas.getContext(<span class="string">"2d"</span>);</span><br><span class="line">    ctx.drawImage(image, (<span class="number">81</span> - width) / <span class="number">2</span>, (<span class="number">81</span> - height) / <span class="number">2</span>);</span><br><span class="line">    archive.append(canvas.toBuffer(), &#123; <span class="attr">name</span>: <span class="string">`<span class="subst">$&#123;basename(path)&#125;</span>`</span> &#125;);</span><br><span class="line">  &#125;</span><br><span class="line">  archive.finalize();</span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure></div><p>这么一看，是不是比我们之前那个分割图片的代码简单多了，我相信小伙伴们肯定早就自己写出来啦~~😊</p><p>最后，跑一下我们写的程序，当前目录就会多出了一个 <code>blank-area.zip</code> 的压缩包，解压出来就是我们需要的图片啦 😊</p><blockquote><p><a href="https://gitee.com/gating/demo/tree/master/canvas-blank-area" target="_blank" rel="noopener">gitee 地址</a>,<a href="https://github.com/GATING/demo/tree/master/canvas-blank-area" target="_blank" rel="noopener">github 地址</a></p></blockquote><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>感谢各位观众老爷的观看 O(∩_∩)O</p>]]></content>
    
    <summary type="html">
    
      不知道你们有没有遇到这样的情况，写小程序的时候，文档里面推荐的 tabbar 图标是 81 * 81，但是实际效果图片又偏大，而且配置项也没有可调节的配置。那么怎么样解决这个问题呢？一般都是找 UI 小姐姐把 icon 四边添加透明填充来调整 icon 大小的。
    
    </summary>
    
    
      <category term="小工具" scheme="https://gatings.cn/categories/%E5%B0%8F%E5%B7%A5%E5%85%B7/"/>
    
    
      <category term="node" scheme="https://gatings.cn/tags/node/"/>
    
      <category term="node-canvas" scheme="https://gatings.cn/tags/node-canvas/"/>
    
  </entry>
  
  <entry>
    <title>手把手教你写一个符合自己需求的小程序日历组件</title>
    <link href="https://gatings.cn/2020-06-08/%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%86%99%E4%B8%80%E4%B8%AA%E7%AC%A6%E5%90%88%E8%87%AA%E5%B7%B1%E9%9C%80%E6%B1%82%E7%9A%84%E5%B0%8F%E7%A8%8B%E5%BA%8F%E6%97%A5%E5%8E%86%E7%BB%84%E4%BB%B6/"/>
    <id>https://gatings.cn/2020-06-08/%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%86%99%E4%B8%80%E4%B8%AA%E7%AC%A6%E5%90%88%E8%87%AA%E5%B7%B1%E9%9C%80%E6%B1%82%E7%9A%84%E5%B0%8F%E7%A8%8B%E5%BA%8F%E6%97%A5%E5%8E%86%E7%BB%84%E4%BB%B6/</id>
    <published>2020-06-07T16:00:00.000Z</published>
    <updated>2021-10-13T01:56:29.707Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>很多时候，我们生活中会有各种打卡的情况，比如 keep 的运动打卡、单词的学习打卡和各种签到打卡或者酒店的入住时间选择，这时候就需要我们书写一个日历组件来处理我们这种需求。</p><p>但是更多时候，我们都是网上找一个插件直接套用了，有没有想过自己实现一下呢？如果有，但是感觉不太会的话，接下来跟着我一起实现符合自己需求的日历吧</p><h1 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h1><p>因为我们是小程序日历嘛，所以必不可少的肯定是微信开发者工具啦。项目目录结构如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Code</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">|-- calendar</span><br><span class="line">    |-- app.js</span><br><span class="line">    |-- app.json</span><br><span class="line">    |-- app.wxss</span><br><span class="line">    |-- project.config.json</span><br><span class="line">    |-- sitemap.json</span><br><span class="line">    |-- components</span><br><span class="line">    |   |-- calendar</span><br><span class="line">    |       |-- index.js</span><br><span class="line">    |       |-- index.json</span><br><span class="line">    |       |-- index.wxml</span><br><span class="line">    |       |-- index.wxss</span><br><span class="line">    |-- pages</span><br><span class="line">        |-- index</span><br><span class="line">            |-- index.js</span><br><span class="line">            |-- index.json</span><br><span class="line">            |-- index.wxml</span><br><span class="line">            |-- index.wxss</span><br></pre></td></tr></table></figure></div><p>使用 <code>git</code> 下载空白模板:</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git <span class="built_in">clone</span> -b calendar  https://gitee.com/gating/demo.git</span><br></pre></td></tr></table></figure></div><blockquote><p>ps: 下面步骤有点啰嗦，如果看目录结构就能懂的话就不需要跟着步骤啦</p></blockquote><ol><li><p>新建一个名为<code>calendar</code>的空文件夹</p></li><li><p>打卡<code>微信开发者工具</code>，新增项目，选中刚刚创建的<code>calendar</code>文件夹，开发模式选中小程序，AppID 使用测试号即可，如图所示：<br><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-08/calendar01.jpg" data-fancybox="group" data-caption="新建calendar项目" class="fancybox"><img alt="新建calendar项目" title="新建calendar项目" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-08/calendar01.jpg" class="lazyload"></a></p></li><li><p>创建完后，开发者工具会默认帮我们生成默认的代码，我们在当前文件夹新增<code>components</code>文件家，再在<code>components</code>文件夹中新增<code>calendar</code>文件夹，再从当前文件夹新增名为<code>index</code>的组件，如图：</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-08/calendar02.jpg" data-fancybox="group" data-caption="新建calendar组件" class="fancybox"><img alt="新建calendar组件" title="新建calendar组件" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-08/calendar02.jpg" class="lazyload"></a></p></li></ol><blockquote><p>ps:因为开发者工具会默认生成初始代码，所以直接使用他创建组件比较方便</p></blockquote><ol start="4"><li>删除一些和本次博文无关的代码，比如<code>app.js</code>中的本地缓存能力，具体参考空白模板</li></ol><h1 id="编写代码"><a href="#编写代码" class="headerlink" title="编写代码"></a>编写代码</h1><p>接下来编写代码部分我们直接在<code>VSCode</code>编写，因为<code>微信开发者工具</code>实在太。。。- -所以还是使用<code>VSCode</code>编写比较合适</p><h2 id="思考一下"><a href="#思考一下" class="headerlink" title="思考一下"></a>思考一下</h2><p>想要实现日历，我们需要知道几个小知识：</p><ol><li><p>根据常识，我们知道一个月最少有 28 天，最多有 31 天，一周有 7 天，那么就可以有 5 排，但如果有一个月的第一天为星期六且当月有 31 天，那么他就会有 6 排格子才对。比如<code>2020年8月</code>，如图所示：</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-08/calendar03.jpg" data-fancybox="group" data-caption="2020年8月" class="fancybox"><img alt="2020年8月" title="2020年8月" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-08/calendar03.jpg" class="lazyload"></a></p></li></ol><p>2) 我们需要知道，当月的第一天是周几</p><p>3) 我们需要知道，当月有多少天</p><p>4) 最重要的是小程序没有 DOM 操作概念，所以我们不能动态往当月第一天插入格子，所以只能根据第一天是周几循环插入格子</p><p>知道以上四点后，我们就可以编写我们的代码啦</p><p>首先，第二第三点是最简单的，我先书写第二第三点，怎么获取当前是周几呢？其实<code>js</code>的<code>Date</code>对象直接有现成的方法，我们直接拿来用就好了</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">console</span>.log(<span class="string">"今天是星期"</span> + <span class="keyword">new</span> <span class="built_in">Date</span>().getDay());</span><br></pre></td></tr></table></figure></div><p>我想有点小难度的是第三点，获取当月有多少天，因为你想，其他的月份的天数是固定的，唯独 2 月，在平年和闰年的不同年份中，2 月的天数也是不同的，那么有没有简单的方法获取当月有多少天呢，其实也是有的，<code>Date</code>实例中的<code>getDate</code>就可以实现我们想要的效果了</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 获取一个月有多少天</span></span><br><span class="line"><span class="keyword">const</span> getMonthDays = <span class="function">(<span class="params">year, month</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> days = <span class="keyword">new</span> <span class="built_in">Date</span>(year, month + <span class="number">1</span>, <span class="number">0</span>).getDate();</span><br><span class="line">  <span class="keyword">return</span> days;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><p>我们通过 <code>Date</code> 的第三个参数传 0 就可以获取上个月的最后一天，最后通过 <code>getDate()</code> 获取它的日期就可以对应我们当月的天数，那么就不需要我们自己处理平年和闰年的 2 月有多少天了</p><blockquote><p>是不是又学到了小知识点呢？</p></blockquote><p>解决了 2.3 两个问题，我们就可以往下书写我们的日历了。</p><p>众所周知，小程序规定宽度为<code>750rpx</code>(<a href="https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxss.html" target="_blank" rel="noopener">尺寸单位</a>)，而我们的一周有 7 天，即 7 格，那么就是每个格子的宽度为<code>107rpx</code>，不建议使用小数，因为 rpx 计算的时候，遇到小数会存在少量偏差。这里我们使用<code>flex</code>布局解决。</p><p>所以接下来就可以写我们的布局和生成我们的数据啦，从上面我们分析了，我们有 6 排格子，一排有 7 个，也就是一共 42 个格子。即需要遍历 42 次</p><p>先定义一下我们所需要的数据，便于我们后续操作：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">json</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight json"><table><tr><td class="code"><pre><span class="line">[</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="attr">"dataStr"</span>: <span class="string">"2020/06/08"</span>,</span><br><span class="line">    <span class="attr">"day"</span>: <span class="string">"08"</span>,</span><br><span class="line">    <span class="attr">"month"</span>: <span class="string">"08"</span>,</span><br><span class="line">    <span class="attr">"otherMonth"</span>: <span class="literal">false</span>,</span><br><span class="line">    <span class="attr">"today"</span>: <span class="literal">true</span>,</span><br><span class="line">    <span class="attr">"year"</span>: <span class="number">2020</span></span><br><span class="line">  &#125;</span><br><span class="line">]</span><br></pre></td></tr></table></figure></div><blockquote><p>这里我只定义个几个简单的基本数据，如果有不同的业务场景可以自行添加基础数据</p></blockquote><h3 id="小-tips"><a href="#小-tips" class="headerlink" title="小 tips"></a>小 tips</h3><p>IOS 端的日期格式必须为<code>/</code>才可以转化为日期格式，比如<code>2018/07/08</code>，而<code>2018-07-08</code>则返回<code>Invalid Date</code>，所以我们需要把<code>-</code>都替换为<code>/</code>。</p><p>不单单是小程序，微信公众号，safari 都是一样的。</p><h2 id="正式开始编写代码"><a href="#正式开始编写代码" class="headerlink" title="正式开始编写代码"></a>正式开始编写代码</h2><p>那么就可以写我们的 js 代码了，在 <code>components -&gt; calendar</code>目录下新建<code>utils.js</code>文件，书写我们创建数据的基础方法：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 获取当月有多少天</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String | Number&#125;</span> </span>year =&gt; 年</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String | Number&#125;</span> </span>month =&gt; 月</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> getMonthDays = <span class="function">(<span class="params">year, month</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> days = <span class="keyword">new</span> <span class="built_in">Date</span>(year, month + <span class="number">1</span>, <span class="number">0</span>).getDate();</span><br><span class="line">  <span class="keyword">return</span> days;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 补0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String | Number&#125;</span> <span class="variable">num</span></span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> toDou = <span class="function">(<span class="params">num</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> num &gt; <span class="number">9</span> ? num : <span class="string">"0"</span> + num;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 转换为日期格式</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> <span class="variable">date</span></span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> transformDate = <span class="function">(<span class="params">date</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">if</span> (!(date <span class="keyword">instanceof</span> <span class="built_in">Date</span>)) &#123;</span><br><span class="line">    date = <span class="keyword">new</span> <span class="built_in">Date</span>(date);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> date;</span><br><span class="line">&#125;;</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 class="doctag">@param <span class="type">&#123;any&#125;</span> </span>date =&gt; 日期对象</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> getDateObj = <span class="function">(<span class="params">date</span>) =&gt;</span> &#123;</span><br><span class="line">  date = transformDate(date);</span><br><span class="line">  <span class="keyword">var</span> year = date.getFullYear();</span><br><span class="line">  <span class="keyword">var</span> month = date.getMonth() + <span class="number">1</span>;</span><br><span class="line">  <span class="keyword">var</span> day = date.getDate();</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    year,</span><br><span class="line">    month,</span><br><span class="line">    day,</span><br><span class="line">    dataStr: <span class="string">`<span class="subst">$&#123;year&#125;</span>/<span class="subst">$&#123;toDou(month)&#125;</span>/<span class="subst">$&#123;toDou(day)&#125;</span>`</span>,</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 获取当月1号的时间戳</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Date&#125;</span> </span>date =&gt; 日期对象</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> startOfMonth = <span class="function">(<span class="params">date</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> date.setDate(<span class="number">1</span>);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取今天，导出供组件作为默认值使用</span></span><br><span class="line"><span class="keyword">const</span> &#123; dataStr &#125; = getDateObj(<span class="keyword">new</span> <span class="built_in">Date</span>());</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 生成日历数据</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Date&#125;</span> </span>date =&gt; 日期对象</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> getDate = <span class="function">(<span class="params">date</span>) =&gt;</span> &#123;</span><br><span class="line">  date = transformDate(date);</span><br><span class="line">  <span class="comment">// 计算需要补的格子</span></span><br><span class="line">  <span class="keyword">let</span> dist;</span><br><span class="line">  <span class="keyword">const</span> &#123; year, month &#125; = getDateObj(date);</span><br><span class="line">  <span class="comment">// 获取当月有多少天</span></span><br><span class="line">  <span class="keyword">const</span> days = getMonthDays(year, month - <span class="number">1</span>);</span><br><span class="line">  <span class="comment">// 获取当前日期是星期几</span></span><br><span class="line">  <span class="keyword">let</span> currentDate = <span class="keyword">new</span> <span class="built_in">Date</span>(startOfMonth(date)).getDay();</span><br><span class="line">  <span class="comment">// 众所周知的原因，一周的第一天时星期天，而我们做的日历星期天是放在最后的，所以我们这里需要改一下值</span></span><br><span class="line">  <span class="keyword">if</span> (currentDate == <span class="number">0</span>) &#123;</span><br><span class="line">    currentDate = <span class="number">7</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  dist = currentDate - <span class="number">1</span>;</span><br><span class="line">  currentDate -= <span class="number">2</span>;</span><br><span class="line">  <span class="keyword">const</span> res = [];</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; <span class="number">42</span>; i++) &#123;</span><br><span class="line">    <span class="comment">// 是否不是当前月</span></span><br><span class="line">    <span class="keyword">const</span> otherMonth = i &gt;= dist + days || i &lt;= currentDate;</span><br><span class="line">    <span class="keyword">const</span> date = <span class="keyword">new</span> <span class="built_in">Date</span>(year, month - <span class="number">1</span>, -currentDate + i);</span><br><span class="line">    <span class="keyword">const</span> dateObj = getDateObj(date);</span><br><span class="line">    res.push(&#123;</span><br><span class="line">      ...dateObj,</span><br><span class="line">      today: dataStr === dateObj.dataStr,</span><br><span class="line">      otherMonth,</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> res;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  getMonthDays,</span><br><span class="line">  toDou,</span><br><span class="line">  getDateObj,</span><br><span class="line">  startOfMonth,</span><br><span class="line">  getDate,</span><br><span class="line">  dataStr,</span><br><span class="line">  transformDate,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></div><p>这里代码都比较简单，注释也有写，所以就不详细解释了，如有问题就评论，我看到会第一时间回复的。。。。</p><p>在 <code>components -&gt; calendar -&gt; index.js</code> 引入一下 <code>utils.js</code> 文件，然后在<code>created</code>这个生命周期打印一下我们的基础数据，看是否符合预期：</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-08/calendar04.jpg" data-fancybox="group" data-caption class="fancybox"><img alt title data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-08/calendar04.jpg" class="lazyload"></a></p><p>如果你打印的和我打印的一致，那么就可以愉快的写我们组件的界面啦 😄</p><p>因为布局大多数都是样式方面的问题，这里就不多讲解啦，我想大家应该都会的，所以这里直接粘贴代码啦，主要部分我就讲解一下</p><p><code>index.wxml</code>代码如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">xml</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">view</span> <span class="attr">class</span>=<span class="string">"calendar-wrapper"</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">view</span> <span class="attr">class</span>=<span class="string">"calendar-controller"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">view</span> <span class="attr">class</span>=<span class="string">"calendar-picker"</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">text</span> <span class="attr">class</span>=<span class="string">"arrow left"</span> <span class="attr">bindtap</span>=<span class="string">"prevMonth"</span>&gt;</span><span class="tag">&lt;/<span class="name">text</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">picker</span></span></span><br><span class="line"><span class="tag">        <span class="attr">mode</span>=<span class="string">'date'</span></span></span><br><span class="line"><span class="tag">        <span class="attr">fields</span>=<span class="string">'month'</span></span></span><br><span class="line"><span class="tag">        <span class="attr">end</span>=<span class="string">"2999-12-31"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">start</span>=<span class="string">"1970-01-01"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">value</span>=<span class="string">"&#123;&#123;monthFormat&#125;&#125;"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">bindchange</span>=<span class="string">"dateChange"</span></span></span><br><span class="line"><span class="tag">      &gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">text</span> <span class="attr">class</span>=<span class="string">"month-format"</span>&gt;</span>&#123;&#123;monthFormat&#125;&#125;<span class="tag">&lt;/<span class="name">text</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">picker</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">text</span> <span class="attr">class</span>=<span class="string">"arrow right"</span> <span class="attr">bindtap</span>=<span class="string">"nextMonth"</span>&gt;</span><span class="tag">&lt;/<span class="name">text</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">view</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">view</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">view</span> <span class="attr">class</span>=<span class="string">"calendar-header"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">view</span> <span class="attr">class</span>=<span class="string">"item"</span> <span class="attr">wx:for</span>=<span class="string">"&#123;&#123;week&#125;&#125;"</span> <span class="attr">wx:key</span>=<span class="string">"*this"</span>&gt;</span>&#123;&#123;item&#125;&#125;<span class="tag">&lt;/<span class="name">view</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">view</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">view</span> <span class="attr">class</span>=<span class="string">"calendar-container"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">view</span> <span class="attr">class</span>=<span class="string">"item &#123;&#123;item.today?'today':''&#125;&#125; &#123;&#123;item.otherMonth?'other-month':''&#125;&#125;"</span> <span class="attr">wx:for</span>=<span class="string">"&#123;&#123;calendar&#125;&#125;"</span> <span class="attr">wx:key</span>=<span class="string">"dataStr"</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">text</span>&gt;</span>&#123;&#123;item.day&#125;&#125;<span class="tag">&lt;/<span class="name">text</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">view</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">view</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">view</span>&gt;</span></span><br></pre></td></tr></table></figure></div><p><code>index.wxss</code>代码如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">css</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight css"><table><tr><td class="code"><pre><span class="line"><span class="selector-class">.calendar-container</span>,</span><br><span class="line"><span class="selector-class">.calendar-controller</span>,</span><br><span class="line"><span class="selector-class">.calendar-header</span>,</span><br><span class="line"><span class="selector-class">.calendar-picker</span>,</span><br><span class="line"><span class="selector-class">.calendar-container</span> <span class="selector-class">.item</span>,</span><br><span class="line"><span class="selector-class">.calendar-header</span> <span class="selector-class">.item</span> &#123;</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">line-height</span>: normal;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.calendar-container</span>,</span><br><span class="line"><span class="selector-class">.calendar-controller</span>,</span><br><span class="line"><span class="selector-class">.calendar-header</span> &#123;</span><br><span class="line">  <span class="attribute">justify-content</span>: space-around;</span><br><span class="line">  <span class="attribute">flex-wrap</span>: wrap;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.calendar-container</span> <span class="selector-class">.item</span>,</span><br><span class="line"><span class="selector-class">.calendar-header</span> <span class="selector-class">.item</span> &#123;</span><br><span class="line">  <span class="attribute">justify-content</span>: center;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">107</span>rpx;</span><br><span class="line">  <span class="attribute">font-size</span>: <span class="number">28</span>rpx;</span><br><span class="line">  <span class="attribute">height</span>: <span class="number">80</span>rpx;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.calendar-header</span> <span class="selector-class">.item</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: <span class="number">#666</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.calendar-container</span> <span class="selector-class">.item</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: <span class="number">#111</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.calendar-container</span> <span class="selector-class">.item</span><span class="selector-class">.other-month</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: <span class="number">#999</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.calendar-container</span> <span class="selector-class">.item</span><span class="selector-class">.today</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: <span class="number">#6190e8</span>;</span><br><span class="line">  <span class="attribute">font-weight</span>: <span class="number">600</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.calendar-picker</span> &#123;</span><br><span class="line">  <span class="attribute">font-size</span>: <span class="number">30</span>rpx;</span><br><span class="line">  <span class="attribute">color</span>: <span class="number">#111</span>;</span><br><span class="line">  <span class="attribute">padding</span>: <span class="number">20</span>rpx <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.month-format</span> &#123;</span><br><span class="line">  <span class="attribute">margin</span>: <span class="number">0</span> <span class="number">30</span>rpx;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.arrow</span> &#123;</span><br><span class="line">  <span class="attribute">display</span>: flex;</span><br><span class="line">  <span class="attribute">padding</span>: <span class="number">10</span>rpx <span class="number">15</span>rpx;</span><br><span class="line">  <span class="attribute">background</span>: <span class="number">#f7f8fc</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.arrow</span><span class="selector-pseudo">::after</span> &#123;</span><br><span class="line">  <span class="attribute">content</span>: <span class="string">""</span>;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">14</span>rpx;</span><br><span class="line">  <span class="attribute">height</span>: <span class="number">14</span>rpx;</span><br><span class="line">  <span class="attribute">border-top</span>: <span class="number">4</span>rpx solid <span class="number">#ccc</span>;</span><br><span class="line">  <span class="attribute">border-left</span>: <span class="number">4</span>rpx solid <span class="number">#ccc</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.arrow</span><span class="selector-class">.left</span><span class="selector-pseudo">::after</span> &#123;</span><br><span class="line">  <span class="attribute">transform</span>: <span class="built_in">rotateY</span>(-<span class="number">45deg</span>) <span class="built_in">rotate</span>(-<span class="number">47deg</span>) <span class="built_in">skew</span>(<span class="number">5deg</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.arrow</span><span class="selector-class">.right</span><span class="selector-pseudo">::after</span> &#123;</span><br><span class="line">  <span class="attribute">transform</span>: <span class="built_in">rotateY</span>(-<span class="number">135deg</span>) <span class="built_in">rotate</span>(-<span class="number">47deg</span>) <span class="built_in">skew</span>(<span class="number">5deg</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p><code>index.js</code>代码如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// components/calendar/index.js</span></span><br><span class="line"><span class="keyword">const</span> &#123; getDate, dataStr, getDateObj &#125; = <span class="built_in">require</span>(<span class="string">"./utils"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> getDateStr = <span class="function">(<span class="params">dataStr</span>) =&gt;</span> dataStr.slice(<span class="number">0</span>, <span class="number">-3</span>).replace(<span class="string">"/"</span>, <span class="string">"-"</span>);</span><br><span class="line">Component(&#123;</span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * 组件的属性列表</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  properties: &#123;&#125;,</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">   */</span></span><br><span class="line">  data: &#123;</span><br><span class="line">    week: [<span class="string">"一"</span>, <span class="string">"二"</span>, <span class="string">"三"</span>, <span class="string">"四"</span>, <span class="string">"五"</span>, <span class="string">"六"</span>, <span class="string">"日"</span>],</span><br><span class="line">    calendar: getDate(<span class="keyword">new</span> <span class="built_in">Date</span>()),</span><br><span class="line">    monthFormat: getDateStr(dataStr),</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * 组件的方法列表</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  methods: &#123;</span><br><span class="line">    dateChange(e) &#123;</span><br><span class="line">      <span class="keyword">const</span> monthFormat = e.detail.value;</span><br><span class="line">      <span class="keyword">this</span>.setData(&#123;</span><br><span class="line">        monthFormat,</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 上个月日期</span></span><br><span class="line">    prevMonth() &#123;</span><br><span class="line">      <span class="keyword">const</span> [year, month] = <span class="keyword">this</span>.data.monthFormat.split(<span class="string">"-"</span>);</span><br><span class="line">      <span class="keyword">const</span> &#123; dataStr &#125; = getDateObj(</span><br><span class="line">        <span class="keyword">new</span> <span class="built_in">Date</span>(year, month, <span class="number">1</span>).setMonth(month - <span class="number">2</span>)</span><br><span class="line">      );</span><br><span class="line">      <span class="keyword">this</span>.setData(&#123;</span><br><span class="line">        monthFormat: getDateStr(dataStr),</span><br><span class="line">        calendar: getDate(<span class="keyword">new</span> <span class="built_in">Date</span>(dataStr)),</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 下个月日期</span></span><br><span class="line">    nextMonth() &#123;</span><br><span class="line">      <span class="keyword">const</span> [year, month] = <span class="keyword">this</span>.data.monthFormat.split(<span class="string">"-"</span>);</span><br><span class="line">      <span class="keyword">const</span> &#123; dataStr &#125; = getDateObj(<span class="keyword">new</span> <span class="built_in">Date</span>(year, month, <span class="number">1</span>));</span><br><span class="line">      <span class="keyword">this</span>.setData(&#123;</span><br><span class="line">        monthFormat: getDateStr(dataStr),</span><br><span class="line">        calendar: getDate(<span class="keyword">new</span> <span class="built_in">Date</span>(dataStr)),</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">  created() &#123;&#125;,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></div><blockquote><p>这里的主要迷惑点就是月份，因为我们得到的月份是转换后的（即月份+1），而<code>js</code>中的月份是从 0 开始的，所以我们获取上个月的时候月份就需要<code>-2</code>才能实现我们要的效果，而获取下个月的时候，因为本身我们月份本身就<code>+1</code>了，所以不需要进行操作。</p></blockquote><p>书写完成布局后，大概会得出下面这个日历：</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-08/calendar05.jpg" data-fancybox="group" data-caption class="fancybox"><img alt title data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-08/calendar05.jpg" class="lazyload"></a></p><p>写到这里，其实整个日历的雏形已经出来了，我们可以通过<code>picker</code>换，可以通过点击切换，也算一个相对可以使用的日历组件啦 😝</p><p>但是其实还是远远不够的，毕竟，我们连<strong>手势左右滑动切换日历</strong>这个功能都没有，所以接下来就完善我们这个日历吧</p><h2 id="无缝滑动思考"><a href="#无缝滑动思考" class="headerlink" title="无缝滑动思考"></a>无缝滑动思考</h2><p>你想，既然要做左右滑动切换了，肯定得无缝吧？既然得无缝，肯定不能生成多份吧？那么怎么才能用最少的 DOM 做到无缝呢？答案是我们只需要在我们可视范围内生成 DOM 结构即可，即我们的可视范围就是三份，如图所示：</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-08/calendar06.gif" data-fancybox="group" data-caption class="fancybox"><img alt title data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-08/calendar06.gif" class="lazyload"></a></p><p>既然说到了<strong>左右滑动</strong>，肯定少不了我们强大的<code>swiper</code>组件啦，我们这次的日历组件就是建立在<code>swiper</code>组件下实现的,既然用到了<code>swiper</code>，那么我们的布局肯定需要进行小改，数据结构也是，需要进行小改动。</p><p>刚才说了，我们的可是范围是三份，所以我们的数据结构就变成了长度为三的数组，即：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="string">"calendarArr"</span>: [calendar, calendar, calendar]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>界面也是，我们新增一个<code>swiper</code>组件，然后遍历<code>calendarArr</code>这个数据，</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">xml</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">view</span> <span class="attr">class</span>=<span class="string">"calendar-wrapper"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">view</span> <span class="attr">class</span>=<span class="string">"calendar-controller"</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">view</span> <span class="attr">class</span>=<span class="string">"calendar-picker"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">text</span> <span class="attr">class</span>=<span class="string">"arrow left"</span> <span class="attr">bindtap</span>=<span class="string">"prevMonth"</span>&gt;</span><span class="tag">&lt;/<span class="name">text</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">picker</span></span></span><br><span class="line"><span class="tag">          <span class="attr">mode</span>=<span class="string">'date'</span></span></span><br><span class="line"><span class="tag">          <span class="attr">fields</span>=<span class="string">'month'</span></span></span><br><span class="line"><span class="tag">          <span class="attr">end</span>=<span class="string">"2999-12-31"</span></span></span><br><span class="line"><span class="tag">          <span class="attr">start</span>=<span class="string">"1970-01-01"</span></span></span><br><span class="line"><span class="tag">          <span class="attr">value</span>=<span class="string">"&#123;&#123;monthFormat&#125;&#125;"</span></span></span><br><span class="line"><span class="tag">          <span class="attr">bindchange</span>=<span class="string">"dateChange"</span></span></span><br><span class="line"><span class="tag">        &gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">text</span> <span class="attr">class</span>=<span class="string">"month-format"</span>&gt;</span>&#123;&#123;monthFormat&#125;&#125;<span class="tag">&lt;/<span class="name">text</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">picker</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">text</span> <span class="attr">class</span>=<span class="string">"arrow right"</span> <span class="attr">bindtap</span>=<span class="string">"nextMonth"</span>&gt;</span><span class="tag">&lt;/<span class="name">text</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">view</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">view</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">view</span> <span class="attr">class</span>=<span class="string">"calendar-header"</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">view</span> <span class="attr">class</span>=<span class="string">"item"</span> <span class="attr">wx:for</span>=<span class="string">"&#123;&#123;week&#125;&#125;"</span> <span class="attr">wx:key</span>=<span class="string">"*this"</span>&gt;</span>&#123;&#123;item&#125;&#125;<span class="tag">&lt;/<span class="name">view</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">view</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">swiper</span></span></span><br><span class="line"><span class="tag">      <span class="attr">circular</span></span></span><br><span class="line"><span class="tag">      <span class="attr">class</span>=<span class="string">"calendar-swiper"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">current</span>=<span class="string">"&#123;&#123;current&#125;&#125;"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">duration</span>=<span class="string">"&#123;&#123;duration&#125;&#125;"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">vertical</span>=<span class="string">"&#123;&#123;isVertical&#125;&#125;"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">skip-hidden-item-layout</span></span></span><br><span class="line"><span class="tag">      <span class="attr">bindchange</span>=<span class="string">"swiperChange"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">bindanimationfinish</span>=<span class="string">"swiperAnimateFinish"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">bindtouchstart</span>=<span class="string">"swipeTouchStart"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">bindtouchend</span>=<span class="string">"swipeTouchEnd"</span></span></span><br><span class="line"><span class="tag">    &gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">block</span> <span class="attr">wx:for</span>=<span class="string">"&#123;&#123;calendarArr&#125;&#125;"</span> <span class="attr">wx:for-item</span>=<span class="string">"calendar"</span> <span class="attr">wx:key</span>=<span class="string">"index"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">swiper-item</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">view</span> <span class="attr">class</span>=<span class="string">"calendar-container"</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">view</span> <span class="attr">class</span>=<span class="string">"item &#123;&#123;item.today?'today':''&#125;&#125; &#123;&#123;item.otherMonth?'other-month':''&#125;&#125;"</span> <span class="attr">wx:for</span>=<span class="string">"&#123;&#123;calendar&#125;&#125;"</span> <span class="attr">wx:key</span>=<span class="string">"dataStr"</span>&gt;</span></span><br><span class="line">              <span class="tag">&lt;<span class="name">text</span>&gt;</span>&#123;&#123;item.day&#125;&#125;<span class="tag">&lt;/<span class="name">text</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">view</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;/<span class="name">view</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">swiper-item</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">block</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">swiper</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">view</span>&gt;</span></span><br></pre></td></tr></table></figure></div><p>样式的话，因为<code>swiper</code>组件有默认样式，高度是<code>150px</code>，而我们这里<code>6 * 80rpx</code>，所以我们需要修改下它的默认样式，即添加下面的 css 即可:</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">css</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight css"><table><tr><td class="code"><pre><span class="line"><span class="selector-class">.calendar-swiper</span> &#123;</span><br><span class="line">  <span class="attribute">height</span>: <span class="number">480</span>rpx;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>之后就是书写我们的逻辑啦，从布局可以看到我们用了<code>touchstart</code>和<code>touchend</code>，本意其实就是判断我们是向左滑还是向右滑（向上划还是向下滑），来切换我们的月份</p><h2 id="如何区分左滑右滑（上滑下滑）"><a href="#如何区分左滑右滑（上滑下滑）" class="headerlink" title="如何区分左滑右滑（上滑下滑）"></a>如何区分左滑右滑（上滑下滑）</h2><ol><li><p>需要定两个变量供我们区分是滑动的方向，一个是<code>swipeStartPoint</code>,一个是<code>isPrevMonth</code></p></li><li><p>既然我们说到了无缝，那么肯定用户就会滑动多次，那么我们也需要一个值来计算用户滑动的次数，我们定义为<code>changeCount</code></p></li><li><p>这点也是最重要的一点，我们需用通过当前我们滑动到第几个<code>swiper-item</code>，来修改我们的上个月和下个月的数据，因为我们知道，当前的<code>swiper-item</code>肯定是中间的那个月份，所以我们也需要一个变量来标记我们当前的是第几个，我们定义为<code>currentSwiperIndex</code>，针对于这里的逻辑，我们举个例子：</p></li></ol><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 假设我们现在是6月，那么数据就是</span></span><br><span class="line"><span class="keyword">let</span> calendar = [<span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>];</span><br><span class="line"><span class="comment">// 那么我们的 currentSwiperIndex 这时是等于1的</span></span><br><span class="line"><span class="comment">// 假设我滑动了五月，currentSwiperIndex 这时变成0了,我们的月份还是不变</span></span><br><span class="line"><span class="comment">// 但是我们的逻辑就发生改变了</span></span><br><span class="line"><span class="comment">// 这时候的上个月变成了7，下个月变成6，我们需要通过 currentSwiperIndex 的值来动态修改他，即</span></span><br><span class="line">calendar = [<span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>];</span><br><span class="line"><span class="comment">// 半伪代码</span></span><br><span class="line"><span class="keyword">const</span> calendarArr = [];</span><br><span class="line"><span class="keyword">const</span> now = getDate(currentDate);</span><br><span class="line"><span class="keyword">const</span> prev = getDate(<span class="keyword">this</span>.getPrevMonth(dataStr));</span><br><span class="line"><span class="keyword">const</span> next = getDate(<span class="keyword">this</span>.getNextMonth(dataStr));</span><br><span class="line"><span class="keyword">const</span> prevIndex = currentSwiperIndex === <span class="number">0</span> ? <span class="number">2</span> : currentSwiperIndex - <span class="number">1</span>;</span><br><span class="line"><span class="keyword">const</span> nextIndex = currentSwiperIndex === <span class="number">2</span> ? <span class="number">0</span> : currentSwiperIndex + <span class="number">1</span>;</span><br><span class="line">calendarArr[prevIndex] = prev;</span><br><span class="line">calendarArr[nextIndex] = next;</span><br><span class="line">calendarArr[currentSwiperIndex] = now;</span><br></pre></td></tr></table></figure></div><p>理清楚上面所有的，基本上我们就可以开始重构我们的代码了</p><h2 id="正式书写我们可滑动的日历组件"><a href="#正式书写我们可滑动的日历组件" class="headerlink" title="正式书写我们可滑动的日历组件"></a>正式书写我们可滑动的日历组件</h2><p>先定义我们之前的所说的变量，和处理这些变量的方法</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 当前的索引值，必须从第一个开始，因为这样我们才能实现视野内的无缝</span></span><br><span class="line"><span class="keyword">let</span> currentSwiperIndex = <span class="number">1</span>,</span><br><span class="line">  generateDate = dataStr, <span class="comment">// 当前时间</span></span><br><span class="line">  swipeStartPoint = <span class="number">0</span>, <span class="comment">// 滑动的坐标</span></span><br><span class="line">  isPrevMonth = <span class="literal">false</span>, <span class="comment">// 是否向右滑动</span></span><br><span class="line">  changeCount = <span class="number">0</span>; <span class="comment">// 滑动的次数</span></span><br><span class="line"></span><br><span class="line">Component(&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  methods: &#123;</span><br><span class="line">    <span class="comment">// 设置当前的索引值</span></span><br><span class="line">    swiperChange(e) &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; current, source &#125; = e.detail;</span><br><span class="line">      <span class="keyword">if</span> (source === <span class="string">"touch"</span>) &#123;</span><br><span class="line">        currentSwiperIndex = current;</span><br><span class="line">        changeCount += <span class="number">1</span>;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 获取手指刚按下的坐标</span></span><br><span class="line">    swipeTouchStart(e) &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; clientY, clientX &#125; = e.changedTouches[<span class="number">0</span>];</span><br><span class="line">      swipeStartPoint = <span class="keyword">this</span>.data.isVertical ? clientY : clientX;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 获取手指松开时的坐标</span></span><br><span class="line">    swipeTouchEnd(e) &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; clientY, clientX &#125; = e.changedTouches[<span class="number">0</span>];</span><br><span class="line">      isPrevMonth = <span class="keyword">this</span>.data.isVertical</span><br><span class="line">        ? clientY - swipeStartPoint &gt; <span class="number">0</span></span><br><span class="line">        : clientX - swipeStartPoint &gt; <span class="number">0</span>;</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></div><p>然后定义一个处理我们日历数据的方法，因为我们日历方法是每个时间都需要使用的，所以我们定义个公用的方法，</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">Component(&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  methods: &#123;</span><br><span class="line">    <span class="comment">// 设置上个月的时间</span></span><br><span class="line">    getPrevMonth(monthFormat) &#123;</span><br><span class="line">      <span class="keyword">const</span> [year, month] = monthFormat.split(<span class="regexp">/\-|\//</span>);</span><br><span class="line">      <span class="keyword">const</span> &#123; dataStr &#125; = getDateObj(</span><br><span class="line">        <span class="keyword">new</span> <span class="built_in">Date</span>(year, month, <span class="number">1</span>).setMonth(month - <span class="number">2</span>)</span><br><span class="line">      );</span><br><span class="line">      <span class="keyword">return</span> dataStr;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 设置下个月的时间</span></span><br><span class="line">    getNextMonth(monthFormat) &#123;</span><br><span class="line">      <span class="keyword">const</span> [year, month] = monthFormat.split(<span class="regexp">/\-|\//</span>);</span><br><span class="line">      <span class="keyword">const</span> &#123; dataStr &#125; = getDateObj(<span class="keyword">new</span> <span class="built_in">Date</span>(year, month, <span class="number">1</span>));</span><br><span class="line">      <span class="keyword">return</span> dataStr;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 生成日历数组</span></span><br><span class="line">    generatorCalendar(date) &#123;</span><br><span class="line">      <span class="keyword">const</span> calendarArr = [];</span><br><span class="line">      <span class="comment">// 转换为 Date 实例</span></span><br><span class="line">      <span class="keyword">const</span> currentDate = transformDate(date);</span><br><span class="line">      <span class="comment">// 获取当前时间的日历数据</span></span><br><span class="line">      <span class="keyword">const</span> now = getDate(currentDate);</span><br><span class="line">      <span class="comment">// 获取当前时间的字符串</span></span><br><span class="line">      <span class="keyword">const</span> &#123; dataStr &#125; = getDateObj(currentDate);</span><br><span class="line">      <span class="comment">// 获取上个月的日历数据</span></span><br><span class="line">      <span class="keyword">const</span> prev = getDate(<span class="keyword">this</span>.getPrevMonth(dataStr));</span><br><span class="line">      <span class="comment">// 获取下个月的日历数据</span></span><br><span class="line">      <span class="keyword">const</span> next = getDate(<span class="keyword">this</span>.getNextMonth(dataStr));</span><br><span class="line">      <span class="comment">// 设置日历数据</span></span><br><span class="line">      <span class="keyword">const</span> prevIndex = currentSwiperIndex === <span class="number">0</span> ? <span class="number">2</span> : currentSwiperIndex - <span class="number">1</span>;</span><br><span class="line">      <span class="keyword">const</span> nextIndex = currentSwiperIndex === <span class="number">2</span> ? <span class="number">0</span> : currentSwiperIndex + <span class="number">1</span>;</span><br><span class="line">      calendarArr[prevIndex] = prev;</span><br><span class="line">      calendarArr[nextIndex] = next;</span><br><span class="line">      calendarArr[currentSwiperIndex] = now;</span><br><span class="line">      <span class="keyword">this</span>.setData(&#123;</span><br><span class="line">        calendarArr,</span><br><span class="line">        monthFormat: getDateStr(dataStr),</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></div><blockquote><p>ps: 因为这里上下月份也可以公用，所以单独提取出来供其他方法使用</p></blockquote><p>最后，我们只需要在动画结束的时候设置日历数据即可，即：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">Component(&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  methods: &#123;</span><br><span class="line">    <span class="comment">// 动画结束后让滑动的次数置0</span></span><br><span class="line">    swiperAnimateFinish() &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; year, month &#125; = getDateObj(generateDate);</span><br><span class="line">      <span class="keyword">const</span> monthDist = isPrevMonth ? -changeCount : changeCount;</span><br><span class="line">      generateDate = <span class="keyword">new</span> <span class="built_in">Date</span>(year, month + monthDist - <span class="number">1</span>);</span><br><span class="line">      <span class="comment">// 清空滑动次数</span></span><br><span class="line">      changeCount = <span class="number">0</span>;</span><br><span class="line">      <span class="keyword">this</span>.generatorCalendar(generateDate);</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></div><p>整合起来就是：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// components/calendar/index.js</span></span><br><span class="line"><span class="keyword">const</span> &#123; getDate, dataStr, getDateObj, transformDate &#125; = <span class="built_in">require</span>(<span class="string">"./utils"</span>);</span><br><span class="line"><span class="keyword">const</span> getDateStr = <span class="function">(<span class="params">dataStr</span>) =&gt;</span> dataStr.slice(<span class="number">0</span>, <span class="number">7</span>).replace(<span class="string">"/"</span>, <span class="string">"-"</span>);</span><br><span class="line"><span class="comment">// 当前的索引值，必须从第一个开始，因为这样我们才能实现视野内的无缝</span></span><br><span class="line"><span class="keyword">let</span> currentSwiperIndex = <span class="number">1</span>,</span><br><span class="line">  generateDate = dataStr, <span class="comment">// 当前时间</span></span><br><span class="line">  swipeStartPoint = <span class="number">0</span>, <span class="comment">// 滑动的坐标</span></span><br><span class="line">  isPrevMonth = <span class="literal">false</span>, <span class="comment">// 是否向右滑动</span></span><br><span class="line">  changeCount = <span class="number">0</span>; <span class="comment">// 滑动的次数</span></span><br><span class="line">Component(&#123;</span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * 组件的属性列表</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  properties: &#123;</span><br><span class="line">    duration: &#123;</span><br><span class="line">      type: <span class="built_in">String</span>,</span><br><span class="line">      value: <span class="number">500</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">    isVertical: &#123;</span><br><span class="line">      type: <span class="built_in">Boolean</span>,</span><br><span class="line">      value: <span class="literal">false</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * 组件的初始数据</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  data: &#123;</span><br><span class="line">    week: [<span class="string">"一"</span>, <span class="string">"二"</span>, <span class="string">"三"</span>, <span class="string">"四"</span>, <span class="string">"五"</span>, <span class="string">"六"</span>, <span class="string">"日"</span>],</span><br><span class="line">    current: <span class="number">1</span>,</span><br><span class="line">    calendarArr: [],</span><br><span class="line">    monthFormat: getDateStr(dataStr),</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * 组件的方法列表</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  methods: &#123;</span><br><span class="line">    <span class="comment">// 设置上个月的时间</span></span><br><span class="line">    getPrevMonth(monthFormat) &#123;</span><br><span class="line">      <span class="keyword">const</span> [year, month] = monthFormat.split(<span class="regexp">/\-|\//</span>);</span><br><span class="line">      <span class="keyword">const</span> &#123; dataStr &#125; = getDateObj(</span><br><span class="line">        <span class="keyword">new</span> <span class="built_in">Date</span>(year, month, <span class="number">1</span>).setMonth(month - <span class="number">2</span>)</span><br><span class="line">      );</span><br><span class="line">      <span class="keyword">return</span> dataStr;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 设置下个月的时间</span></span><br><span class="line">    getNextMonth(monthFormat) &#123;</span><br><span class="line">      <span class="keyword">const</span> [year, month] = monthFormat.split(<span class="regexp">/\-|\//</span>);</span><br><span class="line">      <span class="keyword">const</span> &#123; dataStr &#125; = getDateObj(<span class="keyword">new</span> <span class="built_in">Date</span>(year, month, <span class="number">1</span>));</span><br><span class="line">      <span class="keyword">return</span> dataStr;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 生成日历数组</span></span><br><span class="line">    generatorCalendar(date) &#123;</span><br><span class="line">      <span class="keyword">const</span> calendarArr = [];</span><br><span class="line">      <span class="comment">// 转换为 Date 实例</span></span><br><span class="line">      <span class="keyword">const</span> currentDate = transformDate(date);</span><br><span class="line">      <span class="comment">// 获取当前时间的日历数据</span></span><br><span class="line">      <span class="keyword">const</span> now = getDate(currentDate);</span><br><span class="line">      <span class="comment">// 获取当前时间的字符串</span></span><br><span class="line">      <span class="keyword">const</span> &#123; dataStr &#125; = getDateObj(currentDate);</span><br><span class="line">      <span class="comment">// 获取上个月的日历数据</span></span><br><span class="line">      <span class="keyword">const</span> prev = getDate(<span class="keyword">this</span>.getPrevMonth(dataStr));</span><br><span class="line">      <span class="comment">// 获取下个月的日历数据</span></span><br><span class="line">      <span class="keyword">const</span> next = getDate(<span class="keyword">this</span>.getNextMonth(dataStr));</span><br><span class="line">      <span class="comment">// 设置日历数据</span></span><br><span class="line">      <span class="keyword">const</span> prevIndex = currentSwiperIndex === <span class="number">0</span> ? <span class="number">2</span> : currentSwiperIndex - <span class="number">1</span>;</span><br><span class="line">      <span class="keyword">const</span> nextIndex = currentSwiperIndex === <span class="number">2</span> ? <span class="number">0</span> : currentSwiperIndex + <span class="number">1</span>;</span><br><span class="line">      calendarArr[prevIndex] = prev;</span><br><span class="line">      calendarArr[nextIndex] = next;</span><br><span class="line">      calendarArr[currentSwiperIndex] = now;</span><br><span class="line">      <span class="keyword">this</span>.setData(&#123;</span><br><span class="line">        calendarArr,</span><br><span class="line">        monthFormat: getDateStr(dataStr),</span><br><span class="line">      &#125;);</span><br><span class="line">      <span class="comment">// 通知父组件</span></span><br><span class="line">      <span class="keyword">this</span>.triggerEvent(<span class="string">"change"</span>, <span class="keyword">this</span>.data.monthFormat);</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 设置当前的索引值</span></span><br><span class="line">    swiperChange(e) &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; current, source &#125; = e.detail;</span><br><span class="line">      <span class="keyword">if</span> (source === <span class="string">"touch"</span>) &#123;</span><br><span class="line">        currentSwiperIndex = current;</span><br><span class="line">        changeCount += <span class="number">1</span>;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 动画结束后让滑动的次数置0</span></span><br><span class="line">    swiperAnimateFinish() &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; year, month &#125; = getDateObj(generateDate);</span><br><span class="line">      <span class="keyword">const</span> monthDist = isPrevMonth ? -changeCount : changeCount;</span><br><span class="line">      generateDate = <span class="keyword">new</span> <span class="built_in">Date</span>(year, month + monthDist - <span class="number">1</span>);</span><br><span class="line">      <span class="comment">// 清空滑动次数</span></span><br><span class="line">      changeCount = <span class="number">0</span>;</span><br><span class="line">      <span class="keyword">this</span>.generatorCalendar(generateDate);</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 获取手指刚按下的坐标</span></span><br><span class="line">    swipeTouchStart(e) &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; clientY, clientX &#125; = e.changedTouches[<span class="number">0</span>];</span><br><span class="line">      swipeStartPoint = <span class="keyword">this</span>.data.isVertical ? clientY : clientX;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 获取手指松开时的坐标</span></span><br><span class="line">    swipeTouchEnd(e) &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; clientY, clientX &#125; = e.changedTouches[<span class="number">0</span>];</span><br><span class="line">      isPrevMonth = <span class="keyword">this</span>.data.isVertical</span><br><span class="line">        ? clientY - swipeStartPoint &gt; <span class="number">0</span></span><br><span class="line">        : clientX - swipeStartPoint &gt; <span class="number">0</span>;</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    dateChange(e) &#123;</span><br><span class="line">      <span class="keyword">const</span> monthFormat = e.detail.value;</span><br><span class="line">      <span class="keyword">this</span>.setData(&#123;</span><br><span class="line">        monthFormat,</span><br><span class="line">      &#125;);</span><br><span class="line">      generateDate = getDateStr(monthFormat);</span><br><span class="line">      <span class="keyword">this</span>.generatorCalendar(generateDate);</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 上个月日期</span></span><br><span class="line">    prevMonth() &#123;</span><br><span class="line">      <span class="keyword">this</span>.setData(&#123;</span><br><span class="line">        monthFormat: <span class="keyword">this</span>.getPrevMonth(<span class="keyword">this</span>.data.monthFormat),</span><br><span class="line">      &#125;);</span><br><span class="line">      <span class="keyword">this</span>.generatorCalendar(<span class="keyword">this</span>.data.monthFormat);</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 下个月日期</span></span><br><span class="line">    nextMonth() &#123;</span><br><span class="line">      <span class="keyword">this</span>.setData(&#123;</span><br><span class="line">        monthFormat: <span class="keyword">this</span>.getNextMonth(<span class="keyword">this</span>.data.monthFormat),</span><br><span class="line">      &#125;);</span><br><span class="line">      <span class="keyword">this</span>.generatorCalendar(<span class="keyword">this</span>.data.monthFormat);</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">  ready() &#123;</span><br><span class="line">    <span class="keyword">this</span>.generatorCalendar(generateDate);</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></div><p>页面中使用，</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">xml</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">calendar</span> <span class="attr">bindchange</span>=<span class="string">"calendarChange"</span>&gt;</span><span class="tag">&lt;/<span class="name">calendar</span>&gt;</span></span><br></pre></td></tr></table></figure></div><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">Page(&#123;</span><br><span class="line">  calendarChange(e) &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(e.detail);</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></div><p>写到这里，其实基本上整个日历就已经完成了，但也许你会说，其实还有很多功能没有完善，比如:</p><ul><li><p>跳转到指定到时间</p></li><li><p>范围选择</p></li><li><p>点击标记时间等等</p></li><li><p>…</p></li></ul><p>其实我觉得多数是设置样式问题，比如标记时间这个功能，其实有这个基础的例子，我想大家往里面加功能应该都可以实现的，所以我就不一步一步的往里面写了</p><p>如果后续有时间我会往这个项目多加一下新功能的</p><blockquote><p><a href="https://gitee.com/gating/demo/tree/master/calendar" target="_blank" rel="noopener">gitee 地址</a>,<a href="https://github.com/GATING/demo/tree/master/calendar" target="_blank" rel="noopener">github 地址</a></p></blockquote><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>感觉各位观众老爷的观看，希望你能有所收获 😁</p>]]></content>
    
    <summary type="html">
    
      很多时候，我们生活中会有各种打卡的情况，比如keep的运动打卡、单词的学习打卡和各种签到打卡或者酒店的入住时间选择，这时候就需要我们书写一个日历组件来处理我们这种需求。
    
    </summary>
    
    
      <category term="小程序" scheme="https://gatings.cn/categories/%E5%B0%8F%E7%A8%8B%E5%BA%8F/"/>
    
    
      <category term="小程序" scheme="https://gatings.cn/tags/%E5%B0%8F%E7%A8%8B%E5%BA%8F/"/>
    
  </entry>
  
  <entry>
    <title>Charles（青花瓷/花瓶）的基本使用</title>
    <link href="https://gatings.cn/2020-06-04/Charles%E7%9A%84%E5%9F%BA%E6%9C%AC%E4%BD%BF%E7%94%A8/"/>
    <id>https://gatings.cn/2020-06-04/Charles%E7%9A%84%E5%9F%BA%E6%9C%AC%E4%BD%BF%E7%94%A8/</id>
    <published>2020-06-03T16:00:00.000Z</published>
    <updated>2021-10-13T01:56:29.703Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>Charles 其实是一款代理服务器，通过成为电脑或者浏览器的代理，然后截取请求和请求结果达到分析抓包的目的。其次该软件是用 Java 写的，能够在 Windows，Mac，Linux 上使用。</p><h2 id="什么是抓包？"><a href="#什么是抓包？" class="headerlink" title="什么是抓包？"></a>什么是抓包？</h2><p>抓包（packet capture）就是将网络传输发送与接收的数据包进行截获、重发、编辑、转存等操作，也用来检查网络安全。抓包也经常被用来进行数据截取等。</p><h1 id="Charles-主要功能"><a href="#Charles-主要功能" class="headerlink" title="Charles 主要功能"></a>Charles 主要功能</h1><ul><li>支持 SSL 代理。可以截取分析 SSL 的请求。</li><li>支持流量控制。可以模拟慢速网络以及等待时间（latency）较长的请求。</li><li>支持重发网络请求，方便后端调试。</li><li>支持修改网络请求参数。</li><li>支持网络请求的截获并动态修改。</li></ul><blockquote><p>PS：Charles 是收费软件，可以免费试用 30 天。试用期过后，未付费的用户仍然可以继续使用，但是每次使用时间不能超过 30 分钟，并且启动时将会有 10 秒种的延时。</p></blockquote><h1 id="开始抓包"><a href="#开始抓包" class="headerlink" title="开始抓包"></a>开始抓包</h1><p>默认，他会把电脑设置为代理，这样你会发现，你通过浏览器请求到网址都会出现在这里。<br>通常点击某一个网址后，你会发现右边会出现这个网址请求的大概信息，点击具体的请求后会出现 request 和 response 等信息<br>但是你会发现 <code>https</code> 都是 <code>unknown</code> ，这时候就需要我们安装证书啦。</p><h2 id="安装-ssl-ca-证书"><a href="#安装-ssl-ca-证书" class="headerlink" title="安装 ssl ca 证书"></a>安装 ssl ca 证书</h2><ul><li>配置 Charles 证书；选择 help —&gt; SSL Proxying -&gt; install Charles Root Certificate，就会出现如下界面</li></ul><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles01.png" data-fancybox="group" data-caption="安装 ssl ca 证书" class="fancybox"><img alt="安装 ssl ca 证书" title="安装 ssl ca 证书" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles01.png" class="lazyload"></a></p><ul><li>点击安装证书再点击，安装在“受信任的根证书颁发机构” ，这一步十分重要，如不在这个目录下，则不能抓取 https 请求</li></ul><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles02.png" data-fancybox="group" data-caption="安装 ssl ca 证书" class="fancybox"><img alt="安装 ssl ca 证书" title="安装 ssl ca 证书" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles02.png" class="lazyload"></a></p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles03.png" data-fancybox="group" data-caption="安装 ssl ca 证书" class="fancybox"><img alt="安装 ssl ca 证书" title="安装 ssl ca 证书" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles03.png" class="lazyload"></a></p><p>之后无脑下一步就安装完成啦</p><h2 id="配置-SSL-Proxy-Settings"><a href="#配置-SSL-Proxy-Settings" class="headerlink" title="配置 SSL Proxy Settings"></a>配置 SSL Proxy Settings</h2><p>添加上证书后，会发现还是不能抓取 <code>https</code> 请求，这是因为我们还没有配置 <code>ssl</code> 的代理，接下来我们配置一下<br>在 Proxy -&gt; SSL Proxy Settings，然后选择 Enable SSL Proxying，在点击 Add</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles04.png" data-fancybox="group" data-caption="配置 SSL Proxy Settings" class="fancybox"><img alt="配置 SSL Proxy Settings" title="配置 SSL Proxy Settings" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles04.png" class="lazyload"></a></p><blockquote><p>ps: 通常 https 默认端口就是 443，所以这里我们通过匹配符 *:443 就可以抓取大部分的 https 请求了</p></blockquote><h3 id="打开百度"><a href="#打开百度" class="headerlink" title="打开百度"></a>打开百度</h3><p>搜索一下 12306 就会出现百度根据 12306 查询关联的下拉列表请求了</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles05.png" data-fancybox="group" data-caption="打开百度" class="fancybox"><img alt="打开百度" title="打开百度" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles05.png" class="lazyload"></a></p><p>到这里，基本的 <code>https</code> 抓包就可以使用了<br>接下来，我们连接真机进行真机的抓包体验。。。</p><h2 id="手机安装证书"><a href="#手机安装证书" class="headerlink" title="手机安装证书"></a>手机安装证书</h2><p><strong>首先，手机和电脑需要在同一个局域网才可以进行抓包</strong></p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles06.png" data-fancybox="group" data-caption="手机安装证书" class="fancybox"><img alt="手机安装证书" title="手机安装证书" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles06.png" class="lazyload"></a></p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles07.png" data-fancybox="group" data-caption="手机安装证书" class="fancybox"><img alt="手机安装证书" title="手机安装证书" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles07.png" class="lazyload"></a></p><p>这里会告诉我们，用手机浏览器打开 <a href="http://chls.pro/ssl" target="_blank" rel="noopener">chls.pro/ssl</a> 就可以安装证书了<br>先手动配置下代理，打开 <code>WIFI</code>，设置，代理填写刚刚的 ip 地址，具体如图：</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles08.jpg" data-fancybox="group" data-caption="手机安装证书" class="fancybox"><img alt="手机安装证书" title="手机安装证书" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles08.jpg" class="lazyload"></a></p><p>打开后会出现是否允许设备连接 <code>Charles</code>，点击 <code>allow</code> 即可<br><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles09.png" data-fancybox="group" data-caption="手机安装证书" class="fancybox"><img alt="手机安装证书" title="手机安装证书" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles09.png" class="lazyload"></a></p><p>手机端访问 <a href="http://chls.pro/ssl" target="_blank" rel="noopener">chls.pro/ssl</a>,下载证书就可以安装了</p><p>这里还是以百度为例，手机访问百度之后再次搜索 <code>12306</code>, 同样会出现下面的下拉列表：</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles10.jpg" data-fancybox="group" data-caption="手机安装证书" class="fancybox"><img alt="手机安装证书" title="手机安装证书" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles10.jpg" class="lazyload"></a></p><p><code>Charles</code> 就会截取我们请求这个地址的数据，如图所示：</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles11.png" data-fancybox="group" data-caption="手机安装证书" class="fancybox"><img alt="手机安装证书" title="手机安装证书" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles11.png" class="lazyload"></a></p><p>这样子就实现了我们的真机抓包啦。</p><h3 id="安卓端"><a href="#安卓端" class="headerlink" title="安卓端"></a>安卓端</h3><blockquote><p>ps: 可能是因为我这里之前装过证书了，所以可以抓包成功，实际上安卓 7.0 以上因为一些安全政策，不允许将证书安装到信任目录，想实现抓包则需要将下载的证书复制到/system/etc/security/cacerts/目录</p></blockquote><p>解决条件：手机需要 root</p><p>参考链接：<a href="https://www.cnblogs.com/ilizhu/p/11175431.html" target="_blank" rel="noopener">Android9.0 配置 charles 的 https 抓包</a>,<a href="https://www.cnblogs.com/wangyuehome/p/11203771.html" target="_blank" rel="noopener">android 9.0 以上 charles https 抓包</a></p><blockquote><p>ps: openssl 直接在 gitbash 就可以使用了，所以上面的操作直接在 gitbash 即可</p></blockquote><h3 id="ios-端"><a href="#ios-端" class="headerlink" title="ios 端"></a>ios 端</h3><p>ios 配置比安卓简单了许多，安装完后只需要在 wifi 中设置代理，在之后 打开设置 -&gt; 关于本机 -&gt; 证书信任设置，打开完全信任开关。就可以抓包了。</p><blockquote><p>ps：因为手机抓包，截图不方便，所以下面用 pc 来讲解其他内容，实际使用是一样的</p></blockquote><h1 id="Throttle-Setting-模拟网速"><a href="#Throttle-Setting-模拟网速" class="headerlink" title="Throttle Setting 模拟网速"></a>Throttle Setting 模拟网速</h1><p>有时候在开发的时候我们想要模拟一下网络慢的情况，这时候 Charles 他是可以帮助到你的，在 Proxy -&gt; Throttle Setting，然后选择 Enable Throttling，在 Throttle Preset 下选择网络类型即可，具体设置你可以自行拿捏。</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles12.png" data-fancybox="group" data-caption="Throttle Setting 模拟网速" class="fancybox"><img alt="Throttle Setting 模拟网速" title="Throttle Setting 模拟网速" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles12.png" class="lazyload"></a></p><h1 id="Breakpoints-断点"><a href="#Breakpoints-断点" class="headerlink" title="Breakpoints 断点"></a>Breakpoints 断点</h1><p>在 <code>Charles</code> 发起一个请求的时候，我们是可以给某个请求打一个断点的，然后来观察或者修改请求或者返回的内容，但是在这过程中要注意请求的超时时间问题。要针对某一个请求设置断点，只需要在这个请求网址右击选择 Breakpoints 就可以断点某一个请求了。</p><p>这里我们以简书的右侧推荐作者为例，</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles13.png" data-fancybox="group" data-caption="Breakpoints 断点" class="fancybox"><img alt="Breakpoints 断点" title="Breakpoints 断点" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles13.png" class="lazyload"></a></p><p>右键，打上断点，刷新一下</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles14.png" data-fancybox="group" data-caption="Breakpoints 断点" class="fancybox"><img alt="Breakpoints 断点" title="Breakpoints 断点" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles14.png" class="lazyload"></a></p><p>通过右侧的 Edit Request 就可以修改我们的请求参数啦</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles15.png" data-fancybox="group" data-caption="Breakpoints 断点" class="fancybox"><img alt="Breakpoints 断点" title="Breakpoints 断点" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles15.png" class="lazyload"></a></p><p>比如，我们把 <code>count</code> 修改为 2，则页面返回的数据只有两条了</p><h1 id="Rewrite-Settings-内容替换"><a href="#Rewrite-Settings-内容替换" class="headerlink" title="Rewrite Settings 内容替换"></a>Rewrite Settings 内容替换</h1><p>有时候我们会测一下请求的参数不同会带来不同的返回结果以测试是否达到业务需求，或者需要不同的返回结果来验证我们对数据的处理是否正确，这时候需要后台的同事配合，但是有了 <code>Charles</code>，我们可以自己把控接口返回来的内容，比如数据的空与否，数据的长短等等。</p><blockquote><p>再或者，修改别人的 app 里面接口的返回值，供做一些自己想做的事 😏</p></blockquote><p>击 Tools -&gt; Rewrite Settings , 就可以使用我们的内容替换了，同样是以全部渠道这个接口为例<br>点击 enable，启用该功能（事先复制好接口链接），点击 Add 添加一个重写配置，这里命名为 <code>Test</code></p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles16.png" data-fancybox="group" data-caption="Rewrite Settings 内容替换" class="fancybox"><img alt="Rewrite Settings 内容替换" title="Rewrite Settings 内容替换" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles16.png" class="lazyload"></a></p><p>点击右下 add 按钮，选中 body ，设置要修改的字符，修改即可</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles17.png" data-fancybox="group" data-caption="Rewrite Settings 内容替换" class="fancybox"><img alt="Rewrite Settings 内容替换" title="Rewrite Settings 内容替换" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles17.png" class="lazyload"></a></p><p>这里有个小问题，因为我们修改的是中文，所以我们需要在返回的数据 Header 中 Content-Type 字段声明 charset=UTF-8。即：</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles18.png" data-fancybox="group" data-caption="Rewrite Settings 内容替换" class="fancybox"><img alt="Rewrite Settings 内容替换" title="Rewrite Settings 内容替换" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles18.png" class="lazyload"></a></p><p>修改完成后，我们看下界面的变化，是不是所有 <code>乔汉童</code> 都变成了 <code>磨蹭先生</code> 啊</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles19.png" data-fancybox="group" data-caption="Rewrite Settings 内容替换" class="fancybox"><img alt="Rewrite Settings 内容替换" title="Rewrite Settings 内容替换" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles19.png" class="lazyload"></a></p><h1 id="Map-Remote-请求重定向"><a href="#Map-Remote-请求重定向" class="headerlink" title="Map Remote 请求重定向"></a>Map Remote 请求重定向</h1><p>求重定向的作用是什么呢？开发中一般都是测试环境，如果我们想对比一下和线上版本的区别的话，可以讲测试的请求重定向到正式环境下。在选择 Tools -&gt; Map Remote 下：</p><blockquote><p>ps: 其实这个和重写作用类似，但是比重写适用性更广，而且更可控</p></blockquote><p>和 Rewrite 操作类似，我们同样需要点击 enable ，之后输入我们需要重定向的地址</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles20.png" data-fancybox="group" data-caption="Map Remote 请求重定向" class="fancybox"><img alt="Map Remote 请求重定向" title="Map Remote 请求重定向" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles20.png" class="lazyload"></a></p><p>映射数据如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">json</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight json"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"users"</span>: [</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="attr">"id"</span>: <span class="number">5796592</span>,</span><br><span class="line">      <span class="attr">"slug"</span>: <span class="string">"86b81ed8e35c"</span>,</span><br><span class="line">      <span class="attr">"nickname"</span>: <span class="string">"磨蹭先生"</span>,</span><br><span class="line">      <span class="attr">"avatar_source"</span>: <span class="string">"https://cdn.jsdelivr.net/gh/GATING/blog_imgs@master/blog/avatar.png"</span>,</span><br><span class="line">      <span class="attr">"total_likes_count"</span>: <span class="number">200044</span>,</span><br><span class="line">      <span class="attr">"total_wordage"</span>: <span class="number">36871100</span>,</span><br><span class="line">      <span class="attr">"is_following_user"</span>: <span class="literal">false</span></span><br><span class="line">    &#125;</span><br><span class="line">  ],</span><br><span class="line">  <span class="attr">"total_count"</span>: <span class="number">41931</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>这里主要修改了第一个数据，我们看看效果</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles21.png" data-fancybox="group" data-caption="Map Remote 请求重定向" class="fancybox"><img alt="Map Remote 请求重定向" title="Map Remote 请求重定向" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-06-04/charles21.png" class="lazyload"></a></p><p>发现，数据的确发生了改变，也实现了我们的目的</p><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>感谢各位观众老爷的观看啦，Charles 还有一些其他的妙用。就等着各位慢慢的去发掘吧！</p>]]></content>
    
    <summary type="html">
    
      Charles 其实是一款代理服务器，通过成为电脑或者浏览器的代理，然后截取请求和请求结果达到分析抓包的目的。
    
    </summary>
    
    
      <category term="抓包工具" scheme="https://gatings.cn/categories/%E6%8A%93%E5%8C%85%E5%B7%A5%E5%85%B7/"/>
    
    
      <category term="Charles" scheme="https://gatings.cn/tags/Charles/"/>
    
  </entry>
  
  <entry>
    <title>用vue实现一个简单的时间屏幕</title>
    <link href="https://gatings.cn/2020-03-14/%E7%94%A8vue%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84%E6%97%B6%E9%97%B4%E5%B1%8F%E5%B9%95/"/>
    <id>https://gatings.cn/2020-03-14/%E7%94%A8vue%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84%E6%97%B6%E9%97%B4%E5%B1%8F%E5%B9%95/</id>
    <published>2020-03-13T16:00:00.000Z</published>
    <updated>2021-10-13T01:56:29.707Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>去年用了一个小的 <code>app</code>，叫做 <code>一个木函</code>，本来想着用来做动画优化就删掉了的，不过看到他有个时间屏幕的小工具，就点进去看了下，觉得挺好玩的，就想着能不能自己实现一下。</p><blockquote><p>ps: 闲话不多说，先上例子<a href="https://gating.gitee.io/demo/vue-time/" target="_blank" rel="noopener">点我查看</a>,觉得没啥意思的就不需要再往下看了</p></blockquote><h1 id="简单的搭建一下"><a href="#简单的搭建一下" class="headerlink" title="简单的搭建一下"></a>简单的搭建一下</h1><p>初始化一个 <code>vue</code> 项目</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">shell</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">vue create vue-time</span><br></pre></td></tr></table></figure></div><p>然后无脑下一步就好了（回车），这里我打算用 <code>scss</code> 方便我们书写样式 ，所以创建完成后，我们在安装下 <code>scss</code></p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">shell</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">cd vue-time</span><br><span class="line">npm i sass-loader node-sass -D</span><br></pre></td></tr></table></figure></div><blockquote><p>ps: 如果网络不好，就换下源或者用 <code>cnpm</code> 吧</p></blockquote><h2 id="新建时间屏幕组件"><a href="#新建时间屏幕组件" class="headerlink" title="新建时间屏幕组件"></a>新建时间屏幕组件</h2><p>在 <code>components</code> 目录下新建 <code>TimeScreen.vue</code> 文件，然后通过 <code>vbase</code> 指令生成生成一个最基础的 <code>vue</code> 代码片段</p><blockquote><p>ps: <code>vbase</code> 是 <code>vscode</code> 中 vue 代码片段的一个插件</p></blockquote><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">html</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="javascript">  <span class="keyword">export</span> <span class="keyword">default</span> &#123;</span></span><br><span class="line"><span class="actionscript">    name: <span class="string">"TimeScreen"</span>,</span></span><br><span class="line">  &#125;;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">style</span> <span class="attr">lang</span>=<span class="string">"scss"</span> <span class="attr">scoped</span>&gt;</span><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br></pre></td></tr></table></figure></div><h2 id="思考一下，如何做时间切换的动画"><a href="#思考一下，如何做时间切换的动画" class="headerlink" title="思考一下，如何做时间切换的动画"></a>思考一下，如何做时间切换的动画</h2><p>emmm… 不知是否有看过我之前的一篇文章<a href="https://gatings.cn/2018-06-14/%E7%94%A8jq%E5%AE%9E%E7%8E%B0%E7%9A%84%E5%8D%95%E4%B8%AAspan%E4%B8%BA%E5%8D%95%E4%B8%AA%E7%9A%84%E6%95%B0%E5%AD%97%E5%8A%A8%E7%94%BB/">用 jq 实现的单个 span 为单个的数字动画</a>，没错，其实我们实现的思路，和这里基本一样，所以接下来我们就分析分析我们该怎么来实现了吧</p><p>首先，我们要明确一下，要有多少个 <code>span</code>，众所周知，一天最后就是<strong>23:59:59</strong>，所以我们所需要的 <code>span</code> 数组为 <code>[3, 10, 0, 6, 10, 0, 6, 10]</code></p><blockquote><p>ps: 中间的 <code>:</code> 是只需要一个 <code>span</code> 的</p></blockquote><p>因为布局不是我们要将的重点，所以我们就想想我们该怎么获取我们需要的东西。比如，怎么获取到<code>Saturday 14 March</code>,怎么获取到当前时间</p><p>众所周知啦，我们知道 <code>js</code> 中提供了 <code>Date</code> 这个对象，所以我们可以通过他可以获取我们想要的东西，废话不多说了，开始写代码吧。</p><p>新建 <code>utils</code> 目录，在该目录下新建 <code>time.js</code> 文件，内容如下</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 月份</span></span><br><span class="line"><span class="keyword">const</span> months = [</span><br><span class="line">  <span class="string">"January"</span>,</span><br><span class="line">  <span class="string">"February"</span>,</span><br><span class="line">  <span class="string">"March"</span>,</span><br><span class="line">  <span class="string">"April"</span>,</span><br><span class="line">  <span class="string">"May"</span>,</span><br><span class="line">  <span class="string">"June"</span>,</span><br><span class="line">  <span class="string">"July"</span>,</span><br><span class="line">  <span class="string">"August"</span>,</span><br><span class="line">  <span class="string">"September"</span>,</span><br><span class="line">  <span class="string">"October"</span>,</span><br><span class="line">  <span class="string">"November"</span>,</span><br><span class="line">  <span class="string">"December"</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> weekday = [</span><br><span class="line">  <span class="string">"Sunday"</span>,</span><br><span class="line">  <span class="string">"Monday"</span>,</span><br><span class="line">  <span class="string">"Tuesday"</span>,</span><br><span class="line">  <span class="string">"Wednesday"</span>,</span><br><span class="line">  <span class="string">"Thursday"</span>,</span><br><span class="line">  <span class="string">"Friday"</span>,</span><br><span class="line">  <span class="string">"Saturday"</span>,</span><br><span class="line">];</span><br><span class="line"><span class="comment">// 获取日期</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getTime</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> date = <span class="keyword">new</span> <span class="built_in">Date</span>();</span><br><span class="line">  <span class="keyword">const</span> days = date.getDate();</span><br><span class="line">  <span class="keyword">const</span> month = date.getMonth();</span><br><span class="line">  <span class="keyword">const</span> day = date.getDay();</span><br><span class="line">  <span class="keyword">const</span> hours = toDou(date.getHours());</span><br><span class="line">  <span class="keyword">const</span> minutes = toDou(date.getMinutes());</span><br><span class="line">  <span class="keyword">const</span> seconds = toDou(date.getSeconds());</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    date: <span class="string">`<span class="subst">$&#123;weekday[day]&#125;</span> <span class="subst">$&#123;days&#125;</span> <span class="subst">$&#123;months[month]&#125;</span>`</span>,</span><br><span class="line">    time: <span class="string">`<span class="subst">$&#123;hours&#125;</span>:<span class="subst">$&#123;minutes&#125;</span>:<span class="subst">$&#123;seconds&#125;</span>`</span>,</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 转成两位 eg: 6 =&gt; 06</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">toDou</span>(<span class="params">str</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> num = ~~str;</span><br><span class="line">  <span class="keyword">return</span> num &gt; <span class="number">9</span> ? num : <span class="string">"0"</span> + str;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 测试一下我们写的方法，上线记得注释掉</span></span><br><span class="line"><span class="comment">// console.log(getTime()) // &#123;date: "Saturday 14 March", time: "18:53:40"&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> getTime;</span><br></pre></td></tr></table></figure></div><p>通过上面代码，我们就得到我们需要的格式啦，接下来就是写布局啦，但这里不是我们的重点，所以略过</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">html</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span></span></span><br><span class="line"><span class="tag">    <span class="attr">class</span>=<span class="string">"time-container"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">:class</span>=<span class="string">"&#123; dark: isDark &#125;"</span></span></span><br><span class="line"><span class="tag">    @<span class="attr">click</span>=<span class="string">"toggleClass"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">:date</span>=<span class="string">"date"</span></span></span><br><span class="line"><span class="tag">  &gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"time"</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">template</span> <span class="attr">v-for</span>=<span class="string">"(str, idx) in time"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span></span></span><br><span class="line"><span class="tag">          <span class="attr">class</span>=<span class="string">"time-num"</span></span></span><br><span class="line"><span class="tag">          <span class="attr">v-if</span>=<span class="string">"str !== ':'"</span></span></span><br><span class="line"><span class="tag">          <span class="attr">:style</span>=<span class="string">"numStyle[idx]"</span></span></span><br><span class="line"><span class="tag">          <span class="attr">:key</span>=<span class="string">"idx"</span></span></span><br><span class="line"><span class="tag">        &gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">span</span> <span class="attr">v-for</span>=<span class="string">"(i, spanIdx) in haveSpan[idx]"</span> <span class="attr">:key</span>=<span class="string">"spanIdx"</span></span></span><br><span class="line"><span class="tag">            &gt;</span>&#123;&#123; i - 1 &#125;&#125;<span class="tag">&lt;/<span class="name">span</span></span></span><br><span class="line"><span class="tag">          &gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">span</span>&gt;</span>0<span class="tag">&lt;/<span class="name">span</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"time-dist"</span> <span class="attr">v-else</span> <span class="attr">:key</span>=<span class="string">"idx"</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">span</span>&gt;</span>&#123;&#123; str &#125;&#125;<span class="tag">&lt;/<span class="name">span</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="javascript">  <span class="keyword">import</span> getTime <span class="keyword">from</span> <span class="string">"../utils/time"</span>;</span></span><br><span class="line"><span class="actionscript">  <span class="comment">// 设置样式</span></span></span><br><span class="line"><span class="actionscript">  <span class="function"><span class="keyword">function</span> <span class="title">setStyle</span><span class="params">(val)</span> </span>&#123;</span></span><br><span class="line"><span class="javascript">    <span class="keyword">return</span> <span class="string">`transform: translateY(-<span class="subst">$&#123;~~val * <span class="number">100</span>&#125;</span>%)`</span>;</span></span><br><span class="line">  &#125;</span><br><span class="line"><span class="actionscript">  <span class="comment">// 每个字的样式</span></span></span><br><span class="line"><span class="actionscript">  <span class="function"><span class="keyword">function</span> <span class="title">numStyle</span><span class="params">(time)</span> </span>&#123;</span></span><br><span class="line"><span class="javascript">    <span class="keyword">return</span> time.split(<span class="string">""</span>).map(<span class="function">(<span class="params">val</span>) =&gt;</span> setStyle(val));</span></span><br><span class="line">  &#125;</span><br><span class="line"><span class="javascript">  <span class="keyword">export</span> <span class="keyword">default</span> &#123;</span></span><br><span class="line"><span class="actionscript">    name: <span class="string">"TimeScreen"</span>,</span></span><br><span class="line">    data() &#123;</span><br><span class="line"><span class="actionscript">      <span class="keyword">const</span> &#123; time, date &#125; = getTime();</span></span><br><span class="line"><span class="actionscript">      <span class="keyword">return</span> &#123;</span></span><br><span class="line">        isDark: 0,</span><br><span class="line">        time,</span><br><span class="line">        date,</span><br><span class="line">        numStyle: numStyle(time),</span><br><span class="line">      &#125;;</span><br><span class="line">    &#125;,</span><br><span class="line">    methods: &#123;</span><br><span class="line"><span class="actionscript">      <span class="comment">// 切换样式</span></span></span><br><span class="line">      toggleClass() &#123;</span><br><span class="line"><span class="actionscript">        <span class="keyword">this</span>.isDark = !<span class="keyword">this</span>.isDark;</span></span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">    created() &#123;</span><br><span class="line"><span class="actionscript">      <span class="comment">// 判单有多少个Span</span></span></span><br><span class="line"><span class="actionscript">      <span class="comment">// 比较小时数最多24小时，所以第一位最多是3个，0、1、2</span></span></span><br><span class="line"><span class="actionscript">      <span class="comment">// 这里使用 freeze 是因为这个值已经固定，没有必要进行数据劫持</span></span></span><br><span class="line"><span class="javascript">      <span class="keyword">this</span>.haveSpan = <span class="built_in">Object</span>.freeze([<span class="number">3</span>, <span class="number">10</span>, <span class="number">0</span>, <span class="number">6</span>, <span class="number">10</span>, <span class="number">0</span>, <span class="number">6</span>, <span class="number">10</span>]);</span></span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">style</span> <span class="attr">lang</span>=<span class="string">"scss"</span> <span class="attr">scoped</span>&gt;</span></span><br><span class="line">  %flexCenter &#123;</span><br><span class="line">    display: flex;</span><br><span class="line">    justify-content: center;</span><br><span class="line">    align-items: center;</span><br><span class="line">  &#125;</span><br><span class="line">  $timeColor: #d9d4d0;</span><br><span class="line">  $white: #fff;</span><br><span class="line"><span class="css">  <span class="selector-class">.time-container</span> &#123;</span></span><br><span class="line">    background: $white;</span><br><span class="line">    color: $timeColor;</span><br><span class="line">    position: absolute;</span><br><span class="line">    width: 100%;</span><br><span class="line">    height: 100%;</span><br><span class="line">    max-width: 540px;</span><br><span class="line">    top: 0;</span><br><span class="line">    left: 50%;</span><br><span class="line">    transform: translateX(-50%);</span><br><span class="line"><span class="css">    &amp;<span class="selector-class">.dark</span> &#123;</span></span><br><span class="line"><span class="css">      <span class="selector-tag">background</span>: <span class="selector-id">#000</span>;</span></span><br><span class="line">      color: $white;</span><br><span class="line">    &#125;</span><br><span class="line"><span class="css">    &amp;<span class="selector-pseudo">::after</span> &#123;</span></span><br><span class="line">      content: attr(date);</span><br><span class="line">      position: absolute;</span><br><span class="line">      color: $timeColor;</span><br><span class="line">      font-size: 18px;</span><br><span class="line">      line-height: 1;</span><br><span class="line">      transform: rotate(90deg);</span><br><span class="line">      bottom: 20%;</span><br><span class="line">      left: -48px;</span><br><span class="line">    &#125;</span><br><span class="line"><span class="css">    <span class="keyword">@extend</span> %flexCenter;</span></span><br><span class="line"><span class="css">    <span class="selector-class">.time</span> &#123;</span></span><br><span class="line">      font-size: 70px;</span><br><span class="line">      transform: rotate(90deg);</span><br><span class="line">      position: relative;</span><br><span class="line">      height: 106px;</span><br><span class="line">      line-height: 106px;</span><br><span class="line">      overflow: hidden;</span><br><span class="line"><span class="css">      <span class="keyword">@extend</span> %flexCenter;</span></span><br><span class="line"></span><br><span class="line"><span class="css">      <span class="selector-class">.time-num</span> &#123;</span></span><br><span class="line">        position: relative;</span><br><span class="line">        width: 100%;</span><br><span class="line">        height: 100%;</span><br><span class="line">        text-align: center;</span><br><span class="line">        text-shadow: 0 0 2px $white;</span><br><span class="line"><span class="css">        <span class="selector-tag">transition</span>: 0<span class="selector-class">.5s</span> <span class="selector-tag">all</span>;</span></span><br><span class="line">        span &#123;</span><br><span class="line">          display: block;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line"><span class="css">      <span class="selector-class">.time-dist</span> &#123;</span></span><br><span class="line">        padding-bottom: 15px;</span><br><span class="line">        margin: 0 10px;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br></pre></td></tr></table></figure></div><p>写到这里，其实基本的样子已经出来了，这里我们用到了 <code>attr</code> 函数，用来回选择元素的属性值，这个小技巧在一些场景很用用哦。</p><p>这里我们多加了一个 <code>&lt;span&gt;0&lt;/span&gt;</code> 这里主要是为了无缝，那么我们如何做到无缝呢？即，当我们滚动最低下的时候，在<code>500ms</code>之内让动画取消。</p><blockquote><p>ps: 这里的 <code>500ms</code> 是因为动画设置了 <code>500ms</code>,所以需要用 <code>1s - 500ms</code> 得出来的<code>500ms</code>哦</p></blockquote><p>既然知道了原理，那么我们就开始写我们的代码了</p><p>首先定义一下清空动画之后的样式，即</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 清除样式</span></span><br><span class="line"><span class="keyword">const</span> style = <span class="string">"transform: translateY(0%);transition:0s all"</span>;</span><br></pre></td></tr></table></figure></div><p>那么什么时候清空呢，前面也说了，当滚动到最下面的时候，也就是当 <code>time</code> 这个字符串某个为 <code>0</code> 的时候，我们就要清空了，所以</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">this</span>.time.split(<span class="string">""</span>).forEach(<span class="function">(<span class="params">val, idx</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// 当 val 为 0 时，说明已经滚到最底下，这里需要清除动画，并让他回到最顶上来实现无缝</span></span><br><span class="line">    <span class="keyword">if</span> (val == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">this</span>.numStyle[idx] !== style) &#123;</span><br><span class="line">            <span class="comment">// 500ms后清除当前这个span的动画</span></span><br><span class="line">            <span class="keyword">this</span>.removeAnimate(idx);</span><br><span class="line">            <span class="comment">// 设置样式</span></span><br><span class="line">            <span class="keyword">this</span>.numStyle[idx] = setStyle(<span class="keyword">this</span>.haveSpan[idx]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">this</span>.numStyle[idx] = setStyle(val);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 清除动画</span></span><br><span class="line">removeAnimate(idx) &#123;</span><br><span class="line">    setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">this</span>.numStyle[idx] = style;</span><br><span class="line">        <span class="keyword">this</span>.numStyle = [...this.numStyle];</span><br><span class="line">    &#125;, <span class="number">500</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>最后，就是写一个简单的定时器啦，我想这应该难不倒各位小伙伴啦，所以我就不详解啦，就贴一下代码</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">html</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span></span></span><br><span class="line"><span class="tag">    <span class="attr">class</span>=<span class="string">"time-container"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">:class</span>=<span class="string">"&#123; dark: isDark &#125;"</span></span></span><br><span class="line"><span class="tag">    @<span class="attr">click</span>=<span class="string">"toggleClass"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">:date</span>=<span class="string">"date"</span></span></span><br><span class="line"><span class="tag">  &gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"time"</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">template</span> <span class="attr">v-for</span>=<span class="string">"(str, idx) in time"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span></span></span><br><span class="line"><span class="tag">          <span class="attr">class</span>=<span class="string">"time-num"</span></span></span><br><span class="line"><span class="tag">          <span class="attr">v-if</span>=<span class="string">"str !== ':'"</span></span></span><br><span class="line"><span class="tag">          <span class="attr">:style</span>=<span class="string">"numStyle[idx]"</span></span></span><br><span class="line"><span class="tag">          <span class="attr">:key</span>=<span class="string">"idx"</span></span></span><br><span class="line"><span class="tag">        &gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">span</span> <span class="attr">v-for</span>=<span class="string">"(i, spanIdx) in haveSpan[idx]"</span> <span class="attr">:key</span>=<span class="string">"spanIdx"</span></span></span><br><span class="line"><span class="tag">            &gt;</span>&#123;&#123; i - 1 &#125;&#125;<span class="tag">&lt;/<span class="name">span</span></span></span><br><span class="line"><span class="tag">          &gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">span</span>&gt;</span>0<span class="tag">&lt;/<span class="name">span</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"time-dist"</span> <span class="attr">v-else</span> <span class="attr">:key</span>=<span class="string">"idx"</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">span</span>&gt;</span>&#123;&#123; str &#125;&#125;<span class="tag">&lt;/<span class="name">span</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="javascript">  <span class="keyword">import</span> getTime <span class="keyword">from</span> <span class="string">"../utils/time"</span>;</span></span><br><span class="line"></span><br><span class="line"><span class="actionscript">  <span class="comment">// 清除样式</span></span></span><br><span class="line"><span class="actionscript">  <span class="keyword">const</span> style = <span class="string">"transform: translateY(0%);transition:0s all"</span>;</span></span><br><span class="line"></span><br><span class="line"><span class="actionscript">  <span class="comment">// 设置样式</span></span></span><br><span class="line"><span class="actionscript">  <span class="function"><span class="keyword">function</span> <span class="title">setStyle</span><span class="params">(val)</span> </span>&#123;</span></span><br><span class="line"><span class="javascript">    <span class="keyword">return</span> <span class="string">`transform: translateY(-<span class="subst">$&#123;~~val * <span class="number">100</span>&#125;</span>%)`</span>;</span></span><br><span class="line">  &#125;</span><br><span class="line"><span class="actionscript">  <span class="comment">// 每个字的样式</span></span></span><br><span class="line"><span class="actionscript">  <span class="function"><span class="keyword">function</span> <span class="title">numStyle</span><span class="params">(time)</span> </span>&#123;</span></span><br><span class="line"><span class="actionscript">    <span class="comment">// 这里等于0则清除样式，避免0点时，有奇怪的bug</span></span></span><br><span class="line"><span class="javascript">    <span class="keyword">return</span> time.split(<span class="string">""</span>).map(<span class="function">(<span class="params">val</span>) =&gt;</span> (val == <span class="string">"0"</span> ? style : setStyle(val)));</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line"><span class="javascript">  <span class="keyword">export</span> <span class="keyword">default</span> &#123;</span></span><br><span class="line"><span class="actionscript">    name: <span class="string">"TimeScreen"</span>,</span></span><br><span class="line">    data() &#123;</span><br><span class="line"><span class="actionscript">      <span class="comment">// 获取时间</span></span></span><br><span class="line"><span class="javascript">      <span class="keyword">let</span> &#123; time, date &#125; = getTime();</span></span><br><span class="line"><span class="actionscript">      <span class="keyword">return</span> &#123;</span></span><br><span class="line">        isDark: 0,</span><br><span class="line">        time,</span><br><span class="line">        date,</span><br><span class="line">        numStyle: numStyle(time),</span><br><span class="line">      &#125;;</span><br><span class="line">    &#125;,</span><br><span class="line">    methods: &#123;</span><br><span class="line"><span class="actionscript">      <span class="comment">// 更新样式</span></span></span><br><span class="line">      updateStyle() &#123;</span><br><span class="line"><span class="javascript">        <span class="keyword">this</span>.time.split(<span class="string">""</span>).forEach(<span class="function">(<span class="params">val, idx</span>) =&gt;</span> &#123;</span></span><br><span class="line">          if (val == 0) &#123;</span><br><span class="line"><span class="actionscript">            <span class="keyword">if</span> (<span class="keyword">this</span>.numStyle[idx] !== style) &#123;</span></span><br><span class="line"><span class="actionscript">              <span class="keyword">this</span>.removeAnimate(idx);</span></span><br><span class="line"><span class="actionscript">              <span class="keyword">this</span>.numStyle[idx] = setStyle(<span class="keyword">this</span>.haveSpan[idx]);</span></span><br><span class="line">            &#125;</span><br><span class="line"><span class="actionscript">          &#125; <span class="keyword">else</span> &#123;</span></span><br><span class="line"><span class="actionscript">            <span class="keyword">this</span>.numStyle[idx] = setStyle(val);</span></span><br><span class="line">          &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">      &#125;,</span><br><span class="line"><span class="actionscript">      <span class="comment">// 切换样式</span></span></span><br><span class="line">      toggleClass() &#123;</span><br><span class="line"><span class="actionscript">        <span class="keyword">this</span>.isDark = !<span class="keyword">this</span>.isDark;</span></span><br><span class="line">      &#125;,</span><br><span class="line"><span class="actionscript">      <span class="comment">// 清除样式</span></span></span><br><span class="line">      removeAnimate(idx) &#123;</span><br><span class="line"><span class="javascript">        setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span></span><br><span class="line"><span class="actionscript">          <span class="keyword">this</span>.numStyle[idx] = style;</span></span><br><span class="line"><span class="actionscript">          <span class="keyword">this</span>.numStyle = [...this.numStyle];</span></span><br><span class="line">        &#125;, 500);</span><br><span class="line">      &#125;,</span><br><span class="line"><span class="actionscript">      <span class="comment">// 每秒更新时间</span></span></span><br><span class="line">      updateTime() &#123;</span><br><span class="line"><span class="actionscript">        <span class="keyword">const</span> &#123; time, date &#125; = getTime();</span></span><br><span class="line"><span class="actionscript">        <span class="keyword">this</span>.time = time;</span></span><br><span class="line"><span class="actionscript">        <span class="keyword">this</span>.date = date;</span></span><br><span class="line"><span class="actionscript">        <span class="keyword">this</span>.updateStyle();</span></span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">    created() &#123;</span><br><span class="line"><span class="actionscript">      <span class="comment">// 判单有多少个Span</span></span></span><br><span class="line"><span class="actionscript">      <span class="comment">// 比较小时数最多24小时，所以第一位最多是3个，0、1、2</span></span></span><br><span class="line"><span class="javascript">      <span class="keyword">this</span>.haveSpan = <span class="built_in">Object</span>.freeze([<span class="number">3</span>, <span class="number">10</span>, <span class="number">0</span>, <span class="number">6</span>, <span class="number">10</span>, <span class="number">0</span>, <span class="number">6</span>, <span class="number">10</span>]);</span></span><br><span class="line"><span class="actionscript">      <span class="comment">// 定时器</span></span></span><br><span class="line"><span class="actionscript">      <span class="keyword">this</span>.timer = <span class="literal">null</span>;</span></span><br><span class="line">    &#125;,</span><br><span class="line">    mounted() &#123;</span><br><span class="line"><span class="actionscript">      <span class="comment">// 触发定时器</span></span></span><br><span class="line"><span class="actionscript">      <span class="keyword">this</span>.timer = setInterval(<span class="keyword">this</span>.updateTime, <span class="number">1000</span>);</span></span><br><span class="line">    &#125;,</span><br><span class="line">    destroyed() &#123;</span><br><span class="line"><span class="actionscript">      <span class="comment">// 清除定时器</span></span></span><br><span class="line"><span class="actionscript">      clearInterval(<span class="keyword">this</span>.timer);</span></span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">style</span> <span class="attr">lang</span>=<span class="string">"scss"</span> <span class="attr">scoped</span>&gt;</span></span><br><span class="line">  %flexCenter &#123;</span><br><span class="line">    display: flex;</span><br><span class="line">    justify-content: center;</span><br><span class="line">    align-items: center;</span><br><span class="line">  &#125;</span><br><span class="line">  $timeColor: #d9d4d0;</span><br><span class="line">  $white: #fff;</span><br><span class="line"><span class="css">  <span class="selector-class">.time-container</span> &#123;</span></span><br><span class="line">    background: $white;</span><br><span class="line">    color: $timeColor;</span><br><span class="line">    position: absolute;</span><br><span class="line">    width: 100%;</span><br><span class="line">    height: 100%;</span><br><span class="line">    max-width: 540px;</span><br><span class="line">    top: 0;</span><br><span class="line">    left: 50%;</span><br><span class="line">    transform: translateX(-50%);</span><br><span class="line"><span class="css">    &amp;<span class="selector-class">.dark</span> &#123;</span></span><br><span class="line"><span class="css">      <span class="selector-tag">background</span>: <span class="selector-id">#000</span>;</span></span><br><span class="line">      color: $white;</span><br><span class="line">    &#125;</span><br><span class="line"><span class="css">    &amp;<span class="selector-pseudo">::after</span> &#123;</span></span><br><span class="line">      content: attr(date);</span><br><span class="line">      position: absolute;</span><br><span class="line">      color: $timeColor;</span><br><span class="line">      font-size: 18px;</span><br><span class="line">      line-height: 1;</span><br><span class="line">      transform: rotate(90deg);</span><br><span class="line">      bottom: 20%;</span><br><span class="line">      left: -48px;</span><br><span class="line">    &#125;</span><br><span class="line"><span class="css">    <span class="keyword">@extend</span> %flexCenter;</span></span><br><span class="line"><span class="css">    <span class="selector-class">.time</span> &#123;</span></span><br><span class="line">      font-size: 70px;</span><br><span class="line">      transform: rotate(90deg);</span><br><span class="line">      position: relative;</span><br><span class="line">      height: 106px;</span><br><span class="line">      line-height: 106px;</span><br><span class="line">      overflow: hidden;</span><br><span class="line"><span class="css">      <span class="keyword">@extend</span> %flexCenter;</span></span><br><span class="line"></span><br><span class="line"><span class="css">      <span class="selector-class">.time-num</span> &#123;</span></span><br><span class="line">        position: relative;</span><br><span class="line">        width: 100%;</span><br><span class="line">        height: 100%;</span><br><span class="line">        text-align: center;</span><br><span class="line">        text-shadow: 0 0 2px $white;</span><br><span class="line"><span class="css">        <span class="selector-tag">transition</span>: 0<span class="selector-class">.5s</span> <span class="selector-tag">all</span>;</span></span><br><span class="line">        span &#123;</span><br><span class="line">          display: block;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line"><span class="css">      <span class="selector-class">.time-dist</span> &#123;</span></span><br><span class="line">        padding-bottom: 15px;</span><br><span class="line">        margin: 0 10px;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br></pre></td></tr></table></figure></div><h1 id="最后的最后"><a href="#最后的最后" class="headerlink" title="最后的最后"></a>最后的最后</h1><p>感谢各位观众老爷的观看啦 O(∩_∩)O，希望大家可以一起进步</p>]]></content>
    
    <summary type="html">
    
      去年用了一个小的 app，叫做 一个木函，本来想着用来做动画优化就删掉了的，不过看到他有个时间屏幕的小工具，就点进去看了下，觉得挺好玩的，就想着能不能自己实现以下。
    
    </summary>
    
    
      <category term="小特效" scheme="https://gatings.cn/categories/%E5%B0%8F%E7%89%B9%E6%95%88/"/>
    
    
      <category term="vue" scheme="https://gatings.cn/tags/vue/"/>
    
  </entry>
  
  <entry>
    <title>node实现图片分割</title>
    <link href="https://gatings.cn/2020-03-11/node%E5%AE%9E%E7%8E%B0%E5%9B%BE%E7%89%87%E5%88%86%E5%89%B2/"/>
    <id>https://gatings.cn/2020-03-11/node%E5%AE%9E%E7%8E%B0%E5%9B%BE%E7%89%87%E5%88%86%E5%89%B2/</id>
    <published>2020-03-10T16:00:00.000Z</published>
    <updated>2021-10-13T01:56:29.703Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>最近，女王大大日常找我弄图片，本来之前我一直是 ps 帮他弄得，后来- -，ps 不能分割过长的图片，我就想想能不能通过代码来帮他实现好了。</p><p>经过我在<code>npm</code>搜索一番，发现没有一个纯代码层面的<code>high tools</code>来实现这个功能，索性就自己通过<code>node-canvas</code>这个库弄个小例子出来，自己使用好了</p><blockquote><p>gm 这个库是可以实现的，但是他需要额外安装两个工具，所以- -我就放弃了</p></blockquote><h1 id="简单搭建一下"><a href="#简单搭建一下" class="headerlink" title="简单搭建一下"></a>简单搭建一下</h1><ul><li><p>新建一个 clip 目录</p></li><li><p>初始化一个<code>node</code>项目工程</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">shell</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">npm init -y</span><br></pre></td></tr></table></figure></div></li><li><p>安装依赖，这里主要用到了两个个依赖，分别是<code>处理图片</code>、<code>压缩成zip文件</code></p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">shell</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">npm i canvas archiver -S</span><br></pre></td></tr></table></figure></div></li></ul><h1 id="简单的使用一下"><a href="#简单的使用一下" class="headerlink" title="简单的使用一下"></a>简单的使用一下</h1><p>首先，先说说我的需求，下面是一张名为 <code>clip.png</code> 的图片，</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-03-11/clip.png" data-fancybox="group" data-caption class="fancybox"><img alt title data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-03-11/clip.png" class="lazyload"></a></p><p>我们需要做的就是把他均分为 3 等分，所以，我们尝试写一下我们的代码</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 创建写入流</span></span><br><span class="line"><span class="keyword">const</span> &#123; createWriteStream &#125; = <span class="built_in">require</span>(<span class="string">"fs"</span>);</span><br><span class="line"><span class="comment">// 导入压缩文件库</span></span><br><span class="line"><span class="keyword">const</span> archiver = <span class="built_in">require</span>(<span class="string">"archiver"</span>);</span><br><span class="line"><span class="comment">// 导入canvas库，用于裁剪图片</span></span><br><span class="line"><span class="keyword">const</span> &#123; createCanvas, loadImage &#125; = <span class="built_in">require</span>(<span class="string">"canvas"</span>);</span><br><span class="line">!<span class="function">(<span class="params"><span class="keyword">async</span> (</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// 加载图片</span></span><br><span class="line">  <span class="keyword">const</span> image = <span class="keyword">await</span> loadImage(<span class="string">"./clip.png"</span>);</span><br><span class="line">  <span class="comment">// 获取图片宽高</span></span><br><span class="line">  <span class="keyword">const</span> &#123; width, height &#125; = image;</span><br><span class="line">  <span class="comment">// 创建等宽登高的canvas</span></span><br><span class="line">  <span class="keyword">const</span> mainCanvas = createCanvas(width, height);</span><br><span class="line">  <span class="comment">// 获取canvas上下文</span></span><br><span class="line">  <span class="keyword">const</span> ctx = mainCanvas.getContext(<span class="string">"2d"</span>);</span><br><span class="line">  <span class="comment">// 绘图</span></span><br><span class="line">  ctx.drawImage(image, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">  <span class="comment">// 压缩成zip</span></span><br><span class="line">  <span class="keyword">const</span> archive = archiver(<span class="string">"zip"</span>, &#123;</span><br><span class="line">    zlib: &#123; <span class="attr">level</span>: <span class="number">9</span> &#125;, <span class="comment">// Sets the compression level.</span></span><br><span class="line">  &#125;);</span><br><span class="line">  <span class="comment">// 输出到当前文件夹下的 clip.zip</span></span><br><span class="line">  <span class="keyword">const</span> output = createWriteStream(__dirname + <span class="string">"/clip.zip"</span>);</span><br><span class="line">  archive.pipe(output);</span><br><span class="line">  <span class="comment">// 明确我们需要垂直分割成几份</span></span><br><span class="line">  <span class="keyword">const</span> num = <span class="number">3</span>;</span><br><span class="line">  <span class="comment">// 获取一份的高度</span></span><br><span class="line">  <span class="keyword">const</span> oneHeight = height / num;</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; num; i++) &#123;</span><br><span class="line">    <span class="comment">// 创建一份裁剪的canvas</span></span><br><span class="line">    <span class="keyword">let</span> clipCanvas = createCanvas(width, oneHeight);</span><br><span class="line">    <span class="comment">// 获取canvas上下文</span></span><br><span class="line">    <span class="keyword">const</span> clipCtx = clipCanvas.getContext(<span class="string">"2d"</span>);</span><br><span class="line">    <span class="comment">// 通过 clipCtx 裁剪 mainCanvas</span></span><br><span class="line">    clipCtx.drawImage(</span><br><span class="line">      mainCanvas,</span><br><span class="line">      <span class="number">0</span>,</span><br><span class="line">      oneHeight * i,</span><br><span class="line">      width,</span><br><span class="line">      oneHeight,</span><br><span class="line">      <span class="number">0</span>,</span><br><span class="line">      <span class="number">0</span>,</span><br><span class="line">      width,</span><br><span class="line">      oneHeight</span><br><span class="line">    );</span><br><span class="line">    <span class="comment">// 放到压缩器里</span></span><br><span class="line">    archive.append(clipCanvas.toBuffer(), &#123; <span class="attr">name</span>: <span class="string">`<span class="subst">$&#123;i + <span class="number">1</span>&#125;</span>.png`</span> &#125;);</span><br><span class="line">    <span class="comment">// 主动释放内存</span></span><br><span class="line">    clipCanvas = <span class="literal">null</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  archive.finalize();</span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure></div><p>跑一下我们写的程序，你会惊奇的发现，目录下多了一个<code>clip.zip</code>的压缩包，解压出来就是我们需要的图片啦</p><p>看到这里，其实我们已经实现我们想要的效果了，但是你会发现我们只是实现了水平的切割，如何实现垂直的呢？</p><p>也许你会说，加个判断不就好了？</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 垂直切割...伪代码</span></span><br><span class="line"><span class="keyword">if</span> (direction === <span class="string">"vertical"</span>) &#123;</span><br><span class="line">  <span class="comment">// 获取一份的高度</span></span><br><span class="line">  <span class="keyword">const</span> oneHeight = height / num;</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; num; i++) &#123;</span><br><span class="line">    <span class="keyword">let</span> clipCanvas = createCanvas(width, oneHeight);</span><br><span class="line">    <span class="comment">// ...... 和上面一致</span></span><br><span class="line">    clipCtx.drawImage(</span><br><span class="line">      mainCanvas,</span><br><span class="line">      <span class="number">0</span>,</span><br><span class="line">      oneHeight * i,</span><br><span class="line">      width,</span><br><span class="line">      oneHeight,</span><br><span class="line">      <span class="number">0</span>,</span><br><span class="line">      <span class="number">0</span>,</span><br><span class="line">      width,</span><br><span class="line">      oneHeight</span><br><span class="line">    );</span><br><span class="line">    <span class="comment">// ......</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> oneWidth = width / num;</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; num; i++) &#123;</span><br><span class="line">    <span class="keyword">let</span> clipCanvas = createCanvas(oneWidth, height);</span><br><span class="line">    <span class="comment">// ...... 和上面一致</span></span><br><span class="line">    clipCtx.drawImage(</span><br><span class="line">      mainCanvas,</span><br><span class="line">      oneWidth * i,</span><br><span class="line">      <span class="number">0</span>,</span><br><span class="line">      width,</span><br><span class="line">      oneHeight,</span><br><span class="line">      <span class="number">0</span>,</span><br><span class="line">      <span class="number">0</span>,</span><br><span class="line">      width,</span><br><span class="line">      oneHeight</span><br><span class="line">    );</span><br><span class="line">    <span class="comment">// ......</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>的确，这样写是可以，但是会不会觉得很不优雅？那我们是不是可以找找规律，把他封装成一份呢？答案是肯定可以的</p><p>这里我们可以新建一个数组配置,用来判断我们传递的参数来判断是 <code>width/num</code> 还是 <code>height/num</code>,闲话不多少了，开始写我们的代码把</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 创建写入流</span></span><br><span class="line"><span class="keyword">const</span> &#123; createWriteStream &#125; = <span class="built_in">require</span>(<span class="string">"fs"</span>);</span><br><span class="line"><span class="comment">// 压缩文件</span></span><br><span class="line"><span class="keyword">const</span> archiver = <span class="built_in">require</span>(<span class="string">"archiver"</span>);</span><br><span class="line"><span class="comment">// 批量裁剪</span></span><br><span class="line"><span class="keyword">const</span> &#123; createCanvas, loadImage &#125; = <span class="built_in">require</span>(<span class="string">"canvas"</span>);</span><br><span class="line"><span class="comment">// 切割方向配置</span></span><br><span class="line"><span class="keyword">const</span> directionConfig = [<span class="string">"vertical"</span>, <span class="string">"horizontal"</span>];</span><br><span class="line">!<span class="function">(<span class="params"><span class="keyword">async</span> (</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> image = <span class="keyword">await</span> loadImage(<span class="string">"./clip.png"</span>);</span><br><span class="line">  <span class="keyword">const</span> &#123; width, height &#125; = image;</span><br><span class="line">  <span class="keyword">const</span> mainCanvas = createCanvas(width, height);</span><br><span class="line">  <span class="keyword">const</span> ctx = mainCanvas.getContext(<span class="string">"2d"</span>);</span><br><span class="line">  ctx.drawImage(image, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">  <span class="comment">// 压缩成zip</span></span><br><span class="line">  <span class="keyword">const</span> archive = archiver(<span class="string">"zip"</span>, &#123;</span><br><span class="line">    zlib: &#123; <span class="attr">level</span>: <span class="number">9</span> &#125;, <span class="comment">// Sets the compression level.</span></span><br><span class="line">  &#125;);</span><br><span class="line">  <span class="keyword">const</span> output = createWriteStream(__dirname + <span class="string">"/clip.zip"</span>);</span><br><span class="line">  archive.pipe(output);</span><br><span class="line">  <span class="keyword">const</span> clip = clipNumImage(&#123; <span class="attr">canvas</span>: mainCanvas, width, height &#125;, <span class="number">3</span>);</span><br><span class="line">  clip.forEach(<span class="function">(<span class="params">img, idx</span>) =&gt;</span> &#123;</span><br><span class="line">    archive.append(img, &#123; <span class="attr">name</span>: <span class="string">`<span class="subst">$&#123;idx + <span class="number">1</span>&#125;</span>.png`</span> &#125;);</span><br><span class="line">  &#125;);</span><br><span class="line">  archive.finalize();</span><br><span class="line">&#125;)();</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">clipNumImage</span>(<span class="params">options, num, direction = <span class="string">"horizontal"</span></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; canvas, ...canvasSize &#125; = options;</span><br><span class="line">  <span class="comment">// 生成配置的参数</span></span><br><span class="line">  <span class="keyword">let</span> directionIdx = getIdx(direction);</span><br><span class="line">  <span class="comment">// 公有配置</span></span><br><span class="line">  <span class="keyword">const</span> directionOptions = getOptions(canvasSize, directionIdx, num);</span><br><span class="line">  <span class="keyword">const</span> clip = [];</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; num; i++) &#123;</span><br><span class="line">    <span class="keyword">let</span> clipCanvas = createCanvas(...directionOptions);</span><br><span class="line">    <span class="keyword">const</span> clipCtx = clipCanvas.getContext(<span class="string">"2d"</span>);</span><br><span class="line">    clipCtx.drawImage(</span><br><span class="line">      canvas,</span><br><span class="line">      ...directionOptions.map(<span class="function">(<span class="params">val, idx</span>) =&gt;</span></span><br><span class="line">        idx === directionIdx ? val * i : <span class="number">0</span></span><br><span class="line">      ),</span><br><span class="line">      ...directionOptions,</span><br><span class="line">      <span class="number">0</span>,</span><br><span class="line">      <span class="number">0</span>,</span><br><span class="line">      ...directionOptions</span><br><span class="line">    );</span><br><span class="line">    clip.push(clipCanvas.toBuffer());</span><br><span class="line">    clipCanvas = <span class="literal">null</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> clip;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取传递过来的是垂直切割还是水平切割</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getIdx</span>(<span class="params">direction</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> directionIdx = directionConfig.indexOf(direction);</span><br><span class="line">  <span class="keyword">if</span> (directionIdx === <span class="number">-1</span>) &#123;</span><br><span class="line">    directionIdx = <span class="number">1</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> directionIdx;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 获取切割参数</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getOptions</span>(<span class="params">size, directionIdx, num</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">Object</span>.values(size).map(<span class="function">(<span class="params">val, idx</span>) =&gt;</span></span><br><span class="line">    idx === directionIdx ? val / num : val</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>通过数组，是不是感觉到我们代码比以前更加简洁了呢？好啦，这次小 demo 我们也写完啦，希望你通过这篇文章也学习到一点知识。感谢你的观看</p><blockquote><p><a href="https://gitee.com/gating/demo/tree/master/canvas-clip-image" target="_blank" rel="noopener">gitee 地址</a>,<a href="https://github.com/GATING/demo/tree/master/canvas-clip-image" target="_blank" rel="noopener">github 地址</a></p></blockquote><blockquote><p>ps: 对了，还有一个长图切割器，不写代码的话，直接下载用也是极好的,<a href="https://www.lanzous.com/ia59nfc" target="_blank" rel="noopener">点我下载</a></p></blockquote><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>感谢各位观众老爷的观看 O(∩_∩)O</p>]]></content>
    
    <summary type="html">
    
      最近，女王大大日常找我弄图片，本来之前我一直是ps帮他弄得，后来- -，ps不能分割过长的图片，我就想想能不能通过代码来帮他实现好了。
    
    </summary>
    
    
      <category term="小工具" scheme="https://gatings.cn/categories/%E5%B0%8F%E5%B7%A5%E5%85%B7/"/>
    
    
      <category term="node" scheme="https://gatings.cn/tags/node/"/>
    
      <category term="node-canvas" scheme="https://gatings.cn/tags/node-canvas/"/>
    
  </entry>
  
  <entry>
    <title>Windows10开启Ubuntu子系统并搭建Docker环境</title>
    <link href="https://gatings.cn/2020-02-25/Windows10%E5%BC%80%E5%90%AFUbuntu%E5%AD%90%E7%B3%BB%E7%BB%9F%E5%B9%B6%E6%90%AD%E5%BB%BADocker%E7%8E%AF%E5%A2%83%20-%20%E5%89%AF%E6%9C%AC/"/>
    <id>https://gatings.cn/2020-02-25/Windows10%E5%BC%80%E5%90%AFUbuntu%E5%AD%90%E7%B3%BB%E7%BB%9F%E5%B9%B6%E6%90%AD%E5%BB%BADocker%E7%8E%AF%E5%A2%83%20-%20%E5%89%AF%E6%9C%AC/</id>
    <published>2020-02-24T16:00:00.000Z</published>
    <updated>2021-10-13T01:56:29.703Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>很早就听说微软有个基于<code>Ubuntu</code>的子系统，一直也没机会尝试一下，之前也只是用<code>VMware</code>安装，但是还要单独安装软件，安装镜像，一点都不fit，所以就瞎折腾下（也是因为最近有空）。</p><h1 id="搭建Ubuntu子系统"><a href="#搭建Ubuntu子系统" class="headerlink" title="搭建Ubuntu子系统"></a>搭建Ubuntu子系统</h1><h2 id="wsl是什么"><a href="#wsl是什么" class="headerlink" title="wsl是什么"></a>wsl是什么</h2><p>适用于 Linux 的 Windows 子系统（英语：Windows Subsystem for Linux，简称WSL）是一个为在Windows 10和Windows Server 2019上能够原生运行Linux二进制可执行文件（ELF格式）的兼容层。</p><p>简单的说，就是在<code>window</code>上安装了个<code>Linux</code></p><h2 id="安装wsl"><a href="#安装wsl" class="headerlink" title="安装wsl"></a>安装wsl</h2><ol><li><p>开启开发者模式</p><p>依次打开 设置 -&gt; 更新和安全 -&gt; 开发者选项 -&gt; 开发人员模式</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-02-25/wsl01.jpg" data-fancybox="group" data-caption class="fancybox"><img alt title data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-02-25/wsl01.jpg" class="lazyload"></a></p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-02-25/wsl02.jpg" data-fancybox="group" data-caption class="fancybox"><img alt title data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-02-25/wsl02.jpg" class="lazyload"></a></p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-02-25/wsl03.jpg" data-fancybox="group" data-caption class="fancybox"><img alt title data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-02-25/wsl03.jpg" class="lazyload"></a></p></li></ol><ol start="2"><li><p>开启windows子系统功能</p><p>依次打开 控制面板 -&gt; 程序 -&gt; 启用或关闭Windows功能 -&gt; 启用Windows子系统</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-02-25/wsl04.jpg" data-fancybox="group" data-caption class="fancybox"><img alt title data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-02-25/wsl04.jpg" class="lazyload"></a></p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-02-25/wsl05.jpg" data-fancybox="group" data-caption class="fancybox"><img alt title data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-02-25/wsl05.jpg" class="lazyload"></a></p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-02-25/wsl06.jpg" data-fancybox="group" data-caption class="fancybox"><img alt title data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-02-25/wsl06.jpg" class="lazyload"></a></p></li></ol><ol start="3"><li><p>更新Windows，重启</p></li><li><p>在Microsoft Store中搜索Ubuntu，并安装，如图：</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-02-25/wsl07.jpg" data-fancybox="group" data-caption class="fancybox"><img alt title data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-02-25/wsl07.jpg" class="lazyload"></a></p><p>安装完后，默认是个终端，如下：</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-02-25/wsl08.jpg" data-fancybox="group" data-caption class="fancybox"><img alt title data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-02-25/wsl08.jpg" class="lazyload"></a></p></li></ol><h2 id="进入wsl"><a href="#进入wsl" class="headerlink" title="进入wsl"></a>进入wsl</h2><p>打开<code>PowerShell</code>，输入指令<code>bash</code>即可进入wsl子系统。默认是<code>root</code>账户，无密码。</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-02-25/wsl09.jpg" data-fancybox="group" data-caption class="fancybox"><img alt title data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-02-25/wsl09.jpg" class="lazyload"></a></p><h1 id="Docker环境搭建"><a href="#Docker环境搭建" class="headerlink" title="Docker环境搭建"></a>Docker环境搭建</h1><p>由于<code>Ubuntu</code>默认的源下载速度异常缓慢，所以我们需要替换成国内阿里云的镜像源。</p><h2 id="确认默认源"><a href="#确认默认源" class="headerlink" title="确认默认源"></a>确认默认源</h2><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">shell</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">vim /etc/apt/sources.list</span><br></pre></td></tr></table></figure></div><h2 id="修改默认源"><a href="#修改默认源" class="headerlink" title="修改默认源"></a>修改默认源</h2><ol><li><p>备份默认源</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">shell</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">sudo cp /etc/apt/sources.list /etc/apt/sources.list.20200225</span><br></pre></td></tr></table></figure></div></li><li><p>然后 VIM 打开，替换</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">shell</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">sudo vim /etc/apt/sources.list</span><br><span class="line"></span><br><span class="line"><span class="meta">%</span><span class="bash">s/security.ubuntu/mirrors.aliyun/g</span></span><br><span class="line"><span class="meta">%</span><span class="bash">s/archive.ubuntu/mirrors.aliyun/g</span></span><br></pre></td></tr></table></figure></div></li></ol><h2 id="更新源"><a href="#更新源" class="headerlink" title="更新源"></a>更新源</h2><pre><code><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Code</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">sudo apt-get update</span><br></pre></td></tr></table></figure></div></code></pre><h2 id="安装Docker"><a href="#安装Docker" class="headerlink" title="安装Docker"></a>安装Docker</h2><p>因为都知道的网络原因,安装时可能会timeout等其他情况,所以推荐强烈建议使用国内源，执行以下命令</p><ul><li><code>sudo apt-get remove docker docker-engine docker.io</code></li><li><code>sudo apt-get update</code></li><li><code>sudo apt-get install \ apt-transport-https \ ca-certificates \ curl \ software-properties-common</code></li><li><code>curl -fsSL https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg | sudo apt-key add -</code></li><li><code>sudo add-apt-repository \ &quot;deb [arch=amd64] https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu \ $(lsb_release -cs) \ stable&quot;</code></li><li><code>sudo apt-get update</code></li><li><code>sudo apt-get install docker-ce=18.06.1~ce~3-0~ubuntu</code></li></ul><blockquote><p>ps: 安装高版本的<code>docker</code>好像都会挂,所以我们安装到18.06.1的</p></blockquote><p>一顿操作之后，<code>docker</code>已经安装完成, 使用 <code>docker version</code> 可以查看详细的版本信息，如图：</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-02-25/wsl10.jpg" data-fancybox="group" data-caption class="fancybox"><img alt title data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-02-25/wsl10.jpg" class="lazyload"></a></p><p><strong>随后再以管理员启动WSL控制台，执行：</strong></p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">shell</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">sudo cgroupfs-mount</span><br><span class="line">sudo service docker start</span><br></pre></td></tr></table></figure></div><p>测试安装结果如下：</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-02-25/wsl11.jpg" data-fancybox="group" data-caption class="fancybox"><img alt title data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-02-25/wsl11.jpg" class="lazyload"></a></p><h2 id="测试-Docker-是否安装正确"><a href="#测试-Docker-是否安装正确" class="headerlink" title="测试 Docker 是否安装正确"></a>测试 Docker 是否安装正确</h2><p>最后，当然要测试一下，我们安装的docker 是否争取啦</p><p>但是国内从 <code>Docker Hub</code> 拉取镜像有时会遇到困难，所以我们需要配置镜像加速器。所以，我们需要在<code>/etc/docker/daemon.json</code>中写入如下内容（如果文件不存在请新建该文件）</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Code</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">sudo vi &#x2F;etc&#x2F;docker&#x2F;daemon.json</span><br></pre></td></tr></table></figure></div><p>内容如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">json</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight json"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"registry-mirrors"</span>: [</span><br><span class="line">    <span class="string">"https://dockerhub.azk8s.cn"</span>,</span><br><span class="line">    <span class="string">"https://hub-mirror.c.163.com"</span></span><br><span class="line">  ],</span><br><span class="line">  <span class="attr">"iptables"</span>:<span class="literal">false</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>执行 <code>docker run hello-world</code>，如果输出下列信息，那么恭喜你，docker终于搭建完成啦，可以愉快的玩耍了</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-02-25/wsl12.jpg" data-fancybox="group" data-caption class="fancybox"><img alt title data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs/2020-02-25/wsl12.jpg" class="lazyload"></a></p><h3 id="docker批量删除容器、镜像"><a href="#docker批量删除容器、镜像" class="headerlink" title="docker批量删除容器、镜像"></a>docker批量删除容器、镜像</h3><ol><li>删除所有容器<div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">shell</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">docker rm `docker ps -a -q`</span><br></pre></td></tr></table></figure></div></li><li>删除所有镜像<div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">shell</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">docker rmi `docker images -q`</span><br></pre></td></tr></table></figure></div></li></ol><h1 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h1><p><a href="https://vuepress.mirror.docker-practice.com/install/ubuntu.html#%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C" target="_blank" rel="noopener">Docker 从入门到实践</a></p><p><a href="https://zhuanlan.zhihu.com/p/39187620" target="_blank" rel="noopener">WSL中安装Docker</a></p><p><a href="https://blog.csdn.net/weixin_42971644/article/details/99870601" target="_blank" rel="noopener">Windows 10 的Linux子系统WSL下安装docker</a></p><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>还真是闲的蛋疼，瞎折腾了挺久的= =，不过最后好在弄好了，也希望各位搭建环境的时候少点坑，感谢各位观众老爷的观看</p><blockquote><p>ps: 最后还是发现<code>wsl</code>兼容性一般啊，居然不能运行<code>moongo</code>…</p></blockquote>]]></content>
    
    <summary type="html">
    
      很早就听说微软有个基于Ubuntu的子系统，一直也没机会尝试一下，之前也只是用VMware安装，但是还要单独安装软件，安装镜像，一点都不fit，所以就瞎折腾下（也是因为最近有空）。
    
    </summary>
    
    
      <category term="记录" scheme="https://gatings.cn/categories/%E8%AE%B0%E5%BD%95/"/>
    
    
      <category term="javascript" scheme="https://gatings.cn/tags/javascript/"/>
    
  </entry>
  
  <entry>
    <title>从零搭建Window前端开发环境</title>
    <link href="https://gatings.cn/2020-01-17/%E4%BB%8E%E9%9B%B6%E6%90%AD%E5%BB%BAWindow%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83/"/>
    <id>https://gatings.cn/2020-01-17/%E4%BB%8E%E9%9B%B6%E6%90%AD%E5%BB%BAWindow%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83/</id>
    <published>2020-01-16T16:00:00.000Z</published>
    <updated>2021-10-13T01:56:29.707Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>作为一个小前端，是否因为搭建环境烦恼过，是否因为 npm 等国外镜像踩坑过，不要怕，接下来跟着我一步步搭建适合自己的开发环境吧！！！</p><h1 id="node"><a href="#node" class="headerlink" title="node"></a>node</h1><p>这个不用说了吧，我们经常和他打交道，无论是 <code>gulp</code>、<code>webpack</code>和<code>parcel</code>等打包工具，还是各种脚手架的工具，都离不开<code>node</code>环境的支持，接下来我就介绍一下我常用的一些工具和模块。</p><h2 id="nvm"><a href="#nvm" class="headerlink" title="nvm"></a>nvm</h2><p>管理<code>node</code>版本，通过<code>nvm</code>我们可以同时安装/切换不同的<code>node</code>版本</p><p>不过,<code>nvm</code>不支持<code>window</code>版本,但是有替代方案,就是<code>nvm-window</code>,具体为什么<code>nvm</code>为何不支持<code>windows</code>平台?这里就不做谈论了…</p><blockquote><p>ps: <a href="https://www.lanzous.com/i8prjed" target="_blank" rel="noopener">nvm-window 下载链接</a>，如果网速快就不需要在这里下载了，<a href="https://github.com/coreybutler/nvm-windows/releases" target="_blank" rel="noopener">github 下载链接</a>，建议下载<code>nvm-setup.zip</code>会帮你配置好环境变量</p></blockquote><h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><p>如果没什么特别要求，无脑下一步即可</p><ol><li><p>如果之前已安装<code>node</code>,作者的建议是卸载原有的<code>node</code>版本,避免发生冲突</p></li><li><p>配置 setting.txt 文件,主要是配置为国内镜像源镜像源<br>配置文件在：C:\Users\用户名\AppData\Roaming\nvm 下（如果和我一下，无脑下一步的话）</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Code</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">node_mirror: https:&#x2F;&#x2F;npm.taobao.org&#x2F;mirrors&#x2F;node&#x2F;</span><br><span class="line">npm_mirror: https:&#x2F;&#x2F;npm.taobao.org&#x2F;mirrors&#x2F;npm&#x2F;</span><br></pre></td></tr></table></figure></div></li></ol><blockquote><p>ps: 如果遇到<code>Powershell</code>下禁止执行脚本的问题,请用管理员打开<code>Powershell</code>执行<code>set-ExecutionPolicy RemoteSigned</code>，选择 y 即可</p></blockquote><h3 id="常用命令"><a href="#常用命令" class="headerlink" title="常用命令"></a>常用命令</h3><ol><li><p>可列出已安装的 node 版本 nvm list/nvm ls</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">nvm ls</span><br></pre></td></tr></table></figure></div></li><li><p>安装指定版本的 node nvm install<version>[arch]</version></p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">nvm install latest <span class="comment"># latest表示安装最新版</span></span><br></pre></td></tr></table></figure></div><blockquote><p>ps: arch 为可选的平台架构项（32 位/64 位），，默认为系统平台对应的版本，若设置为 all，则同时安装两个版本。</p></blockquote></li><li><p>卸载指定版本 nvm uninstall<version></version></p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">nvm uninstall 13.6.0 <span class="comment"># latest表示安装最新版</span></span><br></pre></td></tr></table></figure></div></li><li><p>设置镜像源 nvm node_mirror<node_mirror_url></node_mirror_url></p><ul><li>设置<code>node</code>镜像源</li></ul><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">nvm node_mirror https://npm.taobao.org/mirrors/node/</span><br></pre></td></tr></table></figure></div><ul><li>设置<code>npm</code>镜像源</li></ul><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">nvm npm_mirror https://npm.taobao.org/mirrors/node/</span><br></pre></td></tr></table></figure></div></li></ol><h2 id="nrm"><a href="#nrm" class="headerlink" title="nrm"></a>nrm</h2><p>众所周知的一点，<code>npm</code> 日常会挂掉，还时不时丢包，所以我们需要一款切换源的工具，来帮我们解决这个问题。</p><blockquote><p>ps: 虽然可以手动切换源，但是相对来讲还是比较麻烦的，所以推荐使用工具来帮我们完成这件事</p></blockquote><h3 id="安裝"><a href="#安裝" class="headerlink" title="安裝"></a>安裝</h3><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm install -g nrm</span><br></pre></td></tr></table></figure></div><h3 id="常用命令-1"><a href="#常用命令-1" class="headerlink" title="常用命令"></a>常用命令</h3><ol><li><p>列出当前支持切换的源</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">nrm ls</span><br><span class="line"></span><br><span class="line">* npm -------- https://registry.npmjs.org/</span><br><span class="line">yarn ------- https://registry.yarnpkg.com/</span><br><span class="line">cnpm ------- http://r.cnpmjs.org/</span><br><span class="line">taobao ----- https://registry.npm.taobao.org/</span><br><span class="line">nj --------- https://registry.nodejitsu.com/</span><br><span class="line">npmMirror -- https://skimdb.npmjs.com/registry/</span><br><span class="line">edunpm ----- http://registry.enpmjs.org/</span><br></pre></td></tr></table></figure></div></li><li><p>使用 taobao 源作为默认的 npm 源</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">nrm use taobao</span><br><span class="line">    Registry has been <span class="built_in">set</span> to: https://registry.npm.taobao.org/</span><br></pre></td></tr></table></figure></div></li><li><p>测试源速度</p><ul><li>测试一个源</li></ul><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">nrm <span class="built_in">test</span> npm</span><br></pre></td></tr></table></figure></div><ul><li>测试所有源</li></ul><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">nrm <span class="built_in">test</span></span><br></pre></td></tr></table></figure></div></li><li><p>访问源的主页</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">nrm home taobao</span><br></pre></td></tr></table></figure></div><blockquote><p>ps: 此命令会在默认浏览器中打开淘宝源的主页：<a href="https://registry.npm.taobao.org/" target="_blank" rel="noopener">https://registry.npm.taobao.org/</a></p></blockquote></li><li><p>添加/刪除 一个源</p><ul><li>添加源：nrm add<registry><url>[home]，home 参数主要用于访问源的主页（可选）</url></registry></li></ul><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">nrm add gating http://npm.gatings.com/  http://gatings.cn/</span><br></pre></td></tr></table></figure></div><ul><li>刪除源：nrm del<registry></registry></li></ul><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">nrm del gating</span><br></pre></td></tr></table></figure></div><blockquote><p>ps: nrm del 命令不能删除 nrm 自己内置的源。</p></blockquote></li></ol><h2 id="实用模块推荐"><a href="#实用模块推荐" class="headerlink" title="实用模块推荐"></a>实用模块推荐</h2><h3 id="anywhere"><a href="#anywhere" class="headerlink" title="anywhere"></a>anywhere</h3><p>朴灵大佬的 <code>anywhere</code>, 随时随地可以创建一个静态服务器，用于查看 <code>vue</code> 或 <code>react</code> 打包后的代码，或者局域网内资源共享</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm i -g anywhere</span><br><span class="line">anywhere</span><br></pre></td></tr></table></figure></div><h3 id="cloc"><a href="#cloc" class="headerlink" title="cloc"></a>cloc</h3><p>快速统计某文件夹下代码的数据量</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">bash</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm i -g cloc</span><br><span class="line">cloc --exclude-dir=node_modules . --exclude-ext=json,html <span class="comment"># 排除node_modules,排除json,html文件</span></span><br></pre></td></tr></table></figure></div><h1 id="Git"><a href="#Git" class="headerlink" title="Git"></a>Git</h1><p>这个也不用我多说了吧，目前世界上最先进的分布式版本控制系统，也是最常用的版本控制工具，这里就不过多说明了，鉴于各位同学的网速文本，这里就放一下下载链接，另外，修改<code>git bash</code>主题可以参考我博客的这篇文章:<a href="http://www.gatings.cn/%E8%AE%B0%E5%BD%95/2018/11/01/GitBash%E4%B8%BB%E9%A2%98%E9%A3%8E%E6%A0%BC/" target="_blank" rel="noopener">GitBash 主题风格</a></p><blockquote><p>ps: <a href="https://www.lanzous.com/i8prqgh" target="_blank" rel="noopener">Git 蓝奏云链接</a>,<a href="https://git-scm.com/" target="_blank" rel="noopener">Git 官网链接，网速快的小伙伴可以在这里下载</a>，同样是无脑下一步安装</p></blockquote><h1 id="VS-Code"><a href="#VS-Code" class="headerlink" title="VS Code"></a>VS Code</h1><p>个人觉得最适合前端开发的一款编辑器了，没有之一</p><h2 id="安装-1"><a href="#安装-1" class="headerlink" title="安装"></a>安装</h2><p>这个就不多介绍了，注意修改下安装路径和添加 Code 到环境变量即可。</p><blockquote><p>ps: 添加都环境变量主要是为了在终端输入 <code>code .</code> 或 <code>code xxx</code> 就能直接打开目录或文件。</p></blockquote><h2 id="编程字体（FiraCode）"><a href="#编程字体（FiraCode）" class="headerlink" title="编程字体（FiraCode）"></a>编程字体（FiraCode）</h2><p><code>Fira Code</code>是<code>Fira Mono</code>字体的扩展，其中包含一组用于常见编程多字符组合的连字。</p><p>比如把输入的「!=」直接显示成「≠」或者把「&gt;=」变成「≥」等等，以此来提高代码的可读性。</p><p>再比如，..或//，连字允许我们校正间距。下面是官方的对比图</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/all_ligatures.png" data-fancybox="group" data-caption="FiraCode" class="fancybox"><img alt="FiraCode" title="FiraCode" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/all_ligatures.png" class="lazyload"></a></p><blockquote><p>ps: <a href="https://www.lanzous.com/i8prj1a" target="_blank" rel="noopener">FiraCode 蓝奏云链接</a>,<a href="https://github.com/tonsky/FiraCode/releases" target="_blank" rel="noopener">FiraCode 官方链接</a>,同上，网速慢的就下载蓝奏云的即可</p></blockquote><h2 id="快捷键"><a href="#快捷键" class="headerlink" title="快捷键"></a>快捷键</h2><p>熟练使用快捷键是程序员的必备技</p><h3 id="常用快捷键"><a href="#常用快捷键" class="headerlink" title="常用快捷键"></a>常用快捷键</h3><ol><li><code>ctrl + p</code> 根据关键字快速打开一个文件</li><li><code>ctrl + ,</code> 打开配置项</li><li><code>ctrl + d</code> 快速选取多个相同的内容块</li><li><code>ctrl + h</code> 选取所有相同的内容块</li><li><code>alt + 鼠标左键</code> 选取多个</li><li><code>ctrl + g</code> 跳到指定行</li><li><code>ctrl + b</code> 切换侧边栏</li><li><code>shift + alt + 上下箭头</code> 复制当前行</li><li><code>ctrl + shift + k</code> 快速删除行</li><li><code>alt + 数字键</code> 快速切换标签（数字键不是小键盘的数字键）</li><li><code>shift + alt + f</code> 代码自动格式化</li><li><code>ctrl + shift + p</code> 显示所有命令</li><li><code>ctrl + tab</code> 切换不同的文件</li></ol><h3 id="修改左侧边栏快捷键"><a href="#修改左侧边栏快捷键" class="headerlink" title="修改左侧边栏快捷键"></a>修改左侧边栏快捷键</h3><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang"></div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight"><table><tr><td class="code"><pre><span class="line">(&#123;</span><br><span class="line">  "key": "cmd+1",</span><br><span class="line">  "command": "workbench.view.explorer"</span><br><span class="line">&#125;,</span><br><span class="line">&#123;</span><br><span class="line">  <span class="attr">"key"</span>: <span class="string">"cmd+2"</span>,</span><br><span class="line">  <span class="attr">"command"</span>: <span class="string">"workbench.view.search"</span></span><br><span class="line">&#125;,</span><br><span class="line">&#123;</span><br><span class="line">  <span class="attr">"key"</span>: <span class="string">"cmd+3"</span>,</span><br><span class="line">  <span class="attr">"command"</span>: <span class="string">"workbench.view.scm"</span></span><br><span class="line">&#125;,</span><br><span class="line">&#123;</span><br><span class="line">  <span class="attr">"key"</span>: <span class="string">"cmd+4"</span>,</span><br><span class="line">  <span class="attr">"command"</span>: <span class="string">"workbench.view.debug"</span></span><br><span class="line">&#125;,</span><br><span class="line">&#123;</span><br><span class="line">  <span class="attr">"key"</span>: <span class="string">"cmd+5"</span>,</span><br><span class="line">  <span class="attr">"command"</span>: <span class="string">"workbench.view.extensions"</span></span><br><span class="line">&#125;,</span><br><span class="line">&#123;</span><br><span class="line">    <span class="attr">"key"</span>: <span class="string">"ctrl+6"</span>,</span><br><span class="line">    <span class="attr">"command"</span>: <span class="string">"workbench.view.extension.gitlens"</span></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure></div><p>这里主要是针对于和<code>Chrome</code>切换 tab 栏的快捷键保持一致，方便记忆</p><blockquote><p>ps: <code>ctrl+6</code> 是因为我安装了 <code>GitLens</code> 这个拓展，可根据自己的需求把最后一个去掉</p></blockquote><h2 id="插件推荐"><a href="#插件推荐" class="headerlink" title="插件推荐"></a>插件推荐</h2><p>这里介绍一下我常用的插件</p><ul><li><code>Auto Close Tag</code> 自动闭合 HTML 标签</li><li><code>Auto Rename Tag</code> 修改 HTML 标签时,自动修改匹配的标签(虽然有时候有 bug)</li><li><code>Bracket Pair Colorizer</code> 用不同颜色高亮显示匹配的括号</li><li><code>Code Spell checker</code> 单词拼写检查</li><li><code>GitLens</code> 显示文件最近的 commit 和作者，显示当前行 commit 信息</li><li><code>HTML CSS Support</code> css 提示</li><li><code>IntelliSense for css class names</code> html 中 class 输入提示</li><li><code>JavaScript (ES6) code snippets</code> ES6 语法代码段</li><li><code>jQuery Code Snippets</code> jQuery 语法代码片段</li><li><code>npm Intellisense</code> 导入模块时，提示已安装模块名称</li><li><code>open in browser</code> 浏览器中查看</li><li><code>Path Intellisense</code> 路径完成提示</li><li><code>px2rem</code> px 转 rem，主要是用 flexible.js 和响应式做适配使用</li><li><code>Settings Sync</code> 同步设置和插件到 Gist,还可以分享第三方 Gist 共他人同步（下载）</li><li><code>vetur</code> Vue 语法高亮</li><li><code>vscode-icons</code> 文件图标</li><li><code>Vue VSCode Snippets</code> vue 的代码片段</li><li><code>vscode wxml</code> VS Code 提供 wxml 语法支持及代码片段</li><li><code>WakaTime</code> 记录你一天码代码的时间</li></ul><blockquote><p>附上一份 vsc 插件整理的 pdf：<a href="https://www.lanzous.com/i8pua0d" target="_blank" rel="noopener">点我下载</a></p></blockquote><h2 id="VSCode-设置"><a href="#VSCode-设置" class="headerlink" title="VSCode 设置"></a>VSCode 设置</h2><p>这里附上个人的 <code>VSCode</code> 设置,并且已经加上注释</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">json</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight json"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="comment">// 自动保存</span></span><br><span class="line">  <span class="attr">"files.autoSave"</span>: <span class="string">"afterDelay"</span>,</span><br><span class="line">  <span class="attr">"fileheader.Author"</span>: <span class="string">"gating"</span>,</span><br><span class="line">  <span class="attr">"fileheader.LastModifiedBy"</span>: <span class="string">"gating"</span>,</span><br><span class="line">  <span class="comment">// 控制资源管理器是否在把文件删除到废纸篓时进行确认。</span></span><br><span class="line">  <span class="attr">"explorer.confirmDelete"</span>: <span class="literal">false</span>,</span><br><span class="line">  <span class="comment">// 启用后，按下 TAB 键，将展开 Emmet 缩写。</span></span><br><span class="line">  <span class="attr">"emmet.triggerExpansionOnTab"</span>: <span class="literal">true</span>,</span><br><span class="line">  <span class="comment">// 在默认不支持 Emmet 的语言中启用 Emmet 缩写功能。</span></span><br><span class="line">  <span class="attr">"emmet.includeLanguages"</span>: &#123;</span><br><span class="line">    <span class="attr">"javascript"</span>: <span class="string">"javascriptreact"</span>,</span><br><span class="line">    <span class="attr">"vue-html"</span>: <span class="string">"html"</span>,</span><br><span class="line">    <span class="attr">"vue"</span>: <span class="string">"html"</span>,</span><br><span class="line">    <span class="attr">"wxml"</span>: <span class="string">"html"</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// 为指定的语法定义配置文件或使用带有特定规则的配置文件。</span></span><br><span class="line">  <span class="attr">"emmet.syntaxProfiles"</span>: &#123;</span><br><span class="line">    <span class="attr">"vue-html"</span>: <span class="string">"html"</span>,</span><br><span class="line">    <span class="attr">"vue"</span>: <span class="string">"html"</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// 配置语言的文件关联</span></span><br><span class="line">  <span class="attr">"files.associations"</span>: &#123;</span><br><span class="line">    <span class="attr">"*.wxss"</span>: <span class="string">"css"</span>,</span><br><span class="line">    <span class="attr">"*.html"</span>: <span class="string">"html"</span>,</span><br><span class="line">    <span class="attr">"*.wpy"</span>: <span class="string">"vue"</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// 控制编辑器是否显示控制字符。</span></span><br><span class="line">  <span class="attr">"editor.renderControlCharacters"</span>: <span class="literal">true</span>,</span><br><span class="line">  <span class="comment">// 设置vscode默认的终端</span></span><br><span class="line">  <span class="attr">"terminal.integrated.shell.windows"</span>: <span class="string">"C:\\windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"</span>,</span><br><span class="line">  <span class="attr">"px2rem.rootFontSize"</span>: <span class="number">75</span>,</span><br><span class="line">  <span class="attr">"px2rem.isNeedNotes"</span>: <span class="literal">false</span>,</span><br><span class="line">  <span class="comment">// 控制在将设置编辑为 json 时，使用拆分 json 编辑器。</span></span><br><span class="line">  <span class="attr">"workbench.settings.useSplitJSON"</span>: <span class="literal">true</span>,</span><br><span class="line">  <span class="comment">// 使用 json 文件编辑器。</span></span><br><span class="line">  <span class="attr">"workbench.settings.editor"</span>: <span class="string">"json"</span>,</span><br><span class="line">  <span class="comment">//  当在 VS Code 中重命名或移动文件时，始终自动更新路径。</span></span><br><span class="line">  <span class="attr">"javascript.updateImportsOnFileMove.enabled"</span>: <span class="string">"always"</span>,</span><br><span class="line">  <span class="attr">"vetur.format.defaultFormatter.html"</span>: <span class="string">"prettier"</span>,</span><br><span class="line">  <span class="attr">"html-css-class-completion.includeGlobPattern"</span>: <span class="string">"**/*.&#123;css,html,vue,jsx&#125;"</span>,</span><br><span class="line">  <span class="attr">"vetur.format.options.tabSize"</span>: <span class="number">4</span>,</span><br><span class="line">  <span class="comment">// 配置图标主题</span></span><br><span class="line">  <span class="attr">"workbench.iconTheme"</span>: <span class="string">"vscode-icons"</span>,</span><br><span class="line">  <span class="comment">// 配置缩放等级</span></span><br><span class="line">  <span class="attr">"window.zoomLevel"</span>: <span class="number">1</span>,</span><br><span class="line">  <span class="comment">// 配置主题</span></span><br><span class="line">  <span class="attr">"workbench.colorTheme"</span>: <span class="string">"Dracula"</span>,</span><br><span class="line">  <span class="comment">// 配置字体</span></span><br><span class="line">  <span class="attr">"editor.fontFamily"</span>: <span class="string">"Fira Code"</span>,</span><br><span class="line">  <span class="comment">// 配置字体连字。</span></span><br><span class="line">  <span class="attr">"editor.fontLigatures"</span>: <span class="literal">true</span>,</span><br><span class="line">  <span class="attr">"sync.gist"</span>: <span class="string">""</span>,</span><br><span class="line">  <span class="comment">// 控制是否在搜索中跟踪符号链接。</span></span><br><span class="line">  <span class="attr">"search.followSymlinks"</span>: <span class="literal">false</span>,</span><br><span class="line">  <span class="comment">// 控制在资源管理器内拖放移动文件或文件夹时是否进行确认。</span></span><br><span class="line">  <span class="attr">"explorer.confirmDragAndDrop"</span>: <span class="literal">false</span>,</span><br><span class="line">  <span class="comment">// 在保存时格式化文件。</span></span><br><span class="line">  <span class="attr">"editor.formatOnSave"</span>: <span class="literal">true</span>,</span><br><span class="line">  <span class="comment">// 设置路径别名</span></span><br><span class="line">  <span class="attr">"path-intellisense.mappings"</span>: &#123;</span><br><span class="line">    <span class="attr">"@"</span>: <span class="string">"$&#123;workspaceRoot&#125;/src/"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><blockquote><p>如果觉得复制麻烦，并且也不想一个个安装插件，可以同步我的<code>Gist ID</code>，直接使用我的配置即可。。。ID：<code>9d1c53501e76056a81fb1e2c9a6f181b</code></p></blockquote><h3 id="同步设置"><a href="#同步设置" class="headerlink" title="同步设置"></a>同步设置</h3><ol><li><p>安装 Settings Sync 插件</p></li><li><p>快捷键<code>ctrl + shift + p</code>,输入 Sync，选择高级选项</p></li><li><p>选中打开设置</p></li><li><p>复制<code>9d1c53501e76056a81fb1e2c9a6f181b</code>到<code>Gist ID</code>栏，选择自动下载即可</p></li></ol><h1 id="Chrome"><a href="#Chrome" class="headerlink" title="Chrome"></a>Chrome</h1><p>使用人数最多，最强大的浏览器，也是前端开发必备的浏览器，丰富的拓展，搭配谷歌的同步功能，可以说一个账号走天下</p><h2 id="快捷键-1"><a href="#快捷键-1" class="headerlink" title="快捷键"></a>快捷键</h2><p>还是那句话,熟练使用快捷键是程序员的必备技…下面还是介绍我常用的快捷键</p><ul><li><code>ctrl + j</code> 新标签页中打开”下载内容”页</li><li><code>ctrl + h</code> 新标签页中打开”历史记录”页</li><li><code>ctrl + n</code> 打开新窗口</li><li><code>ctrl + t</code> 打开一个新的 Tab</li><li><code>ctrl + u</code> 查看页面源代码</li><li><code>ctrl + w</code> 关闭当前窗口</li><li><code>ctrl + 数字键</code> 快速跳转到和数字键对应的 Tab，ctrl+9 为最后一个</li><li><code>ctrl + shift + n</code> 以无痕模式打开新窗口（可用于多个窗口的 cookie 不共享，便于调试）</li><li><code>ctrl + tab</code> 切换不同的 tab 页</li><li><code>ctrl + g</code> 查找栏中搜索字词相匹配的下一条内容</li><li><code>ctrl + shift + g</code> 查找栏中搜索字词相匹配的上一条内容</li><li><code>shift + esc</code> 任务管理器</li></ul><h2 id="拓展插件"><a href="#拓展插件" class="headerlink" title="拓展插件"></a>拓展插件</h2><ul><li><code>AdBlockPlus</code> 屏蔽烦人的广告（必装插件）</li><li><code>Charset</code> 修改当前页面的编码，因谷歌升级了 55 以上之后，精简编码功能</li><li><code>CSS Used</code> 用来获取某个元素渲染后的 styles（样式），即 computed 后的样式，扒代码小能手</li><li><code>JSON-handle</code> 格式化 JSON 文件,访问 JSON 文件（数据）时获得更愉悦的体验</li><li><code>SwitchyOmega</code> 管理和切换多个代理设置（小飞机必备插件）,具体怎么使用还是百度下吧，这里就不教学了</li><li><code>Tampermonkey</code> 有猴子插件，用于管理用户自定义的脚本（必装插件）</li><li><code>React Developer Tools</code> 开发 React 应用时必装的一款拓展</li><li><code>Vue.js devtools</code> 同上，都是辅助开发的拓展</li><li><code>Wappalyzer</code> 当前网站所使用技术栈(图个新鲜，挺有意思的)</li></ul><blockquote><p>ps: 油猴子脚本下载：<a href="https://greasyfork.org/zh-CN" target="_blank" rel="noopener">https://greasyfork.org/zh-CN</a></p></blockquote><h1 id="Postman"><a href="#Postman" class="headerlink" title="Postman"></a>Postman</h1><p>作为前端，最常打交道的就是后端了，所以我们需要一个调试<code>API</code>的工具，而<code>Postman</code>就是一款非常优秀的 API 接口调试工具。</p><blockquote><p>ps: 如果还没用过 <code>Postman</code> 的，赶紧下载下来体验吧</p></blockquote><h2 id="安装-2"><a href="#安装-2" class="headerlink" title="安装"></a>安装</h2><p>1.众所周知，Postman 最早是 chrome 浏览器的插件，所以你可以通过 Chrome 商店搜索下载即可。</p><p>2.Postman 提供了独立的安装包，不再依赖于 Chrome 浏览器了，所以也可通过安装包直接安装。</p><blockquote><p>ps：众所周知的原因，2018 年初 Chrome 停止对 Chrome 应用程序的支持。所以这里建议使用独立安装包下载</p></blockquote><p>下载也是直接安装即可，这里同样提供下载链接</p><blockquote><p><a href="https://www.lanzous.com/i8prqgh" target="_blank" rel="noopener">Postman 蓝奏云链接</a>,<a href="https://www.getpostman.com/downloads/" target="_blank" rel="noopener">Postman 官网链接，网速快的小伙伴可以在这里下载</a></p></blockquote><h2 id="简单使用"><a href="#简单使用" class="headerlink" title="简单使用"></a>简单使用</h2><h3 id="配置多个环境"><a href="#配置多个环境" class="headerlink" title="配置多个环境"></a>配置多个环境</h3><p>一般正式的开发项目都会有<code>测试环境</code>和<code>线上环境</code>之分，而前端同学拿到这两个地址后，每次都要请求不同的环境都要去修改主机名，造成不必要的时间浪费，而<code>Postman</code>就提供了多个环境的选择，方便我们无缝链接不同环境的接口</p><p>1.设置环境变量</p><p>点击右上角设置图标</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/postman01.jpg" data-fancybox="group" data-caption="设置环境变量" class="fancybox"><img alt="设置环境变量" title="设置环境变量" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/postman01.jpg" class="lazyload"></a></p><p>添加环境变量</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/postman02.jpg" data-fancybox="group" data-caption="设置环境变量" class="fancybox"><img alt="设置环境变量" title="设置环境变量" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/postman02.jpg" class="lazyload"></a></p><p>添加 key/value 值，不同环境的 key 值需一致</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/postman03.jpg" data-fancybox="group" data-caption="设置环境变量" class="fancybox"><img alt="设置环境变量" title="设置环境变量" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/postman03.jpg" class="lazyload"></a><br><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/postman04.jpg" data-fancybox="group" data-caption="设置环境变量" class="fancybox"><img alt="设置环境变量" title="设置环境变量" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/postman04.jpg" class="lazyload"></a></p><p>2.设置对应的 key 值</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/postman05.png" data-fancybox="group" data-caption="设置环境变量" class="fancybox"><img alt="设置环境变量" title="设置环境变量" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/postman05.png" class="lazyload"></a></p><h3 id="配置重复-header"><a href="#配置重复-header" class="headerlink" title="配置重复 header"></a>配置重复 header</h3><p>很多时候，当后端写完一个新接口时，我们每次测试请求，<code>header</code>头很有可能是一样的，比如说，每个接口都需要<code>token</code>，这时候就需要<code>Postman</code>来帮我们设置<code>header</code>头的变量，避免复制重复的<code>header</code>头，做无用功</p><p>1.设置 headers</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/postman06.jpg" data-fancybox="group" data-caption class="fancybox"><img alt title data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/postman06.jpg" class="lazyload"></a></p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/postman07.jpg" data-fancybox="group" data-caption class="fancybox"><img alt title data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/postman07.jpg" class="lazyload"></a></p><p>2.使用 headers</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/postman08.jpg" data-fancybox="group" data-caption class="fancybox"><img alt title data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/postman08.jpg" class="lazyload"></a></p><p>好了，到这里，基础的使用已经回了，当然，这不是<code>Postman</code>的全部功能，它还可以做单元测试、回归测试、自动化测试等等，这就需要你们自己去体验一番了。。。</p><blockquote><p>ps: <code>Postman</code>还有个兄弟款<a href="https://github.com/liyasthomas/postwoman" target="_blank" rel="noopener">Postwoman</a>，开源免费，也很不错</p></blockquote><h1 id="前端开发小提示"><a href="#前端开发小提示" class="headerlink" title="前端开发小提示"></a>前端开发小提示</h1><h2 id="使用-Map-代替-if-else"><a href="#使用-Map-代替-if-else" class="headerlink" title="使用 Map 代替 if/else"></a>使用 Map 代替 if/else</h2><p>开发的时候，经常会用到判断，然后根据判断返回不同的值或方法,这里以微信开发为例,微信接受用户消息时会有 <code>image</code>、<code>text</code>、<code>voice</code>等等 EventKey,这时候我们就需要根据这几个 Key 写不同的方法：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">handleEvent</span>(<span class="params">key</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (key === <span class="string">"image"</span>) &#123;</span><br><span class="line">    <span class="comment">// 处理用户发送图片的方法</span></span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (key === <span class="string">"text"</span>) &#123;</span><br><span class="line">    <span class="comment">// 处理用户发送文本的方法</span></span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (key === <span class="string">"voice"</span>) &#123;</span><br><span class="line">    <span class="comment">// 处理用户发送声音</span></span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="comment">// 处理其他事件</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>我们观察上面代码，随着我们业务的增加，可能会获取用户的地理位置、扫码什么的，这个 if/else 就会越来越多，代码就会变得越来臃肿，可读性也越来越差。。。接下来我们就通过 Map 来优化下我们的代码</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> map = &#123;</span><br><span class="line">    <span class="string">`image`</span>:<span class="function"><span class="params">message</span> =&gt;</span>&#123;</span><br><span class="line">        <span class="comment">// 处理用户发送图片的方法</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="string">`text`</span>:<span class="function"><span class="params">message</span> =&gt;</span>&#123;</span><br><span class="line">        <span class="comment">// 处理用户发送文本的方法</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="string">`voice`</span>:<span class="function"><span class="params">message</span> =&gt;</span>&#123;</span><br><span class="line">        <span class="comment">// 处理用户发送声音</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="string">`default`</span>:&#123;</span><br><span class="line">        <span class="comment">// 处理其他事件</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">handleEvent</span>(<span class="params">key</span>)</span>&#123;</span><br><span class="line">    key = map.hasOwnProperty(key) ? key : <span class="string">'default'</span></span><br><span class="line">    <span class="keyword">return</span> map[key]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><blockquote><p>ps: 推荐观看 <a href="https://juejin.im/post/5bdfef86e51d453bf8051bf8" target="_blank" rel="noopener">JavaScript 复杂判断的更优雅写法</a></p></blockquote><h2 id="性能测试"><a href="#性能测试" class="headerlink" title="性能测试"></a>性能测试</h2><p><code>Chrome DevTools</code>（控制台），我想各位前端小伙伴应该不陌生吧，经常用它来调试页面，<code>element</code>、<code>console</code>和<code>network</code>都是要天天打交道的，不过你可知道他还有个可以测试当前页面性能的面板?没错，他就是 <code>Audits</code> 面板。。。</p><p>chrome 60 之后，他引入了 Google 自家开源的一个项目：<code>LightHouse</code>。</p><blockquote><p>LightHouse 是 Google 开源的一个自动化测试工具，之前是以 chrome 插件 和 命令行 cli 的方式使用，现在可以直接通过开发者面板使用了，所以不推荐在单独安装插件或者 cli 工具。。。</p></blockquote><p>这里以测试百度为例：</p><p>1.首先打开<a href="https://www.baidu.com/" target="_blank" rel="noopener">百度</a>,然后打开控制面板，点击右边的双箭头</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/Audits01.jpg" data-fancybox="group" data-caption="Chrome DevTools控制台" class="fancybox"><img alt="Chrome DevTools控制台" title="Chrome DevTools控制台" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/Audits01.jpg" class="lazyload"></a></p><p>2.选中 <code>Audits</code> 面板</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/Audits02.jpg" data-fancybox="group" data-caption="Audits面板" class="fancybox"><img alt="Audits面板" title="Audits面板" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/Audits02.jpg" class="lazyload"></a></p><p>3.选择桌面设备（就是测试 pc 版），然后点击<code>run audits</code>，之后就会根据这几项指标生产一份报告，如下图</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/Audits03.jpg" data-fancybox="group" data-caption="测试" class="fancybox"><img alt="测试" title="测试" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/Audits03.jpg" class="lazyload"></a></p><p>4.通过点击右侧菜单可以保存整个测试报告</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/Audits04.jpg" data-fancybox="group" data-caption="保存" class="fancybox"><img alt="保存" title="保存" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs//2020-01-17/Audits04.jpg" class="lazyload"></a></p><blockquote><ul><li>同时也有个中文版的线上版本 <a href="https://developers.google.com/speed/pagespeed/insights/" target="_blank" rel="noopener">https://developers.google.com/speed/pagespeed/insights/</a></li><li>移动端兼容性测试：<a href="https://search.google.com/test/mobile-friendly" target="_blank" rel="noopener">https://search.google.com/test/mobile-friendly</a></li><li>结构化网页数据：<a href="https://search.google.com/structured-data/testing-tool/#" target="_blank" rel="noopener">https://search.google.com/structured-data/testing-tool/#</a></li><li>(貌似都需要科学上网才可以访问…)</li></ul></blockquote><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>至此，本次搭建也已经全部结束了，希望你能从文中收所获，也能搭建属于自己适合自己的开发环境</p><p>最后，感谢各位观众老爷观看啦 O(∩_∩)O</p>]]></content>
    
    <summary type="html">
    
      作为一个小前端，是否因为搭建环境烦恼过，是否因为npm等国外镜像踩坑过，不要怕，接下来跟着我一步步搭建适合自己的开发环境吧！！！
    
    </summary>
    
    
      <category term="记录" scheme="https://gatings.cn/categories/%E8%AE%B0%E5%BD%95/"/>
    
    
      <category term="javascript" scheme="https://gatings.cn/tags/javascript/"/>
    
  </entry>
  
  <entry>
    <title>从零教你打造免费、稳定、高效的图床</title>
    <link href="https://gatings.cn/2020-01-13/%E4%BB%8E%E9%9B%B6%E6%95%99%E4%BD%A0%E6%89%93%E9%80%A0%E5%85%8D%E8%B4%B9%E7%A8%B3%E5%AE%9A%E9%AB%98%E6%95%88%E7%9A%84%E5%9B%BE%E5%BA%8A/"/>
    <id>https://gatings.cn/2020-01-13/%E4%BB%8E%E9%9B%B6%E6%95%99%E4%BD%A0%E6%89%93%E9%80%A0%E5%85%8D%E8%B4%B9%E7%A8%B3%E5%AE%9A%E9%AB%98%E6%95%88%E7%9A%84%E5%9B%BE%E5%BA%8A/</id>
    <published>2020-01-12T16:00:00.000Z</published>
    <updated>2021-10-13T01:56:29.707Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>作为一个小博主，写写博客的时候很经常会用到一些图片，但是又因为自己的服务器太菜了，放在自己服务器也不是很好，所以就想到了图床。</p><p>但是作为只是偶尔用用图，付费服务的图床又不太合适（<code>白嫖党</code>）,所以接下来我们就通过 <code>Github</code> 和 <code>JsDelivr</code> 搭建一个免费的小图床啦</p><blockquote><p>ps: 主要是以前写的文章压根没有配图，因为嫌麻烦┭┮﹏┭┮，但是感觉会影响可读性，所以就找个图床来使用了</p></blockquote><h1 id="什么是图床？"><a href="#什么是图床？" class="headerlink" title="什么是图床？"></a>什么是图床？</h1><p>简单来讲，图床就是一个在网上存储图片的地方（其返回给你一个 <code>URL</code> 进行获取图片），目的是为了节省服务器空间，加快图片打开速度。</p><h1 id="JsDelivr又是什么？"><a href="#JsDelivr又是什么？" class="headerlink" title="JsDelivr又是什么？"></a>JsDelivr又是什么？</h1><p>从他的官网可知，他是开源CDN提供商（免费，快速且可靠）,并且 <code>works in China</code> (也就是不会出现墙的问题)，支持给Github、WordPress、NPM免费提供CDN加速</p><p>这么一看，这不是正合适我们的需求吗，免费、快速并且稳定的<code>CDN</code>加速服务</p><p>所以，我们就是用他来配置我们图床的加速访问</p><blockquote><p>使用方法：<a href="https://cdn.JsDelivr.net/gh/你的用户名/你的仓库名@发布的版本号/文件路径，版本号不是必需的，是为了区分新旧资源，如果不使用版本号，将会直接引用最新资源。" target="_blank" rel="noopener">https://cdn.JsDelivr.net/gh/你的用户名/你的仓库名@发布的版本号/文件路径，版本号不是必需的，是为了区分新旧资源，如果不使用版本号，将会直接引用最新资源。</a></p></blockquote><h1 id="搭建"><a href="#搭建" class="headerlink" title="搭建"></a>搭建</h1><p>其实从上面就知道，我们可以直接通过 <code>JsDelivr</code> 访问我们自己 <code>Github</code> 仓库的资源了，那么我们能不能通过一些工具，来提高我们的生产力呢？答案是，肯定有的，接下来我们就一步一步的提高我们的生产力</p><h2 id="创建新项目"><a href="#创建新项目" class="headerlink" title="创建新项目"></a>创建新项目</h2><p>登录/注册Github，新建一个仓库，填写好仓库名，仓库描述，并且一定的是公开项目，不能是私有的</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs@master/2020-01-12/figureBed01.jpg" data-fancybox="group" data-caption="创建新项目" class="fancybox"><img alt="创建新项目" title="创建新项目" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs@master/2020-01-12/figureBed01.jpg" class="lazyload"></a></p><h2 id="生成Token"><a href="#生成Token" class="headerlink" title="生成Token"></a>生成Token</h2><p>先点击头像的下拉菜单，并选择 <code>Settings</code></p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs@master/2020-01-12/figureBed02.jpg" data-fancybox="group" data-caption="Settings" class="fancybox"><img alt="Settings" title="Settings" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs@master/2020-01-12/figureBed02.jpg" class="lazyload"></a></p><p>选择 <code>Developer settings</code></p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs@master/2020-01-12/figureBed03.jpg" data-fancybox="group" data-caption="Developer settings" class="fancybox"><img alt="Developer settings" title="Developer settings" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs@master/2020-01-12/figureBed03.jpg" class="lazyload"></a></p><p>选择 <code>Personal access tokens</code></p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs@master/2020-01-12/figureBed04.jpg" data-fancybox="group" data-caption="Personal access tokens" class="fancybox"><img alt="Personal access tokens" title="Personal access tokens" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs@master/2020-01-12/figureBed04.jpg" class="lazyload"></a></p><p>选择 <code>Generate new token</code>，并填写好 <code>Note</code>，勾上 <code>repo</code>，最底下点击 <code>Generate new token</code> 生成所需要的token</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs@master/2020-01-12/figureBed05.jpg" data-fancybox="group" data-caption="Generate new token" class="fancybox"><img alt="Generate new token" title="Generate new token" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs@master/2020-01-12/figureBed05.jpg" class="lazyload"></a></p><p>这个token只会显示一次，自己先保存下来 （如果忘记了，重复上面一步，生成新的token）</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs@master/2020-01-12/figureBed06.jpg" data-fancybox="group" data-caption="Generate new token" class="fancybox"><img alt="Generate new token" title="Generate new token" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs@master/2020-01-12/figureBed06.jpg" class="lazyload"></a></p><h2 id="使用PicGo上传图片"><a href="#使用PicGo上传图片" class="headerlink" title="使用PicGo上传图片"></a>使用PicGo上传图片</h2><p>读到到了这里，你会发现我们只是创建了一个Github仓库，压根没有提高什么生产力= =。</p><p>骚年，别着急，接下来我们使用 <a href="https://github.com/Molunerfinn/picgo/releases" target="_blank" rel="noopener">PicGo</a> 这个工具来配置我们的 <code>Github</code> 图床啦</p><p>打开软件，选择 <code>图床设置</code> ，在选择 <code>GitHub图床</code>，配置上我们刚创建的仓库名、分支和Token，并设置仓库的存储路径，</p><p>自定义域名则配置成 <code>https://cdn.JsDelivr.net/gh/你的用户名/你的仓库名/</code> ，这里就方便你在相册中复制你图床的谅解</p><p><a href="https://cdn.JsDelivr.net/gh/GATING/blog_imgs@master/2020-01-12/figureBed07.png" data-fancybox="group" data-caption="PicGo" class="fancybox"><img alt="PicGo" title="PicGo" data-src="https://cdn.JsDelivr.net/gh/GATING/blog_imgs@master/2020-01-12/figureBed07.png" class="lazyload"></a></p><p>配置好 <code>PicGo</code> 后,我们就可以愉快的玩耍啦，此外 <code>PicGo</code> 还支持很多功能，这里就不一一介绍，需要你自己探索啦</p><blockquote><p>ps: 上传如果需要压缩的话，推荐使用 TinyPNG 压缩之后，在上传哦</p></blockquote><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>说一下我为什么选择 <code>Github</code> 和 <code>JsDelivr</code></p><ol><li><p>他两都是大厂，不用担心跑路</p></li><li><p>不用担心访问速度和容量</p></li><li><p>不用担心访问速度和容量</p></li><li><p>最关键还免费</p></li></ol><p>这不就是我们所需要吗？</p><p>最后，感谢各位观众老爷观看啦O(∩_∩)O</p>]]></content>
    
    <summary type="html">
    
      作为一个小博主，写写博客的时候很经常会用到一些图片，但是又因为自己的服务器太菜了，放在自己服务器也不是很好，所以就想到了图床。
    
    </summary>
    
    
      <category term="记录" scheme="https://gatings.cn/categories/%E8%AE%B0%E5%BD%95/"/>
    
    
      <category term="Github" scheme="https://gatings.cn/tags/Github/"/>
    
      <category term="JsDelivr" scheme="https://gatings.cn/tags/JsDelivr/"/>
    
      <category term="PicGo" scheme="https://gatings.cn/tags/PicGo/"/>
    
  </entry>
  
  <entry>
    <title>docker-compose mysql和node连接认证mongo问题</title>
    <link href="https://gatings.cn/2019-12-30/docker-compose%E7%9A%84mysql%E5%92%8Cmongo%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3/"/>
    <id>https://gatings.cn/2019-12-30/docker-compose%E7%9A%84mysql%E5%92%8Cmongo%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3/</id>
    <published>2019-12-29T16:00:00.000Z</published>
    <updated>2021-10-13T01:56:29.703Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>最近，想部署一个自己的项目，鉴于自己的服务器是VPS(虚拟主机)，配置也不够，就想到了用 <code>docker</code> 直接部署好了，这样既方便部署也方便不用的时候卸载或更新</p><p>然后本地搭建了环境，踩了一些坑，在这里记录一下</p><h1 id="mysql-问题（初始化创建数据库不成功）"><a href="#mysql-问题（初始化创建数据库不成功）" class="headerlink" title="mysql 问题（初始化创建数据库不成功）"></a>mysql 问题（初始化创建数据库不成功）</h1><p>首先我是使用 <code>docker-compose</code> 来搭建我的服务的，配置如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">yml</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight yml"><table><tr><td class="code"><pre><span class="line"><span class="attr">mysql:</span></span><br><span class="line">    <span class="attr">image:</span> <span class="string">mysql:5.5</span></span><br><span class="line">    <span class="attr">restart:</span> <span class="string">always</span></span><br><span class="line">    <span class="comment"># 构建容器</span></span><br><span class="line">    <span class="attr">build:</span> <span class="string">./mysql</span></span><br><span class="line">    <span class="attr">environment:</span></span><br><span class="line">        <span class="attr">MYSQL_ROOT_PASSWORD:</span> <span class="number">123456</span></span><br><span class="line">        <span class="attr">TZ:</span> <span class="string">Asia/Shanghai</span></span><br><span class="line">    <span class="attr">volumes:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="string">./mysql/db:/var/lib/mysql</span></span><br><span class="line">    <span class="attr">ports:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="number">3306</span><span class="string">:3306</span></span><br></pre></td></tr></table></figure></div><p>所以第一次运行 <code>docker-compose up</code> 的时候没有加上密码(<code>MYSQL_ROOT_PASSWORD</code>配置)，然后出于安全起见，我配置了root密码，然后关闭后，执行 <code>docker-compose up</code>，发现 <code>mysql</code> 容器的数据库并没有创建</p><p>查了下资料：<a href="https://github.com/docker-library/mariadb/issues/68" target="_blank" rel="noopener">https://github.com/docker-library/mariadb/issues/68</a></p><p>挂载的宿主主机的文件夹必须为空文件夹(.gitignore等隐藏文件不影响)，如果里面有文件，就不会初始化数据库。</p><blockquote><p>ps: <code>mongo</code> 创建不成功也可参考如上解决方法</p></blockquote><h1 id="node连接认证mongo问题"><a href="#node连接认证mongo问题" class="headerlink" title="node连接认证mongo问题"></a>node连接认证mongo问题</h1><p>首先我的 <code>docker-compose.yml</code> 配置文件如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">yml</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight yml"><table><tr><td class="code"><pre><span class="line"><span class="attr">mongo:</span></span><br><span class="line">    <span class="attr">image:</span> <span class="string">mongo</span></span><br><span class="line">    <span class="attr">restart:</span> <span class="string">always</span></span><br><span class="line">    <span class="attr">environment:</span></span><br><span class="line">    <span class="attr">MONGO_INITDB_ROOT_USERNAME:</span> <span class="string">gating</span></span><br><span class="line">    <span class="attr">MONGO_INITDB_ROOT_PASSWORD:</span> <span class="number">123456</span></span><br><span class="line">    <span class="attr">TZ:</span> <span class="string">Asia/Shanghai</span></span><br><span class="line">    <span class="comment"># 需要链接本地代码时</span></span><br><span class="line">    <span class="attr">volumes:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">./mongo/db:/data/db</span></span><br><span class="line">    <span class="attr">ports:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="number">27017</span><span class="string">:27017</span></span><br></pre></td></tr></table></figure></div><p>我尝试了通过文档的认证连接,代码如下：</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">mongoose.connect(<span class="string">'mongodb://user:pass@mongo/database'</span>);</span><br></pre></td></tr></table></figure></div><blockquote><p>ps: 这里能用 <code>mongo</code> 是因为我在 <code>docker-compose</code> 中指定了 <code>mongo</code> 容器的名字，在代码里使用 <code>mongo</code> 就表示 <code>localhost:27017</code></p></blockquote><p>连接失败，最后才发现是我缺少了一个配置 authSource，然后配置上了，最后终于连接上了我的mongo，可以愉快的开发部署了</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> mongoose = <span class="built_in">require</span>(<span class="string">'mongoose'</span>)</span><br><span class="line">mongoose.connect(<span class="string">'mongodb://gating:123456@mongo/small_time?authSource=admin'</span>)</span><br></pre></td></tr></table></figure></div>]]></content>
    
    <summary type="html">
    
      最近，想部署一个自己的项目，鉴于自己的服务器是VPS(虚拟主机)，配置也不够，就想到了用 docker 直接部署好了，这样既方便部署也方便不用的时候卸载或更新
    
    </summary>
    
    
      <category term="记录" scheme="https://gatings.cn/categories/%E8%AE%B0%E5%BD%95/"/>
    
    
      <category term="javascript" scheme="https://gatings.cn/tags/javascript/"/>
    
  </entry>
  
  <entry>
    <title>使用ss代理简单爬取小说</title>
    <link href="https://gatings.cn/2019-11-16/%E4%BD%BF%E7%94%A8ss%E4%BB%A3%E7%90%86%E7%AE%80%E5%8D%95%E7%88%AC%E5%8F%96%E5%B0%8F%E8%AF%B4/"/>
    <id>https://gatings.cn/2019-11-16/%E4%BD%BF%E7%94%A8ss%E4%BB%A3%E7%90%86%E7%AE%80%E5%8D%95%E7%88%AC%E5%8F%96%E5%B0%8F%E8%AF%B4/</id>
    <published>2019-11-15T16:00:00.000Z</published>
    <updated>2021-10-13T01:56:29.707Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>前不久，我的腐女女朋友叫我找一本耽美的小说，我在网盘搜索引擎没有找到就 <code>Google</code> 了一下，发现一个网站有，然后就想着写个爬虫爬给女朋友了（狗头保命），但是发现本地运行的 <code>terminal</code> 并不会走 <code>SS</code> ，需要 <code>node</code> 本身发送请求去实现自身的代理…也就有了下面这个小教程</p><blockquote><p>ps: 好像也好久没写博客了，就偷偷写一下</p></blockquote><h1 id="初始化我们的项目"><a href="#初始化我们的项目" class="headerlink" title="初始化我们的项目"></a>初始化我们的项目</h1><ul><li>新建一个文件夹，取名叫做 <code>my-crawler</code></li><li>通过控制台或者git bash定位到当前文件下面，执行命令: <code>npm init -y</code></li></ul><h1 id="安装项目依赖"><a href="#安装项目依赖" class="headerlink" title="安装项目依赖"></a>安装项目依赖</h1><p>执行命令: <code>npm install iconv-lite request request-promise socks5-http-client -S</code></p><blockquote><p>ps: npm 可以省略 <code>-S</code>，他会默认执行 <code>-S</code> 命令，cnpm 则需要加上 <code>-S</code></p></blockquote><h2 id="socks5-http-client"><a href="#socks5-http-client" class="headerlink" title="socks5-http-client"></a>socks5-http-client</h2><blockquote><p>ps: 该客户端仅支持发出HTTP请求。有关HTTPS实施，请参见socks5-https-client。</p></blockquote><h1 id="fs模块的简单使用"><a href="#fs模块的简单使用" class="headerlink" title="fs模块的简单使用"></a>fs模块的简单使用</h1><p>fs是<code>FileSystem(文件系统)</code>的缩写，该模块提供本地文件的读写能力，基本上是<code>POSIX(可移植操作系统接口)</code>文件操作命令的简单包装。但是，这个模块几乎对所有操作提供异步和同步两种操作方式，供开发者选择。</p><p>这里为了方便使用，我们使用同步操作，也更符合我们整个开发流程。</p><p>这个项目我们使用到了 <code>appendFileSync</code> 这个api，接下来我们简单介绍一下他的作用和使用</p><h2 id="appendFileSync"><a href="#appendFileSync" class="headerlink" title="appendFileSync"></a>appendFileSync</h2><ul><li>在 <code>my-crawler</code> 目录下新建一个 <code>demo.js</code>,代码如下:</li></ul><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">'fs'</span>)</span><br><span class="line"><span class="comment">// 通过同步的方式将文本内容或数据添加到文件里，文件不存在则自动创建。</span></span><br><span class="line">fs.appendFileSync(<span class="string">'./demo.txt'</span>,<span class="string">'123465'</span>)</span><br></pre></td></tr></table></figure></div><p>看完上面的代码，是不是发现 appendFileSync 很简单呢？好了，马上进入我们的正题了</p><h1 id="实现爬虫代码"><a href="#实现爬虫代码" class="headerlink" title="实现爬虫代码"></a>实现爬虫代码</h1><p>首先，我们打开 <code>http://wap.danwenku.com/qita/</code>,然后查看源代码你会发现，他是 <code>gb2312</code> 编码的，正常的 <code>request</code> 请求爬下来的东西是乱码的，所以我们安装了 <code>iconv-lite</code> 解决乱码问题</p><p>然后通过 <code>cheerio</code> 来解析 <code>DOM</code>,和 <code>JQ</code> 一样的语法，不用担心不会</p><h2 id="反爬"><a href="#反爬" class="headerlink" title="反爬"></a>反爬</h2><p>当我们准备好这一切之后，发现请求我们需要爬取的网站之后返回了 <code>503</code>，但是我们通过浏览器访问却没有问题，这是为什么呢？其实，这就是网站禁止我们爬取他的内容，这就需要通过反爬机制去解决这个问题。</p><p>而设置 <code>headers</code> 就是解决 <code>requests</code> 请求反爬的最简单的一个方法。</p><blockquote><p>相当于告诉对方服务器，我们是使用浏览器访问网站</p></blockquote><blockquote><p>注意：headers中有很多内容，主要常用的就是user-agent 和 host，如果使用这两个就可以反爬成功，就不需要其他键对；否则，需要加入更多键对形式。</p></blockquote><p>比如我这里，就加入了 <code>Cookie</code> 这个键值对</p><p>以下是我们这个简单的爬虫代码实现</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">!<span class="function">(<span class="params"><span class="keyword">async</span> (</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// fs模块，用来写入文件</span></span><br><span class="line">    <span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">'fs'</span>)</span><br><span class="line">    <span class="comment">// 用来解析DOM</span></span><br><span class="line">    <span class="keyword">const</span> cheerio = <span class="built_in">require</span>(<span class="string">'cheerio'</span>)</span><br><span class="line">    <span class="keyword">const</span> iconv = <span class="built_in">require</span>(<span class="string">'iconv-lite'</span>)</span><br><span class="line">    <span class="comment">// 解决网页编码问题</span></span><br><span class="line">    <span class="keyword">const</span> request = <span class="built_in">require</span>(<span class="string">'request-promise'</span>)</span><br><span class="line">    <span class="comment">// 解决ss代理</span></span><br><span class="line">    <span class="keyword">const</span> Agent = <span class="built_in">require</span>(<span class="string">'socks5-http-client/lib/Agent'</span>)</span><br><span class="line">    <span class="comment">// 需要爬取的url前缀</span></span><br><span class="line">    <span class="keyword">let</span> urlPrefix = <span class="string">`http://wap.danwenku.com/qita/`</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">1</span>; i &lt; <span class="number">65</span>; i++) &#123;</span><br><span class="line">        <span class="keyword">let</span> url = <span class="string">``</span></span><br><span class="line">        <span class="comment">// 通过查看第n页可发现，第一页是54999.html 第n页是54999_n.html</span></span><br><span class="line">        <span class="comment">// 即爬取的url就是urlPrefix + 54999_n.html</span></span><br><span class="line">        <span class="keyword">if</span> (i === <span class="number">1</span>) &#123;</span><br><span class="line">            url = <span class="string">`54999.html`</span></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            url = <span class="string">`54999_<span class="subst">$&#123;i&#125;</span>.html`</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">const</span> request_option = &#123;</span><br><span class="line">            url: urlPrefix + url,</span><br><span class="line">            <span class="comment">// 使用本地代理</span></span><br><span class="line">            agentClass: Agent,</span><br><span class="line">            <span class="comment">// 本地代理配置</span></span><br><span class="line">            agentOptions: &#123;</span><br><span class="line">                socksHost: <span class="string">'127.0.0.1'</span>, <span class="comment">// 默认localhost</span></span><br><span class="line">                socksPort: <span class="number">1080</span> <span class="comment">// 默认1080</span></span><br><span class="line">            &#125;,</span><br><span class="line">            <span class="comment">// 取消设置响应编码，返回二进制数据</span></span><br><span class="line">            encoding: <span class="literal">null</span>,</span><br><span class="line">            <span class="comment">// 对响应的二进制数据重新进行编码，转换成cheerio对象</span></span><br><span class="line">            transform: <span class="function"><span class="keyword">function</span> (<span class="params">body</span>) </span>&#123;</span><br><span class="line">                body = iconv.decode(Buffer.from(body), <span class="string">'GBK'</span>)</span><br><span class="line">                <span class="keyword">return</span> cheerio.load(body);</span><br><span class="line">            &#125;,</span><br><span class="line">            <span class="comment">// 设置请求头，模拟成浏览器取访问网站（最简单反爬方式了）</span></span><br><span class="line">            headers: &#123;</span><br><span class="line">                <span class="string">"Cookie"</span>: <span class="string">`__cfduid=d4d6dff36f6804fbdbb185f1de7cddd581573185661; cf_clearance=d530634f94552a4f8f52966366988dc9f351952c-1573185666-0-150; ZDEDebuggerPresent=php,phtml,php3; UM_distinctid=16e492c5744342-05e7814449385d-7711b3e-1fa400-16e492c574535f; CNZZDATA1252964692=315770227-1573184262-null%7C1573184262`</span>,</span><br><span class="line">                <span class="string">"Host"</span>: <span class="string">"wap.danwenku.com"</span>,</span><br><span class="line">                <span class="string">"User-Agent"</span>: <span class="string">"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36"</span>,</span><br><span class="line">            &#125;,</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 等待请求返回，这里没有做错误处理，因为只是个简单的小栗子，没有必要做错误处理</span></span><br><span class="line">        <span class="keyword">const</span> $ = <span class="keyword">await</span> request(request_option)</span><br><span class="line">        <span class="keyword">let</span> chapterTxt = <span class="string">`第<span class="subst">$&#123;i&#125;</span>章\r\n`</span></span><br><span class="line">        <span class="comment">// 打印下现在正在爬取多少章</span></span><br><span class="line">        <span class="built_in">console</span>.log(chapterTxt)</span><br><span class="line">        $(<span class="string">'.css-pc css-site,.sxwenzhang,.pagela,.pagination'</span>).remove()</span><br><span class="line">        <span class="comment">// 写入到文件中，如果中断应该则通过 `正在爬取多少章` 可知i的值，重新设置i值就好了</span></span><br><span class="line">        fs.appendFileSync(<span class="string">'./二进制.txt'</span>, chapterTxt + $(<span class="string">`div.single-content`</span>).text() + <span class="string">'\r\n'</span>)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)()</span><br></pre></td></tr></table></figure></div><p>最后，感谢各位观众老爷观看吖 O(∩_∩)O~</p>]]></content>
    
    <summary type="html">
    
      前不久，我的腐女女朋友叫我找一本耽美的小说，我在网盘搜索引擎没有找到就 Google 了一下，发现一个网站有，然后就想着写个爬虫爬给女朋友了（狗头保命），但是发现本地运行的 terminal 并不会走 SS ，需要 node 本身发送请求去实现自身的代理...也就有了下面这个小教程
    
    </summary>
    
    
      <category term="爬虫" scheme="https://gatings.cn/categories/%E7%88%AC%E8%99%AB/"/>
    
    
      <category term="node" scheme="https://gatings.cn/tags/node/"/>
    
  </entry>
  
  <entry>
    <title>字符串的方法的总结和使用</title>
    <link href="https://gatings.cn/2019-01-09/%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E6%96%B9%E6%B3%95%E7%9A%84%E6%80%BB%E7%BB%93%E5%92%8C%E4%BD%BF%E7%94%A8/"/>
    <id>https://gatings.cn/2019-01-09/%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E6%96%B9%E6%B3%95%E7%9A%84%E6%80%BB%E7%BB%93%E5%92%8C%E4%BD%BF%E7%94%A8/</id>
    <published>2019-01-08T16:00:00.000Z</published>
    <updated>2021-10-13T01:56:29.707Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>前些天写了篇数组的总结，其实我们再来看看字符串，和数组有异曲同工之妙，字符串也可以通过 <code>split</code> 来变成数组，然后使用数组的方法。</p><p>另外，对于字符串，其实我们关心的只有一个问题，<strong>返回值是什么</strong>。</p><p>接下来，我会带大家一步步的使用 <code>String</code> 里面所拥有的方法和属性</p><h1 id="字符串的属性"><a href="#字符串的属性" class="headerlink" title="字符串的属性"></a>字符串的属性</h1><h2 id="length"><a href="#length" class="headerlink" title="length"></a>length</h2><p>length属性表示一个字符串的长度。该属性返回字符串中字符编码单元的数量。JavaScript 使用 UTF-16 编码，该编码使用一个 16 比特的编码单元来表示大部分常见的字符，使用两个代码单元表示不常用的字符。因此 length 返回值可能与字符串中实际的字符数量不相同。</p><p><strong>空字符串的 length 为 0。</strong></p><p><strong>静态属性 String.length 返回 1。</strong></p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> str = <span class="string">"gating"</span></span><br><span class="line"><span class="built_in">console</span>.log(str.length) <span class="comment">// 6</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> empty = <span class="string">""</span></span><br><span class="line"><span class="built_in">console</span>.log(empty.length) <span class="comment">// 0  </span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// JavaScript 内部，字符以 UTF-16 的格式储存，每个字符固定为2个字节。</span></span><br><span class="line"><span class="comment">// 对于那些需要4个字节储存的字符（Unicode 码点大于0xFFFF的字符），JavaScript 会认为它们是两个字符。</span></span><br><span class="line"><span class="keyword">let</span> double = <span class="string">"𪚥"</span></span><br><span class="line"><span class="built_in">console</span>.log(double.length) <span class="comment">// 2</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 从数组的length我们可得知，数组的 length 来截断数组，字符串的可不可以呢？</span></span><br><span class="line"><span class="comment">// 很遗憾的说，不行，因为字符串的length只可读，不可写</span></span><br><span class="line"><span class="keyword">let</span> name = <span class="string">"gating"</span></span><br><span class="line">name.length = <span class="number">1</span></span><br><span class="line"><span class="built_in">console</span>.log(name,name.length) <span class="comment">// gating,6</span></span><br></pre></td></tr></table></figure></div><h1 id="字符串的静态方法"><a href="#字符串的静态方法" class="headerlink" title="字符串的静态方法"></a>字符串的静态方法</h1><h2 id="字符的-Unicode-表示法"><a href="#字符的-Unicode-表示法" class="headerlink" title="字符的 Unicode 表示法"></a>字符的 Unicode 表示法</h2><p>说到字符串，不得不说 JavaScript 中的字符串的 <code>Unicode</code> 表示形式，JavaScript 允许采用<code>\uxxxx</code>形式表示一个字符，其中<code>xxxx</code>表示字符的<code>Unicode</code>码点。</p><p>但是，这种表示法只限于码点在\u0000~\uFFFF之间的字符。超出这个范围的字符，必须用两个双字节的形式表示。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 比如</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"\u0061"</span>) <span class="comment">// a</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"\uD842\uDFB7"</span>) <span class="comment">// 𠮷</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"\u20BB7"</span>) <span class="comment">// "₻7"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 但是 es6 对这一点做出了改进，只要将码点放入大括号，就能正确解读该字符。</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"\u&#123;20BB7&#125;"</span>)</span><br></pre></td></tr></table></figure></div><blockquote><p>参考链接 <a href="http://es6.ruanyifeng.com/#docs/string" target="_blank" rel="noopener">ECMAScript 6 入门</a> 建议去了解一下，这里我就不做过多解释了，因为我用的真的不多（主要也是不会）</p></blockquote><h2 id="String-fromCharCode"><a href="#String-fromCharCode" class="headerlink" title="String.fromCharCode()"></a>String.fromCharCode()</h2><p>String.fromCharCode() 方法返回使用指定的Unicode值序列创建的字符串。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * String.fromCharCode(numN)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number/String&#125;</span> </span>numN =&gt; 一组序列数字，表示 Unicode 值。</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 class="doctag">@param <span class="type">&#123;String&#125;</span> </span>该方法返回一个字符串，而不是一个 String 对象。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">String</span>.fromCharCode(<span class="number">65</span>)) <span class="comment">// A</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">String</span>.fromCharCode(<span class="number">65</span>,<span class="number">66</span>)) <span class="comment">// AB</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">String</span>.fromCharCode(<span class="string">"----"</span>)) <span class="comment">// " "</span></span><br></pre></td></tr></table></figure></div><h3 id="作用于高位编码（higher-values）"><a href="#作用于高位编码（higher-values）" class="headerlink" title="作用于高位编码（higher values）"></a>作用于高位编码（higher values）</h3><p>尽管绝大部分常用的 Unicode 值可以用一个 16-bit 数字表示（正如 JavaScript 标准化过程早期），并且对于绝大部分值 fromCharCode() 返回一个字符（即对于绝大部分字符 UCS-2 值是 UTF-16 的子集），但是为了处理所有的 Unicode 值（至 21 bits），只用 fromCharCode() 是不足的。由于高位编码字符是用两个低位编码（lower value）表示形成的一个字符，因此String.fromCodePoint() （ES6 规范的一部分）被用来返回这样一对低位编码，从而可以完全表示这些高位编码字符。</p><h2 id="String-fromCodePoint"><a href="#String-fromCodePoint" class="headerlink" title="String.fromCodePoint()"></a>String.fromCodePoint()</h2><p>String.fromCodePoint() 静态方法返回使用指定的代码点序列创建的字符串。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * String.fromCodePoint(numN)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number/String&#125;</span> </span>numN =&gt; 一串 Unicode 编码。如果传入无效的 Unicode 编码，将会抛出一个RangeError</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 class="doctag">@param <span class="type">&#123;String&#125;</span> </span>该方法返回一个字符串，而不是一个 String 对象。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">String</span>.fromCodePoint(<span class="number">65</span>)) <span class="comment">// A</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">String</span>.fromCodePoint(<span class="number">65</span>,<span class="number">66</span>)) <span class="comment">// AB</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">String</span>.fromCodePoint(<span class="string">"----"</span>)) <span class="comment">// RangeError</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// String.fromCharCode() 方法不能单独获取在高代码点位上的字符</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">String</span>.fromCharCode(<span class="number">0x2F804</span>)) <span class="comment">// </span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">String</span>.fromCodePoint(<span class="number">0x2F804</span>)) <span class="comment">// 你</span></span><br></pre></td></tr></table></figure></div><blockquote><p>具体实际用理可以看看 MDN 这里不做阐述了，因为博主这两个方法用着不多 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/fromCharCode" target="_blank" rel="noopener">String.fromCharCode()</a>、<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint" target="_blank" rel="noopener">String.fromCodePoint()</a></p></blockquote><h2 id="String-raw-和-模板字符串"><a href="#String-raw-和-模板字符串" class="headerlink" title="String.raw 和 模板字符串"></a>String.raw 和 模板字符串</h2><h3 id="模板字符串"><a href="#模板字符串" class="headerlink" title="模板字符串"></a>模板字符串</h3><p>了解<code>String.raw</code>时，我们必须先了解一下什么是模板字符串，简单来说模板字符串是增强版的字符串，他用反引号（`）标识。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><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="keyword">let</span> name = <span class="string">"gating"</span>, time = <span class="string">"today"</span>;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"Hello "</span>+name+<span class="string">", how are you "</span> + time) <span class="comment">// Hello gating, how are you today?</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> name = <span class="string">"gating"</span>, time = <span class="string">"today"</span>;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">`Hello <span class="subst">$&#123;name&#125;</span>, how are you <span class="subst">$&#123;time&#125;</span>?`</span>) <span class="comment">// Hello gating, how are you today?</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> str = <span class="string">`&lt;ul&gt;</span></span><br><span class="line"><span class="string">             &lt;li&gt;first&lt;/li&gt;</span></span><br><span class="line"><span class="string">             &lt;li&gt;second&lt;/li&gt;</span></span><br><span class="line"><span class="string">          &lt;/ul&gt;`</span></span><br></pre></td></tr></table></figure></div><h3 id="String-raw"><a href="#String-raw" class="headerlink" title="String.raw"></a>String.raw</h3><p>String.raw() 是一个模板字符串的标签函数，它的作用类似于 Python 中的字符串前缀 r 和 C# 中的字符串前缀 @，是用来获取一个模板字符串的原始字面量值的。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * String.raw(callSite, ...substitutions) || String.raw`templateString`</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Object&#125;</span> </span>callSite =&gt; 一个模板字符串的“调用点对象”。类似&#123; raw: ['foo', 'bar', 'baz'] &#125;。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Any&#125;</span> </span>...substitutions =&gt; 任意个可选的参数，表示任意个内插表达式对应的值。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String&#125;</span> </span>templateString =&gt; 模板字符串。</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 class="doctag">@param <span class="type">&#123;String&#125;</span> </span>给定模板字符串的原始字面量值。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="comment">// "Hi\\u000A!"，这里得到的会是 \、u、0、0、0、A 6个字符</span></span><br><span class="line"><span class="comment">// 任何类型的转义形式都会失效</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">String</span>.raw <span class="string">`Hi\u000A!`</span>) <span class="comment">// Hi\u000A!</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> name = <span class="string">"Bob"</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">String</span>.raw <span class="string">`Hi\n<span class="subst">$&#123;name&#125;</span>!`</span>) <span class="comment">// Hi\nBob!</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 我认为你通常不需要把它当成普通函数来调用</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">String</span>.raw(&#123; <span class="attr">raw</span>: <span class="string">'test'</span> &#125;, <span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>)) <span class="comment">// t0e1s2t</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">String</span>.raw(&#123; <span class="attr">raw</span>: [<span class="string">'t'</span>,<span class="string">'e'</span>,<span class="string">'s'</span>,<span class="string">'t'</span>] &#125;, <span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>)) <span class="comment">// t0e1s2t</span></span><br></pre></td></tr></table></figure></div><p>作为函数，String.raw的代码实现基本如下。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">String</span>.raw = <span class="function"><span class="keyword">function</span> (<span class="params">strings, ...values</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> output = <span class="string">''</span>;</span><br><span class="line">  <span class="keyword">let</span> index;</span><br><span class="line">  <span class="keyword">for</span> (index = <span class="number">0</span>; index &lt; values.length; index++) &#123;</span><br><span class="line">    output += strings.raw[index] + values[index];</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  output += strings.raw[index]</span><br><span class="line">  <span class="keyword">return</span> output;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><blockquote><p><a href="http://es6.ruanyifeng.com/#docs/string#String-raw" target="_blank" rel="noopener">ES6入门 String.raw()</a></p></blockquote><h1 id="正则相关"><a href="#正则相关" class="headerlink" title="正则相关"></a>正则相关</h1><blockquote><p>正则推荐观看文末的 正则表达式30分钟入门教程 ，写的非常好</p></blockquote><h2 id="split"><a href="#split" class="headerlink" title="split"></a>split</h2><p>split() 方法使用指定的分隔符字符串将一个String对象分割成字符串数组，以将字符串分隔为子字符串，以确定每个拆分的位置。</p><blockquote><p>Tip: 如果空字符串(“”)被用作分隔符，则字符串会在每个字符之间分割。</p></blockquote><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.split(separator,limit)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String&#125;</span> </span>separator =&gt; 一个介于0 和字符串长度减1之间的整数。 (0~length-1) 如果没有提供索引，charAt() 将使用 0。如果指定的 index 值超出了该范围，则返回一个空字符串。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>limit =&gt; 一个整数，限定返回的分割片段数量。当提供此参数时，split 方法会在指定分隔符的每次出现时分割该字符串，但在限制条目已放入数组时停止。如果在达到指定限制之前达到字符串的末尾，它可能仍然包含少于限制的条目。新数组中不返回剩下的文本。</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 class="doctag">@param <span class="type">&#123;Array&#125;</span> </span>返回源字符串以分隔符出现位置分隔而成的一个 Array </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"gating"</span></span><br><span class="line"><span class="built_in">console</span>.log(str.split(<span class="string">''</span>)) <span class="comment">// ["g", "a", "t", "i", "n", "g"]</span></span><br><span class="line"><span class="built_in">console</span>.log(str.split(<span class="string">''</span>,<span class="number">1</span>)) <span class="comment">// ["g"]</span></span><br><span class="line"><span class="built_in">console</span>.log(str.split(<span class="string">''</span>,<span class="number">8</span>)) <span class="comment">// ["g", "a", "t", "i", "n", "g"]</span></span><br></pre></td></tr></table></figure></div><p>看到这里，是不是感觉可以用通过<code>split</code>转成数组，使用数组的方法啦？没错是的，还记得前些天我写过一篇数组的方法的总结吗？没错，重新看一遍就可以了O(∩_∩)O</p><p>不信，你看，我们可以通过 <code>split</code> 获取字符串的交集或差集哦</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 返回两个字符串的交集</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String&#125;</span> </span>str =&gt; 字符串1 </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String&#125;</span> </span>str =&gt; 字符串2</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">intersection</span>(<span class="params">str1, str2</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> str1.split(<span class="string">""</span>).filter(<span class="function"><span class="params">v</span> =&gt;</span> str2.split(<span class="string">""</span>).includes(v)).toString().replace(<span class="regexp">/,/g</span>, <span class="string">""</span>)</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">console</span>.log(intersection(<span class="string">"abc"</span>,<span class="string">"bcd"</span>)) <span class="comment">// bc</span></span><br></pre></td></tr></table></figure></div><p>是不是感觉自己已经对字符串了如指掌了吗？</p><blockquote><p>好了，本次教程到此结束了</p></blockquote><p>开个玩笑嘛，接下来我会陆陆续续带大家认识下 <code>string</code> 的方法的。</p><h2 id="match"><a href="#match" class="headerlink" title="match"></a>match</h2><p>当一个字符串与一个正则表达式匹配时， match()方法检索匹配项</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.match(regexp)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;RegExp&#125;</span> </span>regexp =&gt; 个正则表达式对象。如果传入一个非正则表达式对象，则会隐式地使用 new RegExp(obj) 将其转换为一个 RegExp 。如果你未提供任何参数，直接使用 match() ，那么你会得到一个包含空字符串的 Array ：[""] 。</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 class="doctag">@param <span class="type">&#123;String&#125;</span>  </span>如果字符串匹配到了表达式，会返回一个数组，数组的第一项是进行匹配完整的字符串，之后的项是用圆括号捕获的结果。如果没有匹配到，返回null</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"gating"</span></span><br><span class="line"><span class="keyword">const</span> res = str.match(<span class="regexp">/g/g</span>)</span><br><span class="line"><span class="built_in">console</span>.log(res) <span class="comment">// ["g", "g"]</span></span><br><span class="line"><span class="built_in">console</span>.log(str.match()) <span class="comment">// [""]</span></span><br><span class="line"><span class="built_in">console</span>.log(str.match(<span class="regexp">/sadasd/</span>)) <span class="comment">// null</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> str1 = <span class="string">"NaN means not a number. Infinity contains -Infinity and +Infinity in JavaScript."</span>,</span><br><span class="line">    str2 = <span class="string">"My grandfather is 65 years old and My grandmother is 63 years old."</span>,</span><br><span class="line">    str3 = <span class="string">"The contract was declared null and void."</span>;</span><br><span class="line">str1.match(<span class="string">"number"</span>);   <span class="comment">// "number" 是字符串。返回["number"]</span></span><br><span class="line">str1.match(<span class="literal">NaN</span>);        <span class="comment">// NaN的类型是number。返回["NaN"]</span></span><br><span class="line">str1.match(<span class="literal">Infinity</span>);   <span class="comment">// Infinity的类型是number。返回["Infinity"]</span></span><br><span class="line">str1.match(+<span class="literal">Infinity</span>);  <span class="comment">// 返回["Infinity"]</span></span><br><span class="line">str1.match(-<span class="literal">Infinity</span>);  <span class="comment">// 返回["-Infinity"]</span></span><br><span class="line">str2.match(<span class="number">65</span>);         <span class="comment">// 返回["65"]</span></span><br><span class="line">str2.match(+<span class="number">65</span>);        <span class="comment">// 有正号的number。返回["65"]</span></span><br><span class="line">str3.match(<span class="literal">null</span>);       <span class="comment">// 返回["null"]</span></span><br></pre></td></tr></table></figure></div><h2 id="replace"><a href="#replace" class="headerlink" title="replace"></a>replace</h2><p>replace() 方法返回一个由替换值替换一些或所有匹配的模式后的新字符串。模式可以是一个字符串或者一个正则表达式, 替换值可以是一个字符串或者一个每次匹配都要调用的函数。</p><blockquote><p>replace 十分重要，必须掌握</p></blockquote><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.replace(regexp|substr, newSubStr|function)</span></span><br><span class="line"><span class="comment"> *  <span class="doctag">@param <span class="type">&#123;RegExp&#125;</span> </span>regexp =&gt; 一个RegExp 对象或者其字面量。该正则所匹配的内容会被第二个参数的返回值替换掉。</span></span><br><span class="line"><span class="comment"> *  <span class="doctag">@param <span class="type">&#123;String&#125;</span> </span>substr =&gt; 一个要被 newSubStr 替换的字符串。其被视为一整个字符串，而不是一个正则表达式。仅仅是第一个匹配会被替换。</span></span><br><span class="line"><span class="comment"> *  <span class="doctag">@param <span class="type">&#123;String&#125;</span> </span>newSubStr =&gt;  用于替换掉第一个参数在原字符串中的匹配部分的字符串。该字符串中可以内插一些特殊的变量名 </span></span><br><span class="line"><span class="comment"> *  <span class="doctag">@param <span class="type">&#123;Function&#125;</span> </span>function =&gt; 一个用来创建新子字符串的函数，该函数的返回值将替换掉第一个参数匹配到的结果 </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 class="doctag">@param <span class="type">&#123;String&#125;</span>  </span>一个部分或全部匹配由替代模式所取代的新的字符串</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 特殊的变量名 和 function 在实例中 会更详细说明</span></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"gating"</span></span><br><span class="line"><span class="comment">// 如果用 substr ，只会替换找到的第一个字符，替换多个字符则需要用正则</span></span><br><span class="line"><span class="built_in">console</span>.log(str.replace(<span class="string">'g'</span>,<span class="string">'家庭'</span>)) <span class="comment">// 家庭ating</span></span><br><span class="line"><span class="built_in">console</span>.log(str.replace(<span class="regexp">/g/</span>,<span class="string">'家庭'</span>)) <span class="comment">// 家庭ating</span></span><br><span class="line"><span class="built_in">console</span>.log(str.replace(<span class="regexp">/g/g</span>,<span class="string">'家庭'</span>)) <span class="comment">// 家庭atin家庭</span></span><br><span class="line"><span class="built_in">console</span>.log(str.replace(<span class="regexp">/g/i</span>,<span class="string">'G'</span>)) <span class="comment">// Gating</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> word = <span class="string">"hello,gating"</span></span><br><span class="line"><span class="comment">// 单词首字母大写</span></span><br><span class="line"><span class="comment">// 这里的 word 参数时匹配的字符串，下文有说明</span></span><br><span class="line"><span class="keyword">let</span> res = word.replace(<span class="regexp">/\b\w+\b/g</span>,(word)=&gt;(word[<span class="number">0</span>].toUpperCase()+word.slice(<span class="number">1</span>)))</span><br><span class="line"><span class="built_in">console</span>.log(res) <span class="comment">// Hello,Gating</span></span><br></pre></td></tr></table></figure></div><p>实际应用场景中，<code>replace</code> 方法非常灵活，可以做很多我们想要的事，比如：😄</p><p>类似于我们的模板引擎 replace 就可以实现，不过这里我就不带着大家实现了= =</p><p>在写实例时，我希望可以先搞懂几个概念和用法</p><h3 id="newSubStr-的特殊变量名"><a href="#newSubStr-的特殊变量名" class="headerlink" title="newSubStr 的特殊变量名"></a>newSubStr 的特殊变量名</h3><table><thead><tr><th>变量名</th><th>代表的值</th></tr></thead><tbody><tr><td><code>$$</code></td><td>插入一个 “$”。</td></tr><tr><td><code>$&amp;</code></td><td>插入匹配的子串。</td></tr><tr><td><code>$`</code></td><td>插入当前匹配的子串左边的内容。</td></tr><tr><td><code>$&#39;</code></td><td>插入当前匹配的子串右边的内容。</td></tr><tr><td><code>$*n*</code></td><td>假如第一个参数是 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/RegExp" target="_blank" rel="noopener"><code>RegExp</code></a>对象，并且 n 是个小于100的非负整数，那么插入第 n 个括号匹配的字符串。提示：索引是从1开始</td></tr></tbody></table><h3 id="replace-中-function-的参数"><a href="#replace-中-function-的参数" class="headerlink" title="replace 中 function 的参数"></a>replace 中 function 的参数</h3><table><thead><tr><th>变量名</th><th>代表的值</th></tr></thead><tbody><tr><td>match</td><td>匹配的子串。（对应于上述的$&amp;。）</td></tr><tr><td><code>p1,p2, ...</code></td><td>假如replace()方法的第一个参数是一个<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/RegExp" target="_blank" rel="noopener"><code>RegExp</code></a> 对象，则代表第n个括号匹配的字符串。（对应于上述的$1，$2等。）例如, 如果是用 <code>/(\a+)(\b+)/</code>这个来匹配， <code>p1</code>就是匹配的 <code>\a+</code>, <code>p2 就是匹配的</code> <code>\b+。</code></td></tr><tr><td><code>offset</code></td><td>匹配到的子字符串在原字符串中的偏移量。（比如，如果原字符串是“abcd”，匹配到的子字符串是“bc”，那么这个参数将是1）</td></tr><tr><td>string</td><td>被匹配的原字符串。</td></tr></tbody></table><blockquote><p>你可以指定一个函数作为第二个参数。在这种情况下，当匹配执行后， 该函数就会执行。 函数的返回值作为替换字符串。 (注意: 上面提到的特殊替换参数在这里不能被使用。) 另外要注意的是， 如果第一个参数是正则表达式， 并且其为全局匹配模式， 那么这个方法将被多次调用， 每次匹配都会被调用。</p></blockquote><p>在实际应用中,<code>replace</code>的使用可以满足大部分的操作字符串场景,特别是function的引入,极大的增强了replace的实力,从而使得我们操作字符游刃有余.</p><p>在写实例的时，我们需要搞懂几个<code>newSubStr</code>特殊变量名，他在实际应用中也很有作用</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> name = <span class="string">'gating'</span></span><br><span class="line"><span class="comment">// $&amp; 表示匹配到的字串，这里匹配到了 gating ，所以 $&amp; 就等于 gating</span></span><br><span class="line"><span class="built_in">console</span>.log(name.replace(<span class="regexp">/\w+/</span>,<span class="string">'$&amp;-$&amp;'</span>)) <span class="comment">// gating-gating</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"gating say hello for mosen"</span></span><br><span class="line"><span class="comment">// $1 表示匹配第一个括号里的内容 也就是 $1 就是 gating </span></span><br><span class="line"><span class="built_in">console</span>.log(str.replace(<span class="regexp">/(^\w+)(.*?)(\w+)$/</span>,<span class="string">'$3$2$1'</span>)) <span class="comment">// mosen say hello for gating\</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> hello = <span class="string">"hello,gating"</span></span><br><span class="line"><span class="comment">// $`当前匹配的子串左边的内容。</span></span><br><span class="line"><span class="built_in">console</span>.log(hello.replace(<span class="regexp">/gating/</span>,<span class="string">"&amp;.$`mosen"</span>)) <span class="comment">// hello,gating.hello,mosen</span></span><br><span class="line"><span class="comment">// $'当前匹配的子串右边的内容。(不是例子的例子)</span></span><br><span class="line"><span class="built_in">console</span>.log(hello.replace(<span class="regexp">/hello,/</span>,<span class="string">"$' say hi for $`"</span>)) <span class="comment">// gating say hi for gating</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// function 的 用法</span></span><br><span class="line"><span class="keyword">let</span> paragraph = <span class="string">"what is this?"</span></span><br><span class="line"><span class="keyword">let</span> res = paragraph.replace(<span class="string">" is this"</span>,(match,offset,string)=&gt;&#123;</span><br><span class="line">    <span class="comment">// match 匹配的子串</span></span><br><span class="line">    <span class="comment">// offset 偏移量 这是是4 也就是 偏移了what4个字符</span></span><br><span class="line">    <span class="comment">// string 元字符</span></span><br><span class="line">    <span class="built_in">console</span>.log(match,offset,string) <span class="comment">//  is this 4 what is this?</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">" is that"</span></span><br><span class="line">&#125;)</span><br><span class="line"><span class="built_in">console</span>.log(res) <span class="comment">// what is that?</span></span><br></pre></td></tr></table></figure></div><p>了解了这些，那么我们就可以做写点比较实际的了</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// js 隐藏手机中间5位号码</span></span><br><span class="line"><span class="keyword">let</span> phone=<span class="string">'13700000137'</span></span><br><span class="line"><span class="built_in">console</span>.log(phone.replace(<span class="regexp">/(\d&#123;3&#125;)\d+(\d&#123;3&#125;)/</span>, <span class="string">'$1****$2'</span>)) <span class="comment">// 137****137</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 将阿拉伯数字每三位一逗号分隔，如：1000转化为1,000</span></span><br><span class="line"><span class="keyword">let</span> price =  <span class="string">'3521.08'</span></span><br><span class="line"><span class="built_in">console</span>.log(price.replace(<span class="regexp">/(?=(?!^)(?:\d&#123;3&#125;)+(?:\.|$))(\d&#123;3&#125;(\.\d+$)?)/g</span>,<span class="string">',$1'</span>)) <span class="comment">// 3,521.08</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// html 转义，防止用户注入</span></span><br><span class="line"><span class="keyword">let</span> html = <span class="string">'&lt;div&gt;"hello &amp; world"&lt;/div&gt;'</span>;</span><br><span class="line"><span class="keyword">let</span> res = html.replace(<span class="regexp">/[&lt;&gt;\"\'\&amp;']/g</span>,<span class="function"><span class="keyword">function</span>(<span class="params">a</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">switch</span>(a)&#123;</span><br><span class="line">        <span class="keyword">case</span> <span class="string">'&lt;'</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">'&amp;lt;'</span>;</span><br><span class="line">        <span class="keyword">case</span> <span class="string">'&gt;'</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">'&amp;gt;'</span>;</span><br><span class="line">        <span class="keyword">case</span> <span class="string">'\"'</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">'&amp;quot;'</span>;</span><br><span class="line">        <span class="keyword">case</span> <span class="string">'\''</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">'&amp;#39;'</span>;</span><br><span class="line">        <span class="keyword">case</span> <span class="string">'\&amp;'</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">'&amp;amp;'</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br><span class="line"><span class="built_in">console</span>.log(res) <span class="comment">// &amp;lt;div&amp;gt;&amp;quot;hello &amp;amp; world&amp;quot;&amp;lt;/div&amp;gt;</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">let</span> template = <span class="string">"My name is &#123;name&#125;"</span></span><br><span class="line"><span class="keyword">const</span> obj = &#123;<span class="attr">name</span>:<span class="string">'gating'</span>&#125;</span><br><span class="line"><span class="keyword">const</span> tmpl = <span class="function">(<span class="params">template,obj</span>)=&gt;</span> template.replace(<span class="regexp">/([^&#123;&#125;]*)&#123;(.*)&#125;/g</span>,(match,p1,p2) =&gt; (p1+obj[p2]))</span><br><span class="line"><span class="built_in">console</span>.log(tmpl(template,obj)) <span class="comment">// My name is gating</span></span><br></pre></td></tr></table></figure></div><h1 id="字符编码相关"><a href="#字符编码相关" class="headerlink" title="字符编码相关"></a>字符编码相关</h1><h2 id="charAt"><a href="#charAt" class="headerlink" title="charAt"></a>charAt</h2><p>charAt() 方法从一个字符串中返回指定的字符</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.charAt(index)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>index =&gt; 一个介于0 和字符串长度减1之间的整数。 (0~length-1) 如果没有提供索引，charAt() 将使用 0。如果指定的 index 值超出了该范围，则返回一个空字符串。</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 class="doctag">@param <span class="type">&#123;String&#125;</span> </span>当前索引的字符串</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"gating"</span></span><br><span class="line"><span class="built_in">console</span>.log(str.charAt(<span class="number">0</span>)) <span class="comment">// g</span></span><br><span class="line"><span class="built_in">console</span>.log(str.charAt(<span class="number">6</span>)) <span class="comment">// ""</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// charAt 和 fromCharCode 用同样的问题，他不能识别高位编码</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"𠮷"</span>.charAt(<span class="number">0</span>)) <span class="comment">// �</span></span><br></pre></td></tr></table></figure></div><h2 id="charCodeAt"><a href="#charCodeAt" class="headerlink" title="charCodeAt"></a>charCodeAt</h2><p>charCodeAt() 方法返回0到65535之间的整数，表示给定索引处的UTF-16代码单元 (在 Unicode 编码单元表示一个单一的 UTF-16 编码单元的情况下，UTF-16 编码单元匹配 Unicode 编码单元。但在——例如 Unicode 编码单元 &gt; 0x10000 的这种——不能被一个 UTF-16 编码单元单独表示的情况下，只能匹配 Unicode 代理对的第一个编码单元) 。如果你想要整个代码点的值，使用 codePointAt()。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.charCodeAt(index)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>index =&gt; 一个大于等于 0，小于字符串长度的整数。如果不是一个数值，则默认为 0。</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 class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>返回值是一表示给定索引处（String中index索引处）字符的 UTF-16 代码单元值的数字；如果索引超出范围，则返回 NaN。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"ABC"</span>.charCodeAt(<span class="number">0</span>)) <span class="comment">// 65</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"ABC"</span>.charCodeAt(<span class="number">1</span>)) <span class="comment">// 66</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"ABC"</span>.charCodeAt(<span class="number">3</span>)) <span class="comment">// NaN</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 针对高位编码，他会拆分成两个双字节的形式再来获取</span></span><br><span class="line"><span class="comment">// 也就是 "\uD842\uDFB7".charCodeAt(0)</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"𠮷"</span>.charCodeAt(<span class="number">0</span>)) <span class="comment">// 55362</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"𠮷"</span>.charCodeAt(<span class="number">1</span>)) <span class="comment">// 57271</span></span><br></pre></td></tr></table></figure></div><h2 id="codePointAt"><a href="#codePointAt" class="headerlink" title="codePointAt"></a>codePointAt</h2><p>codePointAt() 方法返回 一个 Unicode 编码点值的非负整数。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.codePointAt(pos)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>pos =&gt; 这个字符串中需要转码的元素的位置。</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 class="doctag">@param <span class="type">&#123;String&#125;</span> </span>返回值是在字符串中的给定索引的编码单元体现的数字(10进制数)，如果在索引处没找到元素则返回 undefined 。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"ABC"</span>.charCodeAt(<span class="number">0</span>)) <span class="comment">// 65</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"ABC"</span>.charCodeAt(<span class="number">3</span>)) <span class="comment">// undefined</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"𠮷"</span>.codePointAt(<span class="number">0</span>)) <span class="comment">// 134071</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"𠮷"</span>.codePointAt(<span class="number">1</span>)) <span class="comment">// 57271</span></span><br></pre></td></tr></table></figure></div><p>汉字“𠮷”（注意，这个字不是“吉祥”的“吉”）的码点是0x20BB7，UTF-16 编码为0xD842 0xDFB7（十进制为55362 57271），需要4个字节储存。对于这种4个字节的字符，JavaScript 不能正确处理，字符串长度会误判为2，而且charAt方法无法读取整个字符，charCodeAt方法只能分别返回前两个字节和后两个字节的值。</p><blockquote><p>codePointAt 方法在第一个字符上，正确地识别了“𠮷”，返回了它的十进制码点 134071（即十六进制的20BB7），在第二个字符（即“𠮷”的后两个字节）codePointAt方法的结果与charCodeAt方法相同。</p></blockquote><blockquote><p>codePointAt方法返回的是码点的十进制值，如果想要十六进制的值，可以使用toString(16)方法转换一下。</p></blockquote><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> s = <span class="string">'𠮷a'</span></span><br><span class="line">s.codePointAt(<span class="number">0</span>) <span class="comment">// 134071</span></span><br><span class="line">s.codePointAt(<span class="number">1</span>) <span class="comment">// 57271</span></span><br><span class="line">s.codePointAt(<span class="number">2</span>) <span class="comment">// 97</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 你可能注意到了，codePointAt方法的参数，仍然是不正确的。</span></span><br><span class="line"><span class="comment">// 比如，上面代码中，字符a在字符串s的正确位置序号应该是 1，但是必须向codePointAt方法传入 2。</span></span><br><span class="line"><span class="comment">// 解决这个问题的一个办法是使用for...of循环，因为它会正确识别 32 位的 UTF-16 字符。</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> ch <span class="keyword">of</span> s) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(ch.codePointAt(<span class="number">0</span>).toString(<span class="number">16</span>)) <span class="comment">// 20bb7 61</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"\u&#123;20bb7&#125;"</span>) <span class="comment">// 𠮷</span></span><br></pre></td></tr></table></figure></div><h2 id="normalize"><a href="#normalize" class="headerlink" title="normalize"></a>normalize</h2><p>normalize() 方法会按照指定的一种 Unicode 正规形式将当前字符串正规化.</p><p>许多欧洲语言有语调符号和重音符号。为了表示它们，Unicode 提供了两种方法。一种是直接提供带重音符号的字符，比如Ǒ（\u01D1）。另一种是提供合成符号（combining character），即原字符与重音符号的合成，两个字符合成一个字符，比如O（\u004F）和ˇ（\u030C）合成Ǒ（\u004F\u030C）。</p><p>这两种表示方法，在视觉和语义上都等价，但是 JavaScript 不能识别。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.normalize(form)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String&#125;</span> </span>form =&gt; 四种 Unicode 正规形式 "NFC", "NFD", "NFKC", 以及 "NFKD" 其中的一个, 默认值为 "NFC".</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 class="doctag">@param <span class="type">&#123;String&#125;</span> </span>正规化后的字符串。如果给 form 传入了非法的参数值, 则会抛出 RangeError 异常.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'\u01D1'</span>===<span class="string">'\u004F\u030C'</span>) <span class="comment">// false</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'\u01D1'</span>.normalize() === <span class="string">'\u004F\u030C'</span>.normalize()) <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// NFC参数返回字符的合成形式，NFD参数返回字符的分解形式。</span></span><br><span class="line"><span class="string">'\u004F\u030C'</span>.normalize(<span class="string">'NFC'</span>).length <span class="comment">// 1</span></span><br><span class="line"><span class="string">'\u004F\u030C'</span>.normalize(<span class="string">'NFD'</span>).length <span class="comment">// 2</span></span><br></pre></td></tr></table></figure></div><ol><li><p><code>NFC</code>，默认参数，表示“标准等价合成”（Normalization Form Canonical Composition），返回多个简单字符的合成字符。所谓“标准等价”指的是视觉和语义上的等价。</p></li><li><p><code>NFD</code>，表示“标准等价分解”（Normalization Form Canonical Decomposition），即在标准等价的前提下，返回合成字符分解的多个简单字符。</p></li><li><p><code>NFKC</code>，表示“兼容等价合成”（Normalization Form Compatibility Composition），返回合成字符。所谓“兼容等价”指的是语义上存在等价，但视觉上不等价，比如“囍”和“喜喜”。（这只是用来举例，normalize方法不能识别中文。）</p></li><li><p><code>NFKD</code>，表示“兼容等价分解”（Normalization Form Compatibility Decomposition），即在兼容等价的前提下，返回合成字符分解的多个简单字符。</p></li></ol><blockquote><p>不过，normalize方法目前不能识别三个或三个以上字符的合成。这种情况下，还是只能使用正则表达式，通过 Unicode 编号区间判断。</p></blockquote><blockquote><p>此内容参考了 <a href="http://es6.ruanyifeng.com/#docs/string#normalize" target="_blank" rel="noopener">阮老师的normalize()</a></p></blockquote><h1 id="合并填充相关"><a href="#合并填充相关" class="headerlink" title="合并填充相关"></a>合并填充相关</h1><h2 id="concat"><a href="#concat" class="headerlink" title="concat"></a>concat</h2><p>concat() 方法将一个或多个字符串与原字符串连接合并，形成一个新的字符串并返回。</p><blockquote><p>强烈建议使用 赋值操作符（+, +=）代替 concat 方法。因为 concat 性能比较垃圾</p></blockquote><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.concat(stringN)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String&#125;</span> </span>stringN =&gt; 和原字符串连接的多个字符串</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 class="doctag">@param <span class="type">&#123;String&#125;</span> </span>连接后的字符串</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"hello,"</span></span><br><span class="line"><span class="built_in">console</span>.log(str.concat(<span class="string">"gating"</span>)) <span class="comment">// hello,gating</span></span><br></pre></td></tr></table></figure></div><h2 id="padEnd"><a href="#padEnd" class="headerlink" title="padEnd"></a>padEnd</h2><p>padEnd() 方法会用一个字符串填充当前字符串（如果需要的话则重复填充），返回填充后达到指定长度的字符串。从当前字符串的末尾（右侧）开始填充。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.padEnd(targetLength,padString)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>targetLength =&gt; 当前字符串需要填充到的目标长度。如果这个数值小于当前字符串的长度，则返回当前字符串本身。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String&#125;</span> </span>padString  =&gt; 填充字符串。如果字符串太长，使填充后的字符串长度超过了目标长度，则只保留最左侧的部分，其他部分会被截断。此参数的缺省值为 " "（U+0020）。</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 class="doctag">@param <span class="type">&#123;String&#125;</span> </span>在原字符串末尾填充指定的填充字符串直到目标长度所形成的新字符串。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"abc"</span></span><br><span class="line"><span class="built_in">console</span>.log(str.padEnd(<span class="number">1</span>)) <span class="comment">// abc</span></span><br><span class="line"><span class="built_in">console</span>.log(str.padEnd(<span class="number">1</span>,<span class="string">"123"</span>)) <span class="comment">// abc</span></span><br><span class="line"><span class="built_in">console</span>.log(str.padEnd(<span class="number">4</span>,<span class="string">"123"</span>)) <span class="comment">// abc1</span></span><br><span class="line"><span class="built_in">console</span>.log(str.padEnd(<span class="number">7</span>,<span class="string">"123"</span>)) <span class="comment">// abc1231</span></span><br></pre></td></tr></table></figure></div><h2 id="padStart"><a href="#padStart" class="headerlink" title="padStart"></a>padStart</h2><p>padStart() 方法用另一个字符串填充当前字符串(重复，如果需要的话)，以便产生的字符串达到给定的长度。填充从当前字符串的开始(左侧)应用的。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.padStart(targetLength,padString)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>targetLength =&gt; 当前字符串需要填充到的目标长度。如果这个数值小于当前字符串的长度，则返回当前字符串本身。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String&#125;</span> </span>padString  =&gt; 填充字符串。如果字符串太长，使填充后的字符串长度超过了目标长度，则只保留最左侧的部分，其他部分会被截断。此参数的缺省值为 " "（U+0020）。</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 class="doctag">@param <span class="type">&#123;String&#125;</span> </span>在原字符串开头填充指定的填充字符串直到目标长度所形成的新字符串。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"abc"</span></span><br><span class="line"><span class="built_in">console</span>.log(str.padStart(<span class="number">1</span>)) <span class="comment">// abc</span></span><br><span class="line"><span class="built_in">console</span>.log(str.padStart(<span class="number">3</span>,<span class="string">'123'</span>)) <span class="comment">// abc</span></span><br><span class="line"><span class="built_in">console</span>.log(str.padStart(<span class="number">6</span>,<span class="string">'123'</span>)) <span class="comment">// 123abc</span></span><br><span class="line"><span class="built_in">console</span>.log(str.padStart(<span class="number">7</span>,<span class="string">'123'</span>)) <span class="comment">// 1231abc</span></span><br></pre></td></tr></table></figure></div><p>padStart()的常见用途是为数值补全指定位数和提示字符串格式。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 生成 10 位的数值字符串</span></span><br><span class="line"><span class="string">'1'</span>.padStart(<span class="number">10</span>, <span class="string">'0'</span>) <span class="comment">// "0000000001"</span></span><br><span class="line"><span class="string">'12'</span>.padStart(<span class="number">10</span>, <span class="string">'0'</span>) <span class="comment">// "0000000012"</span></span><br><span class="line"><span class="string">'123456'</span>.padStart(<span class="number">10</span>, <span class="string">'0'</span>) <span class="comment">// "0000123456"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 提示字符串格式</span></span><br><span class="line"><span class="string">'12'</span>.padStart(<span class="number">10</span>, <span class="string">'YYYY-MM-DD'</span>) <span class="comment">// "YYYY-MM-12"</span></span><br><span class="line"><span class="string">'09-12'</span>.padStart(<span class="number">10</span>, <span class="string">'YYYY-MM-DD'</span>) <span class="comment">// "YYYY-09-12"</span></span><br></pre></td></tr></table></figure></div><h2 id="repeat"><a href="#repeat" class="headerlink" title="repeat"></a>repeat</h2><p>repeat() 构造并返回一个新字符串，该字符串包含被连接在一起的指定数量的字符串的副本。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.repeat(count)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>count =&gt; 介于0和正无穷大之间的整数 : [0, +∞) 。表示在新构造的字符串中重复了多少遍原字符串。重复次数不能为负数。重复次数必须小于 infinity，且长度不会大于最长的字符串。</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 class="doctag">@param <span class="type">&#123;String&#125;</span> </span>包含指定字符串的指定数量副本的新字符串。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"gating"</span></span><br><span class="line"><span class="built_in">console</span>.log(str.repeat(<span class="number">2</span>)) <span class="comment">// gatinggating</span></span><br><span class="line"><span class="comment">// 参数count将会被自动转换成整数(向下取整)</span></span><br><span class="line"><span class="built_in">console</span>.log(str.repeat(<span class="number">3.5</span>)) <span class="comment">// gatinggatinggating</span></span><br><span class="line"><span class="built_in">console</span>.log(str.repeat(<span class="number">3.4</span>)) <span class="comment">// gatinggatinggating</span></span><br><span class="line"><span class="comment">// 但是，如果参数是 0 到-1 之间的小数，则等同于 0，这是因为会先进行取整运算。</span></span><br><span class="line"><span class="built_in">console</span>.log(str.repeat(<span class="number">-0.5</span>)) <span class="comment">// ""</span></span><br><span class="line"><span class="built_in">console</span>.log(str.repeat(<span class="number">0</span>)) <span class="comment">// ""</span></span><br><span class="line"><span class="comment">// 参数NaN等同于 0。</span></span><br><span class="line"><span class="built_in">console</span>.log(str.repeat(<span class="literal">NaN</span>)) <span class="comment">// ""</span></span><br><span class="line"><span class="comment">// 如果repeat的参数是字符串，则会先转换成数字</span></span><br><span class="line"><span class="built_in">console</span>.log(str.repeat(<span class="string">"aaa"</span>)) <span class="comment">// ""</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// RangeError: repeat count must be positive and less than inifinity</span></span><br><span class="line"><span class="built_in">console</span>.log(str.repeat(<span class="number">-1</span>)) </span><br><span class="line"><span class="built_in">console</span>.log(str.repeat(<span class="number">1</span>/<span class="number">0</span>))</span><br></pre></td></tr></table></figure></div><p>repeat 其实很适合我们调试的时候生成多份测试数据，比如</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> newData = [<span class="string">"1"</span>,<span class="string">"2"</span>,<span class="string">"3"</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment">// 假设我们想生成 ["1","1","2","2","3","3"] 的双份数据，那么我们就可以通过 repeat 来实现</span></span><br><span class="line"><span class="keyword">let</span> newData = myDate.map(<span class="function"><span class="params">item</span>=&gt;</span>item.repeat(<span class="number">2</span>).split(<span class="string">''</span>)).reduce(<span class="function">(<span class="params">arr,val</span>)=&gt;</span>arr.concat(val))</span><br><span class="line"><span class="built_in">console</span>.log(newData)) <span class="comment">// ["1","1","2","2","3","3"]</span></span><br></pre></td></tr></table></figure></div><h1 id="搜索查找相关"><a href="#搜索查找相关" class="headerlink" title="搜索查找相关"></a>搜索查找相关</h1><h2 id="indexOf"><a href="#indexOf" class="headerlink" title="indexOf"></a>indexOf</h2><p>indexOf() 方法返回调用 String 对象中第一次出现的指定值的索引，开始在 fromIndex进行搜索。如果未找到该值，则返回-1。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.indexOf(searchValue,fromIndex)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String&#125;</span> </span>searchValue =&gt; 一个字符串表示被查找的值。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String/Number&#125;</span> </span>fromIndex =&gt; 表示调用该方法的字符串中开始查找的位置。可以是任意整数。默认值为 0。如果 fromIndex &lt; 0 则查找整个字符串（如同传进了 0）。如果 fromIndex &gt;= str.length，则该方法返回 -1。当被查找的字符串是一个空字符串，fromIndex &lt;= 0时返回0，0 &lt; fromIndex &lt;= str.length时返回fromIndex，fromIndex &gt; str.length时返回str.length。</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 class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>指定值的第一次出现的索引; 如果没有找到 -1。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"gating"</span></span><br><span class="line"><span class="built_in">console</span>.log(str.indexOf(<span class="string">"g"</span>)) <span class="comment">// 0</span></span><br><span class="line"><span class="comment">// 这里的 -1 也就 fromIndex &lt; 0 时，str.indexOf("a",-1) 等同于 str.indexOf("a",0)</span></span><br><span class="line"><span class="built_in">console</span>.log(str.indexOf(<span class="string">"a"</span>,<span class="number">-1</span>)) <span class="comment">// 1</span></span><br><span class="line"><span class="comment">// 这里的 8 也就 fromIndex &gt; str.length 时，str.indexOf("a",8) 等同于 str.indexOf("a",6)</span></span><br><span class="line"><span class="built_in">console</span>.log(str.indexOf(<span class="string">"g"</span>,<span class="number">8</span>)) <span class="comment">// -1</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 区分大小写</span></span><br><span class="line"><span class="built_in">console</span>.log(str.indexOf(<span class="string">"G"</span>)) <span class="comment">// -1</span></span><br></pre></td></tr></table></figure></div><p>实际应用场景中，我们可以使用使用 indexOf 统计一个字符串中某个字符出现的次数</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> str = <span class="string">"gating"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">countStr</span>(<span class="params">str,value</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">let</span> count = <span class="number">0</span></span><br><span class="line">    <span class="keyword">let</span> pos = str.indexOf(<span class="string">'g'</span>);</span><br><span class="line">    <span class="keyword">while</span> (pos !== <span class="number">-1</span>) &#123;</span><br><span class="line">        count++;</span><br><span class="line">        pos = str.indexOf(<span class="string">'g'</span>, pos + <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> count</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(countStr(str,<span class="string">'g'</span>)) <span class="comment">// 2</span></span><br></pre></td></tr></table></figure></div><h2 id="lastIndexOf"><a href="#lastIndexOf" class="headerlink" title="lastIndexOf"></a>lastIndexOf</h2><p>lastIndexOf() 方法返回指定值在调用该方法的字符串中最后出现的位置，如果没找到则返回 -1。从该字符串的后面向前查找，从 fromIndex 处开始。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.indexOf(searchValue,fromIndex)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String&#125;</span> </span>searchValue =&gt; 一个字符串表示被查找的值。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String/Number&#125;</span> </span>fromIndex =&gt; 从调用该方法字符串的此位置处开始查找。可以是任意整数。默认值为 str.length。如果为负值，则被看作 0。如果 fromIndex &gt; str.length，则 fromIndex 被看作 str.length。</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 class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>指定值的第一次出现的索引（从后往前数）; 如果没有找到 -1。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"gating"</span></span><br><span class="line"><span class="built_in">console</span>.log(str.lastIndexOf(<span class="string">'g'</span>)) <span class="comment">// 5</span></span><br><span class="line"><span class="built_in">console</span>.log(str.lastIndexOf(<span class="string">'g'</span>,<span class="number">2</span>)) <span class="comment">// 0 </span></span><br><span class="line"><span class="comment">// 这里的 8 也就 fromIndex &gt; str.length 时，str.lastIndexOf("a",8) 等同于 str.lastIndexOf("a",6) 也就是 str.lastIndexOf("a")</span></span><br><span class="line"><span class="built_in">console</span>.log(str.lastIndexOf(<span class="string">'g'</span>,<span class="number">8</span>)) <span class="comment">// 5 </span></span><br><span class="line"><span class="built_in">console</span>.log(str.lastIndexOf(<span class="string">'g'</span>,<span class="number">-1</span>)) <span class="comment">// -1</span></span><br><span class="line"><span class="built_in">console</span>.log(str.lastIndexOf(<span class="string">'g'</span>,<span class="number">0</span>)) <span class="comment">// 0</span></span><br></pre></td></tr></table></figure></div><h2 id="search"><a href="#search" class="headerlink" title="search"></a>search</h2><p>search() 方法执行正则表达式和 String对象之间的一个搜索匹配。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.search(regexp)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Regexp&#125;</span> </span>regexp =&gt; 一个正则表达式（regular expression）对象。如果传入一个非正则表达式对象，则会使用 new RegExp(obj) 隐式地将其转换为正则表达式对象。</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 class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>如果匹配成功，则 search() 返回正则表达式在字符串中首次匹配项的索引。否则，返回 -1。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"gating"</span></span><br><span class="line"><span class="built_in">console</span>.log(str.search(<span class="string">"g"</span>)) <span class="comment">// 0</span></span><br><span class="line"><span class="built_in">console</span>.log(str.search(<span class="regexp">/g/</span>)) <span class="comment">// 0</span></span><br></pre></td></tr></table></figure></div><h2 id="includes"><a href="#includes" class="headerlink" title="includes"></a>includes</h2><p>includes() 方法用于判断一个字符串是否包含在另一个字符串中，根据情况返回 true 或 false。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.includes(searchString,position)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String&#125;</span> </span>searchString =&gt; 要在此字符串中搜索的字符串。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>position =&gt; 可选。从当前字符串的哪个索引位置开始搜寻子字符串，默认值为0。</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 class="doctag">@param <span class="type">&#123;Boolean&#125;</span> </span>如果当前字符串包含被搜寻的字符串，就返回 true；否则返回 false。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"gating"</span></span><br><span class="line"><span class="built_in">console</span>.log(str.includes(<span class="string">"a"</span>)) <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log(str.includes(<span class="string">"a"</span>,<span class="number">1</span>)) <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log(str.includes(<span class="string">"a"</span>,<span class="number">2</span>)) <span class="comment">// false</span></span><br></pre></td></tr></table></figure></div><blockquote><p>在 Firefox 18 - 39中，这个方法的名称叫 contains()。 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/includes#String.prototype.contains()" target="_blank" rel="noopener">具体原因请查看</a></p></blockquote><h2 id="startsWith"><a href="#startsWith" class="headerlink" title="startsWith"></a>startsWith</h2><p>startsWith()方法用来判断当前字符串是否是以另外一个给定的子字符串“开头”的，根据判断结果返回 true 或 false。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.startsWith(searchString,position)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String&#125;</span> </span>searchString =&gt;要搜索的子字符串。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>position =&gt; 在 str 中搜索 searchString 的开始位置，默认值为 0，也就是真正的字符串开头处。</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 class="doctag">@param <span class="type">&#123;Boolean&#125;</span> </span>如果当前字符串以搜素字符串开头，就返回 true；否则返回 false。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"gating"</span></span><br><span class="line"><span class="built_in">console</span>.log(str.startsWith(<span class="string">'g'</span>)) <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log(str.startsWith(<span class="string">'g'</span>,<span class="number">1</span>)) <span class="comment">// false</span></span><br><span class="line"><span class="built_in">console</span>.log(str.startsWith(<span class="string">'g'</span>,<span class="number">7</span>)) <span class="comment">// false</span></span><br></pre></td></tr></table></figure></div><h2 id="endsWith"><a href="#endsWith" class="headerlink" title="endsWith"></a>endsWith</h2><p>endsWith()方法用来判断当前字符串是否是以另外一个给定的子字符串“结尾”的，根据判断结果返回 true 或 false。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.endsWith(searchString,position)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String&#125;</span> </span>searchString =&gt; 要搜索的子字符串。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>position =&gt; 可选。作为str的长度，默认值为 str.length。</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 class="doctag">@param <span class="type">&#123;Boolean&#125;</span>  </span>如果传入的子字符串在搜索字符串的末尾就返回true；否则将返回 false</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"gating"</span></span><br><span class="line"><span class="built_in">console</span>.log(str.endsWith(<span class="string">'g'</span>)) <span class="comment">// true</span></span><br><span class="line"><span class="comment">// 这里 position 为 2 表示 str 的值就是 ga，也就是相当于 ga.endsWith('g')</span></span><br><span class="line"><span class="built_in">console</span>.log(str.endsWith(<span class="string">'g'</span>,<span class="number">2</span>)) <span class="comment">// false</span></span><br><span class="line"><span class="built_in">console</span>.log(str.endsWith(<span class="string">'g'</span>,<span class="number">7</span>)) <span class="comment">// true</span></span><br></pre></td></tr></table></figure></div><h1 id="截取相关"><a href="#截取相关" class="headerlink" title="截取相关"></a>截取相关</h1><h2 id="slice"><a href="#slice" class="headerlink" title="slice"></a>slice</h2><p>slice() 方法提取一个字符串的一部分，并返回一新的字符串。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.slice(beginSlice,endSlice)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>beginSlice =&gt; 从该索引（以 0 为基数）处开始提取原字符串中的字符。如果值为负数，会被当做 sourceLength + beginSlice 看待，这里的sourceLength 是字符串的长度 (例如， 如果beginSlice 是 -3 则看作是: sourceLength - 3)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>endSlice =&gt; 可选。可选。在该索引（以 0 为基数）处结束提取字符串。如果省略该参数，slice会一直提取到字符串末尾。如果该参数为负数，则被看作是 sourceLength + endSlice，这里的 sourceLength 就是字符串的长度(例如，如果 endSlice 是 -3，则是, sourceLength - 3)。</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 class="doctag">@param <span class="type">&#123;Number&#125;</span>  </span>返回一个从原字符串中提取出来的新字符串</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"gating"</span></span><br><span class="line"><span class="built_in">console</span>.log(str.slice(<span class="number">1</span>)) <span class="comment">// ating</span></span><br><span class="line"><span class="comment">// 这里的-1实际上就是 (6-1) 也就是 5 即 str.slice(5)</span></span><br><span class="line"><span class="built_in">console</span>.log(str.slice(<span class="number">-1</span>)) <span class="comment">// g</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 这里的-1实际上就是 (6-1) 也就是 5 即 str.slice(0,5)</span></span><br><span class="line"><span class="built_in">console</span>.log(str.slice(<span class="number">0</span>,<span class="number">-1</span>)) <span class="comment">// gatin</span></span><br><span class="line"><span class="built_in">console</span>.log(str.slice(<span class="number">-2</span>,<span class="number">-1</span>)) <span class="comment">// n</span></span><br><span class="line"><span class="built_in">console</span>.log(str.slice(<span class="number">-1</span>,<span class="number">-2</span>)) <span class="comment">// ""</span></span><br></pre></td></tr></table></figure></div><blockquote><p>注意：slice() 提取的新字符串包括beginSlice但不包括 endSlice。</p></blockquote><h2 id="substr"><a href="#substr" class="headerlink" title="substr"></a>substr</h2><p>substr() 方法返回一个字符串中从指定位置开始到指定字符数的字符。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.substr(start,length)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>searchString =&gt; 开始提取字符的位置。如果为负值，则被看作 strLength + start，其中 strLength 为字符串的长度（例如，如果 start 为 -3，则被看作 strLength + (-3)）</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>position =&gt; 可选。提取的字符数。默认是strLength</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 class="doctag">@param <span class="type">&#123;String&#125;</span>  </span>返回一个从原字符串中提取出来的新字符串</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"gating"</span></span><br><span class="line"><span class="built_in">console</span>.log(str.substr(<span class="number">1</span>)) <span class="comment">// ating</span></span><br><span class="line"><span class="comment">// 这里的-1实际上就是 (6-1) 也就是 5 即 str.slice(5)</span></span><br><span class="line"><span class="built_in">console</span>.log(str.substr(<span class="number">-1</span>)) <span class="comment">// g</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 因为长度不能小于0,所以返回""</span></span><br><span class="line"><span class="built_in">console</span>.log(str.substr(<span class="number">-1</span>,<span class="number">-2</span>)) <span class="comment">// ""</span></span><br></pre></td></tr></table></figure></div><blockquote><p>注意:在IE8下,substr()方法传递负值会返回原始的字符串,IE9修复了此BUG</p></blockquote><h2 id="substring"><a href="#substring" class="headerlink" title="substring"></a>substring</h2><p>substring() 方法返回一个字符串在开始索引到结束索引之间的一个子集, 或从开始索引直到字符串的末尾的一个子集。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.substring(indexStart,indexEnd)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>indexStart =&gt; 需要截取的第一个字符的索引，该字符作为返回的字符串的首字母。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>indexEnd =&gt; 可选。一个 0 到字符串长度之间的整数，以该数字为索引的字符不包含在截取的字符串内。</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 class="doctag">@param <span class="type">&#123;String&#125;</span>  </span>包含给定字符串的指定部分的新字符串。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"gating"</span></span><br><span class="line"><span class="built_in">console</span>.log(str.substring(<span class="number">0</span>,<span class="number">3</span>)) <span class="comment">// gat</span></span><br><span class="line"><span class="comment">// 如果 indexStart 大于 indexEnd，则会变成 str.substring(0,3)</span></span><br><span class="line"><span class="built_in">console</span>.log(str.substring(<span class="number">3</span>,<span class="number">0</span>)) <span class="comment">// gat</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 如果任一参数小于 0 或为 NaN，则被当作 0。</span></span><br><span class="line"><span class="built_in">console</span>.log(str.substring(<span class="number">-2</span>,<span class="number">3</span>)) <span class="comment">// gat</span></span><br><span class="line"><span class="built_in">console</span>.log(str.substring(<span class="literal">NaN</span>,<span class="number">3</span>)) <span class="comment">// gat</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(str.substring(<span class="number">4</span>,<span class="number">4</span>)) <span class="comment">// ""</span></span><br><span class="line"><span class="built_in">console</span>.log(str.substring(<span class="number">0</span>)) <span class="comment">// gating</span></span><br><span class="line"><span class="built_in">console</span>.log(str.substring(<span class="number">0</span>,<span class="number">10</span>)) <span class="comment">// gating</span></span><br></pre></td></tr></table></figure></div><blockquote><p>substring 提取从 indexStart 到 indexEnd（不包括）</p></blockquote><ul><li>如果 indexStart 等于 indexEnd，substring 返回一个空字符串。</li><li>如果省略 indexEnd，substring 提取字符一直到字符串末尾。</li><li>如果任一参数小于 0 或为 NaN，则被当作 0。</li><li>如果任一参数大于 stringName.length，则被当作 stringName.length。</li><li>如果 indexStart 大于 indexEnd，则 substring 的执行效果就像两个参数调换了一样。</li></ul><h1 id="转换大小写相关"><a href="#转换大小写相关" class="headerlink" title="转换大小写相关"></a>转换大小写相关</h1><blockquote><p>注意: toLocaleLowerCase、toLocaleUpperCase 按照本地方式把字符串转换为小写。只有几种语言（如土耳其语）具有地方特有的大小写映射。</p></blockquote><h2 id="toLowerCase"><a href="#toLowerCase" class="headerlink" title="toLowerCase"></a>toLowerCase</h2><p>toLowerCase() 会将调用该方法的字符串值转为小写形式，并返回。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.toLowerCase()</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 class="doctag">@param <span class="type">&#123;String&#125;</span>  </span>一个新的字符串，表示串转换为小写的调用字符。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"GATING"</span></span><br><span class="line"><span class="built_in">console</span>.log(str.toLowerCase()) <span class="comment">// gating</span></span><br></pre></td></tr></table></figure></div><h2 id="toLocaleLowerCase"><a href="#toLocaleLowerCase" class="headerlink" title="toLocaleLowerCase"></a>toLocaleLowerCase</h2><p>toLocaleLowerCase() 根据任何特定于语言环境的案例映射，将表示调用字符串的新字符串转换为小写。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.toLocaleLowerCase()</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 class="doctag">@param <span class="type">&#123;String&#125;</span>  </span>根据任何特定于语言环境的案例映射，将表示调用字符串的新字符串转换为小写。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"GATING"</span></span><br><span class="line"><span class="built_in">console</span>.log(str.toLocaleLowerCase()) <span class="comment">// gating</span></span><br></pre></td></tr></table></figure></div><h2 id="toUpperCase"><a href="#toUpperCase" class="headerlink" title="toUpperCase"></a>toUpperCase</h2><p>toUpperCase() 将调用该方法的字符串值转换为大写形式，并返回。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.toUpperCase()</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 class="doctag">@param <span class="type">&#123;String&#125;</span>  </span>将调用该方法的字符串值转换为大写形式</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"gating"</span></span><br><span class="line"><span class="built_in">console</span>.log(str.toUpperCase()) <span class="comment">// GATING</span></span><br></pre></td></tr></table></figure></div><h2 id="toLocaleUpperCase"><a href="#toLocaleUpperCase" class="headerlink" title="toLocaleUpperCase"></a>toLocaleUpperCase</h2><p>toLocaleUpperCase() 将调用该方法的字符串值转换为大写形式，并返回。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.toLocaleUpperCase()</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 class="doctag">@param <span class="type">&#123;String&#125;</span>  </span>一个新的字符串，即根据本地化的大小写映射规则将输入的字符串转化成大写形式的结果。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"gating"</span></span><br><span class="line"><span class="built_in">console</span>.log(str.toLocaleUpperCase()) <span class="comment">// GATING</span></span><br></pre></td></tr></table></figure></div><h1 id="删除空白字符相关"><a href="#删除空白字符相关" class="headerlink" title="删除空白字符相关"></a>删除空白字符相关</h1><h2 id="trim"><a href="#trim" class="headerlink" title="trim"></a>trim</h2><p>trim() 方法会从一个字符串的两端删除空白字符。在这个上下文中的空白字符是所有的空白字符 (space, tab, no-break space 等) 以及所有行终止符字符（如 LF，CR）</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.trim()</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 class="doctag">@param <span class="type">&#123;String&#125;</span>  </span>返回方法移除原字符串两端端的连续空白符</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"   gating   "</span></span><br><span class="line"><span class="built_in">console</span>.log(str.trim()) <span class="comment">// "gating"</span></span><br></pre></td></tr></table></figure></div><h2 id="trimRight"><a href="#trimRight" class="headerlink" title="trimRight"></a>trimRight</h2><p>trimRight() 方法从一个字符串的右端移除空白字符。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.trimRight()</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 class="doctag">@param <span class="type">&#123;String&#125;</span>  </span>返回方法移除原字符串右端的连续空白符</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"gating   "</span></span><br><span class="line"><span class="built_in">console</span>.log(str.trimRight()) <span class="comment">// "gating"</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> trimRight = <span class="function">(<span class="params">str</span>)=&gt;</span> str.replace(<span class="regexp">/(\s*$)/g</span>, <span class="string">""</span>)</span><br></pre></td></tr></table></figure></div><blockquote><p>该特性是非标准的，请尽量不要在生产环境中使用它！</p></blockquote><h2 id="trimLeft"><a href="#trimLeft" class="headerlink" title="trimLeft"></a>trimLeft</h2><p>trimLeft() 方法从一个字符串的左端移除空白字符。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.trimLeft()</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 class="doctag">@param <span class="type">&#123;String&#125;</span>  </span>返回方法移除原字符串左端的连续空白符</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"  gating"</span></span><br><span class="line"><span class="built_in">console</span>.log(str.trimLeft()) <span class="comment">// "gating"</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> trimLeft = <span class="function">(<span class="params">str</span>)=&gt;</span> str.replace(<span class="regexp">/(^\s*)/g</span>, <span class="string">""</span>)</span><br></pre></td></tr></table></figure></div><blockquote><p>该特性是非标准的，请尽量不要在生产环境中使用它！</p></blockquote><h1 id="两个不知名的方法"><a href="#两个不知名的方法" class="headerlink" title="两个不知名的方法"></a>两个不知名的方法</h1><h2 id="toString-NaN"><a href="#toString-NaN" class="headerlink" title="toString"></a>toString</h2><p>toString() 方法返回指定对象的字符串形式。</p><p>String 对象覆盖了Object 对象的 toString 方法；并没有继承 Object.toString()。对于 String 对象，toString 方法返回该对象的字符串形式，和 String.prototype.valueOf() 方法返回值一样</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.toString()</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 class="doctag">@param <span class="type">&#123;String&#125;</span>  </span>返回指定对象的字符串形式</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> str = <span class="keyword">new</span> <span class="built_in">String</span>(<span class="string">"gating"</span>);</span><br><span class="line"><span class="built_in">console</span>.log(str.toString()) <span class="comment">// gating</span></span><br></pre></td></tr></table></figure></div><h2 id="valueOf-NaN"><a href="#valueOf-NaN" class="headerlink" title="valueOf"></a>valueOf</h2><p>valueOf() 方法返回一个String对象的原始值（primitive value）。</p><p>String 对象的 valueOf 方法返回一个String对象的原始值。该值等同于String.prototype.toString()。</p><p>该方法通常在 JavaScript 内部被调用，而不是在代码里显示调用。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.valueOf()</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 class="doctag">@param <span class="type">&#123;String&#125;</span>  </span>返回指定对象的字符串形式</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> str = <span class="keyword">new</span> <span class="built_in">String</span>(<span class="string">"gating"</span>);</span><br><span class="line"><span class="built_in">console</span>.log(str.valueOf()) <span class="comment">// gating</span></span><br></pre></td></tr></table></figure></div><h1 id="排序相关"><a href="#排序相关" class="headerlink" title="排序相关"></a>排序相关</h1><h2 id="localeCompare"><a href="#localeCompare" class="headerlink" title="localeCompare"></a>localeCompare</h2><p>localeCompare() 方法返回一个数字来指示一个参考字符串是否在排序顺序前面或之后或与给定字符串相同。</p><p>新的 locales 、 options 参数能让应用程序定制函数的行为即指定用来排序的语言。 locales 和 options 参数是依赖于具体实现的，在旧的实现中这两个参数是完全被忽略的。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * str.localeCompare(compareString,locales,options)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String&#125;</span> </span>compareString =&gt; 用来比较的字符串</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;locales&#125;</span> </span>locales =&gt; 可选。用来表示一种或多种语言或区域的一个符合 BCP 47 标准的字符串或一个字符串数组。locales参数的一般形式与解释， 详情请参考 MDN</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;options&#125;</span> </span>options =&gt; 可选。 支持下列的一些或全部属性的一个对象: 详情请参考 MDN</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 class="doctag">@param <span class="type">&#123;String&#125;</span>  </span>如果引用字符存在于比较字符之前则为负数; 如果引用字符存在于比较字符之后则为正数; 相等的时候返回 0 .不同浏览器之间（以及不同浏览器版本之间）返回的正负数的值各有不同</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"b"</span></span><br><span class="line"><span class="built_in">console</span>.log(str.localeCompare(<span class="string">"b"</span>)) <span class="comment">// 0</span></span><br><span class="line"><span class="built_in">console</span>.log(str.localeCompare(<span class="string">"a"</span>)) <span class="comment">// 1</span></span><br><span class="line"><span class="built_in">console</span>.log(str.localeCompare(<span class="string">"c"</span>)) <span class="comment">// -1</span></span><br></pre></td></tr></table></figure></div><p>实际上，我们就可以通过 localeCompare 实现中文排序了。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = [<span class="string">"北京"</span>,<span class="string">"上海"</span>,<span class="string">"深圳"</span>,<span class="string">"广州"</span>]</span><br><span class="line"><span class="keyword">let</span> res1 = arr.sort(<span class="function">(<span class="params">a,b</span>)=&gt;</span>a-b)</span><br><span class="line"><span class="comment">// 传统的排序方法，你会发现顺序是有问题的</span></span><br><span class="line"><span class="built_in">console</span>.log(res1) <span class="comment">// ["北京", "上海", "广州", "深圳"]</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 通过 localeCompare 我们就实现了中文的排序了</span></span><br><span class="line"><span class="keyword">let</span> res2 = arr.sort(<span class="function">(<span class="params">a,b</span>)=&gt;</span>a.localeCompare(<span class="built_in">String</span>(b)))</span><br><span class="line"><span class="built_in">console</span>.log(res2) <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">let</span> myData = [<span class="string">"北京"</span>,<span class="string">"上海"</span>,<span class="string">"深圳"</span>,<span class="string">"广州"</span>,<span class="literal">null</span>,<span class="literal">undefined</span>,<span class="number">10</span>,<span class="number">2</span>,<span class="string">"apple"</span>,<span class="string">"bird"</span>,<span class="string">"banana"</span>]</span><br><span class="line"><span class="keyword">let</span> result = myData.sort(<span class="function"><span class="keyword">function</span>(<span class="params">a,b</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">var</span> r = a - b;</span><br><span class="line">    <span class="keyword">if</span>(<span class="built_in">isNaN</span>(r))&#123;</span><br><span class="line">        r = <span class="built_in">String</span>(a).localeCompare(<span class="built_in">String</span>(b));</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="keyword">return</span> r;</span><br><span class="line">&#125;)</span><br><span class="line"><span class="built_in">console</span>.log(result)</span><br></pre></td></tr></table></figure></div><blockquote><p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare" target="_blank" rel="noopener">localeCompare方法，十分建议看看MDN</a>，因为我实在太菜了 ┭┮﹏┭┮</p></blockquote><p>当比较大量字符串时， 比如比较大量数组时， 最好创建一个 <code>[Intl.Collator](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Collator)</code> 对象并使用compare 属性所提供的函数。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// Intl.Collator 是用于语言敏感字符串比较的 collators构造函数。</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">letterSort</span>(<span class="params">lang, letters</span>) </span>&#123;</span><br><span class="line">  letters.sort(<span class="keyword">new</span> <span class="built_in">Intl</span>.Collator(lang).compare)</span><br><span class="line">  <span class="keyword">return</span> letters;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 具体请看上面 MDN 的说明</span></span><br><span class="line"><span class="built_in">console</span>.log(letterSort(<span class="string">'de'</span>, [<span class="string">'a'</span>,<span class="string">'z'</span>,<span class="string">'ä'</span>])) <span class="comment">// ["a", "ä", "z"]</span></span><br><span class="line"><span class="built_in">console</span>.log(letterSort(<span class="string">'sv'</span>, [<span class="string">'a'</span>,<span class="string">'z'</span>,<span class="string">'ä'</span>])) <span class="comment">// ["a", "z", "ä"]</span></span><br></pre></td></tr></table></figure></div><h1 id="文中某些知识点参考链接"><a href="#文中某些知识点参考链接" class="headerlink" title="文中某些知识点参考链接"></a>文中某些知识点参考链接</h1><p><a href="http://es6.ruanyifeng.com/#docs/string" target="_blank" rel="noopener">阮老师的es6入门，字符串的扩展 推荐！</a></p><p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare" target="_blank" rel="noopener">localeCompare方法，十分建议看看MDN</a></p><p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Collator" target="_blank" rel="noopener">Intl.Collator</a></p><p><a href="https://github.com/sunmaobin/sunmaobin.github.io/issues/43" target="_blank" rel="noopener">Array.sort高级用法</a></p><p><a href="https://zhuanlan.zhihu.com/p/33335629" target="_blank" rel="noopener">JavaScript 正则表达式匹配汉字</a></p><p><a href="http://deerchao.net/tutorials/regex/regex.htm" target="_blank" rel="noopener">正则表达式30分钟入门教程 推荐</a></p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>这里我就不做总结了，留个小作业给各位观众老爷，自己总结下咯</p><p>最后，感谢各位观众老爷观看啦O(∩_∩)O</p>]]></content>
    
    <summary type="html">
    
      前些天写了篇数组的总结，其实我们再来看看字符串，和数组有异曲同工之妙，字符串也可以通过split来变成数组，然后使用数组的方法。
    
    </summary>
    
    
      <category term="记录" scheme="https://gatings.cn/categories/%E8%AE%B0%E5%BD%95/"/>
    
    
      <category term="javascript" scheme="https://gatings.cn/tags/javascript/"/>
    
  </entry>
  
  <entry>
    <title>数组的方法的总结和使用</title>
    <link href="https://gatings.cn/2018-12-25/%E6%95%B0%E7%BB%84%E7%9A%84%E6%96%B9%E6%B3%95%E7%9A%84%E6%80%BB%E7%BB%93%E5%92%8C%E4%BD%BF%E7%94%A8/"/>
    <id>https://gatings.cn/2018-12-25/%E6%95%B0%E7%BB%84%E7%9A%84%E6%96%B9%E6%B3%95%E7%9A%84%E6%80%BB%E7%BB%93%E5%92%8C%E4%BD%BF%E7%94%A8/</id>
    <published>2018-12-24T16:00:00.000Z</published>
    <updated>2021-10-13T01:56:29.707Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>JavaScript 中的数组是一个很特别的存在，他不像Java ，专门搞了 List 这样的一整套的东西，JS终端数组完全可以当作栈或队列来使用，数组的四大操作：pop、push、shift、unshift。</p><p>我今天写这篇博客，主要是写一篇总结，以备以后查看。</p><p>对于数组方法，我们应该关心的只有两个问题，<b>返回值是什么,会不会修改原数组，</b>典型的例子就是 splice() 和 slice() 这两个方法。</p><p>接下来，带着这两个问题，我会相对详细的总结一下我们<b>数组原型（数组实例）</b>里面所拥有的方法和属性，另外，常用的方法，我会加上一个常用的例子（应用场景）</p><blockquote><p>ps: 文中有些和数组方法不是太相关的知识，仅供了解，这里就不展开了（实际上也是我太垃圾了0 0，不懂） 具体我会在文末放上相关链接</p></blockquote><p>下面将不再重复这些方法的 callbak 的参数</p><blockquote><p>（find、findIndex、some、filter、every、map，forEach，flatMap）的callback 被调用时会传入三个参数：当前元素值，元素的索引，原数组。这些方法都不会改变元素组，但是，如果你操作第三个参数，那就不一样了。</p></blockquote><h1 id="数组的属性"><a href="#数组的属性" class="headerlink" title="数组的属性"></a>数组的属性</h1><h2 id="length"><a href="#length" class="headerlink" title="length"></a>length</h2><p>length 是Array的实例属性。返回或设置一个数组中的元素个数。该值是一个无符号 32-bit 整数，并且总是大于数组最高项的下标。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>]</span><br><span class="line"><span class="built_in">console</span>.log(arr.length) <span class="comment">// 3</span></span><br></pre></td></tr></table></figure></div><p>length 属性的值是一个 0 到 232-1 的整数。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> arr1 = <span class="keyword">new</span> <span class="built_in">Array</span>(<span class="number">4294967296</span>) <span class="comment">// 2的32次方 = 4294967296 </span></span><br><span class="line"><span class="comment">// Uncaught RangeError: Invalid array length无效的数组长度</span></span><br><span class="line"><span class="built_in">console</span>.log(arr1)</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> arr2 = <span class="keyword">new</span> <span class="built_in">Array</span>(<span class="number">-100</span>) <span class="comment">// 负号</span></span><br><span class="line"><span class="comment">// Uncaught RangeError: Invalid array length无效的数组长度</span></span><br><span class="line"><span class="built_in">console</span>.log(arr1)</span><br></pre></td></tr></table></figure></div><p>你还可以通过 length 来截断数组</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>]</span><br><span class="line">arr.length = <span class="number">2</span></span><br><span class="line"><span class="built_in">console</span>.log(arr) <span class="comment">// [1,2]</span></span><br></pre></td></tr></table></figure></div><h2 id="Array-length-属性的属性特性"><a href="#Array-length-属性的属性特性" class="headerlink" title="Array.length 属性的属性特性"></a>Array.length 属性的属性特性</h2><table><thead><tr><th>属性</th><th>值</th></tr></thead><tbody><tr><td>writable</td><td>true</td></tr><tr><td>enumerable</td><td>false</td></tr><tr><td>configurable</td><td>false</td></tr></tbody></table><ul><li>Writable ：如果设置为false，该属性值将不能被修改。</li><li>Configurable ：如果设置为false，删除或更改任何属性都将会失败。</li><li>Enumerable ：如果设置为 true ，属性可以通过迭代器for或for…in进行迭代。</li></ul><p>看到这里，估计有人想问，既然 length 属性是可以修改的，那么我们可不可以重定义数组对象的 length 属性呢？答案是可以的，但是会受到一般的重定义限制。并且并不是所有浏览器都允许 Array.length 的重定义。这里就不展开了，如有兴趣请看<a href="#文中某些知识点参考链接">文末的链接</a></p><h1 id="数组的静态方法"><a href="#数组的静态方法" class="headerlink" title="数组的静态方法"></a>数组的静态方法</h1><h2 id="Array-isArray"><a href="#Array-isArray" class="headerlink" title="Array.isArray()"></a>Array.isArray()</h2><p>在这个方法没出来之前，很多早期类库是通过下列代码来判断的（鸭子判断），文末送上玉伯大佬的链接，希望大家都去了解一下</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">isArray</span>(<span class="params">object</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> object != <span class="literal">null</span> &amp;&amp; <span class="keyword">typeof</span> object === <span class="string">"object"</span> &amp;&amp;</span><br><span class="line">    <span class="string">'splice'</span> <span class="keyword">in</span> object &amp;&amp; <span class="string">'join'</span> <span class="keyword">in</span> object</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>直到后来有神人出山，写出了一段神码，此代码一出，天下震惊，引各路类库竞折腰。这代码，不不仅仅解决了数组的问题，而是解决了 isXxx 一类问题。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">isArray</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">Object</span>.prototype.toString.call(obj) === <span class="string">"[object Array]"</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>对现代浏览器来说，上面的写法，依旧让各大浏览器引擎的实现者觉得很难受，于是直接有了Array.isArray方法</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Array.isArray(obj)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Object&#125;</span> </span>obj =&gt; 需要检测的值</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 class="doctag">@param <span class="type">&#123;Boolean&#125;</span> </span>如果对象是 Array， 则为true;否则为false。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="built_in">Array</span>.isArray([]) <span class="comment">// true</span></span><br><span class="line"> <span class="comment">// 鲜为人知的事实：其实 Array.prototype 也是一个数组。</span></span><br><span class="line"><span class="built_in">Array</span>.isArray(<span class="built_in">Array</span>.prototype) <span class="comment">// true</span></span><br></pre></td></tr></table></figure></div><h2 id="Array-from"><a href="#Array-from" class="headerlink" title="Array.from()"></a>Array.from()</h2><p>Array.from() 方法从一个类似数组或可迭代对象中创建一个新的数组实例</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Array.from(arrayLike,mapFn,thisArg)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Integer&#125;</span> </span>arrayLike =&gt; 想要转换成数组的伪数组对象或可迭代对象。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Function&#125;</span> </span>mapFn =&gt; 如果指定了该参数，新数组中的每个元素会执行该回调函数。 (可选)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Object&#125;</span> </span>thisArg =&gt; 可选参数，执行回调函数 mapFn 时 this 对象。 (可选)</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 class="doctag">@Array </span>一个新的数组实例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">Array</span>.from(<span class="string">'foo'</span>)) <span class="comment">// ["f", "o", "o"]</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">Array</span>.from([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>], x =&gt; x + x)) <span class="comment">// [2,4,6]</span></span><br><span class="line"><span class="built_in">Array</span>.from([<span class="number">1</span>],<span class="function"><span class="keyword">function</span>(<span class="params">x</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="keyword">this</span>) <span class="comment">// &#123;a: 1&#125;</span></span><br><span class="line">    <span class="keyword">return</span> x</span><br><span class="line">&#125;,&#123;<span class="attr">a</span>:<span class="number">1</span>&#125;)</span><br></pre></td></tr></table></figure></div><p>实际上，通过 Array.from 我们就可以做一些很常见的事了，比如我们的数组去重合并</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> combine = <span class="function">(<span class="params">...arg</span>)=&gt;</span>&#123;</span><br><span class="line">     <span class="keyword">let</span> arr = [].concat.apply([], arg); </span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">Array</span>.from(<span class="keyword">new</span> <span class="built_in">Set</span>(arr));</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> m = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">2</span>], n = [<span class="number">2</span>,<span class="number">3</span>,<span class="number">3</span>,<span class="number">4</span>]; </span><br><span class="line"><span class="built_in">console</span>.log(combine(m,n)) <span class="comment">// [1, 2, 3, 4];</span></span><br></pre></td></tr></table></figure></div><h2 id="Array-of"><a href="#Array-of" class="headerlink" title="Array.of()"></a>Array.of()</h2><p>Array.of() 方法创建一个具有可变数量参数的新数组实例，而不考虑参数的数量或类型。</p><p>Array.of() 和 Array 构造函数之间的区别在于处理整数参数：Array.of(7) 创建一个具有单个元素 7 的数组，而 Array(7) 创建一个长度为7的空数组（注意：这是指一个有7个空位的数组，而不是由7个undefined组成的数组）。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Array.of(elementN)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>elementN =&gt; 任意个参数，将按顺序成为返回数组中的元素。</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 class="doctag">@param <span class="type">&#123;Array&#125;</span> </span>新的 Array 实例。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="built_in">Array</span>.of(<span class="number">7</span>);       <span class="comment">// [7] </span></span><br><span class="line"><span class="built_in">Array</span>.of(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>); <span class="comment">// [1, 2, 3]</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">Array</span>(<span class="number">7</span>);          <span class="comment">// [ , , , , , , ]</span></span><br><span class="line"><span class="built_in">Array</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>);    <span class="comment">// [1, 2, 3]</span></span><br></pre></td></tr></table></figure></div><h1 id="拷贝填充相关"><a href="#拷贝填充相关" class="headerlink" title="拷贝填充相关"></a>拷贝填充相关</h1><h2 id="concat"><a href="#concat" class="headerlink" title="concat"></a>concat</h2><p>concat() 方法用于合并两个或多个数组。此方法<b>不会更改原数组</b>，而是返回一个新数组。</p><p>有个降维的例子会在 reduce() ，这里就不过讲解了，避免重复</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * old_array.concat(valueN])</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>valueN =&gt; 将数组和/或值连接成新数组。详情请参阅下文描述。</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 class="doctag">@param <span class="type">&#123;Array&#125;</span> </span>新的 Array 实例</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">let</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">let</span> arr2 = [<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>]</span><br><span class="line"><span class="built_in">console</span>.log(arr1.concat(arr2)) <span class="comment">// [1,2,3,4,5,6]</span></span><br><span class="line"><span class="built_in">console</span>.log(arr1) <span class="comment">// [1,2,3]</span></span><br><span class="line"><span class="built_in">console</span>.log(arr2) <span class="comment">// [4,5,6]</span></span><br></pre></td></tr></table></figure></div><p>实际应用中，我们可以用 concat 和 reduce 搭配，实现扁平化数组(降维)</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 多维数组变成一维数组</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Array&#125;</span> </span>arr =&gt; 数组</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Boolean&#125;</span> </span>shallow =&gt; 数组将只减少一维的嵌套</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> flatten =  <span class="function">(<span class="params">arr, shallow</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">if</span> (shallow) &#123;</span><br><span class="line">      <span class="keyword">var</span> arr = arr.reduce(<span class="function">(<span class="params">arr, val</span>) =&gt;</span> arr.concat(val), [])</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">var</span> arr = arr.reduce(<span class="function">(<span class="params">arr, val</span>) =&gt;</span> arr.concat(<span class="built_in">Array</span>.isArray(val) ? flatten(val) : val), [])</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> arr</span><br><span class="line">&#125;</span><br><span class="line"></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 class="number">4</span>]],<span class="number">5</span>]</span><br><span class="line"><span class="built_in">console</span>.log(flatten(arr,<span class="literal">true</span>)) <span class="comment">// [1, 2, [3,4], 5]</span></span><br><span class="line"><span class="built_in">console</span>.log(flatten(arr)) <span class="comment">// [1, 2, 3, 4, 5]</span></span><br></pre></td></tr></table></figure></div><h2 id="copyWithin"><a href="#copyWithin" class="headerlink" title="copyWithin"></a>copyWithin</h2><p>copyWithin() 方法浅复制数组的一部分到同一数组中的另一个位置，并返回它，而不修改其大小。此方法<b>会更改现有数组</b></p><blockquote><p>ps：参数target,start和end 必须为整数。如果start为负，则其指定的索引位置等同于length+start，length为数组的长度。end也是如此。(copyWithin,fill等方法的start、end参数同理)</p></blockquote><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * arr.copyWithin(target,start,end)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>target =&gt; 0 为基底的索引，复制序列到该位置。如果是负数，target 将从末尾开始计算。</span></span><br><span class="line"><span class="comment">如果 target 大于等于 arr.length，将会不发生拷贝。如果 target 在 start 之后，复制的序列将被修改以符合 arr.length。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>start =&gt; 0 为基底的索引，开始复制元素的起始位置。如果是负数，start 将从末尾开始计算</span></span><br><span class="line"><span class="comment">如果 start 被忽略，copyWithin 将会从0开始复制 (可选)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>end =&gt; 0 为基底的索引，开始复制元素的结束位置。copyWithin 将会拷贝到该位置，但不包括 end 这个位置的元素。如果是负数， end 将从末尾开始计算。如果 end 被忽略，copyWithin 将会复制到 arr.length (可选)</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 class="doctag">@param <span class="type">&#123;Array&#125;</span> </span>改变了的数组</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">let</span> arr = [<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 class="built_in">console</span>.log(arr.copyWithin(<span class="number">0</span>, <span class="number">3</span>, <span class="number">4</span>)) <span class="comment">// [4, 2, 3, 4, 5]</span></span><br><span class="line"><span class="built_in">console</span>.log(arr) <span class="comment">// [4, 2, 3, 4, 5]</span></span><br><span class="line"><span class="comment">// -2表示复制到 ([1, 2, 3, 4, 5].length - 2) 的位置去，这里指3</span></span><br><span class="line"><span class="built_in">console</span>.log([<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>].copyWithin(<span class="number">-2</span>)) <span class="comment">// [1, 2, 3, 1, 2]</span></span><br><span class="line"><span class="built_in">console</span>.log([<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>].copyWithin(<span class="number">0</span>, <span class="number">3</span>)) <span class="comment">// [4, 5, 3, 4, 5]</span></span><br><span class="line"><span class="built_in">console</span>.log([<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>].copyWithin(<span class="number">-2</span>, <span class="number">-3</span>, <span class="number">-1</span>)) <span class="comment">// [1, 2, 3, 3, 4]</span></span><br></pre></td></tr></table></figure></div><h2 id="fill"><a href="#fill" class="headerlink" title="fill"></a>fill</h2><p>fill() 方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。<b>不包括终止索引</b>。此方法<b>会更改现有数组</b></p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * arr.fill(value,start,end)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>value =&gt; 用来填充数组元素的值。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>start =&gt; 起始索引，默认值为0。(可选)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>end =&gt; 起始索引，默认值为this.length。(可选)</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 class="doctag">@param <span class="type">&#123;Array&#125;</span> </span>修改后的数组</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">let</span> arr = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>]</span><br><span class="line"><span class="built_in">console</span>.log(arr.fill(<span class="number">0</span>, <span class="number">2</span>, <span class="number">4</span>)) <span class="comment">// [1, 2, 0, 0]</span></span><br><span class="line"><span class="built_in">console</span>.log([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>].fill(<span class="number">4</span>)) <span class="comment">// [4,4,4]</span></span><br><span class="line"><span class="built_in">console</span>.log([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>].fill(<span class="number">4</span>,<span class="number">1</span>)) <span class="comment">// [1,4,4]</span></span><br><span class="line"><span class="comment">// 这里的-1实际上就是 (-1+3) 也就是 2</span></span><br><span class="line"><span class="built_in">console</span>.log([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>].fill(<span class="number">4</span>,<span class="number">-1</span>)) <span class="comment">// [1,2,4]</span></span><br><span class="line"><span class="built_in">console</span>.log([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>].fill(<span class="number">4</span>,<span class="number">-2</span>,<span class="number">-1</span>)) <span class="comment">// [1,4,3]</span></span><br></pre></td></tr></table></figure></div><h1 id="搜索查找相关"><a href="#搜索查找相关" class="headerlink" title="搜索查找相关"></a>搜索查找相关</h1><h2 id="find"><a href="#find" class="headerlink" title="find"></a>find</h2><p>find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * arr.find(callback,thisArg)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Function&#125;</span> </span>callback =&gt; 用来测试每个元素的函数</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Object&#125;</span> </span>thisArg =&gt; 可选参数，执行回调函数 mapFn 时 this 对象。 (可选)</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * callback 被调用时会传入三个参数：当前元素值，元素的索引，原数组。</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 class="doctag">@param <span class="type">&#123;*&#125;</span> </span>当某个元素通过 callback 的测试时，返回数组中最先通过的值，否则返回 undefined</span></span><br><span class="line"><span class="comment">*/</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 class="number">4</span>]</span><br><span class="line"><span class="built_in">console</span>.log(arr.find(<span class="function"><span class="params">item</span>=&gt;</span>item&gt;<span class="number">2</span>)) <span class="comment">// 3</span></span><br><span class="line"><span class="built_in">console</span>.log(arr.find(<span class="function"><span class="params">item</span>=&gt;</span>item&gt;<span class="number">6</span>)) <span class="comment">// undefined</span></span><br></pre></td></tr></table></figure></div><p>实际应用场景中，我们可以用对象的属性查找数组里的对象</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> people = [&#123;</span><br><span class="line">    name:<span class="string">'gating'</span>,</span><br><span class="line">    age:<span class="number">18</span></span><br><span class="line">&#125;,&#123;</span><br><span class="line">    name:<span class="string">'blue'</span>,</span><br><span class="line">    age:<span class="number">15</span></span><br><span class="line">&#125;,&#123;</span><br><span class="line">    name:<span class="string">'family'</span>,</span><br><span class="line">    age:<span class="number">18</span></span><br><span class="line">&#125;]</span><br><span class="line"><span class="keyword">const</span> findName = <span class="function">(<span class="params">arr</span>) =&gt;</span> arr.name === <span class="string">'gating'</span>;</span><br><span class="line"><span class="built_in">console</span>.log(people.find(findName)) <span class="comment">// &#123;name: "gating", age: 18&#125;</span></span><br></pre></td></tr></table></figure></div><h2 id="findIndex"><a href="#findIndex" class="headerlink" title="findIndex"></a>findIndex</h2><p>findIndex()方法返回数组中满足提供的测试函数的第一个元素的索引。否则返回-1。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * arr.findIndex(callback,thisArg)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Function&#125;</span> </span>callback =&gt; 用来测试每个元素的函数</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Object&#125;</span> </span>thisArg =&gt; 可选参数，执行回调函数 mapFn 时 this 对象。 (可选)</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * callback 被调用时会传入三个参数：当前元素值，元素的索引，原数组。</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 class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>当某个元素通过 callback 的测试时，返回数组中最先通过的值的索引值，否则返回 -1</span></span><br><span class="line"><span class="comment">*/</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 class="number">4</span>]</span><br><span class="line"><span class="built_in">console</span>.log(arr.findIndex(<span class="function"><span class="params">item</span>=&gt;</span>item&gt;<span class="number">2</span>)) <span class="comment">// 2</span></span><br><span class="line"><span class="built_in">console</span>.log(arr.findIndex(<span class="function"><span class="params">item</span>=&gt;</span>item&gt;<span class="number">6</span>)) <span class="comment">// -1</span></span><br></pre></td></tr></table></figure></div><p>实际应用场景同 find() ,这里就不重复了</p><h2 id="indexOf"><a href="#indexOf" class="headerlink" title="indexOf"></a>indexOf</h2><p>indexOf()方法返回在数组中可以找到一个给定元素的第一个索引，如果不存在，则返回-1。</p><blockquote><p>indexOf 和 lastIndexOf 都是使用 === 来进行判断</p></blockquote><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * arr.indexOf(searchElement,fromIndex)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>searchElement =&gt; 要查找的元素</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>fromIndex =&gt; 开始查找的位置。如果该索引值大于或等于数组长度，意味着不会在数组里查找，返回-1。如果参数中提供的索引值是一个负值，则将其作为数组末尾的一个抵消，即-1表示从最后一个元素开始查找，-2表示从倒数第二个元素开始查找 ，以此类推。 注意：如果参数中提供的索引值是一个负值，并不改变其查找顺序，查找顺序仍然是从前向后查询数组。如果抵消后的索引值仍小于0，则整个数组都将会被查询。其默认值为0. (可选)</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 class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>首个被找到的元素在数组中的索引位置; 若没有找到则返回 -1</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> arr = [<span class="string">'a'</span>,<span class="string">'b'</span>,<span class="string">'c'</span>,<span class="string">'a'</span>]</span><br><span class="line"><span class="built_in">console</span>.log(arr.indexOf(<span class="string">'a'</span>)) <span class="comment">// 0</span></span><br><span class="line"><span class="built_in">console</span>.log(arr.indexOf(<span class="string">'a'</span>,<span class="number">-1</span>)) <span class="comment">// 3</span></span><br><span class="line"><span class="built_in">console</span>.log(arr.indexOf(<span class="string">'d'</span>)) <span class="comment">// -1</span></span><br></pre></td></tr></table></figure></div><p>实际应用场景中，我们不可能之只找出一个同名元素的索引值，我们可能需要找出同名的元素的所有位置，于是乎</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 存放索引的数组</span></span><br><span class="line"><span class="keyword">const</span> indices = [];</span><br><span class="line"><span class="keyword">const</span> arr = [<span class="string">'a'</span>,<span class="string">'b'</span>,<span class="string">'c'</span>,<span class="string">'a'</span>]</span><br><span class="line"><span class="comment">// 要查找的元素</span></span><br><span class="line"><span class="keyword">const</span> searchElement = <span class="string">'a'</span></span><br><span class="line"><span class="comment">// 当前的索引</span></span><br><span class="line"><span class="keyword">let</span> idx = arr.indexOf(searchElement);</span><br><span class="line"><span class="keyword">while</span> (idx != <span class="number">-1</span>) &#123;</span><br><span class="line">  indices.push(idx);</span><br><span class="line">  idx = arr.indexOf(searchElement, idx + <span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">console</span>.log(indices) <span class="comment">// [0, 3]</span></span><br></pre></td></tr></table></figure></div><h2 id="lastIndexOf"><a href="#lastIndexOf" class="headerlink" title="lastIndexOf"></a>lastIndexOf</h2><p>lastIndexOf() 方法返回指定元素（也即有效的 JavaScript 值或变量）在数组中的最后一个的索引，如果不存在则返回 -1。从数组的后面向前查找，从 fromIndex 处开始。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * arr.lastIndexOf(searchElement,fromIndex = arr.length - 1)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>searchElement =&gt; 要查找的元素</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>fromIndex =&gt; 从此位置开始逆向查找。默认为数组的长度减 1，即整个数组都被查找。如果该值大于或等于数组的长度，则整个数组会被查找。如果为负值，将其视为从数组末尾向前的偏移。即使该值为负，数组仍然会被从后向前查找。如果该值为负时，其绝对值大于数组长度，则方法返回 -1，即数组不会被查找。 (可选)</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 class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>数组中最后一个元素的索引，如未找到返回-1</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> arr = [<span class="string">'a'</span>,<span class="string">'b'</span>,<span class="string">'c'</span>,<span class="string">'a'</span>]</span><br><span class="line"><span class="built_in">console</span>.log(arr.lastIndexOf(<span class="string">'a'</span>)) <span class="comment">// 0</span></span><br><span class="line"><span class="built_in">console</span>.log(arr.lastIndexOf(<span class="string">'a'</span>,<span class="number">-1</span>)) <span class="comment">// 3</span></span><br><span class="line"><span class="built_in">console</span>.log(arr.lastIndexOf(<span class="string">'d'</span>)) <span class="comment">// -1</span></span><br></pre></td></tr></table></figure></div><p>实例：查找所有元素</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> indices = []</span><br><span class="line"><span class="keyword">const</span> array = [<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'a'</span>, <span class="string">'c'</span>, <span class="string">'a'</span>, <span class="string">'d'</span>]</span><br><span class="line"><span class="keyword">const</span> element = <span class="string">'a'</span></span><br><span class="line"><span class="keyword">let</span> idx = array.lastIndexOf(element)</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> (idx != <span class="number">-1</span>) &#123;</span><br><span class="line">  indices.push(idx);</span><br><span class="line">  idx = (idx &gt; <span class="number">0</span> ? array.lastIndexOf(element, idx - <span class="number">1</span>) : <span class="number">-1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(indices) <span class="comment">// [4, 2, 0];</span></span><br></pre></td></tr></table></figure></div><blockquote><p>注意，我们要单独处理idx==0时的情况，因为如果是第一个元素，忽略了fromIndex参数则第一个元素总会被查找。这不同于indexOf方法</p></blockquote><h2 id="includes"><a href="#includes" class="headerlink" title="includes"></a>includes</h2><p>includes() 方法用来判断一个数组是否包含一个指定的值，根据情况，如果包含则返回 true，否则返回false。</p><ol><li><p>如果fromIndex 大于等于数组长度 ，则返回 false 。该数组不会被搜索</p></li><li><p>如果 fromIndex 为负值，计算出的索引将作为开始搜索searchElement的位置。如果计算出的索引小于 0，则整个数组都会被搜索。</p></li></ol><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * arr.includes(searchElement,fromIndex = 0)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>searchElement =&gt; 要查找的元素</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>fromIndex =&gt; 从该索引处开始查找 searchElement。如果为负值，则按升序从 array.length - fromIndex 的索引开始搜索。默认为 0。 (可选)</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 class="doctag">@param <span class="type">&#123;Boolean&#125;</span> </span>一个 Boolean 值</span></span><br><span class="line"><span class="comment">*/</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 class="literal">NaN</span>]</span><br><span class="line"><span class="built_in">console</span>.log(arr.includes(<span class="number">1</span>, <span class="number">-100</span>)) <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log(arr.includes(<span class="number">1</span>, <span class="number">5</span>)) <span class="comment">// false</span></span><br></pre></td></tr></table></figure></div><p>实际的应用场景，通过可以解决我们找出两个数组的 数组交集 和 数组差集</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 数组交集</span></span><br><span class="line"><span class="keyword">const</span> intersection = <span class="function">(<span class="params">arr1, arr2</span>) =&gt;</span> arr1.filter(<span class="function"><span class="params">v</span> =&gt;</span> arr2.includes(v))</span><br><span class="line"><span class="comment">// 数组差集</span></span><br><span class="line"><span class="keyword">const</span> difference = <span class="function">(<span class="params">arr1, arr2</span>) =&gt;</span> [...arr1, ...arr2].filter(<span class="function"><span class="params">v</span> =&gt;</span> !arr1.includes(v) || !arr2.includes(v))</span><br><span class="line"></span><br><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 = [<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>]</span><br><span class="line"><span class="built_in">console</span>.log(intersection(arr1,arr2)) <span class="comment">// [2, 3]</span></span><br><span class="line"><span class="built_in">console</span>.log(difference(arr1,arr2)) <span class="comment">// [1, 4]</span></span><br></pre></td></tr></table></figure></div><h1 id="操作相关"><a href="#操作相关" class="headerlink" title="操作相关"></a>操作相关</h1><h2 id="slice"><a href="#slice" class="headerlink" title="slice"></a>slice</h2><p>slice() 方法返回一个新的数组对象，这一对象是一个由 begin和 end（不包括end）决定的原数组的浅拷贝。此方法<b>不会更改原数组</b>。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Array.slice(begin,end)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>begin =&gt; 从该索引处开始提取原数组中的元素（从0开始）。（可选）</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>end =&gt; 在该索引处结束提取原数组元素（从0开始）。slice会提取原数组中索引从 begin 到 end 的所有元素（包含begin，但不包含end）。（可选）</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 class="doctag">@param <span class="type">&#123;Array&#125;</span> </span>一个含有提取元素的新数组</span></span><br><span class="line"><span class="comment"> */</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 class="number">4</span>,<span class="number">5</span>]</span><br><span class="line"><span class="built_in">console</span>.log(arr.slice()) <span class="comment">// [1,2,3,4,5]</span></span><br><span class="line"><span class="built_in">console</span>.log(arr.slice(<span class="number">2</span>)) <span class="comment">// [3,4,5]</span></span><br><span class="line"><span class="built_in">console</span>.log(arr.slice(<span class="number">2</span>, <span class="number">4</span>)) <span class="comment">// [3,4]</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// -1表示复制到 ([1, 2, 3, 4, 5].length - 1) 的位置去，这里指4</span></span><br><span class="line"><span class="built_in">console</span>.log(arr.slice(<span class="number">-1</span>)) <span class="comment">// [5]</span></span><br><span class="line"><span class="built_in">console</span>.log(arr.slice(<span class="number">-2</span>,<span class="number">-1</span>)) <span class="comment">// [4]</span></span><br></pre></td></tr></table></figure></div><p>实际应用中，slice 就可以做很多事情啦，比如说，我们的数组切割或者生成二维数组</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 返回一个根据subArrayNum的二维数组</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Array&#125;</span> </span>arr =&gt; 一维数组</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>subArrayNum =&gt; 每组元素的个数</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> chunk = <span class="function">(<span class="params">arr, subArrayNum</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> result = [];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; arr.length; i += subArrayNum) &#123;</span><br><span class="line">        result.push(arr.slice(i, i + subArrayNum));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">console</span>.log(chunk([<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>],<span class="number">2</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"> * 创建一个数组切片， 从array数组的起始元素开始提取n个元素。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Array&#125;</span> </span>arr =&gt; 要检索的数组</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>n =&gt; 要提取的元素个数</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> take = <span class="function">(<span class="params">arr, n</span>) =&gt;</span> arr.slice(<span class="number">0</span>, n ? n : <span class="number">1</span>)</span><br><span class="line"><span class="built_in">console</span>.log(take([<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>],<span class="number">2</span>)) <span class="comment">// [1, 2]</span></span><br></pre></td></tr></table></figure></div><h2 id="splice"><a href="#splice" class="headerlink" title="splice"></a>splice</h2><p>splice()方法通过删除现有元素和/或添加新元素来修改数组,并以数组返回原数组中被修改的内容。此方法<b>会更改现有数组</b>。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Array.splice(start,deleteCount,itemN)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>start =&gt; 指定修改的开始位置（从0计数）。如果超出了数组的长度，则从数组末尾开始添加内容；如果是负值，则表示从数组末位开始的第几位（从-1计数）；如果负数的绝对值大于数组的长度，则表示开始位置为第0位。</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Int&#125;</span> </span>deleteCount  =&gt; 整数，表示要移除的数组元素的个数。如果deleteCount被省略，则其相当于(arr.length - start)。（可选） </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>itemN  =&gt; 要添加进数组的元素,从start 位置开始。如果不指定，则 splice() 将只删除数组元素。（可选）</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 class="doctag">@param <span class="type">&#123;Array&#125;</span> </span>由被删除的元素组成的一个数组。如果只删除了一个元素，则返回只包含一个元素的数组。如果没有删除元素，则返回空数组。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> arr = [<span class="number">1</span>,<span class="number">3</span>]</span><br><span class="line"><span class="keyword">const</span> res = arr.splice(<span class="number">1</span>, <span class="number">0</span>, <span class="number">2</span>)</span><br><span class="line"><span class="built_in">console</span>.log(arr) <span class="comment">// [1,2,3]</span></span><br><span class="line"><span class="built_in">console</span>.log(res) <span class="comment">// []</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> result = arr.splice(<span class="number">2</span>, <span class="number">1</span>)</span><br><span class="line"><span class="built_in">console</span>.log(arr) <span class="comment">// [1,2]</span></span><br><span class="line"><span class="built_in">console</span>.log(result) <span class="comment">// [3]</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// -1表示复制到 ([1,2].length - 1) 的位置去，这里指1</span></span><br><span class="line">arr.splice(<span class="number">-1</span>, <span class="number">1</span>)</span><br><span class="line"><span class="built_in">console</span>.log(arr) <span class="comment">// [1]</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 只有一个参数时 fruits.splice(2) 相当于 fruits.splice(2,fruits.length-1)</span></span><br><span class="line"><span class="keyword">const</span> fruits = [<span class="string">'apple'</span>,<span class="string">'banana'</span>,<span class="string">'orange'</span>,<span class="string">'pear'</span>]</span><br><span class="line"><span class="keyword">const</span> res1 = fruits.splice(<span class="number">2</span>)</span><br><span class="line"><span class="built_in">console</span>.log(fruits) <span class="comment">// ["apple", "banana"]</span></span><br><span class="line"><span class="built_in">console</span>.log(res1) <span class="comment">// ["orange", "pear"]</span></span><br></pre></td></tr></table></figure></div><blockquote><p>因为 js 中只有 splice 这个方法删除数组元素，并且会修改数组长度，所以他的实际应用场景还挺多的0 0，但是我没有太好的例子可以距离</p></blockquote><h2 id="push"><a href="#push" class="headerlink" title="push"></a>push</h2><p>push() 方法将一个或多个元素添加到数组的末尾，并返回该数组的新长度。 此方法<b>会更改现有数组</b>。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Array.push(elementN)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>elementN =&gt; 被添加到数组末尾的元素。</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 class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>当调用该方法时，新的 length 属性值将被返回。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></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="built_in">console</span>.log(arr.push(<span class="number">4</span>)) <span class="comment">// 4</span></span><br><span class="line"><span class="built_in">console</span>.log(arr) <span class="comment">// [1,2,3,4]</span></span><br></pre></td></tr></table></figure></div><p>push、pop、shift、unshift 方法有意具有通用性。该方法和 call() 或 apply() 一起使用时，可应用在类似数组的对象上。这个比较有意义，所以单独在这里送上<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/push#%E5%90%88%E5%B9%B6%E4%B8%A4%E4%B8%AA%E6%95%B0%E7%BB%84" target="_blank" rel="noopener">MDN的链接</a>，当然例子也是MDN的O(∩_∩)O</p><blockquote><p>像数组一样使用对象这个例子 push、pop、shift、unshift 四个方法写法很类似，这里就重复写了</p></blockquote><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 合并两个数组</span></span><br><span class="line"><span class="keyword">var</span> vegetables = [<span class="string">'parsnip'</span>, <span class="string">'potato'</span>]</span><br><span class="line"><span class="keyword">var</span> moreVegs = [<span class="string">'celery'</span>, <span class="string">'beetroot'</span>]</span><br><span class="line"><span class="comment">// 将第二个数组融合进第一个数组</span></span><br><span class="line"><span class="comment">// 相当于 vegetables.push('celery', 'beetroot')</span></span><br><span class="line"><span class="built_in">Array</span>.prototype.push.apply(vegetables, moreVegs)</span><br><span class="line"><span class="built_in">console</span>.log(vegetables)  <span class="comment">// ['parsnip', 'potato', 'celery', 'beetroot']</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> obj = &#123;</span><br><span class="line">    length: <span class="number">0</span>,</span><br><span class="line">    addElem: <span class="function"><span class="keyword">function</span> <span class="title">addElem</span> (<span class="params">elem</span>) </span>&#123;</span><br><span class="line">        <span class="comment">// obj.length is automatically incremented </span></span><br><span class="line">        <span class="comment">// every time an element is added.</span></span><br><span class="line">        [].push.call(<span class="keyword">this</span>, elem);</span><br><span class="line">    &#125;,</span><br><span class="line">    popElem: <span class="function"><span class="keyword">function</span> <span class="title">addElem</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="comment">// obj.length is automatically incremented </span></span><br><span class="line">        <span class="comment">// every time an element is added.</span></span><br><span class="line">        <span class="keyword">return</span> [].pop.call(<span class="keyword">this</span>);</span><br><span class="line">        </span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">obj.addElem(&#123;&#125;)</span><br><span class="line">obj.addElem(&#123;<span class="attr">a</span>:<span class="number">1</span>&#125;)</span><br><span class="line"><span class="built_in">console</span>.log(obj.length) <span class="comment">// 2</span></span><br><span class="line"><span class="built_in">console</span>.log(obj.popElem()) <span class="comment">// &#123;a: 1&#125;</span></span><br><span class="line"><span class="built_in">console</span>.log(obj.length) <span class="comment">// 1</span></span><br></pre></td></tr></table></figure></div><h2 id="pop"><a href="#pop" class="headerlink" title="pop"></a>pop</h2><p>pop()方法从数组中删除最后一个元素，并返回该元素的值。此方法更改数组的长度。此方法<b>会更改现有数组</b>。</p><blockquote><p>如果你在一个空数组上调用 pop()，它返回 undefined。</p></blockquote><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Array.pop()</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 class="doctag">@param <span class="type">&#123;*&#125;</span> </span>从数组中删除的元素(当数组为空时返回undefined)。</span></span><br><span class="line"><span class="comment"> */</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="built_in">console</span>.log(arr.pop()) <span class="comment">// 3</span></span><br><span class="line"><span class="built_in">console</span>.log(arr) <span class="comment">// [1,2]</span></span><br></pre></td></tr></table></figure></div><h2 id="shift"><a href="#shift" class="headerlink" title="shift"></a>shift</h2><p>shift() 方法从数组中删除第一个元素，并返回该元素的值。此方法更改数组的长度。此方法<b>会更改现有数组</b>。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Array.shift()</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 class="doctag">@param <span class="type">&#123;*&#125;</span> </span>从数组中删除的元素(当数组为空时返回undefined)。</span></span><br><span class="line"><span class="comment"> */</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="built_in">console</span>.log(arr.shift()) <span class="comment">// 1</span></span><br><span class="line"><span class="built_in">console</span>.log(arr) <span class="comment">// [2,3]</span></span><br></pre></td></tr></table></figure></div><h2 id="unshift"><a href="#unshift" class="headerlink" title="unshift"></a>unshift</h2><p>unshift() 方法将一个或多个元素添加到数组的开头，并返回该数组的新长度。此方法<b>会更改现有数组</b>。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Array.unshift(elementN)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>elementN =&gt; 要添加到数组开头的元素。</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 class="doctag">@param <span class="type">&#123;Number&#125;</span> </span>当一个对象调用该方法时，返回其 length 属性值。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> arr = [<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>]</span><br><span class="line"><span class="built_in">console</span>.log(arr.unshift(<span class="number">1</span>,<span class="number">2</span>)) <span class="comment">// 5</span></span><br><span class="line"><span class="built_in">console</span>.log(arr) <span class="comment">// [1,2,3,4,5]</span></span><br></pre></td></tr></table></figure></div><blockquote><p>但实际上，我们不建议用 unshift 方法往前面添加元素，为什么呢？从原理就可以知道，unshift()的效率是较低的。原因是，它每添加一个元素，都要把现有元素往下移一个位置。</p></blockquote><p>下面我们简单测试一下，unshift 和 push 的性能区别</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">console</span>.time(<span class="string">'unshift所用时间'</span>)</span><br><span class="line"><span class="keyword">const</span> unshift = []</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; <span class="number">10000</span>; i++) &#123; 　　</span><br><span class="line">  unshift.unshift(i)</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">console</span>.timeEnd(<span class="string">'unshift所用时间'</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.time(<span class="string">'push所用时间'</span>)</span><br><span class="line"><span class="keyword">const</span> push = []</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; <span class="number">10000</span>; i++) &#123; 　　</span><br><span class="line">  push.push(i)</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">console</span>.timeEnd(<span class="string">'push所用时间'</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.time(<span class="string">'reverse所用时间'</span>)</span><br><span class="line">push.reverse(); </span><br><span class="line"><span class="built_in">console</span>.timeEnd(<span class="string">'reverse所用时间'</span>)</span><br></pre></td></tr></table></figure></div><p>大家可是复制一下这段代码，在我们数据量不是特别大的时候，他们的效率差别几十倍，因此，平时还是要慎用unshift()，特别是对大数组。此方法<b>会更改现有数组</b>。</p><p>但是我们可以通过 reverse 和 push 的方法实现我们 unshift ，比如</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> arr = [<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>]</span><br><span class="line"><span class="built_in">console</span>.log(arr.unshift(<span class="number">1</span>,<span class="number">2</span>)) <span class="comment">// 5</span></span><br><span class="line"><span class="built_in">console</span>.log(arr) <span class="comment">// [1,2,3,4,5]</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> arr2 = [<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>]</span><br><span class="line"><span class="comment">// 从前一个代码知道reverse的性能也很快</span></span><br><span class="line">arr2.reverse().push(<span class="number">2</span>,<span class="number">1</span>)</span><br><span class="line">arr2.reverse()</span><br><span class="line"><span class="built_in">console</span>.log(arr2) <span class="comment">// [1,2,3,4,5]</span></span><br></pre></td></tr></table></figure></div><h2 id="sort"><a href="#sort" class="headerlink" title="sort"></a>sort</h2><p>sort() 方法用原地算法对数组的元素进行排序，并返回数组。排序算法现在是稳定的。默认排序顺序是根据字符串Unicode码点。此方法<b>会更改现有数组</b>。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Array.sort(compareFunction)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>compareFunction =&gt; 用来指定按某种顺序进行排列的函数。如果省略，元素按照转换为的字符串的各个字符的Unicode位点进行排序。(可选)</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 class="doctag">@param <span class="type">&#123;Array&#125;</span> </span>排序后的数组。请注意，数组已原地排序，并且不进行复制。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> months = [<span class="string">'March'</span>, <span class="string">'Jan'</span>, <span class="string">'Feb'</span>, <span class="string">'Dec'</span>];</span><br><span class="line">months.sort() </span><br><span class="line"><span class="built_in">console</span>.log(months) <span class="comment">// ["Dec", "Feb", "Jan", "March"]</span></span><br></pre></td></tr></table></figure></div><blockquote><p>如果没有指明 compareFunction ，那么元素会按照转换为的字符串的诸个字符的Unicode位点进行排序。例如 “Banana” 会被排列到 “cherry” 之前。当数字按由小到大排序时，9 出现在 80 之前，但因为（没有指明 compareFunction），比较的数字会先被转换为字符串，所以在Unicode顺序上 “80” 要比 “9” 要靠前。</p></blockquote><p>所以我们怎么解决这个问题了，当然要使用我们的比较函数啦，而且通过我们的比较函数，可以实现很多我们需要的效果</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> arr = [<span class="string">"1"</span>,<span class="number">2</span>,<span class="string">"11"</span>,<span class="number">3</span>,<span class="string">"21"</span>]</span><br><span class="line"><span class="built_in">console</span>.log(arr.sort()) <span class="comment">// ["1", "11", 2, "21", 3]</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 通过比较函数解决该问题</span></span><br><span class="line"><span class="built_in">console</span>.log(arr.sort(<span class="function">(<span class="params">a,b</span>)=&gt;</span>a-b)) <span class="comment">// ["1", 2, 3, "11", "21"]</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">const</span> people = [&#123;<span class="attr">name</span>:<span class="string">'gating'</span>,<span class="attr">age</span>:<span class="number">18</span>&#125;,&#123;<span class="attr">name</span>:<span class="string">'family'</span>,<span class="attr">age</span>:<span class="number">16</span>&#125;,&#123;<span class="attr">name</span>:<span class="string">'blue'</span>,<span class="attr">age</span>:<span class="number">21</span>&#125;]</span><br><span class="line">people.sort(<span class="function">(<span class="params">a,b</span>)=&gt;</span>a.age - b.age) <span class="comment">// [&#123;name:'family',age:16&#125;,&#123;name:'gating',age:18&#125;,&#123;name:'blue',age:21&#125;]</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 当然，通过 sort + Math.random 可以很巧妙的实现数组洗牌（数组乱序）的算法（当然，这个也是不推荐的，不过他可以用在小项目中，当作一个小技巧）</span></span><br><span class="line"><span class="keyword">const</span> numbers = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>]</span><br><span class="line">numbers.sort(<span class="function"><span class="params">()</span>=&gt;</span><span class="built_in">Math</span>.random()<span class="number">-0.5</span>) <span class="comment">// 多尝试几次，看看每次出来的结果是不是不一样的</span></span><br></pre></td></tr></table></figure></div><blockquote><p>关于数组乱序，正确的解法应该是 Fisher–Yates Shuffle，复杂度 O(n)。其实它的思想非常的简单，遍历数组元素，将其与之前的任意元素交换。因为遍历有从前向后和从后往前两种方式，所以该算法大致也有两个版本的实现。</p></blockquote><p>这里送上两个方法，具体的请参考文末的数组乱序文章</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 从后往前的版本</span></span><br><span class="line"><span class="keyword">const</span> shuffle = <span class="function">(<span class="params">array</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">var</span> _array = array.concat();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = _array.length; i--;) &#123;</span><br><span class="line">        <span class="keyword">var</span> j = <span class="built_in">Math</span>.floor(<span class="built_in">Math</span>.random() * (i + <span class="number">1</span>));</span><br><span class="line">        <span class="keyword">var</span> temp = _array[i];</span><br><span class="line">        _array[i] = _array[j];</span><br><span class="line">        _array[j] = temp;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> _array;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 从前往后的版本</span></span><br><span class="line"><span class="keyword">const</span> shuffle =  <span class="function">(<span class="params">a</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">var</span> length = a.length;</span><br><span class="line">    <span class="keyword">var</span> shuffled = <span class="built_in">Array</span>(length);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> index = <span class="number">0</span>, rand; index &lt; length; index++) &#123;</span><br><span class="line">        rand = ~~(<span class="built_in">Math</span>.random() * (index + <span class="number">1</span>));</span><br><span class="line">        <span class="keyword">if</span> (rand !== index)</span><br><span class="line">            shuffled[index] = shuffled[rand];</span><br><span class="line">        shuffled[rand] = a[index];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> shuffled;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><h2 id="reverse"><a href="#reverse" class="headerlink" title="reverse"></a>reverse</h2><p>reverse() 方法将数组中元素的位置颠倒。第一个数组元素成为最后一个数组元素，最后一个数组元素成为第一个。此方法<b>会更改现有数组</b>。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Array.reverse(compareFunction)</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 class="doctag">@param <span class="type">&#123;Array&#125;</span> </span>颠倒后的数组</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> months = [<span class="string">'March'</span>, <span class="string">'Jan'</span>, <span class="string">'Feb'</span>, <span class="string">'Dec'</span>];</span><br><span class="line">months.reverse() </span><br><span class="line"><span class="built_in">console</span>.log(months) <span class="comment">// ["Dec", "Feb", "Jan", "March"]</span></span><br></pre></td></tr></table></figure></div><h2 id="join"><a href="#join" class="headerlink" title="join"></a>join</h2><p>join() 方法将一个数组（或一个类数组对象）的所有元素连接成一个字符串并返回这个字符串。此方法<b>不会更改原数组</b>。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Array.reverse(separator)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String&#125;</span> </span>如果需要(separator)，将分隔符转换为字符串。如果省略()，数组元素用逗号分隔。默认为 ","。</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 class="doctag">@param <span class="type">&#123;String&#125;</span> </span>一个所有数组元素连接的字符串。如果 arr.length 为0，则返回空字符串</span></span><br><span class="line"><span class="comment"> */</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="built_in">console</span>.log(arr.join()) <span class="comment">// 1,2,3</span></span><br><span class="line"><span class="built_in">console</span>.log(arr.join(<span class="string">'-'</span>)) <span class="comment">// 1-2-3</span></span><br></pre></td></tr></table></figure></div><h1 id="迭代-循环遍历-相关"><a href="#迭代-循环遍历-相关" class="headerlink" title="迭代(循环遍历)相关"></a>迭代(循环遍历)相关</h1><h2 id="every"><a href="#every" class="headerlink" title="every"></a>every</h2><p>every() 方法测试数组的所有元素是否都通过了指定函数的测试。此方法<b>不会更改原数组</b>。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * arr.every(callback,thisArg)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Function&#125;</span> </span>callback =&gt; 用来测试每个元素的函数</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Object&#125;</span> </span>thisArg =&gt; 可选参数，执行回调函数 mapFn 时 this 对象。 (可选)</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * callback 被调用时会传入三个参数：当前元素值，元素的索引，原数组。</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 class="doctag">@param <span class="type">&#123;Boolean&#125;</span> </span>为true时，所有元素是否都通过了指定函数的测试，否则就是没有通过</span></span><br><span class="line"><span class="comment">*/</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">4</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">56</span>]</span><br><span class="line"><span class="built_in">console</span>.log(arr.every(<span class="function"><span class="params">item</span>=&gt;</span>item&lt;<span class="number">10</span>)) <span class="comment">// false</span></span><br></pre></td></tr></table></figure></div><h2 id="some"><a href="#some" class="headerlink" title="some"></a>some</h2><p>some() 方法测试数组中的某些元素是否通过由提供的函数实现的测试。此方法<b>不会更改原数组</b>。</p><p>some() 和 every() 的区别在于every是数组所有元素都需要通过测试才返回true，而some只需要有一个通过测试就返回true</p><blockquote><p>对于放在空数组上的任何条件，此方法返回false。</p></blockquote><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * arr.some(callback,thisArg)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Function&#125;</span> </span>callback =&gt; 用来测试每个元素的函数</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Object&#125;</span> </span>thisArg =&gt; 可选参数，执行回调函数 mapFn 时 this 对象。 (可选)</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * callback 被调用时会传入三个参数：当前元素值，元素的索引，原数组。</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 class="doctag">@param <span class="type">&#123;Boolean&#125;</span> </span>为true时，有其中一个或多个元素是否都通过了指定函数的测试，否则就是没有通过</span></span><br><span class="line"><span class="comment">*/</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">4</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">56</span>]</span><br><span class="line"><span class="built_in">console</span>.log(arr.some(<span class="function"><span class="params">item</span>=&gt;</span>item&lt;<span class="number">10</span>)) <span class="comment">// true</span></span><br></pre></td></tr></table></figure></div><p>实际的应用场景，可以实现类似于 includes 的功能 （这里或许你会问，为啥实现类似 includes 的功能，直接用不就好了吗？</p><p>因为 includes 的兼容性太差了，IE是不兼容的，但是 some 在IE9下却可以使用，所以，你懂的</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> fruits = [<span class="string">'apple'</span>, <span class="string">'banana'</span>, <span class="string">'mango'</span>, <span class="string">'guava'</span>];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">checkAvailability</span>(<span class="params">arr, val</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> arr.some(<span class="function"><span class="params">arrVal</span> =&gt;</span> val === arrVal);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">checkAvailability(fruits, <span class="string">'kela'</span>);   <span class="comment">// false</span></span><br><span class="line">checkAvailability(fruits, <span class="string">'banana'</span>); <span class="comment">// true</span></span><br></pre></td></tr></table></figure></div><h2 id="forEach"><a href="#forEach" class="headerlink" title="forEach"></a>forEach</h2><p>forEach() 方法对数组的每个元素执行一次提供的函数。</p><p>for 和 forEach 的区别，forEach中已删除或者未初始化的项将被跳过（例如在稀疏数组上），而 for 不会</p><blockquote><p>注意： 没有办法中止或者跳出 forEach 循环，除了抛出一个异常。</p></blockquote><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * arr.forEach(callback,thisArg)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Function&#125;</span> </span>callback =&gt; 用来测试每个元素的函数</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Object&#125;</span> </span>thisArg =&gt; 可选参数，执行回调函数 mapFn 时 this 对象。 (可选)</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * callback 被调用时会传入三个参数：当前元素值，元素的索引，原数组。</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 class="doctag">@param <span class="type">&#123;undefined&#125;</span> <span class="variable">undefined</span></span></span></span><br><span class="line"><span class="comment">*/</span></span><br><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">arr1.forEach(<span class="function"><span class="params">item</span>=&gt;</span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(item) <span class="comment">// 1 2 3</span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// for 和 forEach 的区别</span></span><br><span class="line"><span class="keyword">const</span> arr2 = [<span class="number">1</span>,<span class="number">2</span>,,<span class="number">4</span>]</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> index = <span class="number">0</span>; index &lt; arr2.length; index++) &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(arr2[index]) <span class="comment">// 1 2 undefined 4</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">arr2.forEach(<span class="function"><span class="params">item</span>=&gt;</span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(item) <span class="comment">// 1 2 4</span></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure></div><p>实际的应用场景，我们可以通过 forEach 实现对对象的复制，当然，这只是对象复制的其中一种方式</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> copy = <span class="function"><span class="params">obj</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> copy = <span class="built_in">Object</span>.create(<span class="built_in">Object</span>.getPrototypeOf(obj))</span><br><span class="line">  <span class="keyword">const</span> propNames = <span class="built_in">Object</span>.getOwnPropertyNames(obj)</span><br><span class="line">  propNames.forEach(<span class="function"><span class="keyword">function</span>(<span class="params">name</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> desc = <span class="built_in">Object</span>.getOwnPropertyDescriptor(obj, name);</span><br><span class="line">    <span class="built_in">Object</span>.defineProperty(copy, name, desc);</span><br><span class="line">  &#125;)</span><br><span class="line">  <span class="keyword">return</span> copy;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> obj1 = &#123;<span class="attr">name</span>:<span class="string">'gating'</span>&#125;</span><br><span class="line"><span class="keyword">const</span> obj2 = copy(obj1)</span><br><span class="line"><span class="built_in">console</span>.log(obj2) <span class="comment">// &#123;name:'gating'&#125;</span></span><br></pre></td></tr></table></figure></div><h2 id="map"><a href="#map" class="headerlink" title="map"></a>map</h2><p>map() 方法创建一个新数组，其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。此方法<b>不会更改原数组</b>。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * arr.map(callback,thisArg)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Function&#125;</span> </span>callback =&gt; 用来测试每个元素的函数</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Object&#125;</span> </span>thisArg =&gt; 可选参数，执行回调函数 mapFn 时 this 对象。 (可选)</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * callback 被调用时会传入三个参数：当前元素值，元素的索引，原数组。</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 class="doctag">@param <span class="type">&#123;Array&#125;</span> </span>一个新数组，每个元素都是回调函数的结果。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></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="built_in">console</span>.log(arr.map(<span class="function"><span class="params">item</span>=&gt;</span>item*<span class="number">2</span>)) <span class="comment">// [2,4,6]</span></span><br></pre></td></tr></table></figure></div><p>map 的应用场景很多，比如我想给数组增加一个id的属性，再比如可以重新格式化我们的数组</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> arr = [&#123;<span class="attr">name</span>:<span class="string">'gating'</span>&#125;,&#123;<span class="attr">name</span>:<span class="string">'family'</span>&#125;]</span><br><span class="line"><span class="keyword">const</span> res = arr.map(<span class="function">(<span class="params">item,index</span>)=&gt;</span>&#123;</span><br><span class="line">  item.id = index</span><br><span class="line">  <span class="keyword">return</span> item</span><br><span class="line">&#125;)</span><br><span class="line"><span class="built_in">console</span>.log(res) <span class="comment">// [&#123;name:'gating',id:0&#125;,&#123;name:'family',id:1&#125;]</span></span><br></pre></td></tr></table></figure></div><h3 id="由一条面试题引发的思考"><a href="#由一条面试题引发的思考" class="headerlink" title="由一条面试题引发的思考"></a>由一条面试题引发的思考</h3><p>通常情况下，map 方法中的 callback 函数只需要接受一个参数，就是正在被遍历的数组元素本身。但这并不意味着 map 只给 callback 传了一个参数。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 下面的语句返回什么呢:</span></span><br><span class="line">[<span class="string">"1"</span>, <span class="string">"2"</span>, <span class="string">"3"</span>].map(<span class="built_in">parseInt</span>);</span><br><span class="line"><span class="comment">// 你可能觉的会是[1, 2, 3]</span></span><br><span class="line"><span class="comment">// 但实际的结果是 [1, NaN, NaN]</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 通常使用parseInt时,只需要传递一个参数.</span></span><br><span class="line"><span class="comment">// 但实际上,parseInt可以有两个参数.第二个参数是进制数.</span></span><br><span class="line"><span class="comment">// 可以通过语句"alert(parseInt.length)===2"来验证.</span></span><br><span class="line"><span class="comment">// map方法在调用callback函数时,会给它传递三个参数:当前正在遍历的元素, </span></span><br><span class="line"><span class="comment">// 元素索引, 原数组本身.</span></span><br><span class="line"><span class="comment">// 第三个参数parseInt会忽视, 但第二个参数不会,也就是说,</span></span><br><span class="line"><span class="comment">// parseInt把传过来的索引值当成进制数来使用.从而返回了NaN.</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">returnInt</span>(<span class="params">element</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">parseInt</span>(element, <span class="number">10</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">[<span class="string">'1'</span>, <span class="string">'2'</span>, <span class="string">'3'</span>].map(returnInt); <span class="comment">// [1, 2, 3]</span></span><br><span class="line"><span class="comment">// 意料之中的结果</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 也可以使用简单的箭头函数，结果同上</span></span><br><span class="line">[<span class="string">'1'</span>, <span class="string">'2'</span>, <span class="string">'3'</span>].map( <span class="function"><span class="params">str</span> =&gt;</span> <span class="built_in">parseInt</span>(str) );</span><br><span class="line"></span><br><span class="line"><span class="comment">// 一个更简单的方式:</span></span><br><span class="line">[<span class="string">'1'</span>, <span class="string">'2'</span>, <span class="string">'3'</span>].map(<span class="built_in">Number</span>); <span class="comment">// [1, 2, 3]</span></span><br><span class="line"><span class="comment">// 与`parseInt` 不同，下面的结果会返回浮点数或指数:</span></span><br><span class="line">[<span class="string">'1.1'</span>, <span class="string">'2.2e2'</span>, <span class="string">'3e300'</span>].map(<span class="built_in">Number</span>); <span class="comment">// [1.1, 220, 3e+300]</span></span><br></pre></td></tr></table></figure></div><h2 id="filter"><a href="#filter" class="headerlink" title="filter"></a>filter</h2><p>filter() 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。此方法<b>不会更改原数组</b>。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * arr.filter(callback,thisArg)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Function&#125;</span> </span>callback =&gt; 用来测试每个元素的函数</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Object&#125;</span> </span>thisArg =&gt; 可选参数，执行回调函数 mapFn 时 this 对象。 (可选)</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * callback 被调用时会传入三个参数：当前元素值，元素的索引，原数组。</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 class="doctag">@param <span class="type">&#123;Array&#125;</span> </span>过滤后的数组</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">const</span> arr = [&#123;<span class="attr">sex</span>:<span class="string">'male'</span>,<span class="attr">name</span>:<span class="string">'gating'</span>&#125;,&#123;<span class="attr">sex</span>:<span class="string">'female'</span>,<span class="attr">name</span>:<span class="string">'blue'</span>&#125;,&#123;<span class="attr">sex</span>:<span class="string">'male'</span>,<span class="attr">name</span>:<span class="string">'family'</span>&#125;]</span><br><span class="line"><span class="comment">// 过滤掉性别为 male 的人</span></span><br><span class="line"><span class="built_in">console</span>.log(arr.filter(<span class="function"><span class="params">item</span>=&gt;</span>item.sex===<span class="string">'female'</span>))</span><br></pre></td></tr></table></figure></div><p>实际的应用场景，通过可以 filter 过滤掉我们不需要的值</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> delListRep = <span class="function">(<span class="params">arr, key</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">var</span> keys = []</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; arr.length; i++) &#123;</span><br><span class="line">        keys.push(arr[i][key])</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> arr.filter(<span class="function">(<span class="params">ele, i, arr</span>) =&gt;</span> keys.indexOf(ele[key]) == i)</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">const</span> arr = [&#123;<span class="attr">sex</span>:<span class="string">'male'</span>,<span class="attr">name</span>:<span class="string">'gating'</span>&#125;,&#123;<span class="attr">sex</span>:<span class="string">'female'</span>,<span class="attr">name</span>:<span class="string">'blue'</span>&#125;,&#123;<span class="attr">sex</span>:<span class="string">'male'</span>,<span class="attr">name</span>:<span class="string">'gating'</span>&#125;]</span><br><span class="line"><span class="built_in">console</span>.log(delListRep(arr,<span class="string">'sex'</span>)) <span class="comment">//  [&#123;sex:'male',name:'gating'&#125;,&#123;sex:'female',name:'blue'&#125;]</span></span><br></pre></td></tr></table></figure></div><h2 id="reduce"><a href="#reduce" class="headerlink" title="reduce"></a>reduce</h2><p>reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行)，将其结果汇总为单个返回值。<b>不会更改原数组</b>。</p><blockquote><p>如果没有提供initialValue，reduce 会从索引1的地方开始执行 callback 方法，跳过第一个索引。如果提供initialValue，从索引0开始。</p></blockquote><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * arr.filter(callback,initialValue)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Function&#125;</span> </span>callback =&gt; 执行数组中每个值的函数</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>initialValue =&gt; 作为第一次调用 callback函数时的第一个参数的值。 如果没有提供初始值，则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。 (可选)</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * callback 被调用时会传入三个参数：accumulator，currentValue，currentIndex，array</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * accumulator =&gt; 累计器累计回调的返回值; 它是上一次调用回调时返回的累积值，或initialValue（见于下方）。</span></span><br><span class="line"><span class="comment"> * currentValue =&gt; 数组中正在处理的元素。</span></span><br><span class="line"><span class="comment"> * currentIndex =&gt; 数组中正在处理的当前元素的索引。 如果提供了initialValue，则起始索引号为0，否则为1。 (可选)</span></span><br><span class="line"><span class="comment"> * array =&gt; 调用reduce()的数组。 (可选)</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 class="doctag">@param <span class="type">&#123;*&#125;</span> </span>函数累计处理的结果</span></span><br><span class="line"><span class="comment">*/</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 class="number">4</span>];</span><br><span class="line"><span class="keyword">const</span> reducer = arr.reduce(<span class="function">(<span class="params">accumulator, currentValue</span>) =&gt;</span> accumulator + currentValue)</span><br><span class="line"><span class="built_in">console</span>.log(reducer) <span class="comment">// 10</span></span><br><span class="line"><span class="built_in">console</span>.log(arr) <span class="comment">// [1, 2, 3, 4]</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> res = arr.reduce(<span class="function">(<span class="params">accumulator, currentValue</span>) =&gt;</span> accumulator + currentValue,<span class="number">5</span>)</span><br><span class="line"><span class="built_in">console</span>.log(res) <span class="comment">// 15</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// initialValue 有值 和 没有值的区别</span></span><br><span class="line"><span class="comment">// 因为 reduce 里，如果没有提供初始值，那么 accumulator 会默认使用数组的第一个元素，因为取了数组的第一个元素了，那么下标当然没有必要再从0开始了</span></span><br><span class="line">arr.reduce(<span class="function">(<span class="params">accumulator, currentValue, index</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(index) <span class="comment">// 0 1 2 3</span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">arr.reduce(<span class="function">(<span class="params">accumulator, currentValue, index</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(index) <span class="comment">// 1 2 3</span></span><br><span class="line">&#125;,<span class="number">5</span>)</span><br></pre></td></tr></table></figure></div><p>看到了 reduce 的用法，实际上也可以想到，reduce 的参数这么丰富，是不是可以做很多我们想要的事，我想说，是的，在实际应用中，reduce 能做到的事，比我们想象中的还要多</p><p>比如，数组扁平化、数组去重、统计数组中每个元素出现的次数、根据属性对Object分类等等等等这种很cool的事，接下来，我们就一一用 reduce 实现吧</p><blockquote><p>reduce 很重要，请务必掌握这个方法 ps：字符串的 replace 也很重要哦！！！也要掌握</p></blockquote><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 根据所提供的字符分组</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Array&#125;</span> </span>arr =&gt; 数组</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String&#125;</span> </span>key =&gt; 需要分组的字段 </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">groupBy</span>(<span class="params">arr, key</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> arr.reduce(<span class="function"><span class="keyword">function</span> (<span class="params">newObj, obj</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (!newObj[obj[key]]) &#123;</span><br><span class="line">            newObj[obj[key]] = [];</span><br><span class="line">            newObj[obj[key]].push(obj);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            newObj[obj[key]].push(obj);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> newObj;</span><br><span class="line">    &#125;, &#123;&#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> people = [</span><br><span class="line">  &#123; <span class="attr">name</span>: <span class="string">'gating'</span>, <span class="attr">age</span>: <span class="number">18</span> &#125;,</span><br><span class="line">  &#123; <span class="attr">name</span>: <span class="string">'family'</span>, <span class="attr">age</span>: <span class="number">16</span> &#125;,</span><br><span class="line">  &#123; <span class="attr">name</span>: <span class="string">'blue'</span>, <span class="attr">age</span>: <span class="number">20</span> &#125;</span><br><span class="line">];</span><br><span class="line"><span class="keyword">const</span> groupedPeople = groupBy(people, <span class="string">'age'</span>) <span class="comment">// &#123;16: Array(1), 18: Array(1), 20: Array(1)&#125;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 统计数组中每个元素出现的次数 Object同理</span></span><br><span class="line"><span class="comment">// 这里用 Map 是为了避免 数字1 和 字符串1 同名键值导致的bug</span></span><br><span class="line"><span class="keyword">const</span> names = [<span class="string">'gating'</span>, <span class="string">'family'</span>, <span class="string">'gating'</span>, <span class="string">'blue'</span>, <span class="string">'family'</span>]</span><br><span class="line"><span class="keyword">const</span> countNum1 = <span class="function">(<span class="params">arr</span>) =&gt;</span> arr.reduce(<span class="function">(<span class="params">m, x</span>)=&gt;</span> m.set(x, (m.get(x) || <span class="number">0</span>) + <span class="number">1</span>), <span class="keyword">new</span> <span class="built_in">Map</span>())</span><br><span class="line"><span class="keyword">const</span> allName = countNum1(names) <span class="comment">// &#123;"gating" =&gt; 2, "family" =&gt; 2, "blue" =&gt; 1&#125;</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">let</span> arr = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">5</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">4</span>,<span class="number">4</span>,<span class="number">4</span>];</span><br><span class="line"><span class="keyword">let</span> result = arr.sort().reduce(<span class="function">(<span class="params">init, current</span>)=&gt;</span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(init.length===<span class="number">0</span> || init[init.length<span class="number">-1</span>]!==current)&#123;</span><br><span class="line">        init.push(current);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> init;</span><br><span class="line">&#125;, []);</span><br><span class="line"><span class="built_in">console</span>.log(result); <span class="comment">//[1,2,3,4,5]</span></span><br></pre></td></tr></table></figure></div><p>其实 reduce 还可以做很多更牛逼的事，看到这里，你是不是也想用 reduce 做更多强大的事呢？那就赶紧把 reduce 学会</p><h2 id="reduceRight"><a href="#reduceRight" class="headerlink" title="reduceRight"></a>reduceRight</h2><p>reduceRight() 方法接受一个函数作为累加器（accumulator）和数组的每个值（从右到左）将其减少为单个值。<b>不会更改原数组</b>。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * arr.filter(callback,initialValue)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Function&#125;</span> </span>callback =&gt; 执行数组中每个值的函数</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>initialValue =&gt; 作为第一次调用 callback函数时的第一个参数的值。 如果没有提供初始值，则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。 (可选)</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * callback 被调用时会传入三个参数：accumulator，currentValue，currentIndex，array</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * accumulator =&gt; 累计器累计回调的返回值; 它是上一次调用回调时返回的累积值，或initialValue（见于下方）。</span></span><br><span class="line"><span class="comment"> * currentValue =&gt; 数组中正在处理的元素。</span></span><br><span class="line"><span class="comment"> * currentIndex =&gt; 数组中正在处理的当前元素的索引。 如果提供了initialValue，则起始索引号为0，否则为1。 (可选)</span></span><br><span class="line"><span class="comment"> * array =&gt; 调用reduce()的数组。 (可选)</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 class="doctag">@param <span class="type">&#123;*&#125;</span> </span>函数累计处理的结果</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> arr = [[<span class="number">0</span>, <span class="number">1</span>], [<span class="number">2</span>, <span class="number">3</span>], [<span class="number">4</span>, <span class="number">5</span>]].reduceRight(</span><br><span class="line">  (accumulator, currentValue) =&gt; accumulator.concat(currentValue)</span><br><span class="line">)</span><br><span class="line"><span class="built_in">console</span>.log(arr) <span class="comment">// [4, 5, 2, 3, 0, 1]</span></span><br></pre></td></tr></table></figure></div><p>reduce 与 reduceRight 之间的区别</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> arr = [<span class="string">'1'</span>, <span class="string">'2'</span>, <span class="string">'3'</span>, <span class="string">'4'</span>, <span class="string">'5'</span>]; </span><br><span class="line"><span class="keyword">const</span> left  = arr.reduce(<span class="function">(<span class="params">prev, cur</span>)=&gt;</span>prev + cur); </span><br><span class="line"><span class="keyword">const</span> right = arr.reduce(<span class="function">(<span class="params">prev, cur</span>)=&gt;</span>prev + cur); </span><br><span class="line"><span class="built_in">console</span>.log(left);  <span class="comment">// "12345"</span></span><br><span class="line"><span class="built_in">console</span>.log(right); <span class="comment">// "54321"</span></span><br></pre></td></tr></table></figure></div><p>由于 reduceRight 和 reduce 区别不是很大，这里的例子就参考 reduce 就行了</p><h1 id="迭代相关-生成一个迭代器"><a href="#迭代相关-生成一个迭代器" class="headerlink" title="迭代相关(生成一个迭代器)"></a>迭代相关(生成一个迭代器)</h1><p>暂时没有太多的应用场景，不详细讲解</p><h2 id="entries"><a href="#entries" class="headerlink" title="entries"></a>entries</h2><p>entries() 方法返回一个新的Array Iterator对象，该对象包含数组中每个索引的键/值对。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * arr.entries()</span></span><br><span class="line"><span class="comment"> * 返回值</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Iterator&#125;</span> </span>一个新的 Array 迭代器对象</span></span><br><span class="line"><span class="comment">*/</span></span><br><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> iterator1 = arr1.entries();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 迭代器通用的两种迭代方式（entries、keys、value同样）</span></span><br><span class="line"><span class="comment">// 1. for of 循环</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> number <span class="keyword">of</span> iterator1) &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(number) <span class="comment">// [0, 1] ， [1, 2] ， [2, 3] 输出三次</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> arr2 = [<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> iterator2 = arr2.entries();</span><br><span class="line"><span class="built_in">console</span>.log(iterator2.next().value); <span class="comment">// [0, 1]</span></span><br><span class="line"><span class="built_in">console</span>.log(iterator2.next().value); <span class="comment">// [1, 2]</span></span><br><span class="line"><span class="built_in">console</span>.log(iterator2.next().value); <span class="comment">// [2, 3]</span></span><br><span class="line"><span class="built_in">console</span>.log(iterator2.next().value); <span class="comment">// undefined，并且 iterator2.next().done 变成true</span></span><br></pre></td></tr></table></figure></div><h2 id="keys"><a href="#keys" class="headerlink" title="keys"></a>keys</h2><p>keys() 方法返回一个包含数组中每个索引键的Array Iterator对象。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * arr.keys()</span></span><br><span class="line"><span class="comment"> * 返回值</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Iterator&#125;</span> </span>一个新的 Array 迭代器对象</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">const</span> arr = [<span class="string">'a'</span>,<span class="string">'b'</span>,<span class="string">'c'</span>]</span><br><span class="line"><span class="keyword">const</span> iterator = arr.keys();</span><br><span class="line">  </span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> key <span class="keyword">of</span> iterator) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(key); <span class="comment">//  0 1 2</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><h2 id="values"><a href="#values" class="headerlink" title="values"></a>values</h2><p>values() 方法返回一个新的 Array Iterator 对象，该对象包含数组每个索引的值</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * arr.values()</span></span><br><span class="line"><span class="comment"> * 返回值</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Iterator&#125;</span> </span>一个新的 Array 迭代器对象</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">const</span> arr = [<span class="string">'a'</span>,<span class="string">'b'</span>,<span class="string">'c'</span>]</span><br><span class="line"><span class="keyword">const</span> iterator = arr.values()</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> value <span class="keyword">of</span> iterator) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(key) <span class="comment">// 'a' 'b' 'c'</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><h1 id="不知名的两个方法"><a href="#不知名的两个方法" class="headerlink" title="不知名的两个方法"></a>不知名的两个方法</h1><h2 id="toString-NaN"><a href="#toString-NaN" class="headerlink" title="toString"></a>toString</h2><p>toString() 返回一个字符串，表示指定的数组及其元素</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * arr.toString()</span></span><br><span class="line"><span class="comment"> * 返回值</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String&#125;</span> </span>一个表示指定的数组及其元素的字符串。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">const</span> arr = [<span class="string">'a'</span>,<span class="string">'b'</span>,<span class="string">'c'</span>]</span><br><span class="line"><span class="built_in">console</span>.log(arr.toString()) <span class="comment">// 'a','b','c'</span></span><br></pre></td></tr></table></figure></div><h2 id="toLocaleString-NaN"><a href="#toLocaleString-NaN" class="headerlink" title="toLocaleString"></a>toLocaleString</h2><p>toLocaleString() 返回一个字符串表示数组中的元素。数组中的元素将使用各自的 toLocaleString 方法转成字符串，这些字符串将使用一个特定语言环境的字符串（例如一个逗号 “,”）隔开。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * arr.toLocaleString(locales,options)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String?&#125;</span>  </span>locales =&gt; 带有BCP 47语言标记的字符串或字符串数组，关于locales参数的形式与解释，请看Intl页面。(可选)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Object&#125;</span>  </span>options =&gt; 一个可配置属性的对象，对于数字 Number.prototype.toLocaleString()，对于日期Date.prototype.toLocaleString() (可选)</span></span><br><span class="line"><span class="comment"> * 返回值</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;String&#125;</span> </span>表示数组元素的字符串</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">var</span> array1 = [<span class="number">1</span>, <span class="string">'a'</span>, <span class="keyword">new</span> <span class="built_in">Date</span>(<span class="string">'21 Dec 1997 14:12:00 UTC'</span>)]</span><br><span class="line"><span class="keyword">var</span> localeString = array1.toLocaleString(<span class="string">'en'</span>, &#123;<span class="attr">timeZone</span>: <span class="string">"UTC"</span>&#125;)</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(localeString) <span class="comment">// 1,a,12/21/1997, 2:12:00 PM</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> prices = [<span class="string">'￥7'</span>, <span class="number">500</span>, <span class="number">8123</span>, <span class="number">12</span>] </span><br><span class="line">prices.toLocaleString(<span class="string">'ja-JP'</span>, &#123; <span class="attr">style</span>: <span class="string">'currency'</span>, <span class="attr">currency</span>: <span class="string">'JPY'</span> &#125;) <span class="comment">// "￥7,￥500,￥8,123,￥12"</span></span><br></pre></td></tr></table></figure></div><p>这个方法我用的真不多，所以这里还是直接放上<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/toLocaleString" target="_blank" rel="noopener">MDN的地址</a>供大家了解，或许以后就用到了呢？</p><h1 id="扁平化数组"><a href="#扁平化数组" class="headerlink" title="扁平化数组"></a>扁平化数组</h1><p>本来这个我写这篇博客的时候，看到<code>MDN</code>写着<code>這是一個實驗中的功能</code>,所以我就没有记录下来，不过最近再看一眼，发现那个<code>waring</code>已经去掉了，所以就写一下这两个方法</p><h2 id="flat"><a href="#flat" class="headerlink" title="flat"></a>flat</h2><p>flat() 方法返回一个包含将数组与子数组中所有元素的新数组。<strong>不会更改原数组</strong>。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * arr.flat([depth])</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;depth&#125;</span>  </span>depth =&gt; 指定要提取嵌套数组的结构深度，默认值为 1。返回值(可选)</span></span><br><span class="line"><span class="comment"> * 返回值</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Array&#125;</span> </span>一个包含将数组与子数组中所有元素的新数组。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">var</span> arr1 = [<span class="number">1</span>,[<span class="number">2</span>],<span class="number">3</span>]</span><br><span class="line"><span class="built_in">console</span>.log(arr1.flat()) <span class="comment">// [1,2,3]</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> arr2 = [<span class="number">1</span>,[<span class="number">2</span>,[<span class="number">3</span>]]]</span><br><span class="line"><span class="built_in">console</span>.log(arr2.flat()) <span class="comment">// [1,2,[3]]</span></span><br><span class="line"><span class="built_in">console</span>.log(arr2.flat(<span class="number">2</span>)) <span class="comment">// [1,2,3]</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用 Infinity，可展开任意深度的嵌套数组</span></span><br><span class="line"><span class="keyword">var</span> arr3 = [<span class="number">1</span>, <span class="number">2</span>, [<span class="number">3</span>, <span class="number">4</span>, [<span class="number">5</span>, <span class="number">6</span>, [<span class="number">7</span>, <span class="number">8</span>, [<span class="number">9</span>, <span class="number">10</span>]]]]]</span><br><span class="line"><span class="built_in">console</span>.log(arr3.flat(<span class="literal">Infinity</span>)) <span class="comment">// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 此外，flat还会扁平化数组空项</span></span><br><span class="line"><span class="keyword">var</span> arr4 = [<span class="number">1</span>, <span class="number">2</span>, , <span class="number">4</span>, <span class="number">5</span>]</span><br><span class="line"><span class="built_in">console</span>.log(arr3.flat()) <span class="comment">// [1, 2, 4, 5]</span></span><br></pre></td></tr></table></figure></div><p>看到这里，你也许会发现，这个不就是我们上边通过<code>reduce</code>实现的扁平化方法吗？没错，es6之后默认给我们自带了这个方法（👍）</p><h2 id="flatMap"><a href="#flatMap" class="headerlink" title="flatMap"></a>flatMap</h2><p>顾名思义啦，从名字我们就可以知道，这个方法就是即执行 <code>map</code> 又执行 <code>flat</code>,对此官方的说法是，首先使用映射函数映射每个元素，然后将结果压缩成一个新数组。它与 <code>map</code> 连着深度值为1的 <code>flat</code> 几乎相同，但 flatMap 通常在合并成一种方法的效率稍微高一些。</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * arr.flatMap(callback,thisArg)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Function&#125;</span> </span>callback =&gt; 用来测试每个元素的函数</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Object&#125;</span> </span>thisArg =&gt; 可选参数，执行回调函数 mapFn 时 this 对象。 (可选)</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * callback 被调用时会传入三个参数：当前元素值，元素的索引，原数组。</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 class="doctag">@param <span class="type">&#123;Array&#125;</span> </span>一个新数组，每个元素都是回调函数的结果,并且结构深度 depth 值为1。</span></span><br><span class="line"><span class="comment">*/</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="built_in">console</span>.log(arr.flatMap(<span class="function"><span class="params">x</span>=&gt;</span>[x*<span class="number">2</span>])) <span class="comment">// [2,4,6]</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 其实就是相当于，先执行 map 在执行 flat</span></span><br><span class="line"><span class="built_in">console</span>.log(arr.map(<span class="function"><span class="params">x</span>=&gt;</span>[x*<span class="number">2</span>]).flat())</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 但其实还有更有意思的,众所周知我们都知道 map 会返回输入数组一样长度的数组</span></span><br><span class="line"><span class="comment">// 而 flatMap 不是，因为他拥有了 flat</span></span><br><span class="line"><span class="comment">// 假设我们有个场景，我想通过 flatMap 过滤掉年龄大于50岁的人</span></span><br><span class="line"><span class="keyword">var</span> people = [&#123;<span class="attr">name</span>:<span class="string">"gating"</span>,<span class="attr">age</span>:<span class="number">18</span>&#125;,&#123;<span class="attr">name</span>:<span class="string">"family"</span>,<span class="attr">age</span>:<span class="number">20</span>&#125;,&#123;<span class="attr">name</span>:<span class="string">"blue"</span>,<span class="attr">age</span>:<span class="number">60</span>&#125;]</span><br><span class="line"><span class="built_in">console</span>.log(people.flatMap(<span class="function"><span class="params">i</span>=&gt;</span>i.age&gt;=<span class="number">60</span>?[]:[i])) <span class="comment">// [&#123;name:"gating",age:18&#125;,&#123;name:"family",age:20&#125;]</span></span><br></pre></td></tr></table></figure></div><p>通过过滤那个例子，我们就可以看出, 输出的列表长度可以不同于输入的列表长度。这也许也是 <code>flatMap</code> 可玩之处（🤤）</p><h1 id="文中某些知识点参考链接"><a href="#文中某些知识点参考链接" class="headerlink" title="文中某些知识点参考链接"></a>文中某些知识点参考链接</h1><p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#Redefining_the_length_property_of_an_Array_object" target="_blank" rel="noopener">重定义数组对象的 length 属性</a></p><p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array" target="_blank" rel="noopener">MDN地址,文中很多例子都采用于MDN，推荐</a></p><p><a href="https://github.com/lifesinger/blog/issues/175" target="_blank" rel="noopener">数组类型判断，玉伯大佬博客，推荐</a></p><p><a href="https://github.com/hanzichi/underscore-analysis/issues/15" target="_blank" rel="noopener">JavaScript 数组乱序</a></p><p><a href="https://www.h5jun.com/post/array-shuffle.html" target="_blank" rel="noopener">数组的完全随机排列</a></p><blockquote><p>鉴于 reduce 和 reduceRight 太过好用（太过牛逼），这里提供他们两个的MDN地址</p></blockquote><p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce" target="_blank" rel="noopener">reduce</a></p><p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/ReduceRight" target="_blank" rel="noopener">reduceRight</a></p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>针对于我们一开始关心的两个问题，我在这里做一个小小的总结</p><blockquote><p>ps: 对于测试性的方法，这里就不做展开了，因为用不上= =</p></blockquote><table><thead><tr><th align="center">方法名</th><th align="center">返回值</th><th align="center">是否修改原数组</th></tr></thead><tbody><tr><td align="center">concat</td><td align="center">返回合并后的 <code>Array</code> 实例</td><td align="center">否</td></tr><tr><td align="center">copyWithin</td><td align="center">改变了的数组</td><td align="center">是</td></tr><tr><td align="center">entries</td><td align="center">一个新的 <code>Array</code> 迭代器对象( [key,value])</td><td align="center">否</td></tr><tr><td align="center">every</td><td align="center">布尔值，表示数组中所有元素是否通过 every 测试</td><td align="center">否</td></tr><tr><td align="center">fill</td><td align="center">修改后的数组</td><td align="center">是</td></tr><tr><td align="center">filter</td><td align="center">一个新的通过测试的元素的集合的数组，如果没有通过测试则返回空数组</td><td align="center">否</td></tr><tr><td align="center">find</td><td align="center">当某个元素通过 <code>callback</code> 的测试时，返回数组中的一个值，否则返回 <code>undefined</code>。</td><td align="center">否</td></tr><tr><td align="center">findIndex</td><td align="center">返回数组中满足提供的测试函数的第一个元素的索引。否则返回-1。</td><td align="center">否</td></tr><tr><td align="center">forEach</td><td align="center"><code>undefined</code></td><td align="center">否</td></tr><tr><td align="center">includes</td><td align="center">布尔值， 判断一个数组是否包含一个指定的值</td><td align="center">否</td></tr><tr><td align="center">indexOf</td><td align="center">首个被找到的元素在数组中的索引位置; 若没有找到则返回 -1</td><td align="center">否</td></tr><tr><td align="center">join</td><td align="center">一个所有数组元素连接的字符串。如果 <code>arr.length</code> 为0，则返回空字符串</td><td align="center">否</td></tr><tr><td align="center">keys</td><td align="center">一个新的 <code>Array</code> 迭代器对象( [key])</td><td align="center">否</td></tr><tr><td align="center">lastIndexOf</td><td align="center">数组中最后一个元素的索引，如未找到返回-1</td><td align="center">否</td></tr><tr><td align="center">map</td><td align="center">一个新数组，每个元素都是回调函数的结果</td><td align="center">否</td></tr><tr><td align="center">pop</td><td align="center">从数组中删除的元素(当数组为空时返回<code>undefined</code>)</td><td align="center">是</td></tr><tr><td align="center">push</td><td align="center">当调用该方法时，新的 <code>length</code> 属性值将被返回。</td><td align="center">是</td></tr><tr><td align="center">reduce</td><td align="center">函数累计处理的结果</td><td align="center">否</td></tr><tr><td align="center">reduceRight</td><td align="center">执行之后的返回值</td><td align="center">否</td></tr><tr><td align="center">reverse</td><td align="center">返回颠倒后的 <code>Array</code></td><td align="center">是</td></tr><tr><td align="center">shift</td><td align="center">返回被删除元素的值</td><td align="center">是</td></tr><tr><td align="center">slice</td><td align="center">一个含有提取元素的新数组</td><td align="center">否</td></tr><tr><td align="center">some</td><td align="center">布尔值，表示数组中至少有一个元素是否通过 every 测试</td><td align="center">否</td></tr><tr><td align="center">sort</td><td align="center">排序后的数组</td><td align="center">是</td></tr><tr><td align="center">splice</td><td align="center">由被删除的元素组成的一个数组。如果只删除了一个元素，则返回只包含一个元素的数组。如果没有删除元素，则返回空数组</td><td align="center">是</td></tr><tr><td align="center">toLocaleString</td><td align="center">表示数组元素的字符串</td><td align="center">否</td></tr><tr><td align="center">toString</td><td align="center">个表示指定的数组及其元素的字符串</td><td align="center">否</td></tr><tr><td align="center">unshift</td><td align="center">返回其 <code>length</code>属性值</td><td align="center">是</td></tr><tr><td align="center">values</td><td align="center">一个新的 <code>Array</code> 迭代器对象( [value])</td><td align="center">否</td></tr><tr><td align="center">flat</td><td align="center">一个包含将数组与子数组中所有元素的新数组</td><td align="center">否</td></tr><tr><td align="center">flatMap</td><td align="center">一个新的数组，其中每个元素都是回调函数的结果，并且结构深度 depth 值为1</td><td align="center">否</td></tr></tbody></table><p>最后，感谢各位观众老爷观看啦O(∩_∩)O</p>]]></content>
    
    <summary type="html">
    
      JavaScript 中的数组是一个很特别的存在，他不像Java ，专门搞了 List 这样的一整套的东西，JS终端数组完全可以当作栈或队列来使用，数组的四大操作：pop、push、shift、unshift。
    
    </summary>
    
    
      <category term="记录" scheme="https://gatings.cn/categories/%E8%AE%B0%E5%BD%95/"/>
    
    
      <category term="javascript" scheme="https://gatings.cn/tags/javascript/"/>
    
  </entry>
  
  <entry>
    <title>Git Bash主题风格</title>
    <link href="https://gatings.cn/2018-11-01/GitBash%E4%B8%BB%E9%A2%98%E9%A3%8E%E6%A0%BC/"/>
    <id>https://gatings.cn/2018-11-01/GitBash%E4%B8%BB%E9%A2%98%E9%A3%8E%E6%A0%BC/</id>
    <published>2018-10-31T16:00:00.000Z</published>
    <updated>2021-10-13T01:56:29.703Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>其实很多人都知道，window下的CMD非常不好用，远没有Linux或者一些SSH工具用起来方便。</p><p>这里我使用的是Git Bash,然而默认的Git Bash的样式不是那么的好看，这里我们修改一下他的样式</p><h1 id="自定义主题样式"><a href="#自定义主题样式" class="headerlink" title="自定义主题样式"></a>自定义主题样式</h1><p>在git bash窗口点击右键，选择option选项。然后save一下- -之后会在 <font color="#f60">C:\Users\gating(用户名)</font> 会生成一个.minttyrc文件， 也就是你的主题文件</p><p>这样子我们就可以根据这个主题文件修改我们的Git Bash主题了，我这里直接贴出我的配置，供参考</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">Code</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">Locale&#x3D;zh_CN</span><br><span class="line">Charset&#x3D;UTF-8</span><br><span class="line">Columns&#x3D;160</span><br><span class="line">Rows&#x3D;45</span><br><span class="line">Font&#x3D;Monaco</span><br><span class="line">FontHeight&#x3D;11</span><br><span class="line">Term&#x3D;xterm-256color</span><br><span class="line">CursorType&#x3D;block</span><br><span class="line">CursorBlinks&#x3D;yes</span><br><span class="line">Transparency&#x3D;low</span><br><span class="line">BoldAsFont&#x3D;yes</span><br><span class="line">AllowBlinking&#x3D;no</span><br><span class="line">Scrollbar&#x3D;none</span><br><span class="line">ScrollbackLines&#x3D;10000</span><br><span class="line">ClickTargetMod&#x3D;off</span><br><span class="line">ComposeKey&#x3D;shift</span><br><span class="line">ForegroundColour&#x3D;248,248,242</span><br><span class="line">BackgroundColour&#x3D;39,40,34</span><br><span class="line">CursorColour&#x3D;255,255,255</span><br><span class="line">Black&#x3D;39,40,34</span><br><span class="line">BoldBlack&#x3D;117,113,94</span><br><span class="line">Red&#x3D;249,38,114</span><br><span class="line">BoldRed&#x3D;204,6,78</span><br><span class="line">Green&#x3D;166,226,46</span><br><span class="line">BoldGreen&#x3D;122,172,24</span><br><span class="line">Yellow&#x3D;255,255,81</span><br><span class="line">BoldYellow&#x3D;240,169,69</span><br><span class="line">Blue&#x3D;144,255,255</span><br><span class="line">BoldBlue&#x3D;33,199,233</span><br><span class="line">Magenta&#x3D;174,129,255</span><br><span class="line">BoldMagenta&#x3D;126,51,255</span><br><span class="line">Cyan&#x3D;161,239,228</span><br><span class="line">BoldCyan&#x3D;95,227,210</span><br><span class="line">White&#x3D;248,248,242</span><br><span class="line">BoldWhite&#x3D;249,248,245</span><br></pre></td></tr></table></figure></div><p>此时，重启Git Bash,你会发现主题配置已经生效！！</p><p>最后，感谢各位观众老爷观看</p>]]></content>
    
    <summary type="html">
    
      其实很多人都知道，window下的CMD非常不好用，远没有Linux或者一些SSH工具用起来方便。这里我使用的是Git Bash,然而默认的Git Bash的样式不是那么的好看，这里我们修改一下他的样式
    
    </summary>
    
    
      <category term="记录" scheme="https://gatings.cn/categories/%E8%AE%B0%E5%BD%95/"/>
    
    
      <category term="Shell" scheme="https://gatings.cn/tags/Shell/"/>
    
  </entry>
  
  <entry>
    <title>awardRotate转盘插件文字模糊问题和图片加载问题</title>
    <link href="https://gatings.cn/2018-10-24/awardRotate%E8%BD%AC%E7%9B%98%E6%8F%92%E4%BB%B6%E6%96%87%E5%AD%97%E6%A8%A1%E7%B3%8A%E9%97%AE%E9%A2%98%E5%92%8C%E5%9B%BE%E7%89%87%E5%8A%A0%E8%BD%BD%E9%97%AE%E9%A2%98/"/>
    <id>https://gatings.cn/2018-10-24/awardRotate%E8%BD%AC%E7%9B%98%E6%8F%92%E4%BB%B6%E6%96%87%E5%AD%97%E6%A8%A1%E7%B3%8A%E9%97%AE%E9%A2%98%E5%92%8C%E5%9B%BE%E7%89%87%E5%8A%A0%E8%BD%BD%E9%97%AE%E9%A2%98/</id>
    <published>2018-10-23T16:00:00.000Z</published>
    <updated>2021-10-13T01:56:29.703Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>最近在做一个转盘抽奖页面,使用了awardRotate.js发现字体和图片都有模糊，绘制的时候图片绘制不全，搜索一下之后发现针对awardRotate的解决方法比较少，针对canvas的比较多，所以这边总结一下。</p><blockquote><p>代码如下：</p></blockquote><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">html</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">style</span>&gt;</span></span><br><span class="line"><span class="css"><span class="selector-class">.canvas</span>&#123;</span></span><br><span class="line">    width: 100%;</span><br><span class="line">&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">article</span> <span class="attr">id</span>=<span class="string">"turntable"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">canvas</span> <span class="attr">id</span>=<span class="string">"wheelcanvas"</span> <span class="attr">width</span>=<span class="string">"422"</span> <span class="attr">height</span>=<span class="string">"422"</span>&gt;</span><span class="tag">&lt;/<span class="name">canvas</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">img</span> <span class="attr">class</span>=<span class="string">"pointer"</span> <span class="attr">src</span>=<span class="string">"./static/img/start.png"</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">article</span>&gt;</span></span><br></pre></td></tr></table></figure></div><blockquote><p>至于为什么会变模糊，这和浏览器处理 canvas 的方式有关，相关的文章可以参考这篇 <a href="http://www.html5rocks.com/en/tutorials/canvas/hidpi/" target="_blank" rel="noopener">High DPI</a>，这里不作深入介绍。</p></blockquote><blockquote><p>解决这个问题的本质就是，先把canvas放大，然后在通过css限制会原始大小</p></blockquote><p>解决方案就是把canvas的行间样式的宽度设为手机端的最大像素值,我这里设置为1688像素，也就是422的4倍, 按照这个像素画完之后, width:100%又会把canvas的宽度缩小至父元素的宽度那么大, 因此整个canvas的宽度被缩小了, 大尺寸的canvas内容被缩小了之后肯定不会产生锯齿现象,</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">html</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">style</span>&gt;</span></span><br><span class="line"><span class="css"><span class="selector-class">.canvas</span>&#123;</span></span><br><span class="line">    width: 100%;</span><br><span class="line">&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">article</span> <span class="attr">id</span>=<span class="string">"turntable"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">canvas</span> <span class="attr">id</span>=<span class="string">"wheelcanvas"</span> <span class="attr">width</span>=<span class="string">"1688"</span> <span class="attr">height</span>=<span class="string">"1688"</span>&gt;</span><span class="tag">&lt;/<span class="name">canvas</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">img</span> <span class="attr">class</span>=<span class="string">"pointer"</span> <span class="attr">src</span>=<span class="string">"./static/img/start.png"</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">article</span>&gt;</span></span><br></pre></td></tr></table></figure></div><p>又因为我们把canva的内容缩小了四倍，接下来我们通过js把canva放大四倍即可，这样就可以解决我们字体和图片模糊的问题了，接下来就是处理所有图片加载的问题了</p><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> canvas = <span class="built_in">document</span>.getElementById(<span class="string">"wheelcanvas"</span>);</span><br><span class="line"><span class="keyword">if</span> (canvas.getContext) &#123;</span><br><span class="line">    <span class="keyword">var</span> ctx = canvas.getContext(<span class="string">"2d"</span>);</span><br><span class="line">    <span class="comment">// 放大四倍</span></span><br><span class="line">    ctx.scale(<span class="number">4</span>, <span class="number">4</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>众所周知，img都有onload事件,我们这里就可以借助Promise和img的onload事件来实现判断所有图片是否加载完成</p><blockquote><p>代码如下：</p></blockquote><div class="code-area-wrap"><div class="highlight-tools"><i class="fa fa-angle-down code-expand code-closed" aria-hidden="true"></i><div class="code_lang">javascript</div><div class="copy-notice"></div><i class="fa fa-clipboard" aria-hidden="true"></i></div><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> promiseAll = [],</span><br><span class="line">    img = [],</span><br><span class="line">    mulitImg = [</span><br><span class="line">        <span class="string">'./static/img/iphonexmax.png'</span>, <span class="string">'./static/img/beats3.png'</span>,</span><br><span class="line">        <span class="string">'./static/img/again.png'</span>, <span class="string">'./static/img/myhb.png'</span>,</span><br><span class="line">        <span class="string">'./static/img/iphonexr.png'</span>, <span class="string">'./static/img/mi.png'</span>,</span><br><span class="line">        <span class="string">'./static/img/again.png'</span>, <span class="string">'./static/img/myhb.png'</span></span><br><span class="line">    ]</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; <span class="number">8</span>; i++) &#123;</span><br><span class="line">    promiseAll[i] = <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">        img[i] = <span class="keyword">new</span> Image()</span><br><span class="line">        img[i].src = mulitImg[i]</span><br><span class="line">        img[i].onload = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">            <span class="comment">//第i张加载完成</span></span><br><span class="line">            resolve(img[i])</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">Promise</span>.all(promiseAll).then(<span class="function">(<span class="params">img</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// 你要处理的函数</span></span><br><span class="line">    <span class="comment">// drawRouletteWheel(img)</span></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure></div>]]></content>
    
    <summary type="html">
    
      最近在做一个转盘抽奖页面,使用了awardRotate.js发现字体和图片都有模糊，绘制的时候图片绘制不全，搜索一下之后发现针对awardRotate的解决方法比较少，针对canvas的比较多，所以这边总结一下。
    
    </summary>
    
    
      <category term="记录" scheme="https://gatings.cn/categories/%E8%AE%B0%E5%BD%95/"/>
    
    
      <category term="javascript" scheme="https://gatings.cn/tags/javascript/"/>
    
  </entry>
  
</feed>
