<?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://xovel.cn/"/>
  <updated>2021-02-10T05:38:59.847Z</updated>
  <id>https://xovel.cn/</id>
  
  <author>
    <name>xovel</name>
    <email>xovel@vip.qq.com</email>
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>2020 年回顾</title>
    <link href="https://xovel.cn/article/summary-2020.html"/>
    <id>https://xovel.cn/article/summary-2020.html</id>
    <published>2021-02-10T03:04:19.000Z</published>
    <updated>2021-02-10T05:38:59.847Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>转眼就快到春节了，是时候来一个不大不小的总结了。</p>
</blockquote>
<p>今年发生的事情比较多，也没有想过要去逐个去进行记录。</p>
<p>说起年度总结，已经有很长一段时间没有进行记录了。</p>
<p>还是按照上一次的总结的提纲来进行简单记录吧。</p>
<h3 id="公司项目"><a href="#公司项目" class="headerlink" title="公司项目"></a>公司项目</h3><p>回顾之下，2020 年做的项目太少了。公司项目只有一个，已经够了。个人项目有点散乱，在之前的项目维护的基础上进行了一番修订。</p>
<p>负责参与的项目为公司内部的核心 A 级项目，本人将负责的内容分阶段进行阐述。</p>
<h4 id="第一季度"><a href="#第一季度" class="headerlink" title="第一季度"></a>第一季度</h4><p>刚入职，那个时候刚巧碰上疫情，在家里远程办公，持续到 3 月份才返回职场。远程的效率其实远远没有现场高，诸多权限限制，得到无法顺利进行编译代码，开发效率不可谓不低。饶是如此，还是取得了不少的进展。</p>
<p>内部几个迭代之后发布的效果还算令人不错，我在其中负责多个模块的管理与开发，包括“企业信用管理”，“任企付”（后改名叫“易企发”），“疫情减息活动”，“数字证”，“数字保管箱”，“数字融资”（涉及到中小企业贷款），“企业资讯”等。</p>
<h4 id="第二季度"><a href="#第二季度" class="headerlink" title="第二季度"></a>第二季度</h4><p>由于公司高层的战略调整，我们的 APP 的功能点与公司内另外一个 APP 的功能点高度重合，于是有了项目合并的操作。合并之后还是叫数字口袋，内部叫法只是在前面加了一个新字以示区别。</p>
<p>新 APP 的功能略有调整，“易企发”由于有高度重合的现有产品，被裁撤。数字融资为两边都有的功能，进行合并。“企业信用管理”由于对接的第三方存在诸多问题，被无限期挂起。“企业资讯”改名叫“企业头条”（后定名为“数字头条”）。</p>
<p>可以看到，数字化运作是我们项目的一大亮点，也是一种运营手段。</p>
<p>期间，“数字证”，“数字保管箱”，“数字头条”是本人核心负责的模块，也参与“数字融资”的部分开发与管理工作。同时发挥自己的技术优势，开始对前端 WEB 开发进行规范性指导，解决开发团队面对的重点和难点问题。</p>
<h4 id="第三季度"><a href="#第三季度" class="headerlink" title="第三季度"></a>第三季度</h4><p>是的，大概从第二季度结尾期间，业务侧准备搞一点大事件，做营销活动。我被指派为营销活动的前端开发负责人。</p>
<p>之前原本计划负责的数字头条内部 CMS 系统建设由于本人的技术重心变更，业务也放弃了这方面的规划。</p>
<p>整个团队之前从未进行过营销活动方面的开发，可以说是毫无经验，我持有的部分经验无法带动带现有体系当中，所以做起来也多半是吃力不讨好的。</p>
<p>经过一番苦战，我主要负责新客大礼包的相关开发和管理，这也是业务准备重点推广的拉新活动。截止到 11 月份，新客大礼包的活动取得的效果卓著，拉新效果远远高出其他所有活动的总和。这一点也是令我们感到荣幸的。</p>
<p>部分开发人员由于合约到期，不能再进行驻场，也是经历几波迎来送往。</p>
<h4 id="第四季度"><a href="#第四季度" class="headerlink" title="第四季度"></a>第四季度</h4><p>团队内部对模块的管理出现了严重的分歧，管理层商议之下之后，决定将几个可以分离的模块分离出去以进行单独管理，以达到快速发版迭代的要求。于是本人被授命担任营销活动模块整体分离的负责人。由于分离需要进行全功能覆盖范围和迁移相关的兼容性测试，工作量较大，好在整个迁移过程平稳并未出现问题。迁移期间由于开发人员和测试人员配合到位，新客大礼包的营销效果一如既往的卓越。</p>
<p>迁移完成之后，由于当时的前端开发体系混乱（甚至有提到前端不需要架构、没有架构可言），本人被指派为前端架构优化整改的负责人之一。虽然说之一，但实际上之一可以拿掉的，另外几个负责人实际上由于各种各样的原因，并没有参与到其中。</p>
<p>在这期间，本人利用自己的技术优势，提供了大量的通用方法，也综合考虑不同的前端页面场景，着重探索了原生 APP 与 HTML5 页面之间的交互问题。提出的一些建议很快得到落实，给团队的整理开发能力上升了一个台阶。</p>
<p>另外，对于部分开发人员的代码风格混乱的场景，将之前建议的 lint 校验工具升级为强制校验。本质上是利用了 <code>git hooks</code> 在提交代码的时候进行代码检测。也算是自己对 Git 工程化辅助能力的一种很好的使用。</p>
<h4 id="综合回顾"><a href="#综合回顾" class="headerlink" title="综合回顾"></a>综合回顾</h4><p>在梳理和整合前端体系的时候，出现了很多令我捉摸不透的问题。这可能跟团队的性质有关。之前我们的团队的主要工作内容是创新性质的，而之后团队整合，变成了求稳。也或许是因为这样，诸多优秀的技术无法得到顺利的落地，在提升效率的道路上遇到了极为严重的阻碍。</p>
<p>在既有资源的支撑下，本人不遗余力的将诸多优秀的工具引入团队中，其中不乏包括优秀的 <code>webpack</code> 插件，极具指导性的风格指导，<code>eslint</code>，<code>shelljs</code>，<code>git hooks</code>，<code>stylus</code> 等。</p>
<p>核心依赖在确保不会出现重大问题的前提之下进行了升级，比如 <code>axios</code>，<code>core-js</code>，<code>vuex</code> 等工具，解决了一些已经被公开的安全漏洞。</p>
<p>期间也发现了团队内部的开发人员的水平普遍具有鲜明的个性，团队建设很多时候变成了一句空谈。可能心还是比较散漫吧，据说是被惯的。</p>
<h4 id="关于敏捷不得不说的一些事"><a href="#关于敏捷不得不说的一些事" class="headerlink" title="关于敏捷不得不说的一些事"></a>关于敏捷不得不说的一些事</h4><p>从我入职的时候开始，团队里面是有一个敏捷教练协助我们进行敏捷化转型。抛开敏捷转型的必要性和可执行性不谈，团队内部对于敏捷教练的态度是令我感到最为困惑的地方。</p>
<p>之前在拥有敏捷教练的团队里面待过，教练拥有非常高的权限，基本上是没有人敢顶撞教练的。然而在我们团队中间，敏捷教练仿佛成为了一个摆设，令不行，禁不止，转型过程谈不上成功。敏捷教练后半年退场，之后团队继续陷入混沌状态。</p>
<p>有一说一，在面对如此的一个团队的时候，可能没有人敢跳出来指正和纠偏吧。</p>
<p>本人对于敏捷的理解可能跟很多人的并不一致，我内心从来没有赞同过敏捷机制，但我尊重这个机制，毕竟在没有找到更加有效地方式之前，敏捷机制给团队带来的效果是显著的。</p>
<h3 id="个人项目"><a href="#个人项目" class="headerlink" title="个人项目"></a>个人项目</h3><h4 id="zob"><a href="#zob" class="headerlink" title="zob"></a><code>zob</code></h4><p>得益于团队内部开发团队能力的建设，我将搁置许久的 <code>zob</code> 项目捡了起来，并且更新了 <code>eslint</code> 插件，添加了一些规则。</p>
<h4 id="zmin"><a href="#zmin" class="headerlink" title="zmin"></a><code>zmin</code></h4><p>尚未整理和进行发布的执行压缩的工具，借助于 <code>image-min</code> 的能力，对常见图片 <code>jpg</code> 和 <code>png</code> 进行压缩，以减少其体积。</p>
<h3 id="人来人往"><a href="#人来人往" class="headerlink" title="人来人往"></a>人来人往</h3><h4 id="有人来"><a href="#有人来" class="headerlink" title="有人来"></a>有人来</h4><p>是的，新冠疫情缠绕我们一整年的时候，到目前为止还并未有丝毫退散之意，这足以令人唏嘘。在疫情笼罩一下，本人有幸能陪伴自己的爱人一路走来，并在 6 月份喜结连理，从此生命中多了一个至关重要的人。9 月份的婚宴，虽然不大，但已然是生命中美好的记忆点了。</p>
<h4 id="有人走"><a href="#有人走" class="headerlink" title="有人走"></a>有人走</h4><blockquote>
<p>逝者已去，生者不息。</p>
</blockquote>
<p>舅舅因疾病救治无效离开人世，给家族带来了悲伤难以挥去。文字太过悲伤，就不再对其进行描述了。</p>
<h3 id="吃喝玩乐"><a href="#吃喝玩乐" class="headerlink" title="吃喝玩乐"></a>吃喝玩乐</h3><h4 id="再回母校"><a href="#再回母校" class="headerlink" title="再回母校"></a>再回母校</h4><p>受限于疫情，妻子不太方便离深，故而并未去其他地方游玩。非要说一个，那就是我的母校湖南大学了。因为自己的身体原因，需要去大医院进行诊治，请了一个礼拜的假，带妻子一起去了长沙。在湘雅医院看病完成之后，便带她去了湖南大学。</p>
<p>彼时，地铁已经开通，可以直达我的母校。</p>
<h4 id="云南之行"><a href="#云南之行" class="headerlink" title="云南之行"></a>云南之行</h4><p>已经好几年没有出去旅游了，这一次，我们携手去往了云南，游玩了丽江古城，嬉戏于泸沽湖之上，还特意去探索了木府，本来应该有一篇游记进行记录的，奈何那个时候自己的重点压根不在文字上面，所以也就没有及时进行记录。等到后续有空，自己的记忆还没有淡去，进行添补也未尝不好。</p>
<h4 id="其他散点"><a href="#其他散点" class="headerlink" title="其他散点"></a>其他散点</h4><p>像洪湖公园，锦绣文化村，玫瑰海岸，大梅沙，海景公园此类市内景点，也是去游玩了一番的。只可惜莲花山的勒杜鹃画展妻子没有能够赶上，翻看单反随手拍的一些照片也算是一种参与方式吧。</p>
<h4 id="厨艺"><a href="#厨艺" class="headerlink" title="厨艺"></a>厨艺</h4><p>没错，因为要照顾妻子的缘故，我将多年搁置的厨艺捡了起来，也尝试许多不同的菜式。以前从来没有做过大菜的我，也进行了尝试。好在效果还算不错，味道也不是那么差，可以继续加油呢。</p>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>已经很久没有进行总结和展望了，或许混沌的前两年给自己带来的改变，或许是抑郁抗争从未停止，我已经不太愿意去跟其他人谈论自己的总结和梦想，甚至连自我总结也不会再轻易提及。</p>
<p>随风来，随风去。</p>
<hr>
<p>最后，总要说点什么新年愿望的，那么我的愿望很简单，就是希望她们母女平安健康，这也是我接下来很长一段时间的核心任务。</p>
<p>至于自我成长，太过遥远，我们先放一放，能将自己的博客或者 <code>camphor</code> 项目捡起来就已经是对自己最大的回报了。</p>
<p>加油！</p>
]]></content>
    
    <summary type="html">
    
      2020 年的一些得失，一些故事，一些感悟。
    
    </summary>
    
      <category term="生活" scheme="https://xovel.cn/categories/life/"/>
    
    
      <category term="经历" scheme="https://xovel.cn/tags/%E7%BB%8F%E5%8E%86/"/>
    
      <category term="2020" scheme="https://xovel.cn/tags/2020/"/>
    
      <category term="2021" scheme="https://xovel.cn/tags/2021/"/>
    
      <category term="回顾" scheme="https://xovel.cn/tags/%E5%9B%9E%E9%A1%BE/"/>
    
  </entry>
  
  <entry>
    <title>北京旅游攻略</title>
    <link href="https://xovel.cn/article/travel-in-beijing.html"/>
    <id>https://xovel.cn/article/travel-in-beijing.html</id>
    <published>2019-04-14T15:37:56.000Z</published>
    <updated>2021-02-10T06:01:42.422Z</updated>
    
    <content type="html"><![CDATA[<p>场景描述：在北京国际会议中心下榻，晚上出游。</p>
<ol>
<li>天安门及周围</li>
</ol>
<p>天安门广场附近是夜北京最美的地方之一。白日里端庄威严的天安门城楼此时被灯光映衬晶莹剔透，仿佛一处玉雕殿堂。城楼前高低起伏的硕大喷泉，仿佛随歌起舞的仙子，轻灵、飘逸潇洒自如，引得无数游人驻足，甚至有不少年轻人走到喷泉近前，伸出双手去迎接那一朵朵四溅开来的珠花。</p>
<blockquote>
<p>前门大街可以考虑去逛逛。</p>
</blockquote>
<p>前门大街是北京著名商业街。位于京城中轴线，北起前门月亮湾，南至天桥路口，与天桥南大街相连。明嘉靖二十九年(1550)建外城前是皇帝出城赴天坛、山川坛的御路，建外城后为外城主要南北街道。</p>
<p>前门大街悠久的历史，造就了这里的许多中华老字号，如六必居酱园、同仁堂药店、瑞蚨祥绸布店、长春堂药店、内联升鞋店、张一元茶庄，还有月盛斋的酱肉店、都一处的烧卖店等16处老字号分列道路两侧。</p>
<blockquote>
<p>长安街也是绝佳的去处。</p>
</blockquote>
<p>长安街在北京，以致全国的的地位十分重要，中国的象征——天安门和天安门广场，就在长安街上，长安街两侧还有人民大会堂、中南海和公安部、商务部等其他中央政府的机关。</p>
<blockquote>
<p>东单/西单</p>
</blockquote>
<p>位于天安门广场的两侧。西单有商业街，东单有很值得一逛的银街小店。东单离王府井不远，可以一并逛逛。</p>
<ol start="2">
<li>什刹海</li>
</ol>
<blockquote>
<p>强烈推荐。可以去看看钟楼和南锣鼓巷，还有一个很有意思的烟袋斜街。</p>
</blockquote>
<p>这里风光优美，而且人文积淀深厚，既有南国水乡的清幽秀丽，又兼具北方皇家的雍容气度，既能感受普通百姓的生活气息，亦能寻访帝王将相昔日的烙印与足迹，是了解老北京历史与文化的绝佳之地，也是目前北京城内老北京风貌保存最完好的地方。</p>
<p>什刹海景区包括前海、后海、西海等水域以及沿岸众多名胜古迹和胡同民居，这里有宋庆龄故居、郭沫若故居、恭王府花园、广化寺、火神庙、钟鼓楼和银锭桥等古迹。</p>
<p>什刹海的夜，层次丰富、韵味浓厚，什刹海的夜，五彩斑斓、光怪陆离。这里可以乘船游览、赏花、游胡同、享美食，街边的小店更是藏满了神秘与诱惑。夏夜，灯火通明的酒吧一条街人头涌动，中外游人云集，古典与现代在这里完美交融，传统与前卫在这里激情碰撞，品酒眺湖，微风扑面，湖光粼粼，游船杨柳，窃窃私语，别有一番风味。</p>
<p>什刹海景区具有大量典型的胡同和四合院，如金丝套地区的大、小金丝胡同，南、北官房胡同和后海北沿的鸦儿胡同以及白米斜街、烟袋斜街等。</p>
<p>依托胡同和四合院，什刹海地区自古以来就有许多富有特色的民裕活动，如放荷灯、泛舟游湖、宴饮赏荷、冰床围酌、大阅冰鞋等。至今，一些有生命力的民俗活动仍然在什刹海地区大量存在，如钓鱼、游泳、划船、赛艇、下棋、弹唱、消夏舞会等。</p>
<ol start="3">
<li>簋街</li>
</ol>
<p>北京簋街的美食一条街可谓是不容错过。嘉陵楼馋嘴城、两岸一家、哈哈镜鸭脖子、宽板凳老灶火锅、胡大饭馆、金簋簋街小山城、俞家小院猪脚巷、兰溪小馆、江边城外烤全鱼等店子都可以去逛逛。</p>
<ol start="4">
<li>三里屯</li>
</ol>
<p>全北京的时尚中心之一、文化中心之一，世界各地的艺术、戏剧、音乐、商务、时尚、餐饮和购物在三里屯融汇。可以在户外的商业街区随心漫步，也可以享受室内餐饮和娱乐的空间。花园、庭院和小巷点缀其间，格局开阔，整体设计凸现空间无界的概念，让人漫步其中，体会无穷乐趣。从经典到前卫，三里屯带给人的感觉是持续变幻出新的，体验也是多方位的。夜色中的三里屯，是潮人的天堂。</p>
<ol start="5">
<li>鸟巢</li>
</ol>
<p>夜幕降临，灯光自鸟巢那盘根错节的无数钢柱中透射出来，加上水中的倒影，柔和之美与力量之美完美融合。而水立方那透亮的蓝光，让人仿佛到了塞班、马尔代夫，置身于清澈的海水中，感受海天一色的美，感受洁白的浪花打在身上时的凉爽。</p>
<p>鸟巢与水立方，一静一动，一刚一柔，相互烘托又相互映衬，加之国家体育场的身份，成为北京夜色的经典。</p>
<ol start="6">
<li>世贸天阶</li>
</ol>
<p>世贸天阶地处首都CBD区域，非常适合下班后休闲或谈事。</p>
<p>晚上通常会放映一些灯光秀或设计秀，这里周末都会聚集一大批文艺青年，来拍照摄影。</p>
<ol start="7">
<li>朝阳公园/蓝色港湾国际商区</li>
</ol>
<p>一个充满了灿烂阳光、幽静湖水、优雅建筑、浪漫小径的购物公园，一个位于亚洲最大的城市公园——朝阳公园西北湖岸的欧式商业小镇。</p>
<ol start="8">
<li>中央广播电视塔/玉渊潭</li>
</ol>
<p>中央广播电视塔是集广播电视发射和旅游观光、餐饮娱乐为一体的综合性建筑，是北京现代化的一个重要标志。中央广播电视塔共分20余层。塔的设计充分考虑首都北京的特殊地理环境，力图表现塔的中国属性和北京特色。中央广播电视塔的夜景照明也是一绝，曾被评为首都夜景照明工作一等奖。在晚上，仰视中央电视塔，如同一个大灯笼一样挂在空中。</p>
<p>玉渊潭公园樱花节，有机会可以去看看。不过鉴于是晚上，估计看不清，另外，要门票 10 块。</p>
<ol start="9">
<li>清华/北大</li>
</ol>
<p>凑数的。</p>
]]></content>
    
    <summary type="html">
    
      搜集的一篇关于北京旅游的文章，仅供参考。
    
    </summary>
    
      <category term="生活" scheme="https://xovel.cn/categories/life/"/>
    
    
      <category term="旅游" scheme="https://xovel.cn/tags/%E6%97%85%E6%B8%B8/"/>
    
      <category term="北京" scheme="https://xovel.cn/tags/%E5%8C%97%E4%BA%AC/"/>
    
  </entry>
  
  <entry>
    <title>2018 年总结</title>
    <link href="https://xovel.cn/article/summary-2018.html"/>
    <id>https://xovel.cn/article/summary-2018.html</id>
    <published>2018-12-31T08:53:15.000Z</published>
    <updated>2019-01-04T16:41:41.251Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>转眼已经是 2018 年的最后一天了，是时候来好好做一个全面的总结了。</p>
</blockquote>
<p>以上这段文字是<del>抄袭</del>自去年的总结。</p>
<p>不知道该写点什么，后面再说。</p>
<hr>
<p>元旦前后，忙于事务，未能更新本篇文章。思来想去，一个象征性的总结还是需要的，所以又回来写点什么吧。</p>
<p>今年（应该叫做去年了，仍然还有中恍惚感）在这个博客上面发布的文章数量竟未过十，想来自己的重心确实已经不在文字创作上面了吧。包括对《圣血记》的创作，停滞的状态从来没有变过。</p>
<hr>
<p>回顾一下 2018 年，令人唏嘘的事情太多，经历的波折也可以专门拎出来写一篇传记了。</p>
<hr>
<blockquote>
<p>其实本篇文章并不是真正意义的总结，真正的总结不在这里。</p>
</blockquote>
<!--
### 走马观花般的回顾

- 1 月，在中电那边的公司出了点不大不小的问题，但也只能迎难而上。同时，筹划新公司的成立。
- 2 月，过年了，我经历了心态上的一个极大的变化，并由此创造出了《圣血记·外传》的章节。核心角色叫做张青荷（后来才改的名），原本叫做轩辕青荷，是张青姿（也就是轩辕青姿）的胞妹。个中细节，因为故事的《圣血记》的整体搁置，也没有继续创作下去了。算是一个新坑。
- 3 月，风风火火的小创业项目开张进入开发期，具体的内容会在后面详述。
- 4 月，诸君不知吾心碎，奈何桥头顾影怜。
- 5 月，参加了童年好友的婚礼，放纵骄狂的队伍又少了一员主力。
- 6 月，火热的年华，终于与中电宣告离别，也临近了内心第二次巨大的波动期。
- 7 月，充满灰暗的下半年由此拉开序幕，首先，工作离我而去；其次，理想逐渐失色；更有萧非玉的出现，宣告了自己人生的失败。关于萧非玉如何出现，又映射的是谁，我想我大概不会再愿意去提起了。
- 8 月，总会有一些人和事，让自己的内心无比煎熬，我开始了堕落之旅，害怕晚上入睡。很长时间以来，只要我一闭上眼，就出现了萧非玉的形象，我的潜意思告诫我，疏远之，疏远之，可终究逃不过。这个时候，有另一个人出现，但并没有缓解多少，我依然还是失落。
- 9 月，上个月成功“劝退”的家伙，遇到了一些问题。我并没有那么足够有耐心，但是还是有了那样的耐心。或许麻痹的我早已经忘记了，努力让自己沉浸于工作之中，并以此为乐。
- 10 月，逐渐的开始发现，即使是背靠大山，躲进山洞，一样会受到风雨侵袭。
- 11 月，内心真正的第一次开始让自己有了转型的念头，但这并不够强烈。已经迷失方向的自己犹如漂泊在大海中的一搜小木船，不知该随波逐流还是逆浪而上。
- 12 月，突然想亲力亲为做一个系统，但这也仅仅是一个念头一闪而过。更多实质性的操作还有待细化。在命名的时候又陷入了困境，正所谓最困难的两件事就是命名和缓存，真真。最终以非常严肃的 `TMD` 作为项目的设定名称，嗯，嗯嗯。

### 关于张青荷

> 这个人跟下面要讲述的萧非玉，可谓是 2018 年最能扼杀我整个人生激情的角色了。~~这里说到扼杀确实是有些过激了~~。

在大学年代，我构思过一部小说，是讲述大学时光，青春懵懂情怀。男主角没有名字，有一个代号叫做“断无忌”，也就是后来我用的一个网名。女主角是许欣羽，但我已经忘记了这个角色原本的人设了，另一个重要的女角色是张青姿。

张青姿有着与众不同的魅力，虽然只是一个年纪轻轻的学生，却有着洞穿世人内心的能力，也数次拯救断无忌于水火，两人结下了深厚的友谊。当初创作这个角色的目的也只是想表示男女之间的友谊本可以纯净无暇。

而这种性格设定，刚好与《圣血记》中的另一个幕后人物极为相似，于是就将其迁移了过期，也是叫做张青姿，身份变更为轩辕家族的后人，也就是轩辕青姿。

当然，这个设定是因为轩辕青荷的原因。这个角色的出现表示我的心已经倒下，将其作为故事角色进行创作意味着我准备将其放下。虽然后来没能够成功，但也是一种尝试，当然，这些都是后话了。

我曾经很喜欢跟她聊天的状态，就算是她不回复，我也能找到内心的静谧。我很希望这种状态能够持续下去，就如同 2017 年我的生日愿望那样。终究是现实残酷，本身条件和资质都不是那么优秀的我，并没有得到伊人的芳心，她最终并不是选择我，我也只好将对她的一片算不上真心的真心掩藏起来，不再轻易告诉任何人，包括我自己。

蓦然一看，我确实已经深陷内心的囹圄。轩辕青荷角色雏形构建出来之后，我开始对其进行打磨和塑造，我妄图将其塑造成一个尽善尽美的角色，可终究太悲苦。为了不让轩辕青荷的角色显得孤单，我给她加了一个姐姐，因为这个世界，可能只有姐姐才最懂得疼惜人了——于是轩辕青姿就是这么出现了。

由于轩辕青荷行走时间的名称叫做张青姿，所以轩辕青荷自然就变成了张青荷。这个这个角色名称和原型的由来。然而，最终，我选择跟另外一个小伙伴一起开创了一家公司，创作之路就此中断。

可叹这世间多少悲欢离合，挡不住人们前进的步伐。我们创业的故事不在本次的总结范围之内，就不过多言明了。

### 关于萧非玉

> 这可能又是一段令人唏嘘的往事。

我的人生，可能早就已经注定跟萧姓分不开关系了，我甚至怀疑我上辈子是不是欠了萧姓什么？

《圣血记》一开始的女主角设定就是萧姓，或许就是冥冥之中天注定吧。

原本我跟她相识于 2016 年，当年的我是那么的血性、年少轻狂又不懂爱恨情仇，所以才放任了她许久。直到 2018 年年初，心灰意冷的我发表了为数不多的消极朋友圈，却被她安慰到。

虽然后来得知并非是她主动，但这一点安慰对于我来说却意味着重生之光（或许换一个“救命稻草”更为贴切）。

但我并没有表现出什么，只当作是曾经的一个朋友的日常互动。直到姐姐跟我提起，她有意撮合我跟她。那个时候，我才明白，这世间的确存在轮回一说，毕竟，能在两年之后再相识，通过这样奇怪的方式，也是一种莫大的机缘。

我开始慌了阵脚，不知道该如何相处。可能是创业公司的一堆事情让我感觉龃龉难行，自己的很多想法得不到肯定，所以一贯选择避世和随和的我选择妥协，磨平菱角的我最终独自承担了前端技术开发的所有工作。

我曾经将压力告诉她，但是她似乎并没有什么举动。或许还是因为我自身的条件太差劲，根本就不够入她的法眼。平下心来，但想，换做是我，我也嫌弃着自己。

此情可待成追忆，只是当时已惘然，这句话是对我跟她很好的总结吧。所以，在 7 月的某个不知道是下着雨还是吹着风的日子，我最困难的阶段，她选择弃我而去。

决然。

我想，我本身确实不够优秀，即便是拥有能俯瞰这个社会阶层的收入跟地位，也不能走进她的心。自那以后，我灰暗的下半年才刚刚开始。

我曾想过自己的过失，对她造成的困惑与影响。很抱歉了，我的出现已经对她造成了诸多的困扰。

萧非玉的角色大概就是在那个时候诞生的吧。为什么叫做萧非玉呢？

因为，她确实是她原本的姓氏，我逃不出这个怪圈，我不会埋怨任何人。毕竟，学会接受现实也是一种成长，说起来我还要感谢她。她若不告诉我她本性萧，或许萧非玉这个角色就永远不会存在。

玉这个字原本就是《圣血记》核心角色萧玉的名字，至于中间的“非”字，其实是一个双关：

- 人间四月芳菲尽中的“芳菲”，取另一半“菲”，没有草丛，没有树林，没有绿色，所以将其部首草字头移除，就是“非”字了。
- “非”本意就是不是（最初的意向字，鸟的双翅展开，表示相背），既然最终她选择离开，自然就不是我所追寻的萧玉。

> 三个字拼起来，感觉有点那么一丝丝的韵味了。

然而，从那以后，我的精神世界开始变得不再正常，我总是在梦里恍惚出现了她和萧非玉的形象，我已经分不清谁是谁了。难道机缘害我如斯？

我不想去辩驳这个问题，有些事情，不要弄清楚或许是最好的状态。
-->
<h3 id="接手的项目"><a href="#接手的项目" class="headerlink" title="接手的项目"></a>接手的项目</h3><p>回顾之下，发现，2018 年做的项目有点多，容我梳理梳理。</p>
<p>嗯，捡重要的简单罗列一下吧。</p>
<h4 id="空港出行管理后台"><a href="#空港出行管理后台" class="headerlink" title="空港出行管理后台"></a>空港出行管理后台</h4><p>这个是 2017 年开始接手的项目，2018 年将其做了一番深入开发，重构了页面框架，支持了大部分网约车相关的后台管理场景。</p>
<h4 id="空港专车"><a href="#空港专车" class="headerlink" title="空港专车"></a>空港专车</h4><p>一个微信小程序，来实现微信端呼叫网约车的功能。接手之后对齐进行了完全重构，但也是使用的原生的方式进行开发，贴近官方的语法。</p>
<h4 id="车享智能"><a href="#车享智能" class="headerlink" title="车享智能"></a>车享智能</h4><p>这个就是 2018 年我们的创业项目，一个妄图赶上共享经济时代的产品。从产品规划到技术实现，包括硬件和软件，我们都深度参与其中，最终做出了一个能面市的小规模产品。</p>
<h4 id="先导客服平台"><a href="#先导客服平台" class="headerlink" title="先导客服平台"></a>先导客服平台</h4><p>这是 7 月份加入新公司新团队之后负责的一款产品，该产品分为三个端，前端均由我进行主力开发。解决在 APP 端没有客服系统的尴尬并对数据、订单、投诉反馈进行统一的管理。</p>
<h4 id="先导约车管理后台"><a href="#先导约车管理后台" class="headerlink" title="先导约车管理后台"></a>先导约车管理后台</h4><p>从单独扒取 API 到重建系统页面，一个标准的从传统开发模式（即以 jQuery 为核心的一套框架）转向三大框架之一的 Vue 的迁移例子。百分之百的还原了出去按钮权限控制（后台并没有直接给出相应接口）之外的所有功能，并对大部分业务场景做了抽象化封装。</p>
<h4 id="先导约车微信端"><a href="#先导约车微信端" class="headerlink" title="先导约车微信端"></a>先导约车微信端</h4><p>类似上面的方式，使用 Vue 技术栈重构微信公众号页面，并解决了诸多之前存在的问题。目前的叫车体验比之前精进的不止一两点。</p>
<h4 id="先导运维管理系统"><a href="#先导运维管理系统" class="headerlink" title="先导运维管理系统"></a>先导运维管理系统</h4><p>辅助进行问题跟踪和页面调试。</p>
<h4 id="先导企业用车管理端"><a href="#先导企业用车管理端" class="headerlink" title="先导企业用车管理端"></a>先导企业用车管理端</h4><p>可以单独拎出来作为子系统的约车子系统，实现了企业用车的 WEB 操作界面独立化。</p>
<h3 id="个人项目"><a href="#个人项目" class="headerlink" title="个人项目"></a>个人项目</h3><h4 id="trademark-classification"><a href="#trademark-classification" class="headerlink" title="trademark-classification"></a><code>trademark-classification</code></h4><p>出于学习的目的，构建了本项目，用于快速查询商标。</p>
<h4 id="node-wxpay-test"><a href="#node-wxpay-test" class="headerlink" title="node-wxpay-test"></a><code>node-wxpay-test</code></h4><p>同样也是出于学习的目的，也是为了更好的实现车享智能项目，制作了这个项目，实践微信公众号支付。</p>
<h4 id="draw-io"><a href="#draw-io" class="headerlink" title="draw.io"></a><code>draw.io</code></h4><p>这个项目是在 2018 年年底萌生的转型想法的雏形项目，是利用在线项目 <code>draw.io</code> 构建流程图的项目载体。</p>
<h4 id="api-test"><a href="#api-test" class="headerlink" title="api-test"></a><code>api-test</code></h4><p>一个简单版本的 API 测试工具，非常简单，简单到我已经将它给删了。</p>
<h4 id="vue-cli3-demo"><a href="#vue-cli3-demo" class="headerlink" title="vue-cli3-demo"></a><code>vue-cli3-demo</code></h4><p>听说 <code>Vue CLI 3</code> 更新了新版本，所以就有了这么一个演示版本。</p>
<h4 id="gank"><a href="#gank" class="headerlink" title="gank"></a><code>gank</code></h4><p>根据干货集中营提供的开放接口，使用 <code>vue</code> 构建了一个 WEB 端。</p>
<h4 id="garden"><a href="#garden" class="headerlink" title="garden"></a><code>garden</code></h4><p>之前位于博客的花园项目，独立出来了。主要是用来展示一些简单的工具和在线文档的。</p>
<h3 id="新增的技术栈"><a href="#新增的技术栈" class="headerlink" title="新增的技术栈"></a>新增的技术栈</h3><p>很惭愧，2018 年在技术栈可谓是毫无建树，并没有追加什么新技术栈。只是稳固了这么久以来使用到的一些技术和知识，特别是 <code>vue</code>，目前已经逐渐变成一个重度使用者了。</p>
<p><code>node</code> 是用的逐渐多了起来，但很多也只是停留在个人实验性阶段，并没有实际的应用，顶多也只是做一个中间件或者转发请求，这些原本使用 <code>nginx</code> 就可以轻松做到。</p>
<h3 id="尝试过的技术"><a href="#尝试过的技术" class="headerlink" title="尝试过的技术"></a>尝试过的技术</h3><ul>
<li><code>react</code></li>
<li><code>dart</code></li>
<li><code>flutter</code></li>
<li><code>redis</code></li>
<li><code>docker</code></li>
</ul>
<blockquote>
<p>仅仅是尝试，挺有意思的，学无止境，大概说的就是这个吧。</p>
</blockquote>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>去年提出的展望是这个：</p>
<blockquote>
<ul>
<li>遇见更好的自己。</li>
<li>让自己内心更加平静。</li>
<li>努力提升自己的情商和魅力值。</li>
</ul>
</blockquote>
<p>很遗憾，只有第二条算做到了。第一条勉强也算，但是第三条的话，因为 7 月份的时候一些事情的发生，苗头被迅速抹杀，同时也直接造就了《圣血记外传之萧非玉》的萌芽。相关细节由于过于悲苦，过往不愿再被提起，就此长久搁置。</p>
<p>回顾整个 2018 年，上半年我是阳光灿烂的存在，但是 7 月份的转机过大，灰霾阴郁持续笼罩。经历了长达半年的失眠抑郁，我却依然没有倒下，充满希望的我收拾自己的行囊，重新踏上征程。灰暗存在，或许只是为了衬托光明的存在。</p>
<h3 id="展望-2019"><a href="#展望-2019" class="headerlink" title="展望 2019"></a>展望 2019</h3><p>没什么，只有一点：继续让自己更加平静。</p>
<p>加油！</p>
<hr>
<p>其实，可以看得出来，我已经不太愿意去写博客了，很多东西其实在我的日志项目中已经做了记录，大部分的技术攻略或者资料均做了简单总结。</p>
<p>前路漫漫，珍重。</p>
]]></content>
    
    <summary type="html">
    
      2018 年年末大总结。
    
    </summary>
    
      <category term="生活" scheme="https://xovel.cn/categories/life/"/>
    
    
      <category term="总结" scheme="https://xovel.cn/tags/%E6%80%BB%E7%BB%93/"/>
    
      <category term="经历" scheme="https://xovel.cn/tags/%E7%BB%8F%E5%8E%86/"/>
    
      <category term="2018" scheme="https://xovel.cn/tags/2018/"/>
    
      <category term="2019" scheme="https://xovel.cn/tags/2019/"/>
    
  </entry>
  
  <entry>
    <title>VSCode 扩展</title>
    <link href="https://xovel.cn/article/vscode-extensions.html"/>
    <id>https://xovel.cn/article/vscode-extensions.html</id>
    <published>2018-10-26T16:05:43.000Z</published>
    <updated>2018-10-26T16:47:02.304Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>之前曾经介绍过一个跟扩展相关的文章，那已经是 18 个月前的事情了：<a href="/article/brackets-emmet.html">Brackets</a>。</p>
<p>近日将再记录两篇文章，一个是跟 <code>VSCode</code> 的扩展，另一个是 <code>Chrome</code> 浏览器的扩展。列出的扩展是本人<strong>使用过</strong>的。本文讲述 <code>VSCode</code> 的扩展。</p>
</blockquote>
<p>话不多说，直接进入正题，本次列举的扩展如下：</p>
<h3 id="Auto-Close-Tag-和-Auto-Rename-Tag"><a href="#Auto-Close-Tag-和-Auto-Rename-Tag" class="headerlink" title="Auto Close Tag 和 Auto Rename Tag"></a>Auto Close Tag 和 Auto Rename Tag</h3><p>这两个扩展通常会放在一起，前者是自动闭合 HTML 标签的，后者是自动重命名 HTML 标签的。</p>
<h3 id="Beautify"><a href="#Beautify" class="headerlink" title="Beautify"></a>Beautify</h3><p>代码格式化工具。</p>
<h3 id="Bracket-Pair-Colorizer"><a href="#Bracket-Pair-Colorizer" class="headerlink" title="Bracket Pair Colorizer"></a>Bracket Pair Colorizer</h3><p>括号匹配高亮。非常实用的一个扩展。</p>
<h3 id="Color-Picker"><a href="#Color-Picker" class="headerlink" title="Color Picker"></a>Color Picker</h3><p>CSS 颜色选择器。</p>
<h3 id="Debugger-for-Chrome"><a href="#Debugger-for-Chrome" class="headerlink" title="Debugger for Chrome"></a>Debugger for Chrome</h3><p>调试神器。</p>
<h3 id="Document-This"><a href="#Document-This" class="headerlink" title="Document This"></a>Document This</h3><p>自动对代码进行智能注释，兼容 <code>JSDoc</code>，可以作用域 <code>function</code>、<code>class</code> 等地方。默认快捷键为<strong>按两次</strong> <code>Ctrl+Alt+D</code>。</p>
<h3 id="ESLint"><a href="#ESLint" class="headerlink" title="ESLint"></a>ESLint</h3><p><code>ESLint</code> 在 <code>VSCode</code> 中的应用。</p>
<h3 id="Git-History"><a href="#Git-History" class="headerlink" title="Git History"></a>Git History</h3><p>管理 Git 提交记录的工具。在多项目中也表现优异。</p>
<h3 id="Guides"><a href="#Guides" class="headerlink" title="Guides"></a>Guides</h3><p>空白换行等的可视化标示。</p>
<h3 id="Markdown-PDF"><a href="#Markdown-PDF" class="headerlink" title="Markdown PDF"></a>Markdown PDF</h3><p>将 markdown 文件转换为 PDF，同时支持转换为 html、jpeg 和 png。</p>
<h3 id="Markdown-TOC"><a href="#Markdown-TOC" class="headerlink" title="Markdown TOC"></a>Markdown TOC</h3><p>给 markdown 生成目录并自动更新。</p>
<h3 id="Material-Icon-Theme"><a href="#Material-Icon-Theme" class="headerlink" title="Material Icon Theme"></a>Material Icon Theme</h3><p>一套 Material Design 风格的文件图标。</p>
<h3 id="Polacode"><a href="#Polacode" class="headerlink" title="Polacode"></a>Polacode</h3><p>将指定代码生成一张看起来挺不错的图片。</p>
<h3 id="RegExp-Preview-and-Editor"><a href="#RegExp-Preview-and-Editor" class="headerlink" title="RegExp Preview and Editor"></a>RegExp Preview and Editor</h3><p>正则表达式的可视化工具。</p>
<h3 id="Sublime-Text-Keymap-and-Settings-Importer"><a href="#Sublime-Text-Keymap-and-Settings-Importer" class="headerlink" title="Sublime Text Keymap and Settings Importer"></a>Sublime Text Keymap and Settings Importer</h3><p>使用 Sublime 的快捷键。之前习惯使用 Sublime，所以也将几个常用的快捷键搬了过来。</p>
<h3 id="SVG-Viewer"><a href="#SVG-Viewer" class="headerlink" title="SVG Viewer"></a>SVG Viewer</h3><p><code>svg</code> 图片直接查看工具。</p>
<h3 id="Toggle-Quotes"><a href="#Toggle-Quotes" class="headerlink" title="Toggle Quotes"></a>Toggle Quotes</h3><p>切换引号。引号组是 <code>&quot;</code>、<code>&#39;</code>、<code>`</code>。</p>
<h3 id="Vetur"><a href="#Vetur" class="headerlink" title="Vetur"></a>Vetur</h3><p>是的，就是 <code>vue</code>。</p>
<hr>
<p>介绍完毕。</p>
<p><del>下一篇的走起了。</del></p>
]]></content>
    
    <summary type="html">
    
      本文简单介绍本人在实际开发中使用到的几个 VSCode 扩展。
    
    </summary>
    
      <category term="随笔" scheme="https://xovel.cn/categories/essay/"/>
    
    
      <category term="VSCode" scheme="https://xovel.cn/tags/vscode/"/>
    
      <category term="扩展" scheme="https://xovel.cn/tags/extension/"/>
    
  </entry>
  
  <entry>
    <title>Chrome 扩展</title>
    <link href="https://xovel.cn/article/chrome-extensions.html"/>
    <id>https://xovel.cn/article/chrome-extensions.html</id>
    <published>2018-10-26T16:05:43.000Z</published>
    <updated>2018-10-26T17:16:31.303Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>之前曾经介绍过一个跟扩展相关的文章，那已经是 18 个月前的事情了：<a href="/article/brackets-emmet.html">Brackets</a>。</p>
<p>近日将再记录两篇文章，一个是跟 <code>VSCode</code> 的扩展，另一个是 <code>Chrome</code> 浏览器的扩展。列出的扩展是本人<strong>使用过</strong>的。本文讲述 <code>Chrome</code> 的扩展。</p>
</blockquote>
<p>话不多说，直接进入正题，本次列举的扩展如下：</p>
<h3 id="AdBlock"><a href="#AdBlock" class="headerlink" title="AdBlock"></a>AdBlock</h3><p>广告拦截插件，实力拦截不入眼的东西。</p>
<h3 id="Axure-RP-Extension-for-Chrome"><a href="#Axure-RP-Extension-for-Chrome" class="headerlink" title="Axure RP Extension for Chrome"></a>Axure RP Extension for Chrome</h3><p>Axure 原型图工具导出的 HTML 文档的查看工具，直接通过 file 协议查看的话会有所限制，使用该插件之后就可以愉快的浏览 Axure 的制作的原型图了。</p>
<blockquote>
<p>目前已经转战墨刀。</p>
</blockquote>
<h3 id="GitHub-Hovercard"><a href="#GitHub-Hovercard" class="headerlink" title="GitHub Hovercard"></a>GitHub Hovercard</h3><p><code>GitHub</code> 辅助工具，鼠标悬浮上去的时候拉取关键信息并进行显示。</p>
<h3 id="Isometric-Contributions"><a href="#Isometric-Contributions" class="headerlink" title="Isometric Contributions"></a>Isometric Contributions</h3><p>在 <code>GitHub</code> 首页，将贡献记录渲染成 <code>3D</code> 视图，更加立体的展现。</p>
<h3 id="JSONView"><a href="#JSONView" class="headerlink" title="JSONView"></a>JSONView</h3><p>格式化 <code>JSON</code> 文件。</p>
<h3 id="OctoLinker"><a href="#OctoLinker" class="headerlink" title="OctoLinker"></a>OctoLinker</h3><p>快速查看代码中引用的文件或类库。</p>
<h3 id="Octotree"><a href="#Octotree" class="headerlink" title="Octotree"></a>Octotree</h3><p><code>GitHub</code> 项目查看时使用 Code tree 模式进行查看和管理。</p>
<h3 id="Vue-js-devtools"><a href="#Vue-js-devtools" class="headerlink" title="Vue.js devtools"></a>Vue.js devtools</h3><p><code>Vue</code> 开发工具。</p>
<h3 id="Wappalyzer"><a href="#Wappalyzer" class="headerlink" title="Wappalyzer"></a>Wappalyzer</h3><p>检测当前访问的网站所使用到的技术，包含服务器、使用到的工具、框架等等。</p>
<hr>
<blockquote>
<p>以下部分安装在非 <code>Chrome</code> 浏览器上，但是也是在 <code>Chromium</code> 内核的浏览器运行的扩展/插件。</p>
</blockquote>
<h3 id="CSSViewer"><a href="#CSSViewer" class="headerlink" title="CSSViewer"></a>CSSViewer</h3><p>CSS 查看工具。</p>
<h3 id="Fiddler"><a href="#Fiddler" class="headerlink" title="Fiddler"></a>Fiddler</h3><p>简单的 API 侦听工具。</p>
<h3 id="Frameworks"><a href="#Frameworks" class="headerlink" title="Frameworks"></a>Frameworks</h3><p>跟上面的 <code>Wappalyzer</code> 的功能类似，但只检测使用到的框架和类库。</p>
<h3 id="Page-Ruler"><a href="#Page-Ruler" class="headerlink" title="Page Ruler"></a>Page Ruler</h3><p>页面尺寸丈量工具。</p>
<h3 id="Postman-Interceptor"><a href="#Postman-Interceptor" class="headerlink" title="Postman Interceptor"></a>Postman Interceptor</h3><p>Postman 拦截器工具。</p>
<blockquote>
<p>依然不能愉快的解决跨域问题，所以直接弃用了，采用了客户端软件。浏览器的自己编写了超级简单的一个<a href="https://hdk4.com/garden/tools/api-test.html" target="_blank" rel="noopener">工具</a>。</p>
</blockquote>
<h3 id="QQ空间放大"><a href="#QQ空间放大" class="headerlink" title="QQ空间放大"></a>QQ空间放大</h3><p>专门用于放大 <code>QQ空间</code> 里面的图片的一个工具。<del>现在已经基本不逛了</del>。</p>
<h3 id="Redirector"><a href="#Redirector" class="headerlink" title="Redirector"></a>Redirector</h3><p>伪装重定向工具，通过某种设置可以做出翻墙工具的效果，但有效性堪忧。</p>
<h3 id="Responsive-Web-Design-Tester"><a href="#Responsive-Web-Design-Tester" class="headerlink" title="Responsive Web Design Tester"></a>Responsive Web Design Tester</h3><p>快速切换浏览器大小。</p>
<h3 id="Web-Developer"><a href="#Web-Developer" class="headerlink" title="Web Developer"></a>Web Developer</h3><p>很实用的一套 WEB 开发工具组合。</p>
<h3 id="WhatFont"><a href="#WhatFont" class="headerlink" title="WhatFont"></a>WhatFont</h3><p>快速查看 CSS 字体。</p>
<h3 id="好声音"><a href="#好声音" class="headerlink" title="好声音"></a>好声音</h3><p>获取当前网页中的音效和音乐的实际地址。</p>
<h3 id="眼不见心不烦（新浪微博）"><a href="#眼不见心不烦（新浪微博）" class="headerlink" title="眼不见心不烦（新浪微博）"></a>眼不见心不烦（新浪微博）</h3><p>新浪微博美化工具。</p>
<h3 id="草料二维码"><a href="#草料二维码" class="headerlink" title="草料二维码"></a>草料二维码</h3><p>二维码管理工具。</p>
]]></content>
    
    <summary type="html">
    
      本文简单介绍本人在实际开发中使用到的几个 Chrome 扩展。
    
    </summary>
    
      <category term="随笔" scheme="https://xovel.cn/categories/essay/"/>
    
    
      <category term="扩展" scheme="https://xovel.cn/tags/extension/"/>
    
      <category term="Chrome" scheme="https://xovel.cn/tags/chrome/"/>
    
  </entry>
  
  <entry>
    <title>Buffer 简介</title>
    <link href="https://xovel.cn/article/node-buffer.html"/>
    <id>https://xovel.cn/article/node-buffer.html</id>
    <published>2018-10-07T14:05:26.000Z</published>
    <updated>2018-10-07T15:13:42.490Z</updated>
    
    <content type="html"><![CDATA[<p><code>Buffer</code> 是 <code>node</code> 中的一个全局对象，用来读取或者操作二进制数据流。</p>
<a id="more"></a>
<blockquote>
<p>本文直接使用 <code>Buffer</code> 讲述这个对象，该对象翻译成中文，可以叫做“缓冲区对象”。</p>
</blockquote>
<p><a href="https://nodejs.org/api/buffer.html" target="_blank" rel="noopener"><strong>官方文档</strong></a>的介绍如下：</p>
<blockquote>
<p>Prior to the introduction of <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray" target="_blank" rel="noopener">TypedArray</a>, the JavaScript language had no mechanism for reading or manipulating streams of binary data. The Buffer class was introduced as part of the Node.js API to enable interaction with octet streams in TCP streams, file system operations, and other contexts.</p>
</blockquote>
<p>在 <code>ECMAScript 2015</code> 引入 <code>TypedArray</code> 之后，<code>Buffer</code> 类使用了一种更优化、更适合 <code>Node.js</code> 用例的方式实现了 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array" target="_blank" rel="noopener"><code>Uint8Array</code></a> API。</p>
<p><code>Buffer</code> 类可以视作一个跟 <code>Array</code> 类一样的对象，区别在于 <code>Buffer</code> 的大小是固定的，且在 <code>v8</code> <strong>堆外</strong>分配物理内存。<code>Buffer</code> 的大小在创建时就确定了，无法调整。</p>
<p>先来看一段官方文档提供的示例代码：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Creates a zero-filled Buffer of length 10.</span></span><br><span class="line"><span class="keyword">const</span> buf1 = Buffer.alloc(<span class="number">10</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Creates a Buffer of length 10, filled with 0x1.</span></span><br><span class="line"><span class="keyword">const</span> buf2 = Buffer.alloc(<span class="number">10</span>, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Creates an uninitialized buffer of length 10.</span></span><br><span class="line"><span class="comment">// This is faster than calling Buffer.alloc() but the returned</span></span><br><span class="line"><span class="comment">// Buffer instance might contain old data that needs to be</span></span><br><span class="line"><span class="comment">// overwritten using either fill() or write().</span></span><br><span class="line"><span class="keyword">const</span> buf3 = Buffer.allocUnsafe(<span class="number">10</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Creates a Buffer containing [0x1, 0x2, 0x3].</span></span><br><span class="line"><span class="keyword">const</span> buf4 = Buffer.from([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Creates a Buffer containing UTF-8 bytes [0x74, 0xc3, 0xa9, 0x73, 0x74].</span></span><br><span class="line"><span class="keyword">const</span> buf5 = Buffer.from(<span class="string">'tést'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Creates a Buffer containing Latin-1 bytes [0x74, 0xe9, 0x73, 0x74].</span></span><br><span class="line"><span class="keyword">const</span> buf6 = Buffer.from(<span class="string">'tést'</span>, <span class="string">'latin1'</span>);</span><br></pre></td></tr></table></figure>
<hr>
<p><code>Buffer</code> 实例一般用于表示编码字符的序列，用过指定字符编码，可以在 <code>Buffer</code> 实例与常规 <code>JavaScript</code> 字符串之间进行相互转换。</p>
<p>这样的描述，可能太过于抽象，直接上代码解释吧：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">btoa</span>(<span class="params">str</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> buf = Buffer.from(str);</span><br><span class="line">  <span class="keyword">return</span> buf.toString(<span class="string">'base64'</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">atob</span>(<span class="params">str</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> buf = Buffer.from(str, <span class="string">'base64'</span>);</span><br><span class="line">  <span class="keyword">return</span> buf.toString();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>上面的代码，是 <code>node</code> 环境下关于浏览器 <a href="https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/atob" target="_blank" rel="noopener"><code>atob</code></a> 和 <a href="https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/btoa" target="_blank" rel="noopener"><code>btoa</code></a> 方法的实现。</p>
<p><code>Node.js</code> 目前支持的字符编码如下：</p>
<ul>
<li><code>ascii</code>，仅支持 7 位 ASCII 数据。</li>
<li><code>utf8</code>，多字节编码的 Unicode 字符。许多网页和其他文档格式都使用 <code>UTF-8</code>。</li>
<li><code>utf16le</code>，2 或 4 字节，<code>little-endian</code>(小字节序)编码的 Unicode 字符。支持 <code>U+10000</code> 到 <code>U+10FFFF</code> 的代理对。</li>
<li><code>usc2</code>，即 <code>utf16le</code>。</li>
<li><code>base64</code>，即 <code>Base64</code> 编码。</li>
<li><code>latin1</code>，即 <code>Latin-1</code> 编码，具体由 IANA 在 <a href="https://tools.ietf.org/html/rfc1345" target="_blank" rel="noopener">RFC1345</a> 中定义。</li>
<li><code>binary</code>，即 <code>latin1</code>。</li>
<li><code>hex</code>，将每个字节编码为两个十六进制字符。</li>
</ul>
<blockquote>
<p>注意，现代浏览器遵循 <a href="https://encoding.spec.whatwg.org/" target="_blank" rel="noopener">WHATWG 编码标准</a> 将 <code>latin1</code> 和 <code>ISO-8859-1</code> 别名为 <code>win-1252</code>。这意味着在进行例如 <code>http.get()</code> 获取到的字符编码在规范列表中，服务器可能返回 <code>win-1252</code> 编码的数据，此时如果使用 <code>latin1</code> 字符编码，可能会得到错误的解码数据。</p>
</blockquote>
<p><code>Buffer</code> 对象可以使用 <code>for...of</code> 进行内部迭代。同样的，实例对象的 <code>.values</code>、<code>.keys</code> 和 <code>.entries</code> 方法都可以创建迭代。</p>
<p>使用 <code>new</code> 操作符实例化一个 <code>Buffer</code> 的方法已经被弃用。注意，<strong>已经弃用的方法本文不做介绍</strong>。</p>
<p>创建 <code>Buffer</code> 实例的方法有下面几个：</p>
<ul>
<li><code>Buffer.from(array)</code>，从数组进行创建。</li>
<li><code>Buffer.from(arrayBuffer[, byteOffset[, length]])</code>，从 <code>arrayBuffer</code> 的实例进行创建。</li>
<li><code>Buffer.from(buffer)</code>，直接根据 <code>buffer</code> 创建。</li>
<li><code>Buffer.from(string[, encoding])</code>，根据字符串创建，<code>encoding</code> 即使用的编码方式。</li>
<li><code>Buffer.from(object[, offsetOrEncoding[, length]])</code>，根据对象创建，该对象需要支持 <code>Symbol.toPrimitive</code> 或者有 <code>valueOf()</code> 方法，比如 <code>Buffer.from(new String(&#39;this is a test&#39;));</code>。</li>
<li><code>Buffer.alloc(size[, fill[, encoding]])</code>，创建一个指定长度的对象，<code>fill</code> 表示用来填充新建的 <code>Buffer</code> 的值，默认为 <code>0</code>。</li>
<li><code>Buffer.allocUnsafe(size)</code>，分配一个大小为 <code>size</code> 的对象。该方式创建的 <code>Buffer</code> 对象是未经过初始化的，内容未知，可能包含敏感数据。</li>
</ul>
<blockquote>
<p>更多的创建方式不在本文的记述范围之内。</p>
</blockquote>
<p>创建之后的实例，我们使用 <code>buf</code> 进行表示，<code>buf</code> 的特性跟数组类似。</p>
<p><code>buf.buffer</code> 指向了 <code>Buffer</code> 底层的 <code>ArrayBuffer</code> 对象。</p>
<p>可以使用 <code>fill</code> 方法进行填充，语法为 <code>buf.fill(value[, offset[, end]][, encoding])</code>。</p>
<p><code>toString</code> 可以将 <code>buf</code> 转为一个常规字符串，语法为 <code>buf.toString([encoding[, start[, end]]])</code>。</p>
<p>之前做了一个简单的获取网页源代码的方法，代码大概是这样的：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> http = <span class="built_in">require</span>(<span class="string">'http'</span>);</span><br><span class="line"><span class="keyword">const</span> https = <span class="built_in">require</span>(<span class="string">'https'</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="keyword">function</span> <span class="title">getHTML</span>(<span class="params">url</span>) </span>&#123;</span><br><span class="line"></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="keyword">function</span> (<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">    (<span class="regexp">/^https/i</span>.test(url) ? https : http).get(url, <span class="function"><span class="keyword">function</span> (<span class="params">res</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">var</span> html = <span class="string">''</span>;</span><br><span class="line"></span><br><span class="line">      res.on(<span class="string">'data'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">chunk</span>) </span>&#123;</span><br><span class="line">        <span class="comment">// chunk is a Buffer instance, use the method toString to get the string</span></span><br><span class="line">        html += chunk;</span><br><span class="line">      &#125;);</span><br><span class="line"></span><br><span class="line">      res.on(<span class="string">'end'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        resolve(html);</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;).on(<span class="string">'error'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">      reject();</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>是的，这里面的 <code>chunk</code> 对象就是一个 <code>Buffer</code> 实例。</p>
<hr>
<p>真·简介。本文到此就结束了。</p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;code&gt;Buffer&lt;/code&gt; 是 &lt;code&gt;node&lt;/code&gt; 中的一个全局对象，用来读取或者操作二进制数据流。&lt;/p&gt;
    
    </summary>
    
      <category term="Node" scheme="https://xovel.cn/categories/node/"/>
    
    
      <category term="node" scheme="https://xovel.cn/tags/node/"/>
    
      <category term="buffer" scheme="https://xovel.cn/tags/buffer/"/>
    
  </entry>
  
  <entry>
    <title>ES6 模块</title>
    <link href="https://xovel.cn/article/es6-modules.html"/>
    <id>https://xovel.cn/article/es6-modules.html</id>
    <published>2018-05-24T09:54:29.000Z</published>
    <updated>2021-02-03T13:08:21.540Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>阅读本文需要有一定的 ES6 基础。</p>
</blockquote>
<p>ES6 模块是 ES6 中的一个非常重要的特性，它收集了多年来各种模块化的优点，解决了其间的差异化，并期望能最终形成一个大家共同遵守的规范。</p>
<blockquote>
<p>关于 JS 的模块化历程，可以参考 <a href="https://appendto.com/2016/06/the-short-history-of-javascript-module-loaders/" target="_blank" rel="noopener">这篇文章</a>。</p>
</blockquote>
<p>本文从 <code>import</code> 和 <code>export</code> 两部分对 ES6 模块做一个基础的介绍，这两部分内容也就是 ES6 模块的全部了。</p>
<h3 id="import"><a href="#import" class="headerlink" title="import"></a>import</h3><p>来看一组现有的 <code>import</code> 语法：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> defaultExport <span class="keyword">from</span> <span class="string">"module-name"</span>;</span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> name <span class="keyword">from</span> <span class="string">"module-name"</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="keyword">export</span> &#125; <span class="keyword">from</span> <span class="string">"module-name"</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="keyword">export</span> <span class="keyword">as</span> alias &#125; <span class="keyword">from</span> <span class="string">"module-name"</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; export1 , export2 &#125; <span class="keyword">from</span> <span class="string">"module-name"</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; export1 , export2 <span class="keyword">as</span> alias2 , [...] &#125; <span class="keyword">from</span> <span class="string">"module-name"</span>;</span><br><span class="line"><span class="keyword">import</span> defaultExport, &#123; <span class="keyword">export</span> [ , [...] ] &#125; <span class="keyword">from</span> <span class="string">"module-name"</span>;</span><br><span class="line"><span class="keyword">import</span> defaultExport, * <span class="keyword">as</span> name <span class="keyword">from</span> <span class="string">"module-name"</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">"module-name"</span>;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>以上代码来自 MDN 上面关于 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import" target="_blank" rel="noopener">import</a> 的介绍。</p>
</blockquote>
<p><code>import</code> 是模块导入语句的关键字，通过指定具体的导入方式，可以进行相应模块的导入操作。</p>
<p>以下是逐条说明：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 导入默认模块</span></span><br><span class="line"><span class="keyword">import</span> defaultExport <span class="keyword">from</span> <span class="string">"module-name"</span>;</span><br><span class="line"><span class="comment">// 导入所有模块并指定一个新的名称，导入后的对象包含了所有具名模块</span></span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> name <span class="keyword">from</span> <span class="string">"module-name"</span>;</span><br><span class="line"><span class="comment">// 导入指定模块</span></span><br><span class="line"><span class="keyword">import</span> &#123; <span class="keyword">export</span> &#125; <span class="keyword">from</span> <span class="string">"module-name"</span>;</span><br><span class="line"><span class="comment">// 导入指定的模块并指定一个新的名称，即直接进行重命名操作</span></span><br><span class="line"><span class="keyword">import</span> &#123; <span class="keyword">export</span> <span class="keyword">as</span> alias &#125; <span class="keyword">from</span> <span class="string">"module-name"</span>;</span><br><span class="line"><span class="comment">// 导入多个模块</span></span><br><span class="line"><span class="keyword">import</span> &#123; export1 , export2 &#125; <span class="keyword">from</span> <span class="string">"module-name"</span>;</span><br><span class="line"><span class="comment">// 导入多个模块，并对其中的某一部分进行重命名</span></span><br><span class="line"><span class="keyword">import</span> &#123; export1 , export2 <span class="keyword">as</span> alias2 , [...] &#125; <span class="keyword">from</span> <span class="string">"module-name"</span>;</span><br><span class="line"><span class="comment">// 同时导入默认模块和其他模块</span></span><br><span class="line"><span class="keyword">import</span> defaultExport, &#123; <span class="keyword">export</span> [ , [...] ] &#125; <span class="keyword">from</span> <span class="string">"module-name"</span>;</span><br><span class="line"><span class="comment">// 同时导入模块模块，并将所有模块以新名称进行导入</span></span><br><span class="line"><span class="keyword">import</span> defaultExport, * <span class="keyword">as</span> name <span class="keyword">from</span> <span class="string">"module-name"</span>;</span><br><span class="line"><span class="comment">// 直接引入模块，不导入任何东西</span></span><br><span class="line"><span class="keyword">import</span> <span class="string">"module-name"</span>;</span><br></pre></td></tr></table></figure>
<h3 id="export"><a href="#export" class="headerlink" title="export"></a>export</h3><p><code>export</code> 是模块导出语句的关键字，同样的，来看一组 <code>export</code> 的语法，代码来自 MDN 上面的 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export" target="_blank" rel="noopener">export</a>：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 导出指定模块</span></span><br><span class="line"><span class="keyword">export</span> &#123; name1, name2, …, nameN &#125;;</span><br><span class="line"><span class="comment">// 导出指定模块，并进行重命名</span></span><br><span class="line"><span class="keyword">export</span> &#123; variable1 <span class="keyword">as</span> name1, variable2 <span class="keyword">as</span> name2, …, nameN &#125;;</span><br><span class="line"><span class="comment">// 导出变量</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">let</span> name1, name2, …, nameN; <span class="comment">// also var</span></span><br><span class="line"><span class="comment">// 导出变量，并对变量直接进行定义</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">let</span> name1 = …, name2 = …, …, nameN; <span class="comment">// also var, const</span></span><br><span class="line"><span class="comment">// 导出函数</span></span><br><span class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">FunctionName</span>(<span class="params"></span>)</span>&#123;...&#125;</span><br><span class="line"><span class="comment">// 导出类</span></span><br><span class="line"><span class="keyword">export</span> <span class="class"><span class="keyword">class</span> <span class="title">ClassName</span> </span>&#123;...&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 导出一个表达式作为默认模块</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> expression;</span><br><span class="line"><span class="comment">// 导出匿名函数作为默认模块，类和生成器函数亦可</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> (<span class="params">…</span>) </span>&#123; … &#125; <span class="comment">// also class, function*</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">name1</span>(<span class="params">…</span>) </span>&#123; … &#125; <span class="comment">// also class, function*</span></span><br><span class="line"><span class="comment">// 将指定模块作为默认模块进行导出</span></span><br><span class="line"><span class="keyword">export</span> &#123; name1 <span class="keyword">as</span> <span class="keyword">default</span>, … &#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 从另一个模块提取所有模块进行导出</span></span><br><span class="line"><span class="keyword">export</span> * <span class="keyword">from</span> …;</span><br><span class="line"><span class="comment">// 从另一个模块提取指定模块进行导出</span></span><br><span class="line"><span class="keyword">export</span> &#123; name1, name2, …, nameN &#125; <span class="keyword">from</span> …;</span><br><span class="line"><span class="comment">// 从另一个模块提取指定的模块并进行重命名后再导出</span></span><br><span class="line"><span class="keyword">export</span> &#123; import1 <span class="keyword">as</span> name1, import2 <span class="keyword">as</span> name2, …, nameN &#125; <span class="keyword">from</span> …;</span><br><span class="line"><span class="comment">// 从另一个模块导出默认模块</span></span><br><span class="line"><span class="keyword">export</span> &#123; <span class="keyword">default</span> &#125; <span class="keyword">from</span> …;</span><br></pre></td></tr></table></figure>
<h3 id="一些说明"><a href="#一些说明" class="headerlink" title="一些说明"></a>一些说明</h3><ol>
<li>在进行 <code>import</code> 的时候，<code>import { export } from &quot;module-name&quot;;</code> 中的 <code>{ export }</code> 语法并不等同于对象的解构，这是 <code>import</code> 专门的语法。</li>
<li><code>export</code> 和 <code>export default</code> 的区别是在后续进行引入的时候需要分别使用 <code>import { ... } from</code> 和 <code>import ... from</code> 进行操作。</li>
<li>作为默认模块进行导出时，匿名函数和具名函数在导出之后并无区分。</li>
<li>导出后的模块，如果存在变量，该变量对外是不可变的。可以通过模块内置的方法进行改变。</li>
</ol>
<hr>
<p>大体上就是这些内容了，ES6 的模块化给编程领域尤其是前端开发这一块，带来非常大的便利。</p>
<blockquote>
<p>由于市面上直接支持 ES6 模块的产品的覆盖率依然不够理想，所以需要通过一些转译工具进行转译之后再进行使用。</p>
<blockquote>
<p>目前，本人主要工作是在进行微信小程序的开发，好在小程序现在的开发环境已经基本支持 <code>import</code> 和 <code>export</code> 语法（除了 <code>export ... from</code>），所以可以放心无忧的进行现代化 ES6 代码的编写了。目前使用原生代码进行微信小程序的编写，感觉良好。至于 <code>wepy</code> 和 <code>mpvue</code> 之类的前瞻框架，由于未进行全面使用，所以不做过多评述。</p>
</blockquote>
</blockquote>
<hr>
<p>在撰写本文的时候，参考了大量的资料，但是提笔的时候却又感觉很词穷，所以只是简单的做了一个代码层次的注释说明，更多的参考资料罗列如下，请诸君自行参阅。</p>
<h3 id="参考资料与拓展阅读"><a href="#参考资料与拓展阅读" class="headerlink" title="参考资料与拓展阅读"></a>参考资料与拓展阅读</h3><ul>
<li><a href="http://2ality.com/2014/09/es6-modules-final.html" target="_blank" rel="noopener">ECMAScript 6 modules: the final syntax</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import" target="_blank" rel="noopener">import - JavaScript | MDN</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export" target="_blank" rel="noopener">export - JavaScript | MDN</a></li>
<li><a href="https://blogs.windows.com/msedgedev/2016/05/17/es6-modules-and-beyond/" target="_blank" rel="noopener">Previewing ES6 Modules and more from ES2015, ES2016 and beyond</a></li>
<li><a href="https://hacks.mozilla.org/2015/08/es6-in-depth-modules/" target="_blank" rel="noopener">ES6 In Depth: Modules</a></li>
<li><a href="https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/" target="_blank" rel="noopener">ES modules: A cartoon deep-dive</a></li>
<li><a href="http://exploringjs.com/es6/ch_modules.html#_modules" target="_blank" rel="noopener">Exploring ES6 - 16. Modules</a></li>
<li><a href="https://github.com/eslint/espree/pull/43" target="_blank" rel="noopener">eslint/espree - #43 - adds module import and export grammar</a></li>
<li><a href="https://github.com/tc39/proposal-export-default-from" target="_blank" rel="noopener">tc39/proposal-export-default-from</a></li>
<li><a href="https://github.com/tc39/proposal-dynamic-import" target="_blank" rel="noopener">tc39/proposal-dynamic-import</a></li>
<li><a href="http://2ality.com/2015/07/es6-module-exports.html" target="_blank" rel="noopener">What do ES6 modules export?</a></li>
<li><a href="https://www.sitepoint.com/understanding-es6-modules/" target="_blank" rel="noopener">Understanding ES6 Modules</a></li>
<li><a href="http://www.commonjs.org/" target="_blank" rel="noopener">CommonJS</a></li>
<li><a href="http://jsmodules.io/" target="_blank" rel="noopener">JavaScript Modules</a></li>
<li><a href="https://developer.telerik.com/featured/choose-es6-modules-today/" target="_blank" rel="noopener">Choose ES6 modules Today!</a></li>
<li><a href="https://medium.freecodecamp.org/javascript-modules-a-beginner-s-guide-783f7d7a5fcc" target="_blank" rel="noopener">JavaScript Modules: A Beginner’s Guide</a></li>
<li><a href="https://appendto.com/2016/06/the-short-history-of-javascript-module-loaders/" target="_blank" rel="noopener">History and Background of JavaScript Module Loaders</a></li>
<li><a href="http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html" target="_blank" rel="noopener">JavaScript Module Pattern: In-Depth</a></li>
</ul>
<p>&lt;本文完&gt;</p>
]]></content>
    
    <summary type="html">
    
      本文是对 ES6 模块化的简单介绍。
    
    </summary>
    
      <category term="WEB" scheme="https://xovel.cn/categories/web/"/>
    
      <category term="文档" scheme="https://xovel.cn/categories/web/docs/"/>
    
    
      <category term="es6" scheme="https://xovel.cn/tags/es6/"/>
    
      <category term="module" scheme="https://xovel.cn/tags/module/"/>
    
      <category term="import" scheme="https://xovel.cn/tags/import/"/>
    
      <category term="export" scheme="https://xovel.cn/tags/export/"/>
    
      <category term="javascript" scheme="https://xovel.cn/tags/javascript/"/>
    
  </entry>
  
  <entry>
    <title>使用 nginx</title>
    <link href="https://xovel.cn/article/use-nginx.html"/>
    <id>https://xovel.cn/article/use-nginx.html</id>
    <published>2018-03-26T01:07:00.000Z</published>
    <updated>2018-03-31T04:47:25.294Z</updated>
    
    <content type="html"><![CDATA[<p><img src="https://nginx.org/nginx.png" alt="nginx"></p>
<blockquote>
<p>本文根据个人日志中相关记录进行整理而成。</p>
<p>注意，这并不是一篇配置攻略或者是文档性质的，阅读时请不要以本篇文章描述的为主，请适当进行斟酌。</p>
</blockquote>
<p>导语中的文本来自于百度百科，不过根据官网的描述，<code>nginx</code> 是这样的一个东西：</p>
<blockquote>
<p>nginx [engine x] is an HTTP and reverse proxy server, a mail proxy server, and a generic TCP/UDP proxy server, originally written by Igor Sysoev.</p>
</blockquote>
<p>本文将一句个人日志项目中关于 <code>nginx</code> 的记载进行阐述。</p>
<p>最初，在去年 9 月份，我第一次记录了关于 <code>nginx</code> 的东西，是关于如何在 <code>nginx</code> 站点中开启文件目录的浏览：在 <code>location</code> 模块中使用 <code>autoindex on;</code> 指令即可。</p>
<p>然后没过多久，写了一篇关于开启指定域名访问的记录，即设置 <code>server_name</code> 指令。</p>
<p>在 12 月初，写了一篇重量级的文章，安装 <code>nginx</code>，这也是我第一次完全自己部署前端站点的一次尝试。</p>
<p><code>nginx</code> 的常规依赖：</p>
<ul>
<li><code>zlib</code>，用于开启 <code>gzip</code> 压缩。</li>
<li><code>pcre</code>，用于开启伪静态或者重定向。</li>
<li><code>openssh</code>，用于开启 <code>ssl</code>，或者换个说法，叫做 <code>https</code>。</li>
</ul>
<p>安装过程中需要 <code>gcc-c++</code> 运行库。</p>
<p>紧接着没多久，写了一篇如何配置 <code>nginx</code> 的日志，讲述了如何开启多配置文件模式，主要是 <code>include</code> 指令。顺便阐述了一下过滤非法域名访问的问题，<code>default_server</code> 关键字进行设置即可。</p>
<p>之后在一篇微信支付相关的记录中，提到了关于反向代理转发真实 IP 地址的方法。</p>
<p>过完年之后，由于等级保护评估测试相关的原因，针对前端全站开启了 <code>https</code> 模式。关键配置为 <code>listen 443 ssl;</code>，结合 <code>ssl_certificate</code> 和 <code>ssl_certificate_key</code> 进行相关文件的配置即可完成。</p>
<p>由于 <code>https</code> 的访问模式，页面不能直接使用 <code>http</code> 请求，于是针对之前提到的反向代理 <code>proxy_pass</code> 进行了再一次的阐述，并对负载均衡进行了说明。<code>nginx</code> 的负载均衡是一个非常强大的功能，主要依赖 <code>upstream</code> 来进行相关配置。</p>
<p>之后没多久，有需要页面重定向的需求，于是对 <code>rewrite</code> 指令做了简单的介绍。</p>
<p>因为某一次的服务器改版，转发 <code>https</code> 到 <code>http</code> 的时候，发现端口出现了稍稍的偏差。于是针对 <code>X-Forwarded-Proto</code> 之类的参数设置进行了相关的解释。</p>
<p>后来因个人项目，部署的时候想尝试一下路由的 <code>history</code> 模式，于是 <code>nginx</code> 的 <code>try_files</code> 指令被提上日程，但是目前我并没有进行日志记录。</p>
<p>至此，本人日志项目关于 <code>nginx</code> 的记录就回顾完毕，这大概也就是本篇文章的主要内容了。</p>
<p>另外，在阅读子项目中，曾经记录了一篇跟 <code>nginx</code> 密切相关的书籍《Nginx 开发从入门到精通》，该书是 <code>tengine</code> 开发团队主笔的。</p>
<hr>
<ul>
<li><a href="http://nginx.org/" target="_blank" rel="noopener">nginx 官网</a></li>
<li><a href="http://nginx.org/en/docs/" target="_blank" rel="noopener">nginx documentation</a></li>
<li><a href="http://nginx.org/en/docs/stream/ngx_stream_upstream_module.html" target="_blank" rel="noopener">Module ngx_stream_upstream_module</a></li>
<li><a href="https://trac.nginx.org/nginx/browser/nginx/src/http/modules/ngx_http_proxy_module.c" target="_blank" rel="noopener">ngx_http_proxy_module.c in nginx/src/http/modules – nginx</a></li>
<li><a href="https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/" target="_blank" rel="noopener">If Is Evil | NGINX</a></li>
<li><a href="http://tengine.taobao.org/book/index.html" target="_blank" rel="noopener">Nginx开发从入门到精通</a></li>
<li><a href="https://serverfault.com/questions/630413/nginx-apache-set-hsts-only-if-x-forwarded-proto-is-https" target="_blank" rel="noopener">Nginx/Apache: set HSTS only if X-Forwarded-Proto is https - Server Fault</a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      Nginx (engine x) 是一个高性能的 HTTP 和反向代理服务器，也是一个 IMAP/POP3/SMTP 服务器。
    
    </summary>
    
      <category term="随笔" scheme="https://xovel.cn/categories/essay/"/>
    
    
      <category term="nginx" scheme="https://xovel.cn/tags/nginx/"/>
    
  </entry>
  
  <entry>
    <title>node 下的微信公众号支付初探</title>
    <link href="https://xovel.cn/article/node-wxpay.html"/>
    <id>https://xovel.cn/article/node-wxpay.html</id>
    <published>2018-01-30T15:06:50.000Z</published>
    <updated>2018-01-31T02:53:28.513Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>那么，就直接开门见山呗。本篇博客根据个人日志记录整理而成。</p>
<p>项目地址：<a href="https://github.com/xovel/node-wxpay-test" target="_blank" rel="noopener">https://github.com/xovel/node-wxpay-test</a></p>
</blockquote>
<p>其实大部分的文档，微信官方开发文档里面都提供了，我们只需要按照其说明严格执行就可以达到目的。</p>
<p>注意，这里只讲述公众号支付相关的情形，其他的基本类似，本人未作深入实践，故此不敢多言。</p>
<p>这次的讲解，主要从四个方面进行说明：</p>
<ol>
<li>相关账户配置</li>
<li>后台的搭建</li>
<li>前端页面的展示</li>
<li>支付流程</li>
</ol>
<h3 id="账户"><a href="#账户" class="headerlink" title="账户"></a>账户</h3><p>首先，肯定是需要一个微信公众号的。公众号的性质需要满足开通商户号和微信支付，并且实际上已经开通好了。这次我用的账号是认证好了的服务号。</p>
<p>进入公众号设置界面，对相关的域名进行配置，本次调试，我使用的域名是 <code>z.hdk4.com</code>，故此在公众的管理页面的 <code>【设置】-【公众号设置】-【功能设置】</code> 中对 <code>业务域名</code>、<code>JS接口安全域名</code>、<code>网页授权域名</code> 全部进行了相应的设置。</p>
<blockquote>
<p>至于开发者的添加和公众号密钥和商户号密钥等数据的获取，属于入门级别，这里不再赘述。</p>
</blockquote>
<p>要做微信支付，需要事先对 <code>支付授权目录</code> 进行设置，在商户号管理页面就能够找得到，直接进行设置即可。注意这里是 <code>支付目录</code> 的设置，即浏览器访问下的 <code>location.pathname</code> 对应的访问路径，结尾必须以 <code>/</code> 结束且<strong>区分大小写</strong>。</p>
<h3 id="后台"><a href="#后台" class="headerlink" title="后台"></a>后台</h3><p>这一块最为重要也是核心所在，由于只是演示与测验，所以采用了简单粗暴的 <code>express</code> 来作为后台服务器。详情容后再禀。</p>
<h3 id="前端"><a href="#前端" class="headerlink" title="前端"></a>前端</h3><p>前端页面相对来说简单一些，在引入微信的官方 <code>js</code>，即 <a href="http://res.wx.qq.com/open/js/jweixin-1.2.0.js" target="_blank" rel="noopener">http://res.wx.qq.com/open/js/jweixin-1.2.0.js</a> 之后，调用 <code>WeixinJSBridge</code> 的 API，发起 <code>getBrandWCPayRequest</code> 请求即可。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">WeixinJSBridge.invoke(<span class="string">'getBrandWCPayRequest'</span>, &#123;</span><br><span class="line">  appId: data.appId,</span><br><span class="line">  timeStamp: data.timeStamp,</span><br><span class="line">  nonceStr: data.nonceStr,</span><br><span class="line">  package: data.package,</span><br><span class="line">  signType: data.signType,</span><br><span class="line">  paySign: data.paySign</span><br><span class="line">&#125;, <span class="function"><span class="keyword">function</span> (<span class="params">res</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p>当然，在这里之前，需要先获取用户的 <code>openid</code>。</p>
<p>当然，在获取 <code>openid</code> 之前，还需要获取一下用户的访问 <code>code</code>。</p>
<p>那么，这个访问的 <code>code</code> 如何获取呢？</p>
<p>根据微信开发文档提供的链接 <a href="https://mp.weixin.qq.com/wiki?t=resource/res_main&amp;id=mp1421140842" target="_blank" rel="noopener">https://mp.weixin.qq.com/wiki?t=resource/res_main&amp;id=mp1421140842</a>，我们先判断链接是否有 <code>code</code> 字段，如果没有则跳转到微信的链接去进行获取。获取到了之后再进行 <code>openid</code> 的获取。文档方面的信息这里不详细展开，直接上代码进行说明吧！</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> code = getUrlParam(<span class="string">'code'</span>) || sessionStorage.code;</span><br><span class="line"><span class="keyword">var</span> openid = sessionStorage.openid;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 没有 openid 则尝试获取 code</span></span><br><span class="line"><span class="comment">// code 获取之后跳回原链接进行临时存储</span></span><br><span class="line"><span class="keyword">if</span> (!openid) &#123;</span><br><span class="line">  <span class="keyword">if</span> (!code) &#123;</span><br><span class="line">    <span class="keyword">var</span> redirect_uri = <span class="built_in">encodeURIComponent</span>(<span class="string">'http://z.hdk4.com/d.html'</span>);</span><br><span class="line">    <span class="built_in">window</span>.location.replace(<span class="string">'https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxbce0a3daf503097c&amp;redirect_uri='</span> + redirect_uri + <span class="string">'&amp;response_type=code&amp;scope=snsapi_base&amp;state=7'</span>);</span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!sessionStorage.code) &#123;</span><br><span class="line">    sessionStorage.code = code;</span><br><span class="line">    <span class="built_in">window</span>.location.replace(<span class="string">'http://z.hdk4.com/d.html'</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>几点说明：</p>
<ul>
<li><code>redirect_uri</code> 中的 <code>state=7</code> 是随便写的一个值。</li>
<li><code>window.location.replace</code> 用于直接替换，如果不想要替换效果，可以修改为 <code>window.location.href = &#39;...&#39;</code>。</li>
<li>采用 <code>sessionStorage</code> 进行数据的缓存。</li>
<li><code>getUrlParam</code> 为提取链接中的 <code>querystring</code>。</li>
<li><del>不要管 <code>yoda</code> 什么的了</del></li>
</ul>
<p>前端差不多能说的就这么多了。</p>
<h3 id="支付流程"><a href="#支付流程" class="headerlink" title="支付流程"></a>支付流程</h3><p>整个支付流程浓缩起来就是：</p>
<ol>
<li>获取 <code>code</code></li>
<li>获取 <code>openid</code></li>
<li>开始进行支付操作</li>
<li>后台发起预支付订单</li>
<li>后台将获取到的预支付信息返回给前端</li>
<li>前端尝试唤起微信支付</li>
<li>进行支付</li>
<li>前端/后台处理支付回调。是的，都进行处理。前端无所谓，后台必须处理，不然微信会发很多次请求。</li>
<li>支付完成</li>
<li>后续一些相关操作。由于本次只是演示与测验，就不做处理了。</li>
</ol>
<hr>
<p>现在来说一下核心的重点，即后台的相关逻辑处理与接口的设计。</p>
<h3 id="后台-1"><a href="#后台-1" class="headerlink" title="后台"></a>后台</h3><p>相关的库的引入与声明，这里就不赘述了。</p>
<h4 id="api-getopenid-接口"><a href="#api-getopenid-接口" class="headerlink" title="/api/getopenid 接口"></a><code>/api/getopenid</code> 接口</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 获取 openid</span></span><br><span class="line">app.get(<span class="string">'/api/getopenid'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">req, res</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> code = req.query.code;</span><br><span class="line">  <span class="keyword">const</span> access_token_url = <span class="string">`https://api.weixin.qq.com/sns/oauth2/access_token?appid=<span class="subst">$&#123;config.app_id&#125;</span>&amp;secret=<span class="subst">$&#123;config.app_secret&#125;</span>&amp;code=<span class="subst">$&#123;code&#125;</span>&amp;grant_type=authorization_code`</span>;</span><br><span class="line"></span><br><span class="line">  request.post(&#123; <span class="attr">url</span>: access_token_url &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">error, response, body</span>) </span>&#123;</span><br><span class="line">    wFile(<span class="string">'openid'</span>, body);</span><br><span class="line">    <span class="keyword">if</span> (error) &#123;</span><br><span class="line">      res.json(&#123; <span class="attr">error</span>: body &#125;);</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (response.statusCode === <span class="number">200</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (body.errcode === <span class="number">40029</span> ) &#123;</span><br><span class="line">        res.json(&#123; <span class="attr">error</span>: body &#125;);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        body = <span class="built_in">JSON</span>.parse(body);</span><br><span class="line">        res.json(&#123; <span class="attr">data</span>: body &#125;);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      res.json(&#123; <span class="attr">error</span>: <span class="number">-1</span> &#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>
<blockquote>
<p><code>wFile</code> 是日志记录相关操作，可以无视。</p>
</blockquote>
<p>该接口是用来获取用户的 <code>openid</code> 的，要求的东西是 <code>code</code>，这个 <code>code</code> 就是上面前端花费一番周折得到的东西。</p>
<p>前端调用方式：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getOpenId</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span> (<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (sessionStorage.openid) &#123;</span><br><span class="line">      resolve(sessionStorage.openid);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      $.get(<span class="string">'/api/getopenid?code='</span> + sessionStorage.code).then(<span class="function"><span class="keyword">function</span> (<span class="params">res</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">var</span> data = res.data;</span><br><span class="line">        <span class="keyword">if</span> (data &amp;&amp; data.openid) &#123;</span><br><span class="line">          sessionStorage.openid = data.openid;</span><br><span class="line">          resolve(data.openid)</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">          reject();</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>
<ul>
<li>是的， <code>$</code> 就是大名鼎鼎的 <code>jQuery</code>。</li>
<li>采用了 <code>Promise</code> 对象。</li>
</ul>
<h4 id="api-unifiedorder-接口"><a href="#api-unifiedorder-接口" class="headerlink" title="/api/unifiedorder 接口"></a><code>/api/unifiedorder</code> 接口</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 获取微信支付的统一下单相关数据</span></span><br><span class="line">app.get(<span class="string">'/api/unifiedorder'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">req, res</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> openid = req.query.openid;</span><br><span class="line">  <span class="keyword">const</span> ip = getClientIp(req);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 商户订单号</span></span><br><span class="line">  <span class="keyword">const</span> out_trade_no = <span class="string">'test'</span> + <span class="keyword">new</span> <span class="built_in">Date</span>().getTime();</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 统一下单的相关参数</span></span><br><span class="line">  <span class="keyword">const</span> paramUnifiedOrder = &#123;</span><br><span class="line">    appid: config.app_id,</span><br><span class="line">    attach: <span class="string">'test'</span>,</span><br><span class="line">    body: <span class="string">'desc'</span>,</span><br><span class="line">    mch_id: config.mch_id,</span><br><span class="line">    nonce_str: createNonceStr(),</span><br><span class="line">    notify_url: config.notify_url, <span class="comment">// 微信付款后的回调地址</span></span><br><span class="line">    openid: openid,</span><br><span class="line">    out_trade_no: out_trade_no,</span><br><span class="line">    spbill_create_ip: ip,</span><br><span class="line">    total_fee: <span class="number">1</span>,</span><br><span class="line">    trade_type: <span class="string">'JSAPI'</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">  paramUnifiedOrder.sign = getSign(paramUnifiedOrder);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 请求微信支付下单接口，获取预订单编号</span></span><br><span class="line">  request.post(&#123; <span class="attr">url</span>: <span class="string">'https://api.mch.weixin.qq.com/pay/unifiedorder'</span>, <span class="attr">body</span>: <span class="built_in">JSON</span>.stringify(getUnifiedOrderXml(paramUnifiedOrder)) &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">error, response, body</span>) </span>&#123;</span><br><span class="line">    wFile(<span class="string">'unifiedorder'</span>, body);</span><br><span class="line">    <span class="keyword">if</span> (error) &#123;</span><br><span class="line">      res.json(&#123; <span class="attr">error</span>: body &#125;);</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (response.statusCode === <span class="number">200</span>) &#123;</span><br><span class="line">      <span class="keyword">let</span> prepay_id = <span class="string">''</span>; <span class="comment">// 预订单编号</span></span><br><span class="line">      <span class="comment">// 微信返回的数据为 xml 格式，需要进行解析</span></span><br><span class="line">      xml2jsparseString(body, &#123; <span class="attr">async</span>: <span class="literal">true</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">error, result</span>) </span>&#123;</span><br><span class="line">        prepay_id = result.xml.prepay_id[<span class="number">0</span>]; <span class="comment">// 获取预订单编号</span></span><br><span class="line">        <span class="keyword">const</span> paramWCPay = &#123;</span><br><span class="line">          appId: config.app_id,</span><br><span class="line">          timeStamp: <span class="built_in">parseInt</span>(<span class="keyword">new</span> <span class="built_in">Date</span>().getTime() / <span class="number">1000</span>).toString(),</span><br><span class="line">          nonceStr: createNonceStr(),</span><br><span class="line">          package: <span class="string">'prepay_id='</span> + prepay_id,</span><br><span class="line">          signType: <span class="string">'MD5'</span></span><br><span class="line">        &#125;;</span><br><span class="line">        paramWCPay.paySign = getSign(paramWCPay); <span class="comment">// 微信支付签名</span></span><br><span class="line">        res.json(&#123; <span class="attr">data</span>: paramWCPay &#125;);</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      res.json(&#123; <span class="attr">error</span>: <span class="number">-1</span> &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<ul>
<li><code>getClientIp</code> 是获取客户端真实 IP 地址的方法。</li>
<li><code>createNonceStr</code> 是生成随机串的方式，非常简单粗暴。</li>
<li><code>out_trade_no</code> 是商户自定义的订单号。</li>
<li><code>getUnifiedOrderXml</code> 是用来拼接发送给微信支付统一下单接口的数据，要 <code>xml</code> 格式的，所以就给一个封装。</li>
<li><code>xml2jsparseString</code> 是解析 <code>xml</code> 文件的库。微信支付统一下单接口返回的数据是 <code>xml</code> 格式的，需要进行相应的解码。</li>
<li><code>getSign</code> 是生成签名的方法。</li>
</ul>
<hr>
<p>如果不出意外，下单接口会返回一个 <code>xml</code> 数据，然后我们就可以提取到 <code>prepay_id</code> 这个核心的信息了。</p>
<h4 id="api-wxresponse-接口"><a href="#api-wxresponse-接口" class="headerlink" title="/api/wxresponse 接口"></a><code>/api/wxresponse</code> 接口</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 处理微信支付的回调</span></span><br><span class="line">app.post(<span class="string">'/api/wxresponse'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">req, res</span>) </span>&#123;</span><br><span class="line">  wFile(<span class="string">'response'</span>, req.body);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">let</span> xmlData = req.body.xml;</span><br><span class="line">  <span class="keyword">let</span> ret = <span class="string">''</span>;</span><br><span class="line">  <span class="keyword">if</span> (xmlData.sign === getSign(xmlData)) &#123;</span><br><span class="line">    ret = <span class="string">`&lt;xml&gt;</span></span><br><span class="line"><span class="string">  &lt;return_code&gt;&lt;![CDATA[SUCCESS]]&gt;&lt;/return_code&gt;</span></span><br><span class="line"><span class="string">  &lt;return_msg&gt;&lt;![CDATA[OK]]&gt;&lt;/return_msg&gt;</span></span><br><span class="line"><span class="string">&lt;/xml&gt;`</span>;</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    ret = <span class="string">`&lt;xml&gt;</span></span><br><span class="line"><span class="string">  &lt;return_code&gt;&lt;![CDATA[SIGNATRURE_ERROR]]&gt;&lt;/return_code&gt;</span></span><br><span class="line"><span class="string">  &lt;return_msg&gt;&lt;![CDATA[FAIL]]&gt;&lt;/return_msg&gt;</span></span><br><span class="line"><span class="string">&lt;/xml&gt;`</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  res.send(ret);</span><br><span class="line"></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p>该接口是用来处理 <code>notify_url</code> 指定的回调链接的相关数据的，本次用的具体值就是 <a href="http://z.hdk4.com/api/wxresponse" target="_blank" rel="noopener">http://z.hdk4.com/api/wxresponse</a>。</p>
<p>如果不对该接口做回应，微信支付会一直发请求，直至系统判定失败。</p>
<blockquote>
<p>注意，使用 <code>express</code> 进行开发时，会收不到 <code>req.body</code>，采用以下方法进行解决即可：</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> bodyParser = <span class="built_in">require</span>(<span class="string">'body-parser'</span>);</span><br><span class="line"><span class="built_in">require</span>(<span class="string">'body-parser-xml'</span>)(bodyParser);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 解决微信支付通知回调数据</span></span><br><span class="line">app.use(bodyParser.xml(&#123;</span><br><span class="line">  limit: <span class="string">'1MB'</span>,   <span class="comment">// Reject payload bigger than 1 MB</span></span><br><span class="line">  xmlParseOptions: &#123;</span><br><span class="line">    normalize: <span class="literal">true</span>,     <span class="comment">// Trim whitespace inside text nodes</span></span><br><span class="line">    normalizeTags: <span class="literal">true</span>, <span class="comment">// Transform tags to lowercase</span></span><br><span class="line">    explicitArray: <span class="literal">false</span> <span class="comment">// Only put nodes in array if &gt;1</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;));</span><br></pre></td></tr></table></figure>
<h4 id="签名算法"><a href="#签名算法" class="headerlink" title="签名算法"></a>签名算法</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 签名算法</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getSign</span>(<span class="params">paramSign</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 按 key 值的 ascii 排序</span></span><br><span class="line">  <span class="keyword">const</span> keys = <span class="built_in">Object</span>.keys(paramSign).sort();</span><br><span class="line">  <span class="keyword">const</span> temp = [];</span><br><span class="line">  keys.forEach(<span class="function"><span class="params">v</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (paramSign[v] &amp;&amp; v !== <span class="string">'sign'</span>) &#123;</span><br><span class="line">      temp.push(<span class="string">`<span class="subst">$&#123;v&#125;</span>=<span class="subst">$&#123;paramSign[v]&#125;</span>`</span>);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;);</span><br><span class="line">  temp.push(<span class="string">`key=<span class="subst">$&#123;config.mch_key&#125;</span>`</span>);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> ret = temp.join(<span class="string">'&amp;'</span>);</span><br><span class="line">  <span class="comment">// 生成签名</span></span><br><span class="line">  <span class="keyword">return</span> crypto.createHash(<span class="string">'md5'</span>).update(ret, <span class="string">'utf8'</span>).digest(<span class="string">'hex'</span>).toUpperCase();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>非常<del>简单粗暴</del>的算法，嗯，如上所示。如果是其他语言，在生成 <code>MD5</code> 值的时候方法可能会有所不同，使用对应的方法即可。</p>
<h4 id="随机字符串方法"><a href="#随机字符串方法" class="headerlink" title="随机字符串方法"></a>随机字符串方法</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">createNonceStr</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">Math</span>.random().toString(<span class="number">36</span>).substr(<span class="number">2</span>, <span class="number">15</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><del>是的，这一条是滥竽充数的</del>。</p>
<h3 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h3><ul>
<li><a href="https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_4" target="_blank" rel="noopener">获取openid</a></li>
<li><a href="https://mp.weixin.qq.com/wiki?t=resource/res_main&amp;id=mp1421140842" target="_blank" rel="noopener">微信网页授权</a></li>
<li><a href="https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&amp;index=6" target="_blank" rel="noopener">微信内H5调起支付</a></li>
<li><a href="https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3" target="_blank" rel="noopener">签名算法</a></li>
<li><a href="https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1" target="_blank" rel="noopener">统一下单</a></li>
<li><a href="https://github.com/wxpay/WXPay-SDK-Node.js" target="_blank" rel="noopener">wxpay/WXPay-SDK-Node.js</a></li>
<li><a href="https://gitee.com/anziguoer/wechatPay" target="_blank" rel="noopener">nodejs 微信公众号支付开发</a>，<em>大部分代码参考于此</em>。</li>
<li><a href="https://www.cnblogs.com/yimiyan/p/5603657.html" target="_blank" rel="noopener">微信公众号支付开发全过程 –JAVA</a></li>
<li><a href="http://blog.csdn.net/aofavx/article/details/52220394" target="_blank" rel="noopener">微信公众号支付详细步骤(整理)</a></li>
<li><a href="https://segmentfault.com/a/1190000005797170" target="_blank" rel="noopener">Nginx向ExpressJS转发真实IP地址</a></li>
<li><a href="https://www.jianshu.com/p/0496ef49b2a5" target="_blank" rel="noopener">Nodejs 部署到阿里云全过程</a></li>
<li><a href="http://www.expressjs.com.cn/starter/static-files.html" target="_blank" rel="noopener">利用 Express 托管静态文件</a></li>
<li><a href="http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass" target="_blank" rel="noopener">proxy_pass</a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      本文记录一下关于 node/express 下的微信公众号支付测试小 demo 的开发过程。
    
    </summary>
    
      <category term="Node" scheme="https://xovel.cn/categories/node/"/>
    
    
      <category term="node" scheme="https://xovel.cn/tags/node/"/>
    
      <category term="wxpay" scheme="https://xovel.cn/tags/wxpay/"/>
    
      <category term="公众号" scheme="https://xovel.cn/tags/%E5%85%AC%E4%BC%97%E5%8F%B7/"/>
    
  </entry>
  
  <entry>
    <title>Gulp 4 已发布！</title>
    <link href="https://xovel.cn/article/gulp-4.html"/>
    <id>https://xovel.cn/article/gulp-4.html</id>
    <published>2018-01-10T15:29:19.000Z</published>
    <updated>2018-01-10T16:29:05.308Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>千呼万唤始出来，犹抱琵琶半遮面。</p>
</blockquote>
<p>在这一天发布的日子里面，其实 <code>gulp</code> 一口气是出了四个版本的：</p>
<ul>
<li><code>v4.0.0-alpha.1</code></li>
<li><code>v4.0.0-alpha.2</code></li>
<li><code>v4.0.0-alpha.3</code></li>
<li><code>v4.0.0</code></li>
</ul>
<p>前面三个是打了 <code>pre-release</code> 的标签的。</p>
<p><a href="https://github.com/gulpjs/gulp/releases" target="_blank" rel="noopener">更新记录</a>这里就不赘述了，简单来说一下发布带来的新特性吧。</p>
<p>首先是 API 的变化，新增了两个非常重要的改进：<code>gulp.parallel</code> 和 <code>gulp.series</code>。这一点我之前在<a href="https://github.com/xovel/diary/blob/master/2017/10/2017-10-22-gulp-4.md" target="_blank" rel="noopener">个人日志项目</a>里面也进行过记录，当时的说明还是略显简陋的。</p>
<p><code>gulp.parallel</code> 是用于执行并联任务的，相对的 <code>gulp.series</code> 则用于执行串联任务。</p>
<p><code>gulp</code> 任务的处理内核由之前的 <a href="https://github.com/robrich/orchestrator" target="_blank" rel="noopener"><code>orchestrator</code></a> 迁移到为 <code>gulp</code> 而生的 <a href="https://github.com/gulpjs/undertaker" target="_blank" rel="noopener"><code>undertaker</code></a>。迁移之后的处理方式更加高效便捷。支持的任务命名方式也灵活许多。</p>
<p><code>gulp.task</code> 调整，删除之前第二个任务列表的参数，前置任务之类的全部并入到上面的两个 API 中进行处理，可以更好的控制 <code>gulp</code> 任务的执行顺序。</p>
<p><code>gulp.watch</code> 改进，在 <a href="https://github.com/paulmillr/chokidar" target="_blank" rel="noopener"><code>chokidar</code></a> 的基础上支持各种文件操作方式的监视。</p>
<p>另外新增了几个 API：</p>
<ul>
<li><code>gulp.symlink</code> 用于创建 <code>symlinks</code>。</li>
<li><code>gulp.lastRun</code> 用于获得指定任务上一次的执行时间。</li>
<li><code>gulp.tree</code> 用于获取当前任务的树形结构。</li>
<li><code>gulp.registry</code> 用于获取或者设置注册任务。</li>
</ul>
<p>所有 API 的详细说明可以在这里找到：<a href="https://github.com/gulpjs/gulp/blob/v4.0.0/docs/API.md" target="_blank" rel="noopener">https://github.com/gulpjs/gulp/blob/v4.0.0/docs/API.md</a>。</p>
<p>另外，对于命令行执行方式，也添加更加丰富的指令，详情可以参阅：<a href="https://github.com/gulpjs/gulp/blob/v4.0.0/docs/CLI.md" target="_blank" rel="noopener">https://github.com/gulpjs/gulp/blob/v4.0.0/docs/CLI.md</a></p>
<p>更新之后，对现有的生态系统影响不会太大，因为还是保留了 <code>v3.9.1</code> 的安装方式。要安装 <code>v4</code>，需要使用 <code>npm install gulp@next</code> 命令来进行安装和获取。这些也都在其 <code>README.md</code> 文件里面有详细说明。</p>
<hr>
<p>鉴于发布的太过于突然，我来不及反应，也暂时没有想法要将现有系统的构建脚本升级到适配 <code>gulp v4</code> 的版本。</p>
<p>前不久在 <code>parcel</code> 爆发之后，自己曾经说过，<code>gulp v4</code> 发布遥遥无期，这还不到一个月，我就被打脸了，想想也真是如梦如幻啊。</p>
]]></content>
    
    <summary type="html">
    
      是的，就在 2017 与 2018 交替的时间里面，gulp v4 发布了，发布时间为北京时间 2018 年 1 月 1 日 上午 9 点。
    
    </summary>
    
      <category term="随笔" scheme="https://xovel.cn/categories/essay/"/>
    
    
      <category term="gulp" scheme="https://xovel.cn/tags/gulp/"/>
    
  </entry>
  
  <entry>
    <title>2017 年总结</title>
    <link href="https://xovel.cn/article/summary-2017.html"/>
    <id>https://xovel.cn/article/summary-2017.html</id>
    <published>2017-12-31T13:20:48.000Z</published>
    <updated>2018-01-01T12:11:52.025Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>转眼已经是 2017 年的最后一天了，是时候来好好做一个全面的总结了。</p>
</blockquote>
<p>以上这段文字是<del>抄袭</del>自去年的总结。</p>
<p>今年的总结就换一个方式吧。</p>
<h3 id="先做检讨"><a href="#先做检讨" class="headerlink" title="先做检讨"></a>先做检讨</h3><p>2017 年设定了许多的目标，但是很遗憾，基本上没有完成。最为痛心的是继续弃坑《圣血记》，这一部当初踌躇满志气势恢宏的小说，如今也只剩一声叹息。或许是现实生活与我的理念背道而驰，或许是自己的诸多想法不切实际过于天真，导致我没有太多的心思去创作。近期我每每念及此处，都不禁哀叹遗憾。</p>
<!--
《圣血记》是一部作者用来反应社会现实的玄幻浪漫主义小说，有些架空和修仙的成分。有三部曲，前两部分别为《鸣泉录》、《寻玉》，最后一部暂未进行构思（有强烈的依赖关系）。了解这个系列的人应该会知道我为什么没有给第三部定名。

《鸣泉录》是以作者自己为原型，用一种近乎自恋的手法来描述了一段荡气回肠的爱情故事。故事玄幻色彩很重，讲述了幽冥、驾驭魂魄的一些的现实生活中几乎不可能发生的事情，也在试图为幽魂之事正名，使其显得并不那么幽暗恐怖。《鸣泉录》因为故事中映射的女主角原型的人生经历变化，已被作者强行中止，整个故事的走向发生了剧变。在宏大场面还没有来得及展开的情况下强行中止，说起来也是一种遗憾。

《寻玉》讲述的是另一位与《鸣泉录》男主人公有密切联系（为表兄弟关系）的人物寻找失落宝物相关的故事，故事本身其实是一个很大的轮回。遍寻许久没有找到的东西，原来一直就在自己身边，这也是作者将“远在天边近在眼前”的种种擦肩错过进行艺术化处理的尝试。《鸣泉录》中未能叙述的故事也将在《寻玉》中进行补充。而正当进行到了补充的阶段，《寻玉》也难以继续下去。

我想这应该是一个我绕不过去的坎。总之，从 2016 年下半年开始，就一直搁置了《圣血记》的故事创作。原本打算 2017 年进行重构，但诸多之前的设定都发生了变化，我没有继续创作下去。整个 2017 年，只写了一部分关于《鸣泉录》男主角恢复神志的情节。如今，回过头看《寻玉》的创作，都快变成多主人公的设定了，有点模仿《天龙八部》和《海上牧云记》的节奏。

在阅读一些作品的评鉴的时候，得知一个这样的信息：所有的小说创作，其背景其实都是为了剧情和人物而服务。如果依赖背景去进行创作，除非是有一个非常统一并且经得起推敲的背景故事，否则往往会令人曲解了故事的本原。

*****************

好了，上面其实都是扯谈，意图分散诸君的注意力，也是在弱化自己的侧重点。
-->
<p>工作方面，没有能够稳定住现有的工作。是的，我在 11 月份的时候从上一家公司离职了。上一家公司对我很好，同事之间相处也非常融洽，领导也很是器重我。我选择离开，无论如何，都是工作不稳定的一种体现。原本自己是想打算在上一家公司静静打拼两年，积攒一些人脉，但是事实上我并没有这样去做。这也可能是促使我做出离职决定的一个重大原因吧。</p>
<p>生活方面，说来也甚是杯具。我依然在人生的道路上孤军奋战，我内心多么渴望能有一个人和我一起共同面对未知的旅途呀。说多了都是泪的节奏，归根结底都是因为自己自身的原因。所幸，2017 年，我到底还是学会了如何去勇敢面对。有时候内心面临崩溃，身边又孤立无援的时候，我不再像之前在杭州那般堕落消极，而是努力寻找解决方案。</p>
<h3 id="挖过的坑"><a href="#挖过的坑" class="headerlink" title="挖过的坑"></a>挖过的坑</h3><p>2017 年，我给自己是挖了很多的坑的。这里也稍作罗列，希望还能有时间和精力去填补。</p>
<ol>
<li><code>zob</code> 项目。该项目最初设定的是解决 <code>MVVM</code> 框架的问题，后来项目失败，项目的定位迁移成了代码和写作风格指导。</li>
<li><code>moonlight</code> 项目。该项目原本计划是在 2017 年产出至少两个的子项目，但是很遗憾，目前只有一个半成品的小项目。</li>
<li><code>lui</code> 项目。这个坑挖的太大了，导致项目直接崩盘。该项目的定位是用于解决终端之间的样式风格问题，而目前市面上的大多数 UI 库都致力于解决这个问题，并不是一朝一夕的事情。<del><code>L</code> 是法语单词 <code>libellule</code> 的首字母，意思为_蜻蜓_</del>。</li>
<li>创作系列之<strong>内心小剧场</strong>。这其实也算是一个不大不小的坑，最初的灵感来源是国庆假期的时候去见了她。一直想用一些文字来描述自己的内心世界，开设这个小项目，也是为了释放一下自己心中的情感。</li>
<li>代码风格统一化。这也算团队协作的一个组成部分吧，然而目前只是做到了自己编码风格的统一，并没有对团队成员做统一规范要求。</li>
</ol>
<h3 id="有过的想法"><a href="#有过的想法" class="headerlink" title="有过的想法"></a>有过的想法</h3><p>在 2017 年有过的想法，但是一直没有去执行的事情：</p>
<ol>
<li>日历项目。记录所有重要的日期。</li>
<li>运营一个微信公众号。</li>
<li>收集某一垂直领域的相关资料。拟定的方向有两个，分别为养生和建筑。</li>
<li>成为 <code>vue.js</code> 的贡献者。</li>
</ol>
<p>可以将理由统一定性为懒惰，不思进取。这些事情也逐渐让我明白了一个道理：一个人的精力毕竟还是有限的。</p>
<hr>
<p>下面来总结一下 2017 可圈可点的事情：</p>
<h3 id="接手的项目"><a href="#接手的项目" class="headerlink" title="接手的项目"></a>接手的项目</h3><h4 id="中小学装备管理系统"><a href="#中小学装备管理系统" class="headerlink" title="中小学装备管理系统"></a>中小学装备管理系统</h4><p>上一家公司拳头产品，我在其中负责前端的开发。</p>
<h4 id="集中采购系统"><a href="#集中采购系统" class="headerlink" title="集中采购系统"></a>集中采购系统</h4><p>上一家公司的另一款采购物品的管理后台产品，与装备管理系统密切相关。同样的，我也是前端主程。</p>
<h4 id="数据直报系统"><a href="#数据直报系统" class="headerlink" title="数据直报系统"></a>数据直报系统</h4><p>与上一个项目基本类似，用于学校端的数据填写上报，行管端的审批与汇总，方便管理系统进行数据的统一管理。</p>
<blockquote>
<p>以上各个项目的版本因为公司的战略计划，统一都是 <code>V9</code>。</p>
</blockquote>
<h4 id="实验教学平台"><a href="#实验教学平台" class="headerlink" title="实验教学平台"></a>实验教学平台</h4><p>该项目分为三个迭代阶段，我都参与了。第一个阶段为项目雏形时期，采用传统的 <code>jQuery/Bootstrap</code> 搭建，该阶段直接被第二个阶段替代。第二个阶段为采用 <code>angular.js</code> 进行搭建，我参与了主框架的搭建，并进行了路由配置以及懒加载方案的探索与实现。第三个阶段为 <code>vue.js</code> 阶段，此阶段为项目前端正式转型之后采用的框架，我只参与了部分主框架的搭建，之后因公司战略调整，我回到了装备开发团队。</p>
<h4 id="空港出行管理后台"><a href="#空港出行管理后台" class="headerlink" title="空港出行管理后台"></a>空港出行管理后台</h4><p>这便就是当前自己经手的核心项目。</p>
<h3 id="与工作相关的子项目"><a href="#与工作相关的子项目" class="headerlink" title="与工作相关的子项目"></a>与工作相关的子项目</h3><h4 id="sinput-js"><a href="#sinput-js" class="headerlink" title="sinput.js"></a><a href="https://github.com/xovel/sinput/tree/1.x" target="_blank" rel="noopener"><code>sinput.js</code></a></h4><p>一个用于列表模糊检索的 <code>jQuery</code> 插件。因前面提到的装备管理系统中，存在大量的模糊检索的需求，为了应对复杂的产品需求，在采用了诸多现有的插件之后，便有了这样一个子项目的诞生。主要目的是解决千奇百怪的需求。插件性能和效果表现均十分良好。</p>
<h4 id="dreport-xlsx"><a href="#dreport-xlsx" class="headerlink" title="dreport-xlsx"></a><code>dreport-xlsx</code></h4><p>因数据直报系统中使用到了大量的表格导入导出功能，后台人员在制作模板的过程中，需要面临大量重复冗余的操作，遂在 <code>node-xlsx</code> 的基础之上，为缩短人工操作的时间和避免操作失误，开发了这一款用于生成数据模板的子项目。</p>
<blockquote>
<p>不过，因为离职走的匆忙，相关的代码我没有带走。</p>
</blockquote>
<h4 id="util-js"><a href="#util-js" class="headerlink" title="util.js"></a><code>util.js</code></h4><p>单独将此拎出来是因为该文件的定位至关重要。该项目收集并整理了部分在前端开发中使用频率非常高的工具方法。在上一家公司之后，又加入诸多方法比如重新封装 <code>ajax</code> 方法，表格组件的支持更加丰富，加入了部分类似与 <code>vue</code> 和 <code>angular</code> 过滤器的概念方法等等。</p>
<h3 id="个人项目"><a href="#个人项目" class="headerlink" title="个人项目"></a>个人项目</h3><h4 id="htree"><a href="#htree" class="headerlink" title="htree"></a><a href="https://github.com/xovel/htree" target="_blank" rel="noopener"><code>htree</code></a></h4><p>一款用于 <code>node.js</code> 环境的类似于 <code>Linux</code> 系统的 <code>tree</code> 命令的命令行工具。更多介绍可以参考之前在博客中发表的这篇文章：<a href="/article/htree.html">htree，一款模仿 tree 命令的本地文件结构查看工具</a>。</p>
<h4 id="最合拍"><a href="#最合拍" class="headerlink" title="最合拍"></a>最合拍</h4><p>一款面向羽毛球运动的微信商城项目。<del>后该项目因为迭代升级原因，我个人退出下一个版本的开发</del>。</p>
<h4 id="第五空间文学网"><a href="#第五空间文学网" class="headerlink" title="第五空间文学网"></a>第五空间文学网</h4><p>一款面向小说后台管理的静态页面实现。</p>
<h3 id="新增的技术栈"><a href="#新增的技术栈" class="headerlink" title="新增的技术栈"></a>新增的技术栈</h3><ul>
<li><code>angular.js</code>。开始对大型应用的 <code>MVVM</code> 模式做深入的了解与应用。</li>
<li><code>jenkins</code> 的使用，本人的日志项目中有许多记录是关于 <code>jenkins</code> 这一款 <code>CI</code> 系统的。</li>
<li><code>gulp</code> 构建复杂的大型应用。配合简单的 <code>node</code> 命令完成各种场景需求。</li>
<li><code>node.js</code> 编写简单基础的应用。如上面提到的 <code>dreport-xlsx</code>，上一条提到的 <code>gulp</code> 生成 <code>config</code> 配置文件，均采用 <code>node</code> 执行文件来产出。</li>
<li><code>nginx</code> 搭建与配置，代理纯前端站点。多文件的配置模式。</li>
<li><code>xshell</code> 连接远程主机进行调试。主要是操作 <code>nginx</code> 和查看 <code>tomcat</code> 的后台日志记录文件。</li>
<li><code>webpack</code> 技术，使用 <code>vue</code> 进行项目的骨架搭建。</li>
<li><code>vue</code>，非常优秀的前端框架，算是入了门。组件库的使用这里就不赘述了。</li>
</ul>
<h3 id="阅读与写作"><a href="#阅读与写作" class="headerlink" title="阅读与写作"></a>阅读与写作</h3><p>说来惭愧，2017 年，自己的写作任务全面处于弃坑状态，所以这里就针对自己的阅读情况做一个简单的记录。</p>
<ul>
<li>《圣血记》的创作，持续搁置。“她”的原型再度丢失，我失去了创作的方向。</li>
<li>购买了一些技术方面的书籍，如《代码整洁之道》、《深入理解 ES6》、《HTTP 指南》等。基本上都是下半年购买的，远远没有达到年初既定的目标（<code>≥ 10</code>）。</li>
<li>图书馆去的次数大概在 20 次，每次借阅的书籍数量平均为两本。也是没有能够达到最初的目标的。书籍的范围大都是技术和哲学相关的。希望 2018 年能够借阅更多类型的书籍来拓展自己的知识视野。</li>
<li>各大博客和站点，以掘金、简书、开发者头条、干货集中营、前端之巅、前端日刊等为代表，几乎推送过来的文章，跟前端和 UI 相关的都进行了阅读，并针对部分文章做了收藏与整理记录。</li>
<li>个人博客的创作，2017 年的产出量不超过 10 篇，能拿得出手的其实只有两篇，一篇是介绍 <code>brackets</code> 插件的，另一篇是介绍 <code>htree</code> 的。</li>
<li>个人日志项目，坚持了下来。虽然每天都记录了，但是个人认为有效记录的比率在 60% 左右（即有大约 30% ~ 40% 是并没有什么实际意义的记录或者是流水账式的记录）。我会抽一个时间对 2017 年写下的日志做一个整理，看看自己的技术成长路线是个什么样子的。<del>嗯，这又算是给自己挖了一个坑</del>。</li>
</ul>
<h3 id="关于音乐"><a href="#关于音乐" class="headerlink" title="关于音乐"></a>关于音乐</h3><p>截止目前为止，我在网易云音乐上面的听歌总数达到了八千多首，主要类型可以分为两类，一类是纯音乐，包括史诗、空间、纯电音、新世纪等等；另一类是柔和舒缓类型的。</p>
<p>自己的音乐梦想，到目前为止，已经中断将近 20 年了。说起来也甚是滑稽可笑，我其实是有条件进一步接触音乐的，但我一直没有勇气将其拎起来。我并不知道何时才能重拾年幼时的梦想，也可能一辈子都不会再捡起来了吧。</p>
<p>自己的歌唱水平比以前好了很多很多。以前 100% 走调的我，现在也能逐渐掐准节奏了。深情，有故事，这是我得到的最多的评价。</p>
<h3 id="展望-2018"><a href="#展望-2018" class="headerlink" title="展望 2018"></a>展望 2018</h3><p>说了这么多，我其实也是在一直犹豫，对于 2018 年的规划是什么样子的，其实早在 2017 年 7 月份，我就已经做出了明确的规划。</p>
<p>有些事情，是不方便明说的。2017 年没有来得及完成的事情，2018 年也不会特意去处理，毕竟各有各的规划。若是刻意去完成某件事情，是否又偏离了原定计划呢？</p>
<p>简单粗暴的总结一下 2018 年的展望：</p>
<ul>
<li>遇见更好的自己。</li>
<li>让自己内心更加平静。</li>
<li>努力提升自己的情商和魅力值。</li>
</ul>
<blockquote>
<p>嗯，后面两点是<del>充数</del>的。</p>
</blockquote>
<hr>
<blockquote>
<p><em>知道的越多，就越发现自己不知道的其实更多</em>。<del>这句不是鲁迅说的，是古希腊大哲学家芝诺说的</del>。</p>
<p>因此而产生恐惧，那是万万不可取的。</p>
</blockquote>
<hr>
<p>2018 年，加油！</p>
]]></content>
    
    <summary type="html">
    
      2017 年年末大总结。
    
    </summary>
    
      <category term="生活" scheme="https://xovel.cn/categories/life/"/>
    
    
      <category term="2017" scheme="https://xovel.cn/tags/2017/"/>
    
      <category term="总结" scheme="https://xovel.cn/tags/%E6%80%BB%E7%BB%93/"/>
    
      <category term="经历" scheme="https://xovel.cn/tags/%E7%BB%8F%E5%8E%86/"/>
    
      <category term="2018" scheme="https://xovel.cn/tags/2018/"/>
    
  </entry>
  
  <entry>
    <title>中国的商标分类</title>
    <link href="https://xovel.cn/article/trademark-classification.html"/>
    <id>https://xovel.cn/article/trademark-classification.html</id>
    <published>2017-12-31T11:54:33.000Z</published>
    <updated>2017-12-31T15:58:52.650Z</updated>
    
    <content type="html"><![CDATA[<p>商标是区别商品或服务来源的一种标志，每一个注册商标都是指定用于某一商品或服务上的。</p>
<a id="more"></a>
<p>不想引述大段现有的资料。</p>
<p>打算设立一个项目，就是针对开启高强度的模糊搜索效果的商标范围在线搜索。</p>
<p>为什么不提供精确的商标查询效果？说来惭愧，没有能够顺利破解 <code>商标网</code> 提供的 <code>API</code>。前天听后台开发的同事讲解了一些开放平台和网关拦截的知识，我想我是碰到了。我没有找到合适的破解方案，时间一到就告知权限失效或者查无数据了。所以，暂时先提供一个商标范围的搜索项目，名字就叫做 <code>trademark-classification</code>。所有数据来源来自商标网的官方数据，采集时间为 <code>2017-10-31</code>。</p>
<p>部分参考资料：</p>
<ul>
<li><a href="http://sbj.saic.gov.cn/sbsq/sphfwfl/" target="_blank" rel="noopener">商标注册用商品和服务分类说明</a></li>
<li><a href="http://wsjs.saic.gov.cn" target="_blank" rel="noopener">中国商标网</a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;商标是区别商品或服务来源的一种标志，每一个注册商标都是指定用于某一商品或服务上的。&lt;/p&gt;
    
    </summary>
    
      <category term="随笔" scheme="https://xovel.cn/categories/essay/"/>
    
    
      <category term="商标" scheme="https://xovel.cn/tags/%E5%95%86%E6%A0%87/"/>
    
      <category term="分类" scheme="https://xovel.cn/tags/%E5%88%86%E7%B1%BB/"/>
    
  </entry>
  
  <entry>
    <title>PARCEL - 一个极快零配置的打包工具</title>
    <link href="https://xovel.cn/article/parcel-bundler.html"/>
    <id>https://xovel.cn/article/parcel-bundler.html</id>
    <published>2017-12-12T01:33:28.000Z</published>
    <updated>2017-12-12T01:51:43.059Z</updated>
    
    <content type="html"><![CDATA[<p><img src="http://wx4.sinaimg.cn/large/79be2309gy1fmdqbevvmgj20m806dmz8.jpg" alt=""></p>
<blockquote>
<p>前端的发展真是日新月异，这不，打包工具方面最近又出了一个爆款，名字叫做 <code>parcel</code>。本篇博客转载其 <code>README.md</code>，暂不做翻译。原文请移步：<a href="https://github.com/parcel-bundler/parcel" target="_blank" rel="noopener">parcel-bundler/parcel</a>。</p>
</blockquote>
<hr>
<p>Blazing fast, zero configuration web application bundler <a href="https://parceljs.org" target="_blank" rel="noopener">https://parceljs.org</a></p>
<h2 id="Features"><a href="#Features" class="headerlink" title="Features"></a>Features</h2><ul>
<li>🚀 <strong>Blazing fast</strong> bundle times - multicore compilation, and a filesystem cache for fast rebuilds even after a restart.</li>
<li>📦 Out of the box support for JS, CSS, HTML, file assets, and more - <strong>no plugins to install</strong>.</li>
<li>🐠 <strong>Automatically transforms modules</strong> using Babel, PostCSS, and PostHTML when needed - even <code>node_modules</code>.</li>
<li>✂️ Zero configuration <strong>code splitting</strong> using dynamic <code>import()</code> statements.</li>
<li>🔥 Built in support for <strong>hot module replacement</strong></li>
<li>🚨 Friendly error logging experience - syntax highlighted code frames help pinpoint the problem.</li>
</ul>
<h2 id="Getting-started"><a href="#Getting-started" class="headerlink" title="Getting started"></a>Getting started</h2><ol>
<li>Install with yarn:</li>
</ol>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn global add parcel-bundler</span><br></pre></td></tr></table></figure>
<p>or with npm:<br><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install -g parcel-bundler</span><br></pre></td></tr></table></figure></p>
<ol start="2">
<li>Parcel can take any type of file as an entry point, but an HTML or JavaScript file is a good place to start. If you link your main JavaScript file in the HTML using a relative path, Parcel will also process it for you, and replace the reference with a URL to the output file.</li>
</ol>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">"./index.js"</span>&gt;</span><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure>
<ol start="3">
<li>Parcel has a development server built in, which will automatically rebuild your app as you change files and supports hot module replacement for fast development. Just point it at your entry file:</li>
</ol>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">parcel index.html</span><br></pre></td></tr></table></figure>
<ol start="4">
<li>Now open <a href="http://localhost:1234/" target="_blank" rel="noopener">http://localhost:1234/</a> in your browser. If needed, you can also override the default port with the -p option.</li>
</ol>
<p>See <a href="https://parceljs.org" target="_blank" rel="noopener">parceljs.org</a> for more documentation!</p>
<h2 id="Benchmarks"><a href="#Benchmarks" class="headerlink" title="Benchmarks"></a>Benchmarks</h2><p>Based on a reasonably sized app, containing 1726 modules, 6.5M uncompressed. Built on a 2016 MacBook Pro with 4 physical CPUs.</p>
<table>
<thead>
<tr>
<th>Bundler</th>
<th>Time</th>
</tr>
</thead>
<tbody>
<tr>
<td>browserify</td>
<td>22.98s</td>
</tr>
<tr>
<td>webpack</td>
<td>20.71s</td>
</tr>
<tr>
<td><strong>parcel</strong></td>
<td><strong>9.98s</strong></td>
</tr>
<tr>
<td><strong>parcel - with cache</strong></td>
<td><strong>2.64s</strong></td>
</tr>
</tbody>
</table>
<h2 id="Why-parcel"><a href="#Why-parcel" class="headerlink" title="Why parcel?"></a>Why parcel?</h2><p>There are many web application bundlers out there with huge adoption, including webpack and browserify. So, why do we need another one? The main reasons are around developer experience.</p>
<p>Many bundlers are built around configuration and plugins, and it is not uncommon to see applications with upwards of 500 lines of configuration just to get things working. This configuration is not just tedious and time consuming, but is also hard to get right and must be duplicated for each application. Oftentimes, this can lead to sub-optimized apps shipping to production. <code>parcel</code> is designed to need zero configuration: just point it at the entry point of your application, and it does the right thing.</p>
<p>Existing bundlers are also very slow. Large applications with lots of files and many dependencies can take minutes to build, which is especially painful during development when things change all the time. File watchers can help with rebuilds, but the initial launch is often still very slow. <code>parcel</code> utilizes worker processes to compile your code in parallel, utilizing modern multicore processors. This results in a huge speedup for initial builds. It also has a file system cache, which saves the compiled results per file for even faster subsequent startups.</p>
<p>Finally, existing bundlers are built around string loaders/transforms, where the transform takes in a string, parses it, does some transformation, and generates code again. Oftentimes this ends up causing many parses and code generation runs on a single file, which is inefficient. Instead, <code>parcel</code>‘s transforms work on ASTs so that there is one parse, many transforms, and one code generation per file.</p>
<h2 id="How-it-works"><a href="#How-it-works" class="headerlink" title="How it works"></a>How it works</h2><p><code>parcel</code> transforms a tree of assets to a tree of bundles. Many other bundlers are fundamentally based around JavaScript assets, with other formats tacked on - for example, by default inlined as strings into JS files. <code>parcel</code> is file-type agnostic - it will work with any type of assets the way you’d expect, with no configuration.</p>
<p><code>parcel</code> takes as input a single entry asset, which could be any type: a JS file, HTML, CSS, image, etc. There are various asset types defined in <code>parcel</code> which know how to handle specific file types. The assets are parsed, their dependencies are extracted, and they are transformed to their final compiled form. This creates a tree of assets.</p>
<p>Once the asset tree has been constructed, the assets are placed into a bundle tree. A bundle is created for the entry asset, and child bundles are created for dynamic imports, which cause code splitting to occur. Child bundles are also created when assets of a different type are imported, for example if you imported a CSS file from JavaScript, it would be placed into a sibling bundle to the corresponding JavaScript. If an asset is required in more than one bundle, it is hoisted up to the nearest common ancestor in the bundle tree so it is not included more than once.</p>
<p>After the bundle tree is constructed, each bundle is written to a file by a packager specific to the file type. The packagers know how to combine the code from each asset together into the final file that is loaded by a browser.</p>
<h2 id="Community"><a href="#Community" class="headerlink" title="Community"></a>Community</h2><p>All feedback and suggestions are welcome!</p>
<ul>
<li>💬 Chat: Join us on <a href="https://slack.parceljs.org/" target="_blank" rel="noopener">slack</a>.</li>
<li>📣 Stay up to date on new features and announcements on <a href="https://twitter.com/parceljs" target="_blank" rel="noopener">@parceljs</a>.</li>
</ul>
<h2 id="License"><a href="#License" class="headerlink" title="License"></a>License</h2><p>MIT</p>
<hr>
<p><a href="http://www.css88.com/doc/parcel/" target="_blank" rel="noopener">Parcel 中文文档</a>。不得不佩服这个网站，以飞快之速完成了对文档的翻译。<del>当然，现在的官方文档内容其实也不多</del>。</p>
<p>目前这个项目吸引了很多的开发者，诸多在 <code>webpack</code> 中的特性也会逐步加入进来，不出意外的话，这个是继 <code>rollup</code> 之后，又一个与 <code>webpack</code> 争天下的项目。</p>
]]></content>
    
    <summary type="html">
    
      前端的发展真是日新月异，这不，打包工具方面最近又出了一个爆款，名字叫做 `parcel`。本文转载其说明文档 `README.md`。
    
    </summary>
    
      <category term="开发" scheme="https://xovel.cn/categories/dev/"/>
    
      <category term="文档" scheme="https://xovel.cn/categories/dev/docs/"/>
    
    
      <category term="parcel" scheme="https://xovel.cn/tags/parcel/"/>
    
      <category term="bunlder" scheme="https://xovel.cn/tags/bunlder/"/>
    
  </entry>
  
  <entry>
    <title>编程命名规则</title>
    <link href="https://xovel.cn/article/naming-rule.html"/>
    <id>https://xovel.cn/article/naming-rule.html</id>
    <published>2017-09-24T04:07:13.000Z</published>
    <updated>2017-09-24T08:21:16.547Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>There are only two hard things in Computer Science: cache invalidation and naming things. – Phil Karlton</p>
</blockquote>
<p>正如上面这一句经典的话语，道出了计算机领域两个最大的难题：命名与缓存。</p>
<p>本文就“命名”这个东西，做一个浅显的解读。</p>
<h3 id="匈牙利命名法"><a href="#匈牙利命名法" class="headerlink" title="匈牙利命名法"></a>匈牙利命名法</h3><p>该方法也叫做 <code>HN 命名法</code>，通常认为是微软的一个雇员 <code>Charles Simonyi</code> 发明的，通过微软的各种产品和文档资料传播开来。这位雇员是一个匈牙利（Hungarian）人，这也就是这个命名方法的名字由来。</p>
<p>匈牙利命名法的基本原则是：变量名依次由属性、类型、描述组成。</p>
<p>这里就通过举例来进行直接的说明：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> iUserName; <span class="comment">// i 为 int 类型缩写</span></span><br><span class="line"><span class="keyword">char</span> cItemList; <span class="comment">// c 为 char 类型缩写</span></span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> bInitLogin; <span class="comment">// b 为 布尔值 boolean 缩写</span></span><br></pre></td></tr></table></figure>
<figure class="highlight vb"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">dim</span> frmInfoDetail; <span class="comment">' frm 为窗体 form 的缩写</span></span><br></pre></td></tr></table></figure>
<h3 id="驼峰命名法"><a href="#驼峰命名法" class="headerlink" title="驼峰命名法"></a>驼峰命名法</h3><p>驼峰命名法 <code>camelCase</code>，来自于 Perl 语言普遍使用的大小写混合格式，后来被 Java 广泛采用，逐渐成为更加通用的命名方法。</p>
<p>驼峰命名法的基本规则是变量名或者函数名是由一个或者多个单词连接在一起，构成唯一识别符，第一个单词以小写字母开始，后面的单词首字母大写，这样看起来跟驼峰一样此起彼伏，故此得名。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> myStudentCount;</span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">fs.readFileSync</span><br></pre></td></tr></table></figure>
<h3 id="帕斯卡命名法"><a href="#帕斯卡命名法" class="headerlink" title="帕斯卡命名法"></a>帕斯卡命名法</h3><p>跟驼峰命名法类似，帕斯卡命名法 <code>PascalCase</code> 的不同之处在于第一个单词首字母为<strong>大写</strong>。在一些构造器命名和全局特殊变量命名的时候通常会采用这种命名方法。</p>
<p>鉴于跟驼峰命名法的相似，也有人将帕斯卡命名法称之为 <code>大驼峰命名法</code>，而上面的就称之为 <code>小驼峰命名法</code>。</p>
<h3 id="蛇形命名法"><a href="#蛇形命名法" class="headerlink" title="蛇形命名法"></a>蛇形命名法</h3><p>蛇形命名法 <code>snake_case</code>，变量名由多个部分组成，每个部分之间使用下划线 <code>_</code> 进行连接，所以也称之为 <code>下划线命名法</code>。</p>
<h3 id="脊柱命名法"><a href="#脊柱命名法" class="headerlink" title="脊柱命名法"></a>脊柱命名法</h3><p>脊柱命名法 <code>spinal-case</code>，跟蛇形命名法类似，不过连接符为连接符 <code>-</code>。也称之为 <code>kebab-case</code>、<code>train-case</code>。在 URL 命名，HTML 属性、CSS 属性、Lisp 语言比较常见，所以也称之为 <code>lisp-case</code>。</p>
<blockquote>
<p>kebab 意思是阿拉伯烤肉，与 train、spinal 一样，因为其生动的形象，跟连字符风格类似，均以此得名。</p>
</blockquote>
<h3 id="混杂大小写"><a href="#混杂大小写" class="headerlink" title="混杂大小写"></a>混杂大小写</h3><p><code>Studly caps</code>，这一种命名方法，其实我不知道该如何去翻译和描述，算是一种很非主流的命名方式。就是杂乱无章的大小写混拼方式。比如这种命名方式来命名自己的名称，可以是 StUdLyCaPs 或者 STuDLyCaPS。</p>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>其实上面的种种命名方式，在语义学上面，统称为 <code>case</code>。这也是为什么其英文名称中大都出现了 <code>case</code> 这个词眼的原因。</p>
<p>现罗列一下常见的各种 <code>case</code>，每一种都是用自身的方式进行书写的。</p>
<ul>
<li><code>CamelCase</code></li>
<li><code>snake_case</code></li>
<li><code>kebab-case</code></li>
<li><code>StUdLyCaPs</code></li>
</ul>
<p>另外加上两个<del>凑数</del>：</p>
<ul>
<li><code>lowercase</code></li>
<li><code>UPPERCASE</code></li>
</ul>
<blockquote>
<p>嗯。是的，一个是小写，一个是大写。</p>
</blockquote>
<hr>
<p>在如今的编码过程中，并不会总是遵循某一种规则，而是各种风格混合使用，这样也能更好的适应当前的业务场景。</p>
]]></content>
    
    <summary type="html">
    
      编程命名规则有很多种，本文就简单做一个介绍。
    
    </summary>
    
      <category term="开发" scheme="https://xovel.cn/categories/dev/"/>
    
    
      <category term="naming" scheme="https://xovel.cn/tags/naming/"/>
    
      <category term="case" scheme="https://xovel.cn/tags/case/"/>
    
  </entry>
  
  <entry>
    <title>htree，一款模仿 tree 命令的本地文件结构查看工具</title>
    <link href="https://xovel.cn/article/htree.html"/>
    <id>https://xovel.cn/article/htree.html</id>
    <published>2017-09-21T14:53:22.000Z</published>
    <updated>2017-09-22T01:09:05.100Z</updated>
    
    <content type="html"><![CDATA[<h3 id="htree-介绍"><a href="#htree-介绍" class="headerlink" title="htree 介绍"></a>htree 介绍</h3><p>首先，介绍一下 <a href="https://github.com/xovel/htree" target="_blank" rel="noopener"><code>htree</code></a> 这一款工具。<code>htree</code> 是一款用于在 <code>node</code> 环境下运行的工具，安装之后可以直接在 <code>node</code> 命令行窗口输入 <code>htree</code> 进行使用。</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">E:\Git\xovel\htree</span><br><span class="line">├─index.js</span><br><span class="line">├─LICENSE</span><br><span class="line">├─package.json</span><br><span class="line">├─README.md</span><br><span class="line">├─bin</span><br><span class="line">│  └─htree</span><br><span class="line">├─docs</span><br><span class="line">│  └─help</span><br><span class="line">├─node_modules</span><br><span class="line">└─util</span><br><span class="line">   ├─genText.js</span><br><span class="line">   ├─getByteLength.js</span><br><span class="line">   ├─getSize.js</span><br><span class="line">   ├─getTextList.js</span><br><span class="line">   └─walk.js</span><br></pre></td></tr></table></figure>
<blockquote>
<p>以上代码为在 <code>htree</code> 开发文件夹的命令行工具下执行命令 <code>htree</code> 生成的结果。</p>
</blockquote>
<h3 id="参数简介"><a href="#参数简介" class="headerlink" title="参数简介"></a>参数简介</h3><p><code>htree</code> 目前版本为 <code>0.2.0</code>，支持的参数如下：</p>
<h4 id="ignore"><a href="#ignore" class="headerlink" title="ignore"></a>ignore</h4><p>忽略列表，为一个数组，包括文件和文件夹。在列表之中的值均会跳过不进行处理。</p>
<h4 id="exclude"><a href="#exclude" class="headerlink" title="exclude"></a>exclude</h4><p>排除的文件夹。该值如果设置，应该是一个正则表达式。符合正则表达式匹配的文件夹会跳过。</p>
<h4 id="maxDepth"><a href="#maxDepth" class="headerlink" title="maxDepth"></a>maxDepth</h4><p>读取文件夹的最大深度。该深度是基于当前传入的文件夹。</p>
<h4 id="gap"><a href="#gap" class="headerlink" title="gap"></a>gap</h4><p>布尔值，为真则在文件/文件夹名称前面添加一个空格。</p>
<h4 id="concatLength"><a href="#concatLength" class="headerlink" title="concatLength"></a>concatLength</h4><p>节点前缀和文件/文件名之间的连接符的长度。</p>
<h4 id="indent"><a href="#indent" class="headerlink" title="indent"></a>indent</h4><p>布尔值，为真则在子文件/文件夹前面添加空格作为缩进。</p>
<h4 id="indentLength"><a href="#indentLength" class="headerlink" title="indentLength"></a>indentLength</h4><p>上面的缩进的空格的长度。</p>
<h4 id="suffix"><a href="#suffix" class="headerlink" title="suffix"></a>suffix</h4><p>布尔值，是否在文件夹后面添加一个后缀，默认不添加。</p>
<h4 id="strSuffix"><a href="#strSuffix" class="headerlink" title="strSuffix"></a>strSuffix</h4><p>上面的后缀的文本，默认为 <code>/</code>。</p>
<h4 id="strComment"><a href="#strComment" class="headerlink" title="strComment"></a>strComment</h4><p>注释标识符，类型为字符串。如果设置为非空字符串，<code>htree</code> 会自动计算文件/文件夹最大长度，然后再拼接 <code>padLength</code> 长度的空格，之后拼接 <code>strComment</code> 作为列表文本。非最大长度的行会使用空格进行补正对齐。</p>
<h4 id="padLength"><a href="#padLength" class="headerlink" title="padLength"></a>padLength</h4><p>补齐 <code>strComment</code> 所用的空格的长度。</p>
<h4 id="dir"><a href="#dir" class="headerlink" title="dir"></a>dir</h4><p><code>htree</code> 的执行主目录，默认为当前命令行窗口所在的文件夹。</p>
<h4 id="folder"><a href="#folder" class="headerlink" title="folder"></a>folder</h4><p>布尔值，为真时只读取文件夹，跳过非文件夹的部分（即普通文件）。</p>
<h4 id="sort"><a href="#sort" class="headerlink" title="sort"></a>sort</h4><p>布尔值，为真时对同级的文件/文件夹列表进行排序。通常这个设置没有什么效果，但在处理一些特殊字符开头的文件/文件夹之时会用到。默认为真。</p>
<blockquote>
<p>如下划线 <code>_</code> 开头的文件/文件夹默认排在后面。</p>
</blockquote>
<h4 id="size"><a href="#size" class="headerlink" title="size"></a>size</h4><p>布尔值，为真时显示文件的大小，如果 <code>strComment</code>未指定，则将其设置为 <code>#</code>。</p>
<h4 id="order"><a href="#order" class="headerlink" title="order"></a>order</h4><p>对同级文件/文件夹进行归类显示的参数。为<strong>字符串</strong> <code>after</code> 时，文件会被统一放在文件夹后面；为真的其他情况时，文件会统一放在文件夹前面；为假则不做任何处理。</p>
<h4 id="showDir"><a href="#showDir" class="headerlink" title="showDir"></a>showDir</h4><p>布尔值，为真时在最开始处显示当前执行的文件夹路径。</p>
<h4 id="dot"><a href="#dot" class="headerlink" title="dot"></a>dot</h4><p>布尔值，为真时处理以点号 <code>.</code> 号开头的文件夹，否则跳过。</p>
<h4 id="underline"><a href="#underline" class="headerlink" title="underline"></a>underline</h4><p>布尔值，为真时处理以下划线 <code>_</code> 开头的文件夹，否则跳过。</p>
<h3 id="命令行参数"><a href="#命令行参数" class="headerlink" title="命令行参数"></a>命令行参数</h3><p>上面的所有设置均可以通过命令行方式进行参数指定，指定方式为小写连字符风格的字符串加上 <code>--</code> 的前缀。如要指定最大文件夹访问深度 <code>maxDepth</code> 为 <code>10</code>，则使用命令：<code>htree --max-depth 10</code>，或者 <code>htree --max-depth=10</code>。</p>
<p>支持的短命令列表如下：</p>
<ul>
<li><code>-v</code>，即 <code>--version</code>，显示当前 <code>htree</code> 的版本号。</li>
<li><code>-h</code>，即 <code>--help</code>，显示当前 <code>htree</code> 的帮助文件。</li>
<li><code>-f</code>，即 <code>--folder</code>。</li>
<li><code>-d</code>，即 <code>--dir</code>。</li>
<li><code>-i</code>，即 <code>--ignore</code>。</li>
<li><code>-o</code>，即 <code>--output</code>。</li>
<li><code>-s</code>，即 <code>--show-dir</code>。</li>
<li><code>-m</code>，即 <code>--max-depth</code>。</li>
</ul>
<p>为布尔值的参数，可以通过前缀 <code>--no-</code> 设置为 <code>false</code> 值。</p>
<blockquote>
<p>在设置 <code>--dir</code>、<code>--output</code>、<code>--exclude</code>、<code>--str-comment</code> 的值之时，必要时请对特殊字符进行转义操作。</p>
</blockquote>
<h3 id="缘起"><a href="#缘起" class="headerlink" title="缘起"></a>缘起</h3><p>为什么要开发这一款工具呢？其实很早以前就有过这样的想法，但是后来由于工作忙碌就搁置了。直到最近在处理一个树形结构的展示的时候，需要通过设置自定义数据来实现多级树结构的嵌套生成。这个方式与之前设想的文件夹结构展开方式类似，于是决定将这个项目正式开发出来。</p>
<p>经过陆陆续续的改动，对参数配置进行了较大幅度的修改，版本号目前 <code>0.2.0</code>。项目已经趋于稳定，想来应该是没有什么太大的改动了。</p>
<h3 id="模块化"><a href="#模块化" class="headerlink" title="模块化"></a>模块化</h3><p>这个项目里面，算是体验了一把模块化编程。大部分功能都抽离成了模块，<del>虽然也没多少个</del>。</p>
<h3 id="递归"><a href="#递归" class="headerlink" title="递归"></a>递归</h3><p>在获取多级文件夹时候用到了以前上学时期学过的一个技术：递归。</p>
<p>原则上，没有无限极的目录深度，所以递归调用肯定会有某个结束的场景。但是我还是设定了默认的最大目录深度为 <code>5</code>。</p>
<h3 id="npm-相关"><a href="#npm-相关" class="headerlink" title="npm 相关"></a>npm 相关</h3><p>在进行本地开发的时候，我并没有创建 <code>node_modules</code> 文件夹，实际开发中，也没有依赖其他的项目，而在本地连接发布 <code>npm link</code> 的时候会自动进行创建。这也是为什么上面的目录中会有这个文件夹而上传到 <code>github</code> 上面的时候由于默认机制的原因忽略了。之前进行 <code>npm</code> 发布的遇到过一个<a href="https://github.com/xovel/xovel.github.io/issues/26" target="_blank" rel="noopener">不大不小的问题</a>，这一次的发布又遇到了，是的，我又设置了淘宝的国内镜像源。</p>
<h3 id="遇到的问题"><a href="#遇到的问题" class="headerlink" title="遇到的问题"></a>遇到的问题</h3><p>开发过程难免碰到一些乍看起来不可思议的问题，比如在获取 <code>windows</code> 磁盘根目录下的文件列表时，由于部分系统文件夹默认禁止访问，如 <code>System Volume Information</code>。即便是开启了管理员权限，也不能直接访问这些文件/文件夹。经过一番思考，目前采用了 <code>try/catch</code> 命令来避免程序报错，并退出当前操作。一些经过特意设置的文件夹如加密过的，也是不能直接读取其属性的，这就无法判断是否是文件夹了。</p>
<p>当然，根据提供的 <code>ignore</code> 和 <code>exclude</code> 参数，可以跳过这些文件/文件夹的访问。</p>
<p>另一个问题是命令行执行的时候，获取当前文件夹错误，原因是最开始的命令行文件 <code>bin/htree</code> 没有对执行的环境进行指定，亦即在最开始的部分并没有这样一句代码：<code>#!/usr/bin/env node</code>。这段代码的作用的是让程序在运行时候的运行环境变为 <code>node</code>，设置为这样之后在获取目录的时候调用 <code>process.cwd()</code> 等方法就表现正常了。</p>
<p>在处理字符串长度的时候，获取字符串的实际占用长度的时候存在一个不大不小的问题，部分字符会占据两个字母的长度，比如中文。由于本人当前主要是在中文环境下面工作，故此在获取字符串长度的时候直接将中文汉字和标点转成了两个字母长度的，再返回其长度，这样来保证设置了 <code>size</code> 或者 <code>strComment</code> 参数的时候，右侧的标识符能够对齐显示。</p>
<p>文件字节长度的计算，这里是采用下面这段代码来实现：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getSize</span>(<span class="params">size</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> suffix = [<span class="string">'B'</span>, <span class="string">'KB'</span>, <span class="string">'MB'</span>, <span class="string">'GB'</span>, <span class="string">'TB'</span>, <span class="string">'PB'</span>, <span class="string">'EB'</span>, <span class="string">'ZB'</span>, <span class="string">'YB'</span>, <span class="string">'DB'</span>, <span class="string">'NB'</span>];</span><br><span class="line">  <span class="keyword">let</span> i = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">while</span> (size &gt;= <span class="number">1000</span>) &#123;</span><br><span class="line">    size /= <span class="number">1024</span>;</span><br><span class="line">    i++;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (i &gt;= suffix.length) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">'...'</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> (i === <span class="number">0</span> ? size : size.toFixed(<span class="number">2</span>)) + <span class="string">' '</span> + suffix[i];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>为此还特意去查阅了一下计算机中的相关单位，还是挺有意思的。</p>
<h3 id="关于代码规范"><a href="#关于代码规范" class="headerlink" title="关于代码规范"></a>关于代码规范</h3><p><code>htree</code> 其实并没有遵循特定的编码规范，当前的编码风格是自己设定的一套风格，即 <code>zob</code>，同样的由于各种原因，该项目并没有进行公布。参考了部分谷歌的<a href="https://google.github.io/styleguide/jsguide.html" target="_blank" rel="noopener">JS 编码风格</a>以及一些当前项目常用的 <a href="https://eslint.org/" target="_blank" rel="noopener">ESLint</a> 规则。</p>
<h3 id="参考项目"><a href="#参考项目" class="headerlink" title="参考项目"></a>参考项目</h3><p>所谓前人栽树，后人乘凉，在完成 <code>htree</code> 这个项目的过程中，遍寻了大量的资料。</p>
<p>命令行代码主要是仿照 <a href="https://github.com/chjj/marked" target="_blank" rel="noopener">marked</a> 项目下的 <code>bin/marked</code> 来编写的。该项目也是已经长久搁置的 <code>zmd</code> 项目的核心参考资源。原本在实现命令行参数的读取的时候要采用某些依赖，比如 <a href="https://github.com/yargs/yargs" target="_blank" rel="noopener">yargs</a> 和 <a href="https://github.com/tj/commander.js" target="_blank" rel="noopener">commander.js</a>，但后来心想，干脆就自己尝试编写纯粹的命令行读取方法吧，就写成了现在这个样子的了。</p>
<p><code>nodejs.org</code> 的官网上的 <a href="https://nodejs.org/api/" target="_blank" rel="noopener">API 文档</a>也提供了不少解决方案，主要是文件读取与写入方面的。</p>
<hr>
<p><code>htree</code> 项目的名字来源，看起来是 <code>h</code> + <code>tree</code> 的组合，粗看之下不会有人知道 <code>h</code> 代表了什么，但是我想要说的是其实这个是一个名字的谐音。嗯，是的，文本到底就结束了，希望后续能做制作一些方便使用的工具，这样也比较符合清风工具的总纲：简易、实用。</p>
]]></content>
    
    <summary type="html">
    
      本文是针对作者开发的一款命令行工具 htree 的介绍，讲述为什么要制作这样的一个工具，以及中途碰到的问题和相应的一些解决方案，权当是一个 node 开发入门的演练。在这期间也加深了对模块化与命令行工具编写的理解，将这些分享出来，与大家分享自己的一些心得。
    
    </summary>
    
      <category term="Node" scheme="https://xovel.cn/categories/node/"/>
    
    
      <category term="npm" scheme="https://xovel.cn/tags/npm/"/>
    
      <category term="node" scheme="https://xovel.cn/tags/node/"/>
    
      <category term="tree" scheme="https://xovel.cn/tags/tree/"/>
    
      <category term="htree" scheme="https://xovel.cn/tags/htree/"/>
    
      <category term="CLI" scheme="https://xovel.cn/tags/CLI/"/>
    
  </entry>
  
  <entry>
    <title>URLSearchParams 简介</title>
    <link href="https://xovel.cn/article/url-search-params.html"/>
    <id>https://xovel.cn/article/url-search-params.html</id>
    <published>2017-09-11T23:52:10.000Z</published>
    <updated>2017-09-11T17:35:47.587Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>本文根据 <code>URLSearchParams</code> 的<a href="https://url.spec.whatwg.org/#urlsearchparams" target="_blank" rel="noopener">官方规范文档</a>和 <a href="https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams" target="_blank" rel="noopener">MDN 文档</a>整理而成。</p>
</blockquote>
<p>URLSearchParams 是一个 WEB 接口，它定义了一些方法来处理 URL 的查询字符串。</p>
<p>构造函数 <code>URLSearchParams()</code> 返回一个 URLSearchParams 对象，这个对象没有特别的属性，也不继承任何方法。</p>
<p>URLSearchParams 的构造实例的方法如下：</p>
<h3 id="append"><a href="#append" class="headerlink" title="append"></a><code>append</code></h3><p>插入一个新的搜索参数。该方法接受两个参数，无返回值，调用形式为 <code>URLSearchParams.append(name, value)</code>，<code>name</code> 为插入搜索参数的键名，<code>value</code> 为对应的值。</p>
<h3 id="delete"><a href="#delete" class="headerlink" title="delete"></a><code>delete</code></h3><p>删除指定名称的<strong>所有</strong>搜索参数。接受一个参数 <code>name</code> 即要删除的键值名称，无返回值。</p>
<h3 id="entries"><a href="#entries" class="headerlink" title="entries"></a><code>entries</code></h3><p><strong>不同于</strong> <code>Object.entries</code>，这里的 <code>entries</code> 方法会返回一个 <code>iterator</code>，可以遍历所有键值对的对象。每一个键值对均为 <code>USVString</code> 对象。</p>
<blockquote>
<ul>
<li>iterator 是迭代协议 Iterator 对象的实例。</li>
<li><code>entries</code> 方法通常在 <code>Web Workers</code> 中使用。</li>
<li><code>USVString</code> 为 unicode 标量值（unicode scalar values）。</li>
</ul>
</blockquote>
<p>代码示例：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Create a test URLSearchParams object</span></span><br><span class="line"><span class="keyword">var</span> searchParams = <span class="keyword">new</span> URLSearchParams(<span class="string">"key1=value1&amp;key2=value2"</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Display the key/value pairs</span></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">var</span> pair <span class="keyword">of</span> searchParams.entries()) &#123;</span><br><span class="line">   <span class="built_in">console</span>.log(pair[<span class="number">0</span>]+ <span class="string">', '</span>+ pair[<span class="number">1</span>]);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="get"><a href="#get" class="headerlink" title="get"></a><code>get</code></h3><p>获取指定搜索参数的第一个值。</p>
<h3 id="getAll"><a href="#getAll" class="headerlink" title="getAll"></a><code>getAll</code></h3><p>获取指定搜索参数的所有值。返回一个数组。</p>
<h3 id="has"><a href="#has" class="headerlink" title="has"></a><code>has</code></h3><p>判断是否存在指定的搜索参数。</p>
<h3 id="keys"><a href="#keys" class="headerlink" title="keys"></a><code>keys</code></h3><p>返回 iterator 对象，包含了键/值对的所有键名。其他说明与上面的 <code>entries</code> 一样。</p>
<h3 id="set"><a href="#set" class="headerlink" title="set"></a><code>set</code></h3><p>设置指定搜索参数对应的值。接受的参数跟 <code>append</code> 中的一样，如果存在多个搜索参数对应的值，则删除其他所有值。</p>
<h3 id="sort"><a href="#sort" class="headerlink" title="sort"></a><code>sort</code></h3><p>按键名排序。排序规则为按键名的 <code>unicode</code> 码点，该规则是稳定排序，对相等的键值的相对顺序不做变更。</p>
<h3 id="toString-NaN"><a href="#toString-NaN" class="headerlink" title="toString"></a><code>toString</code></h3><p>返回一个字符串，由所有搜索参数组成的字符串，可以直接用在 URL 上。</p>
<h3 id="values"><a href="#values" class="headerlink" title="values"></a><code>values</code></h3><p>返回 iterator 对象，包含了键/值对的所有值。其他说明与上面的 <code>entries</code> 和 <code>keys</code> 一样。</p>
<h3 id="forEach"><a href="#forEach" class="headerlink" title="forEach"></a><code>forEach</code></h3><p>扩展方法，提供接近于数组 forEach 的遍历方式，<del>但它并不是一个数组，也不是一个类数组</del>。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">searchParams.forEach(<span class="function">(<span class="params">...args</span>)=&gt;</span><span class="built_in">console</span>.log(args))</span><br></pre></td></tr></table></figure>
<blockquote>
<p>上面的代码中，<code>args</code> 依次为键名、值、对应的 searchParam。</p>
</blockquote>
<p><em>参考资料</em>：</p>
<ul>
<li><a href="https://url.spec.whatwg.org/#urlsearchparams" target="_blank" rel="noopener">URL Standard</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams" target="_blank" rel="noopener">URLSearchParams - Web APIs | MDN</a></li>
<li><a href="https://nodejs.org/api/url.html#url_class_urlsearchparams" target="_blank" rel="noopener">URL | Node.js v8.4.0 Documentation</a></li>
<li><a href="http://caniuse.com/#feat=urlsearchparams" target="_blank" rel="noopener">Can I use - Feature: URLSearchParams</a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      本文简单介绍一下 URLSearchParams。
    
    </summary>
    
      <category term="开发" scheme="https://xovel.cn/categories/dev/"/>
    
      <category term="文档" scheme="https://xovel.cn/categories/dev/docs/"/>
    
    
      <category term="web" scheme="https://xovel.cn/tags/web/"/>
    
      <category term="URLSearchParams" scheme="https://xovel.cn/tags/URLSearchParams/"/>
    
  </entry>
  
  <entry>
    <title>十个教育家</title>
    <link href="https://xovel.cn/article/10-educationists.html"/>
    <id>https://xovel.cn/article/10-educationists.html</id>
    <published>2017-08-27T17:24:44.000Z</published>
    <updated>2017-09-12T23:40:56.436Z</updated>
    
    <content type="html"><![CDATA[<p>本文介绍十个古今中外的教育家。</p>
<a id="more"></a>
<blockquote>
<p>原本没有想要做这样一篇文章，但是契机到了，一方面是因为自己最近从事的工作是教育方面的，对于一些教育方面的认知不够；另一个方面的原因是自己在意的一些人和事近期的动态都在表明，是时候好好了解一下古今中外相关教育界的代表人物了。</p>
</blockquote>
<p>那么，我们就开始吧。</p>
<h3 id="孔子"><a href="#孔子" class="headerlink" title="孔子"></a>孔子</h3><p>作为有着 <code>至圣</code> 之称的儒家学派创始人，孔子在每一个中国人心中的印象都不可磨灭，我们从丫丫学语开始就在接触着孔子的教诲。</p>
<p>孔子在教育界领域也是鼎鼎大名，这主要体现在下面四个方面：</p>
<ol>
<li>《论语》。由孔子弟子及后人编纂的这一部《论语》是四书五经中的代表，是孔子与其子弟对话的浓缩精炼。诸多脍炙人口的名句至今读来仍然是掷地有声，发生深省。</li>
<li>“有教无类”、“不愤不启不悱不发”、“学而不思则罔，思而不学则殆”的教育思想。</li>
<li>在教学原则方面崇尚启发性教学和因材施教。</li>
<li>探索出典型的教学过程：学 - 思 - 行。</li>
</ol>
<p>孔子对教育的认知在当时是极为先明的，其创立的儒学思想对中国和世界都有着深远的影响。</p>
<h3 id="孟子"><a href="#孟子" class="headerlink" title="孟子"></a>孟子</h3><p>“亚圣”孟轲，中国另一位伟大的思想家、教育家，儒家学派代表人物，与孔子并称“孔孟”。</p>
<p>在《孟子·尽心上》，“教育”一词被最早提及。推崇的教育理念是内发论，认为教育的目的与 17 世纪提出的“个人本位论”如出一辙。</p>
<blockquote>
<p>内发论，强调人的身心发展的力量主要源于人自身的内在需要，身心发展的顺序是由身心成熟机制决定的。</p>
<p>个人本位论，主张以个人为本位，根据个人发展的需要确定教育目的和进行教育的一种理论。</p>
</blockquote>
<p>孟子认为人性本善，万物皆备于我心，是仁义礼智四性的根源，人只要善于修身养性，向内寻求，这些品质就能得到发展。</p>
<p>孟子学派崇尚的教学过程总结起来是：博学之、审问之、明辨之、慎思之、笃行之。</p>
<h3 id="朱熹"><a href="#朱熹" class="headerlink" title="朱熹"></a>朱熹</h3><p>程朱理学代表人物，南宋著名的理学家、思想家、哲学家、教育家、诗人，有“朱子”之称。朱熹的理学思想对元、明、清三朝影响很大，成为三朝的官方哲学，是中国教育史上继孔子后的又一人。</p>
<p>朱熹是最早使用“课程”这个词语的人，有“宽着期限，紧着课程”的理念。朱熹长期从事讲学活动，《四书集注》为其精心编撰的教材，该书也被明清两代列为科举考试的内容。</p>
<p>朱熹认为 8~15 岁为小学教育阶段，主要任务是培养“圣贤坯璞”；15 岁以后为大学教育，其任务是在“坯璞”的基础上再加“光饰”，把他们培养成国家所需要的人才。这种见解为中国古代教育思想增添了一些光鲜。</p>
<h3 id="苏格拉底"><a href="#苏格拉底" class="headerlink" title="苏格拉底"></a>苏格拉底</h3><p>苏格拉底是古希腊著名的思想家、哲学家、教学家、公民陪审员，与其学生柏拉图，以及柏拉图的学生亚里士多德并称“古希腊三贤”，是公认的西方哲学的奠基者。</p>
<p>在教学方法上，苏格拉底总结出了一套独特的方法，人们称之为“苏格拉底方法”，但他本人则称之为“产婆术”（一作助产术）。苏格拉底把教师比喻为“知识的产婆”，这一教育理论是西方最早的启发式教育，本文上面提到的孔子则是中国最早提出启发式教育的教育家（不愤不启，不悱不发）。</p>
<p>苏格拉底方法是以师生问答的形式进行，所有又称之为“问答法”。教师在教学生获得某种概念时，不是直接把这种概念告诉学生，而是先向学生提问，让学生回答；如果学生回答错了，不进行纠正而是提出另外的问题引导学生思考，从而一步一步得出正确的结论，这为启发式教育奠定了基础。直到今天，问答法仍然是一种重要的教学方法。</p>
<h3 id="柏拉图"><a href="#柏拉图" class="headerlink" title="柏拉图"></a>柏拉图</h3><p>柏拉图是古希腊伟大的哲学家、教育家，西方客观唯心主义哲学的创始人，是苏格拉底的学生，其创造的柏拉图思想、柏拉图主义、柏拉图式爱情对后世影响深远。</p>
<p>柏拉图是西方教育史上第一个提出完整的学前教育思想并建立了完整的教育体系的人。</p>
<p>柏拉图主张唯心教学，认为人的一切知识都是由天赋而来，教学的目的是为了恢复人的固有知识，教学过程即是回忆理念的过程。柏拉图反对强制灌输知识，提倡通过问答形式，提出问题揭露矛盾，然后进行分析、归纳、综合、判断，最后得出结论。</p>
<p>《理想国》（一作《共和国》）是柏拉图重要的对话体著作，包括哲学、伦理、教育、文艺、政治等内容。在这本对话录中，柏拉图以苏格拉底之口通过与其他人对话的方式设计了一个真善美相统一的政体，即可以达到公正的理想国。</p>
<h3 id="昆体良"><a href="#昆体良" class="headerlink" title="昆体良"></a>昆体良</h3><p>古罗马时期著名的律师、教育家，是教育史上发展和完善教育方法和思想的先驱。</p>
<p>昆体良主张对儿童的教育应是鼓励的，能激发他们的兴趣，并认为教学质量的关键在于教师，重视学生记忆能力的培养，提倡因材施教。</p>
<p>昆体良的教育理论和实践都以培养雄辩家为宗旨。在他退休后，著述了《雄辩术原理》（即《论演说家的教育》），这部著作是他自己约二十年教育工作的经验总结，是古希腊、古罗马教育经验的集大成者，也是西方最早的教学方法论著。</p>
<h3 id="夸美纽斯"><a href="#夸美纽斯" class="headerlink" title="夸美纽斯"></a>夸美纽斯</h3><p>17 世纪捷克伟大的民主主义教育家，西方近代教育理论的奠基者，被称为“近代教育之父”。</p>
<p>夸美纽斯受到人文主义的深刻影响，主张通过教育使人获得和谐发展，并通过教育改良社会，实现教派和民族的平等。夸美纽斯是最早的公共教育的拥护者，这中理念在其著作《大教学论》中有提出，该书也是近代第一本系统的教育学著作。</p>
<p>夸美纽斯在教育史上的另一个重要的贡献是最早从理论上详细阐述了班级授课制以及相关的学年制、学日制、考查、考试制度，是最早提出“班主任”概念的教育家。</p>
<p>夸美纽斯主张义务教育和泛智教育，“把一切知识教给一切人”，在当时的历史条件下，打破了封建主义的禁锢，加速了社会的发展。</p>
<p>夸美纽斯还提出了许多教育原则，如直观性原则、启发诱导原则、量力性原则、循序渐进原则、巩固性原则、因材施教原则等等。</p>
<h3 id="赫尔巴特"><a href="#赫尔巴特" class="headerlink" title="赫尔巴特"></a>赫尔巴特</h3><p>19 世纪德国哲学家、心理学家、教育家，是科学教育学的奠基人，“教育科学之父”。</p>
<p>反映其教育思想的代表作《普通教育学》是公认的第一部具有科学体系的教育学著作，这本书也使得教育学开始成为一门独立的科学。</p>
<p>赫尔巴特明确提出教育学的科学性问题，“教育学作为一种科学，是以实践哲学和心理学为基础的。前者说明教育的目的，后者说明教育的途径、手段与障碍。”</p>
<p>赫尔巴特主张的教育原则为教学的教育性，明确指出“不存在‘无教学的教育’这个概念，正如发过来，我不承认有任何‘无教育的教学’”，“德育问题是不能同整个教育分离开来的，而是同其他教育问题必然地、广泛深远地联系在一起的”，这极大的推进了教育理论的发展。</p>
<p>赫尔巴特将教学过程分为四个阶段：清楚、联想、系统、方法。</p>
<h3 id="杜威"><a href="#杜威" class="headerlink" title="杜威"></a>杜威</h3><p>美国哲学家、教育家，实用主义的集大成者。</p>
<p>杜威从实用主义经验论和机能心理学出发，批判了传统的学校教育，并就教育本质提出了“教育即生活”和“学校即社会”的观点。杜威认为，教育过程在它的自身意外无目的，教育的目的就在教育的过程之中。</p>
<p>教学论方面，杜威提倡“从做中学”。从“思维五步”出发，杜威认为教学过程也相应的分为五步：</p>
<ul>
<li>教师给儿童提供一个与社会生活经验相关联的情境；</li>
<li>使儿童有准备去应付在情境中产生的问题；</li>
<li>使儿童产生对解决问题的思考和假设；</li>
<li>儿童自己对解决问题的假设加以整理和排列；</li>
<li>儿童通过应用来校验这些假设。</li>
</ul>
<blockquote>
<p>思维五步：疑难的情境、确定疑难所在、提出解决疑难的各种假设、对这些假设进行推断、验证或者修改假设。</p>
</blockquote>
<h3 id="舒尔茨"><a href="#舒尔茨" class="headerlink" title="舒尔茨"></a>舒尔茨</h3><p>首先，这个人更多的身份应该是经济学家。当然，他并不是星巴克的创始人，他是获得 1979 年诺贝尔经济学奖的一位经济哲学家西奥多·舒尔茨。</p>
<p>西奥多·舒尔茨提出的“人力资本理论”对当代教育影响很深，并在美国第 73 届经济学年会上的演讲“人力资本投资”中证明了教育对经济发展的巨大贡献。</p>
<p>舒尔茨的人力资本理论主要观点有两个：<strong>人力资本的积累是社会经济增长的源泉</strong>和<strong>教育也是使个人收入的社会分配趋于平等的因素</strong>。</p>
<p>舒尔茨论述到教育对个人收入的影响：</p>
<ul>
<li>工资的差别主要是由所受的教育的差别引起的，教育可以提高收入的能力。</li>
<li>教育水平的提高会使因教育不同而产生的相对收入差别趋于减缓。</li>
<li>人力资本投资的增加还可以使物力资本投资和财产收入趋于下降。</li>
</ul>
<hr>
<p>行文到此，本文就算是完结了，没有什么要特别进行说明的。嗯，就是这样。这十个名单我也忘记了从哪里看到的了。看到的时候做了一个记录，但是上面的资料很<del>简陋</del>，我重新查阅了网络上的一些资料，并进行一番整理，于是就有了这一篇文章。</p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本文介绍十个古今中外的教育家。&lt;/p&gt;
    
    </summary>
    
      <category term="随笔" scheme="https://xovel.cn/categories/essay/"/>
    
    
      <category term="教育" scheme="https://xovel.cn/tags/%E6%95%99%E8%82%B2/"/>
    
      <category term="教育家" scheme="https://xovel.cn/tags/%E6%95%99%E8%82%B2%E5%AE%B6/"/>
    
  </entry>
  
  <entry>
    <title>ECMAScript 2017 新特性一览</title>
    <link href="https://xovel.cn/article/es8.html"/>
    <id>https://xovel.cn/article/es8.html</id>
    <published>2017-07-21T04:41:27.000Z</published>
    <updated>2017-07-21T15:00:06.603Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>本文主要参考自 <code>2ality</code> 的文章：<a href="http://2ality.com/2016/02/ecmascript-2017.html" target="_blank" rel="noopener">ECMAScript 2017 (ES8): the final feature set</a>。部分章节会有修改，加入自己的一些理解与观点。<em>阅读本文需要有一定的 ES 基础</em>。</p>
</blockquote>
<blockquote>
<p>官方公告：<a href="http://www.ecma-international.org/publications/standards/Ecma-262.htm" target="_blank" rel="noopener">Standard ECMA-262</a>。</p>
</blockquote>
<h2 id="主要特性"><a href="#主要特性" class="headerlink" title="主要特性"></a>主要特性</h2><h3 id="异步函数"><a href="#异步函数" class="headerlink" title="异步函数"></a>异步函数</h3><p>ES6 发布的时候，引入了 <code>Promise</code> 来处理异步操作，一个常见的 Async 函数如下：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">fetchJson</span>(<span class="params">url</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> fetch(url)</span><br><span class="line">  .then(<span class="function"><span class="params">request</span> =&gt;</span> request.text())</span><br><span class="line">  .then(<span class="function"><span class="params">text</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">JSON</span>.parse(text);</span><br><span class="line">  &#125;)</span><br><span class="line">  .catch(<span class="function"><span class="params">error</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">`ERROR: <span class="subst">$&#123;error.stack&#125;</span>`</span>);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br><span class="line">fetchJson(<span class="string">'http://example.com/some_file.json'</span>)</span><br><span class="line">.then(<span class="function"><span class="params">obj</span> =&gt;</span> <span class="built_in">console</span>.log(obj));</span><br></pre></td></tr></table></figure>
<p>现在，使用 ES2017 中新增的关键字 <code>async</code> 和 <code>await</code>，我们可以实现以看起来像是同步的代码来处理异步的操作。如上面的代码可以写成：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">fetchData</span>(<span class="params">url</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> request = <span class="keyword">await</span> fetch(url);</span><br><span class="line">  <span class="keyword">let</span> text = <span class="keyword">await</span> request.text();</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">JSON</span>.parse(text);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">catch</span> (err) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">`Error: <span class="subst">$&#123;err.stack&#125;</span>`</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>异步函数还有一些变体，罗列如下：</p>
<ul>
<li>异步函数声明：<code>async function foo() {}</code></li>
<li>异步函数表达式：<code>const foo = async function () {};</code></li>
<li>异步方法定义：<code>let obj = {async foo () {}};</code></li>
<li>异步箭头函数：<code>const foo = async () =&gt; {};</code></li>
</ul>
<p>更多资料可参阅：<a href="https://github.com/tc39/ecmascript-asyncawait" target="_blank" rel="noopener">tc39/ecmascript-asyncawait: Async/await for ECMAScript</a>，<a href="http://exploringjs.com/es6/ch_generators.html#sec_co-library" target="_blank" rel="noopener">Simplifying asynchronous computations via generators (section in “Exploring ES6”)</a></p>
<h3 id="共享内存和原子"><a href="#共享内存和原子" class="headerlink" title="共享内存和原子"></a>共享内存和原子</h3><p>共享内存 <code>shared memory</code> 主要是处理并行事件对于资源调用的一种机制。</p>
<p>该功能引入一个新的低级别 <code>Atomics</code> 命名空间对象和一个 <code>Shared Array Buffer</code> 构造函数来作为高级别并发抽象的原始构建块。这样可以使得开发人员可以使用多个 <code>service worker</code> 和核心线程之间的 <code>Shared Array Buffer</code> 对象的数据。通过这种方式，可以更轻松的在 <code>worker</code> 之间进行数据共享，改善它们之间的协调性。</p>
<ul>
<li><a href="https://github.com/tc39/ecmascript_sharedmem" target="_blank" rel="noopener">tc39/ecmascript_sharedmem: Shared memory and atomics for ECMAscript</a></li>
<li><a href="http://2ality.com/2017/01/shared-array-buffer.html" target="_blank" rel="noopener">ES proposal: Shared memory and atomics</a></li>
</ul>
<h2 id="次要更新"><a href="#次要更新" class="headerlink" title="次要更新"></a>次要更新</h2><h3 id="Object-values-Object-entries"><a href="#Object-values-Object-entries" class="headerlink" title="Object.values/Object.entries"></a>Object.values/Object.entries</h3><p>对象为键值对的数据结构时，每一个键值对都是一个 <code>entry</code>。<code>Object.entries</code> 提供将对象转换为其可枚举的每一个对象的集合的方法。而 <code>Object.values</code> 提取出对象中可枚举的字符串键值属性的所有值。</p>
<p><code>Object.values</code> 跟 <code>Object.keys</code> 功能类似。</p>
<p>举个例子，有一个这样的对象：<code>foo = {a: 1, b: 2, c: 3};</code>，上面提到的三个方法的结果分别为：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">&gt; foo = &#123;a:1,b:2,c:3&#125;</span><br><span class="line">&lt; Object &#123;a: 1, b: 2, c: 3&#125;</span><br><span class="line">&gt; Object.values(foo)</span><br><span class="line">&lt; (3) [1, 2, 3]</span><br><span class="line">&gt; Object.keys(foo)</span><br><span class="line">&lt; (3) [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]</span><br><span class="line">&gt; Object.entries(foo)</span><br><span class="line">&lt; (3) [Array(2), Array(2), Array(2)]0: Array(2)1: Array(2)2: Array(2)length: 3__proto__: Array(0)</span><br><span class="line"></span><br><span class="line">------</span><br><span class="line">&gt; Object.entries(&#123; one: 1, two: 2 &#125;)</span><br><span class="line">[ [ &apos;one&apos;, 1 ], [ &apos;two&apos;, 2 ] ]</span><br><span class="line"></span><br><span class="line">&gt; Object.entries(&#123; [Symbol()]: 123, foo: &apos;abc&apos; &#125;);</span><br><span class="line">[ [ &apos;foo&apos;, &apos;abc&apos; ] ]</span><br></pre></td></tr></table></figure>
<ul>
<li><a href="http://2ality.com/2015/11/stage3-object-entries.html" target="_blank" rel="noopener">ES proposal: Object.entries() and Object.values()</a></li>
<li><a href="https://github.com/tc39/proposal-object-values-entries" target="_blank" rel="noopener">Object.values/Object.entries</a></li>
</ul>
<h3 id="字符串填充"><a href="#字符串填充" class="headerlink" title="字符串填充"></a>字符串填充</h3><p>针对 <code>String</code> 对象，引入了 <code>String Padding</code> 的规范，为字符串的处理添加了两个字符串填充的方法：<code>padStart</code> 和 <code>padEnd</code>。</p>
<h4 id="String-prototype-padStart"><a href="#String-prototype-padStart" class="headerlink" title="String.prototype.padStart"></a>String.prototype.padStart</h4><p>字符串的头部填充。接收两个参数 <code>String.prototype.padStart(maxLength, fillString=&#39; &#39;)</code>，第一个参数为填充的最大长度，第二个参数为指定填充的字符串。</p>
<p>假设我们有一个这样的操作：<code>s.padStart(n, f)</code>，s 为待填充的字符串，n 为填充后的长度，f 为填充的字符串。如果 f 的长度不够 n，则会重复使用，直到填充的长度达到了 n。如果 s 本身的长度已经超过 n 则不会进行填充。</p>
<h4 id="String-prototype-padEnd"><a href="#String-prototype-padEnd" class="headerlink" title="String.prototype.padEnd"></a>String.prototype.padEnd</h4><p>跟上面的方法类似，填充的位置在后面，其他处理方式一致。</p>
<ul>
<li><a href="https://github.com/tc39/proposal-string-pad-start-end" target="_blank" rel="noopener">ECMAScript spec proposal for String.prototype.{padStart,padEnd}</a></li>
</ul>
<h3 id="Object-getOwnPropertyDescriptors"><a href="#Object-getOwnPropertyDescriptors" class="headerlink" title="Object.getOwnPropertyDescriptors"></a>Object.getOwnPropertyDescriptors</h3><p>该方法是 <code>Object.getOwnPropertyDescriptor</code> 的复数形式：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getOwnPropertyDescriptors</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> result = &#123;&#125;;</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> key <span class="keyword">of</span> <span class="built_in">Reflect</span>.ownKeys(obj)) &#123;</span><br><span class="line">    result[key] = <span class="built_in">Object</span>.getOwnPropertyDescriptor(obj, key);</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></pre></td></tr></table></figure>
<p>旨在简化对象复制的过程，它允许装饰器能够轻松的从另一个类或对象中提取出所有描述符，并将它们分配给一个新的对象。</p>
<p>由于 <code>Object.assign</code> 方法不够完美，这个是用一种吞噬行为的方式复制对象，也就是依然会出现键值覆盖的情形。</p>
<p>配合 <code>Object.create</code> 可以实现一个对象的深拷贝：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> clone = <span class="function"><span class="params">obj</span> =&gt;</span> <span class="built_in">Object</span>.create(<span class="built_in">Object</span>.getPrototypeOf(obj), <span class="built_in">Object</span>.getOwnPropertyDescriptors(obj));</span><br></pre></td></tr></table></figure>
<p>更多 <code>Object.getOwnPropertyDescriptor</code> 的使用场景可以参阅：<a href="http://2ality.com/2016/02/object-getownpropertydescriptors.html" target="_blank" rel="noopener">ES proposal: Object.getOwnPropertyDescriptors()</a>。</p>
<h3 id="函数的拖尾逗号"><a href="#函数的拖尾逗号" class="headerlink" title="函数的拖尾逗号"></a>函数的拖尾逗号</h3><p>以前的函数在参数定义时或者调用时，不允许出现拖尾逗号，如下面的代码：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span></span></span><br><span class="line"><span class="function"><span class="params">  bar,</span></span></span><br><span class="line"><span class="function"><span class="params">  baz,</span></span></span><br><span class="line"><span class="function"><span class="params"></span>) </span>&#123; ... &#125;</span><br><span class="line">foo(</span><br><span class="line">  param1,</span><br><span class="line">  param2,</span><br><span class="line">);</span><br></pre></td></tr></table></figure>
<p>在之前的版本中是错误的，但是新规范中，这可以的，拖尾逗号将被忽略，如同<strong>数组和对象字面量</strong>中的拖尾逗号。</p>
<h2 id="拓展阅读"><a href="#拓展阅读" class="headerlink" title="拓展阅读"></a>拓展阅读</h2><ul>
<li><a href="http://2ality.com/2016/01/ecmascript-2016.html" target="_blank" rel="noopener">The final feature set of ECMAScript 2016 (ES7)</a></li>
<li><a href="https://github.com/tc39/proposals/blob/master/README.md#active-proposals" target="_blank" rel="noopener">tc39/proposals - Active proposals</a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      近日 TC39 正式发布了 ECMAScript 的第八个版本的规范，包含了许多新特性，本文根据 2ality 的文章对这些新特性进行一个综合整理与翻译。
    
    </summary>
    
      <category term="WEB" scheme="https://xovel.cn/categories/web/"/>
    
      <category term="JS" scheme="https://xovel.cn/categories/web/js/"/>
    
    
      <category term="js" scheme="https://xovel.cn/tags/js/"/>
    
      <category term="es" scheme="https://xovel.cn/tags/es/"/>
    
      <category term="es8" scheme="https://xovel.cn/tags/es8/"/>
    
  </entry>
  
  <entry>
    <title>npm install 命令参数的一些简写方式</title>
    <link href="https://xovel.cn/article/npm-alias.html"/>
    <id>https://xovel.cn/article/npm-alias.html</id>
    <published>2017-05-19T16:32:38.000Z</published>
    <updated>2017-05-21T15:56:40.213Z</updated>
    
    <content type="html"><![CDATA[<p>在使用 <code>npm install</code> 命令时，有许多指定参数的命令是可以进行缩写的，本文就简单梳理一下。</p>
<p><code>npm install</code>本身有一个别名，即<code>npm i</code>，可以使用这种缩写方式来运行命令，打到简化的效果。</p>
<p>以下为指定的一些命令行参数的缩写方式：</p>
<h3 id="g"><a href="#g" class="headerlink" title="-g"></a><code>-g</code></h3><p><code>--global</code>，缩写为<code>-g</code>，表示安装包时，视作全局的包。安装之后的包将位于系统预设的目录之下，一般来说</p>
<h3 id="S"><a href="#S" class="headerlink" title="-S"></a><code>-S</code></h3><p><code>--save</code>，缩写为<code>-S</code>，表示安装的包将写入<code>package.json</code>里面的<code>dependencies</code>。</p>
<h3 id="D"><a href="#D" class="headerlink" title="-D"></a><code>-D</code></h3><p><code>--save-dev</code>，缩写为<code>-D</code>，表示将安装的包将写入<code>packege.json</code>里面的<code>devDependencies</code>。</p>
<h3 id="O"><a href="#O" class="headerlink" title="-O"></a><code>-O</code></h3><p><code>--save-optional</code>缩写为<code>-O</code>，表示将安装的包将写入<code>packege.json</code>里面的<code>optionalDependencies</code>。</p>
<h3 id="E"><a href="#E" class="headerlink" title="-E"></a><code>-E</code></h3><p><code>--save-exact</code>缩写为<code>-E</code>，表示安装的包的版本是精确指定的。</p>
<h3 id="B"><a href="#B" class="headerlink" title="-B"></a><code>-B</code></h3><p><code>--save-bundle</code>缩写为<code>-B</code>，表示将安装的包将写入<code>packege.json</code>里面的<code>bundleDependencies</code>。</p>
<hr>
<blockquote>
<p><code>https://docs.npmjs.com/cli/install</code>，参考来源。</p>
</blockquote>
<hr>
<p><del>好吧，这篇算作是滥竽充数</del>，六月计划提前开始吧。做一个有内涵的技术写作者是得有多大的勇气呢？</p>
]]></content>
    
    <summary type="html">
    
      对 npm install 命令的一些参数的简写方式，这里稍作一番总结，主要是为了方便查阅。
    
    </summary>
    
      <category term="开发" scheme="https://xovel.cn/categories/dev/"/>
    
      <category term="文档" scheme="https://xovel.cn/categories/dev/docs/"/>
    
    
      <category term="npm" scheme="https://xovel.cn/tags/npm/"/>
    
      <category term="install" scheme="https://xovel.cn/tags/install/"/>
    
  </entry>
  
  <entry>
    <title>angular函数组件简介</title>
    <link href="https://xovel.cn/article/angular-function.html"/>
    <id>https://xovel.cn/article/angular-function.html</id>
    <published>2017-05-17T14:20:40.000Z</published>
    <updated>2017-05-19T16:30:35.808Z</updated>
    
    <content type="html"><![CDATA[<p>本人前段时间对<code>angular</code>进行了一定程度的了解，<code>angular</code>系列的博文就从本篇开始吧。本文就对其提供的函数组件做一个入门级的介绍。大部分文档直接翻译自官方文档：<a href="https://docs.angularjs.org/api/ng/function" target="_blank" rel="noopener">Function components in ng</a>。</p>
<blockquote>
<p>阅读本文需要一定的<code>jQuery</code>基础。</p>
</blockquote>
<p>在函数组件中，<code>angular</code>提供了许多方便使用的功能，下面我将按照官方文档的顺序一一做介绍。</p>
<h3 id="angular-forEach"><a href="#angular-forEach" class="headerlink" title="angular.forEach"></a>angular.forEach</h3><p>遍历数组或对象。功能类似<a href="http://api.jquery.com/each/" target="_blank" rel="noopener"><code>jQuery.each</code></a>。不同的是<code>angular.forEach</code>支持传入一个上下文作为第三个参数：<code>angular.forEach(obj, iterator, [context]);</code>。</p>
<h3 id="angular-extend"><a href="#angular-extend" class="headerlink" title="angular.extend"></a>angular.extend</h3><p>扩展对象。功能类似<code>jQuery.extend</code>，不过这里是个浅复制。</p>
<h3 id="angular-merge"><a href="#angular-merge" class="headerlink" title="angular.merge"></a>angular.merge</h3><p>合并对象，深度合并，可以视为深复制。</p>
<h3 id="angular-noop"><a href="#angular-noop" class="headerlink" title="angular.noop"></a>angular.noop</h3><p>空操作，即：<code>function () {}</code>。</p>
<h3 id="angular-identity"><a href="#angular-identity" class="headerlink" title="angular.identity"></a>angular.identity</h3><p>返回第一个参数本身，用于函数式编程。</p>
<h3 id="angular-isUndefined"><a href="#angular-isUndefined" class="headerlink" title="angular.isUndefined"></a>angular.isUndefined</h3><p>判断参数是否为<code>undefined</code>。</p>
<h3 id="angular-isDefined"><a href="#angular-isDefined" class="headerlink" title="angular.isDefined"></a>angular.isDefined</h3><p>判断参数是否被定义过，与<code>angular.isUndefined</code>的结果相反。</p>
<h3 id="angular-isObject"><a href="#angular-isObject" class="headerlink" title="angular.isObject"></a>angular.isObject</h3><p>判断参数是否为对象。</p>
<blockquote>
<p>不同于<code>typeof</code>，<code>null</code>将被视为非对象，数组视为对象。</p>
</blockquote>
<h3 id="angular-isString"><a href="#angular-isString" class="headerlink" title="angular.isString"></a>angular.isString</h3><p>判断参数是否为字符串。</p>
<h3 id="angular-isNumber"><a href="#angular-isNumber" class="headerlink" title="angular.isNumber"></a>angular.isNumber</h3><p>判断参数是否为数组，包括<code>NaN</code>、<code>+Infinity</code>以及<code>-Infinity</code>。</p>
<h3 id="angular-isDate"><a href="#angular-isDate" class="headerlink" title="angular.isDate"></a>angular.isDate</h3><p>判断参数是否是一个日期对象。</p>
<h3 id="angular-isArray"><a href="#angular-isArray" class="headerlink" title="angular.isArray"></a>angular.isArray</h3><p><code>Array.isArray</code>的别名，判断是否为数组。</p>
<h3 id="angular-isFunction"><a href="#angular-isFunction" class="headerlink" title="angular.isFunction"></a>angular.isFunction</h3><p>判断是否为函数。</p>
<h3 id="angular-isElement"><a href="#angular-isElement" class="headerlink" title="angular.isElement"></a>angular.isElement</h3><p>判断是否为<code>DOM</code>元素或者<code>jQuery</code>元素。</p>
<h3 id="angular-copy"><a href="#angular-copy" class="headerlink" title="angular.copy"></a>angular.copy</h3><p>复制对象或数组，深复制。</p>
<h3 id="angular-equals"><a href="#angular-equals" class="headerlink" title="angular.equals"></a>angular.equals</h3><p>判断两个参数是否相等。</p>
<p>符合以下条件之一的均视为相等：</p>
<ul>
<li>通过严格等于<code>===</code>的判断</li>
<li>对象的类型一样并且其所有属性通过<code>angular.equals</code>判断均相等</li>
<li>两者均为<code>NaN</code></li>
<li>两者为正则表达式，并且其表达式相等</li>
</ul>
<h3 id="angular-bind"><a href="#angular-bind" class="headerlink" title="angular.bind"></a>angular.bind</h3><p>对函数进行绑定对象。<code>angular.bind</code>的参数形式类似函数的<code>call</code>方法。</p>
<p>通常用于偏函数和柯里化。</p>
<h3 id="angular-toJson"><a href="#angular-toJson" class="headerlink" title="angular.toJson"></a>angular.toJson</h3><p>转为<code>JSON</code>风格的字符串。这个过程可以称之为序列化。</p>
<h3 id="angular-fromJson"><a href="#angular-fromJson" class="headerlink" title="angular.fromJson"></a>angular.fromJson</h3><p>将一个<code>JSON</code>字符串还原，即反序列化。</p>
<h3 id="angular-bootstrap"><a href="#angular-bootstrap" class="headerlink" title="angular.bootstrap"></a>angular.bootstrap</h3><p>手动启动<code>AngularJS</code>的应用程序。</p>
<h3 id="angular-reloadWithDebugInfo"><a href="#angular-reloadWithDebugInfo" class="headerlink" title="angular.reloadWithDebugInfo"></a>angular.reloadWithDebugInfo</h3><p>以调试模式重载当前的应用程序。</p>
<h3 id="angular-injector"><a href="#angular-injector" class="headerlink" title="angular.injector"></a>angular.injector</h3><p>注入器的声明与使用。详情可参阅：<a href="https://docs.angularjs.org/guide/di" target="_blank" rel="noopener">dependency injection</a></p>
<h3 id="angular-element"><a href="#angular-element" class="headerlink" title="angular.element"></a>angular.element</h3><p>以<code>jQuery</code>风格包裹<code>DOM</code>元素或者<code>HTML String</code>。如果未引入<code>jQuery</code>，则采用<code>angular</code>自带的<code>jqLite</code>简化版。<code>jqLite</code>提供常见的<code>jQuery API</code>，可以满足大部分的情况。</p>
<h3 id="angular-module"><a href="#angular-module" class="headerlink" title="angular.module"></a>angular.module</h3><p><code>angular</code>的核心，用于声明或者获取模块。</p>
<h3 id="angular-errorHandlingConfig"><a href="#angular-errorHandlingConfig" class="headerlink" title="angular.errorHandlingConfig"></a>angular.errorHandlingConfig</h3><p>错误处理的配置。</p>
<hr>
<p>至此，<code>angular</code>自带的函数组件就梳理完毕了。更多详细的介绍，就请诸君自行查阅相关文档了。</p>
<p>接下来的空余时间里面，将会逐步针对<code>angular</code>里面的知识点做一个全面的介绍。</p>
<blockquote>
<p>通常来说，本系列博文对应的<code>angular</code>的版本为<code>1.6.x+</code>。</p>
</blockquote>
<hr>
<p>部分函数，诸如<code>angular.lowercase</code>，<code>angular.uppercase</code>已经被废弃，故此这里也不再列出。</p>
]]></content>
    
    <summary type="html">
    
      本文为针对AngularJS中的自带的函数组件的简要介绍。
    
    </summary>
    
      <category term="WEB" scheme="https://xovel.cn/categories/web/"/>
    
      <category term="JS" scheme="https://xovel.cn/categories/web/js/"/>
    
    
      <category term="angular" scheme="https://xovel.cn/tags/angular/"/>
    
      <category term="function" scheme="https://xovel.cn/tags/function/"/>
    
      <category term="components" scheme="https://xovel.cn/tags/components/"/>
    
      <category term="angularjs" scheme="https://xovel.cn/tags/angularjs/"/>
    
  </entry>
  
</feed>
