<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Here. There.</title>
  
  <subtitle>Love ice cream. Love sunshine. Love life. Love the world. Love myself. Love you.</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://godbasin.github.io/"/>
  <updated>2019-12-21T07:46:51.437Z</updated>
  <id>https://godbasin.github.io/</id>
  
  <author>
    <name>被删</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>小程序开发月刊第12期（20191216）</title>
    <link href="https://godbasin.github.io/2019/12/16/wxapp-latest-20191216/"/>
    <id>https://godbasin.github.io/2019/12/16/wxapp-latest-20191216/</id>
    <published>2019-12-16T15:34:12.000Z</published>
    <updated>2019-12-21T07:46:51.437Z</updated>
    
    <content type="html"><![CDATA[<p>今年最后一刊啦<del>~明年见</del>~<br><a id="more"></a></p><h1 id="小程序-latest"><a href="#小程序-latest" class="headerlink" title="小程序 latest"></a>小程序 latest</h1><h2 id="小程序能力"><a href="#小程序能力" class="headerlink" title="小程序能力"></a>小程序能力</h2><h3 id="小程序代码包总包上限提升至12M"><a href="#小程序代码包总包上限提升至12M" class="headerlink" title="小程序代码包总包上限提升至12M"></a>小程序代码包总包上限提升至12M</h3><p>为了让开发者开发出功能更丰富的小程序，小程序或小游戏代码包总包上限<strong>由8M提升到12M</strong>。建议开发者优化小程序性能并将每个分包做得尽可能小，以便提升用户的打开速度，优化用户体验。</p><h3 id="可以在位置消息中用小程序打车"><a href="#可以在位置消息中用小程序打车" class="headerlink" title="可以在位置消息中用小程序打车"></a>可以在位置消息中用小程序打车</h3><p>为了让用户更便捷地使用小程序的打车服务，我们在位置消息详情页的菜单中，新增了打车小程序入口，详情如下：</p><ol><li>打开聊天中的位置消息，点击详情页右下角绿色按钮，菜单中会展示符合条件的打车小程序，用户可以直接发起目的地为该位置的打车服务。</li><li>小程序的注册类目为“打车（网约车）”，且有用户最近使用的记录，才可以出现在该菜单中。</li><li>在此处点击打开小程序后，需要直接进入到发起打车页面。</li></ol><ul><li><a href="https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/location-message.html" target="_blank" rel="external">接入方法说明</a></li></ul><h3 id="OCR识别能力升级-amp-支持付费购买使用"><a href="#OCR识别能力升级-amp-支持付费购买使用" class="headerlink" title="OCR识别能力升级&amp;支持付费购买使用"></a>OCR识别能力升级&amp;支持付费购买使用</h3><ol><li>基础能力升级。  </li></ol><ul><li>针对少数民族地区身份证进行了识别优化；</li><li>针对独特的身份证样式（如长期身份证及超长住址等）进行了能力升级</li></ul><ol><li>OCR能力支持付费购买使用。</li></ol><ul><li><a href="https://developers.weixin.qq.com/miniprogram/dev/extended/service-market/intro.html" target="_blank" rel="external">微信OCR识别说明</a></li></ul><h3 id="小程序模板消息开发能力调整说明"><a href="#小程序模板消息开发能力调整说明" class="headerlink" title="小程序模板消息开发能力调整说明"></a>小程序模板消息开发能力调整说明</h3><p>由于小程序模板消息能力将于2020年1月10日下线，请开发者尽快将模板消息切换为订阅消息，以免影响相关业务。近期，我们将对小程序模板消息开发能力进行以下调整：</p><ol><li>2019年12月6日起，小程序帐号将无法添加旧模板，开发者可使用订阅消息，添加订阅模板来进行新业务开发。</li><li>开发者工具将不再支持调试模板消息，仅支持调试订阅消息。</li><li>小程序模板消息接口的日调用量上限将以每周25%的比例逐步下调，具体计划如下：</li></ol><ul><li>2019年12月16日：下调至75%</li><li>2019年12月23日：下调至50%</li><li>2019年12月30日：下调至25%</li><li>2020年1月10日：小程序模板消息接口下线</li></ul><h3 id="微信卡券“刷卡买单”能力即将下线通知"><a href="#微信卡券“刷卡买单”能力即将下线通知" class="headerlink" title="微信卡券“刷卡买单”能力即将下线通知"></a>微信卡券“刷卡买单”能力即将下线通知</h3><p>微信卡券将于2020年1月2日下线微信卡券-微信支付代金券打通券能力（即微信卡券刷卡买单能力）。下线后，创建微信卡券流程中，“核销方式”将不再支持刷卡买单。本次调整仅针对卡券中的代金券类型，折扣券、兑换券、团购券、通用券等券类型不受影响。<br>你可尝试直接创建微信支付代金券作为替代，微信支付代金券同样支持覆盖全流程的 API 接口，创建入口：<a href="https://pay.weixin.qq.com/index.php/core/home/login?return_url=%2F" target="_blank" rel="external">微信支付商户平台/服务商平台</a> - 营销中心。  </p><ul><li><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/marketing/convention/chapter1_1.shtml" target="_blank" rel="external">API接口参考</a></li></ul><h3 id="关于微信小程序部分类目报备审核说明"><a href="#关于微信小程序部分类目报备审核说明" class="headerlink" title="关于微信小程序部分类目报备审核说明"></a>关于微信小程序部分类目报备审核说明</h3><p>自微信小程序平台上线以来，为了保障小程序内容合规，发布时事新闻、具有社交属性或以视频、电台为载体的小程序需在上线前，完成向省/自治区/直辖市属地网信部门申请报备的工作。特别是，为避免小程序违法违规风险，UGC小程序需要对用户发布的内容做好安全审查措施。</p><ul><li><a href="https://developers.weixin.qq.com/community/operate/doc/00002a6a0b8d98a965993666a51001" target="_blank" rel="external">查看帖子</a></li></ul><h3 id="更新日志"><a href="#更新日志" class="headerlink" title="更新日志"></a>更新日志</h3><ul><li><a href="https://developers.weixin.qq.com/community/develop/doc/000c8e132c47c8e19b99a4aa85b001" target="_blank" rel="external">社区问题反馈以及功能优化更新（12.02-12.06）</a>  </li><li><a href="https://developers.weixin.qq.com/community/develop/doc/000886cafa82e043029950a5151001" target="_blank" rel="external">社区问题反馈以及功能优化更新（11.25-11.29）</a>  </li><li><a href="https://developers.weixin.qq.com/community/develop/doc/000c42feb30720b672898a1215b001" target="_blank" rel="external">社区问题反馈以及功能优化更新（11.18-11.22）</a>  </li><li><a href="https://developers.weixin.qq.com/community/develop/doc/0008e47f970a5880cf799f32c5ec01" target="_blank" rel="external">社区问题反馈以及功能优化更新（11.11-11.15）</a>  </li></ul><h2 id="小程序·云开发"><a href="#小程序·云开发" class="headerlink" title="小程序·云开发"></a>小程序·云开发</h2><h3 id="小程序·云开发新增自定义数据库读写权限的能力"><a href="#小程序·云开发新增自定义数据库读写权限的能力" class="headerlink" title="小程序·云开发新增自定义数据库读写权限的能力"></a>小程序·云开发新增自定义数据库读写权限的能力</h3><p>小程序·云开发新增自定义前端数据库读写权限的能力。通过自定义规则，开发者可以精细化的控制集合中所有记录的读、写权限，系统会自动拒绝不符合安全规则的前端数据库请求，保障数据安全。<br>基于该能力，开发者可以：  </p><ol><li>灵活自定义集合记录的读写权限：获得比基础的四种基础权限设置更灵活、强大的读写权限控制，让读写权限控制不再强制依赖于<code>_openid</code>字段和用户 <code>openid</code>。  </li><li>防止越权访问和越权更新：用户只能获取通过安全规则限制的用户所能获取的内容，越权获取数据将被拒绝。  </li><li>限制新建数据的内容：让新建数据必须符合规则，如可以要求权限标记字段必须为用户<code>openid</code>开发者可通过下载最新 <a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/nightly.html" target="_blank" rel="external">Nightly版</a> 开发者工具体验功能。</li></ol><ul><li><a href="https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/database/security-rules.html#%E7%AE%80%E4%BB%8B" target="_blank" rel="external">数据库安全规则参考</a></li></ul><h3 id="小程序·云开发新增自定义告警能力"><a href="#小程序·云开发新增自定义告警能力" class="headerlink" title="小程序·云开发新增自定义告警能力"></a>小程序·云开发新增自定义告警能力</h3><p>为了方便开发者及时发现小程序运行过程中发生的异常，小程序·云开发新增自定义告警能力。开发者可以通过告警指标、统计周期、比较条件、持续周期和告警频率等参数的自由组合灵活地配置所需的告警规则。 开发者可通过下载最新 <a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/nightly.html" target="_blank" rel="external">Nightly版</a> 开发者工具体验功能。</p><ul><li><a href="https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/alarm.html" target="_blank" rel="external">告警设置参考</a></li></ul><h2 id="开发者工具"><a href="#开发者工具" class="headerlink" title="开发者工具"></a>开发者工具</h2><h3 id="微信开发者工具功能更新！"><a href="#微信开发者工具功能更新！" class="headerlink" title="微信开发者工具功能更新！"></a>微信开发者工具功能更新！</h3><p>为提高代码编辑效率和优化开发体验，微信开发者工具的编辑器功能进行了全面升级，并对产品 Logo 进行了更新：<br><img src="https://mmbiz.qlogo.cn/mmbiz_png/UB2CE27Dppm0h0zPD1HXMSMAjKXDWFTibibhzRueUYptye69ZahU3yzrLrOic1jEYyWJPcYicnsf5PveuJPD5F4ia7Q/0?wx_fmt=png" alt=""></p><ol><li>编辑器功能优化：</li></ol><ul><li>新增大纲结构视图和文件的面包屑导航，同时编辑区域还支持分栏视图，方便开发者同时查看和编辑多个文件。</li><li>优化了文件搜索功能，支持更加精细化地搜索和替换功能，进一步提高了开发者的操作效率。</li><li>在编写 JS/TS 文件时，编辑器现在提供完整的项目代码补全和联想功能，使用 TS 的语言服务分析并提供代码错误及警告提示。</li></ul><ol><li>兼容部分 VS Code 扩展 （Beta）：<br>新版微信开发者工具编辑器兼容了部分 VS Code 扩展插件，目前正在功能完善阶段。开发者可根据需要安装对应插件，进一步提高开发效率。</li></ol><p>开发者可通过下载最新 <a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/nightly.html" target="_blank" rel="external">Nightly版</a> 开发者工具体验功能。</p><h3 id="内置扩展库支持"><a href="#内置扩展库支持" class="headerlink" title="内置扩展库支持"></a>内置扩展库支持</h3><p>方便开发者使用，最新的 <a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/nightly.html" target="_blank" rel="external">Nightly版</a> 开发者工具支持了内置扩展库，开发者只需在app.json配置文件声明引用指定扩展库即可，无需自行引入相关 npm 包且不计入小程序代码包大小，目前支持了kbone和WeUI两种扩展库，更多详情请参考文档。</p><ul><li><a href="https://developers.weixin.qq.com/miniprogram/dev/extended/weui/" target="_blank" rel="external">拓展能力说明</a></li><li><a href="https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#useExtendedLib" target="_blank" rel="external">配置使用说明</a></li></ul><blockquote><p>工具里不计入总包大小，但是下载代码包依然会变大噢</p></blockquote><h3 id="更多更新说明"><a href="#更多更新说明" class="headerlink" title="更多更新说明"></a>更多更新说明</h3><p>更多的内容可以查看：</p><ul><li><a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/stable.html" target="_blank" rel="external">稳定版 Stable Build 更新日志</a></li><li><a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/nightly.html" target="_blank" rel="external">开发版 Nightly Build 更新日志</a></li></ul><h1 id="小程序教程"><a href="#小程序教程" class="headerlink" title="小程序教程"></a>小程序教程</h1><h2 id="社区精选文章"><a href="#社区精选文章" class="headerlink" title="社区精选文章"></a>社区精选文章</h2><ul><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/0008424ea884e83b47895f5c45b813" target="_blank" rel="external">揭开微信小程序 Kbone 的神秘面纱</a></li><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/00088ebe4e0500035999338cb56813" target="_blank" rel="external">小程序奇技淫巧之 – globalDataBehavior管理全局状态</a></li><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/00006c4afb0d28925699bba915b013" target="_blank" rel="external">小程序奇技淫巧之 – 日志能力</a></li><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/000a0a78a582d0e053997a1c05b813" target="_blank" rel="external">小程序奇技淫巧之 – 页面跳转管理</a></li></ul><p>往期内容可以查看<a href="https://developers.weixin.qq.com/community/develop/article" target="_blank" rel="external">文章分享</a></p><h2 id="最新踩坑-amp-amp-Tips"><a href="#最新踩坑-amp-amp-Tips" class="headerlink" title="最新踩坑 &amp;&amp; Tips"></a>最新踩坑 &amp;&amp; Tips</h2><h3 id="【同步踩坑信息】-小程序换行会导致多余空格"><a href="#【同步踩坑信息】-小程序换行会导致多余空格" class="headerlink" title="【同步踩坑信息】- 小程序换行会导致多余空格"></a>【同步踩坑信息】- 小程序换行会导致多余空格</h3><p>一般使用编辑器开发，格式化会导致一些换行，在小程序里会导致多余空格，如图：<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-latest-20191015.png" alt=""><br>经小程序团队定位，win 版换行符 \r\n 会有问题，浏览器也会。可以修改各自的编辑器的换行符使用 \n 可以解决。</p><ul><li>VS Code：Setting -&gt; Text Editor -&gt; Files -&gt; Eol -&gt; 设置为 \n</li></ul><h3 id="【同步开发Tips】-手机宽高比"><a href="#【同步开发Tips】-手机宽高比" class="headerlink" title="【同步开发Tips】- 手机宽高比"></a>【同步开发Tips】- 手机宽高比</h3><p>ios最小高宽比一般是960 x 640也就是我们的iPhone 比例是1.5，android最小宽高比960 x 540 比例是1.777777778。所以最小比例我们可以参考iPhone4，最大比例没有限制，因为手机屏幕只会越来越大，但是比例来来回回就是2.16的。</p><h3 id="【同步开发Tips】-小程序转发"><a href="#【同步开发Tips】-小程序转发" class="headerlink" title="【同步开发Tips】- 小程序转发"></a>【同步开发Tips】- 小程序转发</h3><p>小程序里控制右上角的转发行为方式：</p><ol><li>动态显隐转发按钮<code>wx.showShareMenu</code>、<code>wx.hideShareMenu</code>。</li><li>不定义<code>onShareAppMessage</code>，默认不会有转发。</li></ol><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><p>我们倾向于以结果判断决定——而不是当时作决定的过程。这种思维错误又名史学家错误。– 《清醒思考的艺术》</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;今年最后一刊啦&lt;del&gt;~明年见&lt;/del&gt;~&lt;br&gt;
    
    </summary>
    
      <category term="小程序双皮奶" scheme="https://godbasin.github.io/categories/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%8F%8C%E7%9A%AE%E5%A5%B6/"/>
    
    
      <category term="教程" scheme="https://godbasin.github.io/tags/%E6%95%99%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>小程序奇技淫巧之页面跳转管理</title>
    <link href="https://godbasin.github.io/2019/12/08/wxapp-navigate/"/>
    <id>https://godbasin.github.io/2019/12/08/wxapp-navigate/</id>
    <published>2019-12-08T11:12:08.000Z</published>
    <updated>2019-12-14T05:01:23.475Z</updated>
    
    <content type="html"><![CDATA[<p>小程序页面跳转也是一个很容易踩到坑的开发流程，本文介绍页面跳转相关的一些好用的实践和封装的组件库。<br><a id="more"></a></p><h2 id="小程序页面跳转"><a href="#小程序页面跳转" class="headerlink" title="小程序页面跳转"></a>小程序页面跳转</h2><hr><p>一个小程序可以有很多页面，每个页面承载不同的功能，页面之间可以互相跳转。我们知道，小程序分为渲染层和逻辑层，渲染层中包含了多个 WebView，每个 WebView 对应到我们的小程序里就是一个页面 Page，每一个页面都独立运行在一个页面层级上，如图：</p><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-navigate-1.png" alt=""></p><h3 id="页面栈管理"><a href="#页面栈管理" class="headerlink" title="页面栈管理"></a>页面栈管理</h3><p>一个小程序拥有多个页面，我们知道<code>wx.navigateTo</code>可以推入一个新的页面。我们看看小程序示例小程序里，在首页使用2次<code>wx.navigateTo</code>后，页面层级会有三层：</p><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-17.jpg" alt="">  </p><p>我们把这样的一个页面层级称为页面栈。在小程序中所有页面的路由全部由框架进行管理，框架以栈的形式维护了当前的所有页面。小程序中页面栈最多十层，也就是十个 WebView，如果超过了十个之后，就没法再打开新的页面了。</p><p>对于每一个新的页面层级，渲染层都需要进行一些额外的准备工作。在小程序启动前，客户端会提前准备好一个页面层级用于展示小程序的首页。除此以外，每当一个页面层级被用于渲染页面，客户端都会提前开始准备一个新的页面层级，使得每次调用<code>wx.navigateTo</code>都能够尽快展示一个新的页面。</p><h3 id="页面间跳转"><a href="#页面间跳转" class="headerlink" title="页面间跳转"></a>页面间跳转</h3><p>小程序启动时仅有一个页面层级，而在小程序运行中，页面之间的跳转表现包括这些：  </p><table><thead><tr><th>API</th><th>页面表现</th><th>页面栈表现</th></tr></thead><tbody><tr><td><code>wx.navigateTo</code></td><td>保留当前页面，跳转到应用内的某个页面</td><td>创建一个新的页面层级</td></tr><tr><td><code>wx.navigateBack</code></td><td>关闭当前页面，返回上一页面或多级页面</td><td>销毁一个页面层级</td></tr><tr><td><code>wx.redirectTo</code></td><td>关闭当前页面，跳转到应用内的某个页面</td><td>将当前页面层级重新初始化。重新传入页面的初始数据、路径等，视图层清空当前页面层级的渲染结果然后重新渲染页面</td></tr><tr><td><code>wx.reLaunch</code></td><td>关闭所有页面，打开到应用内的某个页面</td><td>销毁所有页面层级，再创建一个新的页面层级</td></tr><tr><td><code>wx.switchTab</code></td><td>关闭其他所有非 tabBar 页面，跳转到 tabBar 页面</td><td>销毁所有非 tabBa 页面层级，打开tabBar页面层级</td></tr></tbody></table><p>上面提到了 tabBar。除了普通的页面跳转，小程序里还支持配置 tabBar。tabBar 就是类似客户端 APP 底部的 tab 切换，为了获得更好的体验，小程序提供了这样的全局组件，在 app.json 文件中设置 tabBar，因此我们小程序会区分 tabBar 页面和非 tabBar 页面。tabBar 页面之间的切换都只会有一个层级，而跳转到非 tabBar 页面之后，就有了页面层级和页面栈的管理。</p><h2 id="页面跳转技巧"><a href="#页面跳转技巧" class="headerlink" title="页面跳转技巧"></a>页面跳转技巧</h2><hr><p>小程序的 JS 脚本是运行在 JsCore 的线程里，小程序的每个页面各自有一个 WebView 线程进行渲染，所以小程序切换页面时，小程序逻辑层的 JS 脚本运行上下文依旧在同一个 JsCore 线程中。</p><p>因为在同一个 JsCore 线程中，我们就会有一些问题会遇到，也可以有一些小技巧来处理。</p><h3 id="判断跳转来源"><a href="#判断跳转来源" class="headerlink" title="判断跳转来源"></a>判断跳转来源</h3><p>由于切换页面后，业务逻辑依然在同一个 JsCore 线程中。因此，即使是小程序页面被关闭 unload 之后，如果有原本在执行的逻辑，会继续执行完毕。</p><p>在这样的情况下，如果有重定向、跳转等逻辑，在跳转之后后续的逻辑依然会继续执行，这时候如果还有其他的跳转逻辑，可能会导致页面连续跳转，严重的话跳转参数丢书会导致白屏。</p><p>为了防止用户自行返回等操作，可以添加当前页面的条件判断是否要执行，页面栈可以通过<code>getCurrentPages</code>拿到，例如我们可以添加这样的方法处理：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 处理是否有当前路由</span></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">matchOriginPath</span>(<span class="params">originPageUrl</span>) </span>&#123;</div><div class="line">  <span class="keyword">let</span> currentPages = getCurrentPages();</div><div class="line">  <span class="keyword">const</span> currentPage = currentPages[currentPages.length - <span class="number">1</span>].route;</div><div class="line">  <span class="comment">// 判断是否设置了特定页面才进行跳转</span></div><div class="line">  <span class="comment">// 如果设置了，判断当前页面是否特定页面，是才跳转</span></div><div class="line">  <span class="comment">// 用于判断当前页面是否已经被跳转走（用户手动关闭等）</span></div><div class="line">  <span class="keyword">const</span> isMatch = !originPageUrl || (originPageUrl &amp;&amp; currentPage.indexOf(originPageUrl) &gt; <span class="number">-1</span>);</div><div class="line">  <span class="comment">// 如果设置了，当页面路径不匹配，则进行报错提示</span></div><div class="line">  <span class="keyword">if</span> (!isMatch) &#123;</div><div class="line">    <span class="built_in">console</span>.error(</div><div class="line">      <span class="string">"matchOriginPath do not match"</span>,</div><div class="line">      <span class="string">`currentPage: <span class="subst">$&#123;currentPage&#125;</span>, originPageUrl: <span class="subst">$&#123;originPageUrl&#125;</span>`</span></div><div class="line">    );</div><div class="line">  &#125;</div><div class="line">  <span class="keyword">return</span> isMatch;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>通过这样的检查方式，我们可以通过传参来判断下是否要检查：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * 跳转到页面</div><div class="line"> * @param &#123;object&#125; url 要跳转的页面地址</div><div class="line"> * @param &#123;object&#125; originPageUrl 原始页面地址，用于判断来源是否符合</div><div class="line"> */</div><div class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">navigateTo</span>(<span class="params">url: string, originPageUrl?: string</span>) </span>&#123;</div><div class="line">  <span class="comment">// 不符合源页面条件则不跳转</span></div><div class="line">  <span class="keyword">if</span> (!matchOriginPath(originPageUrl!)) &#123;</div><div class="line">    logger.RUN(<span class="string">"navigateTo"</span>, <span class="string">"originPageUrl != currentPage, return"</span>);</div><div class="line">    <span class="keyword">return</span> <span class="built_in">Promise</span>.resolve();</div><div class="line">  &#125;</div><div class="line">  <span class="built_in">console</span>.log(url);</div><div class="line">  wx.navigateTo(&#123; url &#125;);</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>这样，我们跳转的时候可以添加参数，预防页面非预期的跳转：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">navigateTo(&#123;<span class="attr">url</span>: <span class="string">'/pages/pageB'</span>&#125;, <span class="string">'/pages/pageA'</span>);</div><div class="line"><span class="comment">// 后面的逻辑在页面跳转之后，不会再生效</span></div><div class="line">navigateTo(&#123;<span class="attr">url</span>: <span class="string">'/pages/pageC'</span>&#125;, <span class="string">'/pages/pageA'</span>);</div></pre></td></tr></table></figure><h3 id="跳转传参"><a href="#跳转传参" class="headerlink" title="跳转传参"></a>跳转传参</h3><p>小程序提供的跳转相关 API，需要在 url 后面添加参数的方式来传参，但有些时候我们不仅仅需要携带简单的字符串或者数字，我们还可能需要携带一个较大的对象数据。那么这种情况下，由于小程序页面切换依然在同一个 JsCore 上下文，我们可以通过共享对象的方式来传递。</p><p>共享对象需要在公共库中存储一个当前跳转的传参内容，同时为了避免页面同时跳转导致传参内容不匹配，我们可以通过一个随机 ID 的方式来标记：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">getRandomId</span>(<span class="params"></span>) </span>&#123;</div><div class="line">  <span class="comment">// 时间戳（9位） + 随机串（10位）</span></div><div class="line">  <span class="keyword">return</span> (<span class="built_in">Date</span>.now()).toString(<span class="number">32</span>) + <span class="built_in">Math</span>.random().toString(<span class="number">32</span>).substring(<span class="number">2</span>);</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>跳转的时候，我们可以根据 url 传参的方式，还是共享对象传参的方式，来进行不同的判断处理：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> globalPageParams = <span class="literal">undefined</span>; <span class="comment">// 全局页面跳转参数</span></div><div class="line"><span class="keyword">let</span> globalPageParamsId: any = <span class="literal">undefined</span>; <span class="comment">// 全局页面跳转参数Id，用于标识某一次跳转的数据</span></div><div class="line"></div><div class="line"><span class="comment">// 跳转时参数处理</span></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">mangeUrl</span>(<span class="params">url, options</span>) </span>&#123;</div><div class="line">  <span class="keyword">const</span> &#123; urlParams, pageParams &#125; = options;</div><div class="line"></div><div class="line">  <span class="comment">// url参数处理</span></div><div class="line">  <span class="keyword">if</span> (urlParams) &#123;</div><div class="line">    url = addUrlParams(url, urlParams);</div><div class="line">  &#125;</div><div class="line"></div><div class="line">  <span class="comment">// 页面参数处理</span></div><div class="line">  <span class="keyword">if</span> (pageParams) &#123;</div><div class="line">    globalPageParams = objectCopy(pageParams);</div><div class="line">    <span class="comment">// 获取随机 ID</span></div><div class="line">    globalPageParamsId = getRandomId();</div><div class="line">    <span class="comment">// 将随机 ID 带入 url 参数中，可用来获取全局参数</span></div><div class="line">    url = addUrlParams(url, &#123; <span class="attr">randomid</span>: globalPageParamsId &#125;);</div><div class="line">  &#125; <span class="keyword">else</span> &#123;</div><div class="line">    globalPageParams = <span class="literal">undefined</span>;</div><div class="line">    globalPageParamsId = <span class="literal">undefined</span>;</div><div class="line">  &#125;</div><div class="line">  <span class="keyword">return</span> url;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>这样，我们的跳转方法可以这么处理：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * 跳转到页面</div><div class="line"> * @param &#123;object&#125; url 要跳转的页面地址</div><div class="line"> * @param &#123;object&#125; options 要携带的参数信息</div><div class="line"> * @param &#123;object&#125; originPageUrl 原始页面地址，用于判断来源是否符合</div><div class="line"> */</div><div class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">navigateTo</span>(<span class="params">url, options = &#123;&#125;, originPageUrl</span>) </span>&#123;</div><div class="line">  url = mangeUrl(url, options);</div><div class="line"></div><div class="line">  <span class="comment">// 不符合源页面条件则不跳转</span></div><div class="line">  <span class="keyword">if</span> (!matchOriginPath(originPageUrl!)) &#123;</div><div class="line">    logger.RUN(<span class="string">"navigateTo"</span>, <span class="string">"originPageUrl != currentPage, return"</span>);</div><div class="line">    <span class="keyword">return</span> <span class="built_in">Promise</span>.resolve();</div><div class="line">  &#125;</div><div class="line">  wx.navigateTo(&#123; url &#125;);</div><div class="line">&#125;</div></pre></td></tr></table></figure><h3 id="结合-Component-自动取参"><a href="#结合-Component-自动取参" class="headerlink" title="结合 Component 自动取参"></a>结合 Component 自动取参</h3><p>我们有介绍过如何使用 Component 来开发页面，使用 Component 有几个好处：</p><ol><li>可以通过 Behavior 来拓展组件的通用能力。</li><li>可以直接通过定义 properties 来获取页面参数。</li></ol><blockquote><p>页面如何使用 Behavior<br>看看<a href="https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/component.html" target="_blank" rel="external">官方文档</a>：事实上，小程序的页面也可以视为自定义组件。因而，页面也可以使用<code>Component</code>构造器构造，拥有与普通组件一样的定义段与实例方法。但此时要求对应<code>json</code>文件中包含<code>usingComponents</code>定义段。</p></blockquote><p>这里配合跳转传参，我们可以省略很多的逻辑。例如我们有一个结果页面，页面展示直接从 url 中取值（如果使用 Page 的 query 方式获取参数，需要自己进行  decodeURIComponent 才能使用，而使用组件的 properties 则不需要）：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">Component(&#123;</div><div class="line">  <span class="comment">// 其他配置省略</span></div><div class="line">  properties: &#123;</div><div class="line">    <span class="attr">type</span>: <span class="built_in">String</span>,  <span class="comment">// 结果类型，成功-success，失败-warn</span></div><div class="line">    title: <span class="built_in">String</span>, <span class="comment">// 主要文案</span></div><div class="line">    info: <span class="built_in">String</span> <span class="comment">// 辅助文案</span></div><div class="line">  &#125;</div><div class="line">&#125;);</div></pre></td></tr></table></figure><p>我们这样进行跳转：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">navigateTo(<span class="string">"/pages/result/result"</span>, &#123;</div><div class="line">  <span class="comment">// 直接带入参数，result组件可通过properties直接拿到</span></div><div class="line">  urlParams: &#123;</div><div class="line">    <span class="attr">type</span>: <span class="string">"success"</span>,</div><div class="line">    <span class="attr">title</span>: <span class="string">"操作成功"</span>,</div><div class="line">    <span class="attr">info</span>: <span class="string">"成功就是这么简单"</span></div><div class="line">  &#125;</div><div class="line">&#125;);</div></pre></td></tr></table></figure><p>则可以直接在模板中显示：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="comment">&lt;!-- 使用的 weui 组件库 --&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">view</span> <span class="attr">class</span>=<span class="string">"page"</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">weui-msg</span> <span class="attr">type</span>=<span class="string">"&#123;&#123;type&#125;&#125;"</span> <span class="attr">title</span>=<span class="string">"&#123;&#123;title&#125;&#125;"</span>&gt;</span></div><div class="line">        <span class="tag">&lt;<span class="name">view</span> <span class="attr">slot</span>=<span class="string">"desc"</span>&gt;</span>&#123;&#123;info&#125;&#125;<span class="tag">&lt;/<span class="name">view</span>&gt;</span></div><div class="line">    <span class="tag">&lt;/<span class="name">weui-msg</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">view</span>&gt;</span></div></pre></td></tr></table></figure><p>如果是通过页面传参的方式，则需要通过随机 ID 来获取对应的参数：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 通过随机 ID 获取对应参数</span></div><div class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">getPageParams</span>(<span class="params">randomId</span>) </span>&#123;</div><div class="line">  <span class="keyword">if</span> (globalPageParamsId === randomId) &#123;</div><div class="line">    <span class="keyword">return</span> globalPageParams || &#123;&#125;;</div><div class="line">  &#125;</div><div class="line">  <span class="keyword">return</span> &#123;&#125;;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>组件中可以通过 properties 来获取随机 ID，然后获取对应参数：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">Component(&#123;</div><div class="line">  <span class="comment">// 其他配置省略</span></div><div class="line">  properties: &#123;</div><div class="line">    <span class="attr">randomid</span>: <span class="built_in">String</span>,  <span class="comment">// 随机 ID</span></div><div class="line">  &#125;,</div><div class="line">  <span class="attr">methods</span>: &#123;</div><div class="line">    onLoad() &#123;</div><div class="line">      <span class="comment">// 获取参数</span></div><div class="line">      <span class="keyword">const</span> params = getPageParams(<span class="keyword">this</span>.data.randomid);</div><div class="line">      <span class="comment">// 处理参数</span></div><div class="line">    &#125;</div><div class="line">  &#125;</div><div class="line">&#125;);</div></pre></td></tr></table></figure><p>具体的代码实现，可参考项目代码：<a href="https://github.com/godbasin/wxapp-typescript-demo" target="_blank" rel="external">wxapp-typescript-demo</a>。</p><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><ul><li><a href="https://developers.weixin.qq.com/ebook?action=get_post_info&amp;docid=000a64a29c48b0eb0086f161b5940a" target="_blank" rel="external">7.2 页面层级准备</a></li><li><a href="https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/component.html" target="_blank" rel="external">Component构造器</a></li><li><a href="https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/behaviors.html" target="_blank" rel="external">behaviors</a></li></ul><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><hr><p>使用自定义组件的方式来开发页面，简直不能太赞啦。通过 Behavior 来各种拓展组件的能力，同时还能简化一些取参的逻辑，还是特别方便的。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;小程序页面跳转也是一个很容易踩到坑的开发流程，本文介绍页面跳转相关的一些好用的实践和封装的组件库。&lt;br&gt;
    
    </summary>
    
      <category term="小程序双皮奶" scheme="https://godbasin.github.io/categories/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%8F%8C%E7%9A%AE%E5%A5%B6/"/>
    
    
      <category term="教程" scheme="https://godbasin.github.io/tags/%E6%95%99%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>小程序奇技淫巧之日志能力</title>
    <link href="https://godbasin.github.io/2019/12/07/wxapp-logs/"/>
    <id>https://godbasin.github.io/2019/12/07/wxapp-logs/</id>
    <published>2019-12-07T05:42:28.000Z</published>
    <updated>2019-12-14T05:01:40.724Z</updated>
    
    <content type="html"><![CDATA[<p>小程序定位问题也是一大难题，而官方提供了 LogManager 和实时日志，这两个能力是如何结合使用、是否还有更便捷的方式来使用呢？<br><a id="more"></a></p><h2 id="日志与反馈"><a href="#日志与反馈" class="headerlink" title="日志与反馈"></a>日志与反馈</h2><hr><p>前端开发在进行某个问题定位的时候，日志是很重要的。因为机器兼容性问题、环境问题等，我们常常无法复现用户的一些bug。而微信官方也提供了较完整的日志能力，我们一起来看一下。</p><h3 id="用户反馈"><a href="#用户反馈" class="headerlink" title="用户反馈"></a>用户反馈</h3><p>小程序官方提供了用户反馈携带日志的能力，大概流程是：  </p><ol><li>开发中日志打印，使用日志管理器实例 LogManager。  </li><li>用户在使用过程中，可以在小程序的 profile 页面（【右上角胶囊】-【关于xxxx】），点击【投诉与反馈】-【功能异常】（旧版本还需要勾选上传日志），则可以上传日志。</li><li>在小程序管理后台，【管理】-【反馈管理】，就可以查看上传的日志（还包括了很详细的用户和机型版本等信息）。 </li></ol><p>这个入口可能对于用户来说过于深入（是的，官方也发现这个问题了，所以后面有了实时日志），我们小程序也可以通过<code>button</code>组件，设置<code>openType</code>为<code>feedback</code>，然后用户点击按钮就可以直接拉起意见反馈页面了。利用这个能力，我们可以监听用户截屏的操作，然后弹出浮层引导用户主动进行反馈。</p><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">&lt;view class="dialog" wx:if="&#123;&#123;isFeedbackShow&#125;&#125;"&gt;</div><div class="line">  &lt;view&gt;是否遇到问题？&lt;/view&gt;</div><div class="line">  &lt;button open-type="feedback"&gt;点击反馈&lt;/button&gt;</div><div class="line">&lt;/view&gt;</div><div class="line"></div><div class="line">wx.onUserCaptureScreen(() =&gt; &#123;</div><div class="line">  // 设置弹窗出现</div><div class="line">  this.setData(&#123;isFeedbackShow: true&#125;)</div><div class="line">&#125;);</div></pre></td></tr></table></figure><h3 id="LogManager"><a href="#LogManager" class="headerlink" title="LogManager"></a>LogManager</h3><p>关于小程序的 LogManager，大概是非常实用又特别低调的一个能力了。它的使用方式其实和 console 很相似，提供了 log、info、debug、warn 等日志方式。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> logger = wx.getLogManager()</div><div class="line">logger.log(&#123;<span class="attr">str</span>: <span class="string">'hello world'</span>&#125;, <span class="string">'basic log'</span>, <span class="number">100</span>, [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>])</div><div class="line">logger.info(&#123;<span class="attr">str</span>: <span class="string">'hello world'</span>&#125;, <span class="string">'info log'</span>, <span class="number">100</span>, [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>])</div><div class="line">logger.debug(&#123;<span class="attr">str</span>: <span class="string">'hello world'</span>&#125;, <span class="string">'debug log'</span>, <span class="number">100</span>, [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>])</div><div class="line">logger.warn(&#123;<span class="attr">str</span>: <span class="string">'hello world'</span>&#125;, <span class="string">'warn log'</span>, <span class="number">100</span>, [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>])</div></pre></td></tr></table></figure><p>打印的日志，从管理后台下载下来之后，也是很好懂：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line">2019-6-25 22:11:6 [log] wx.setStorageSync api invoke</div><div class="line">2019-6-25 22:11:6 [log] wx.setStorageSync return</div><div class="line">2019-6-25 22:11:6 [log] wx.setStorageSync api invoke</div><div class="line">2019-6-25 22:11:6 [log] wx.setStorageSync return</div><div class="line">2019-6-25 22:11:6 [log] [v1.1.0] request begin</div><div class="line">2019-6-25 22:11:6 [log] wx.request api invoke with seq 0</div><div class="line">2019-6-25 22:11:6 [log] wx.request success callback with msg request:ok with seq 0</div><div class="line">2019-6-25 22:11:6 [log] [v1.1.0] request done</div><div class="line">2019-6-25 22:11:7 [log] wx.navigateTo api invoke</div><div class="line">2019-6-25 22:11:7 [log] page packquery/pages/index/index onHide have been invoked</div><div class="line">2019-6-25 22:11:7 [log] page packquery/pages/logs/logs onLoad have been invoked</div><div class="line">2019-6-25 22:11:7 [log] [v1.1.0] logs  |  onShow  |    |  []</div><div class="line">2019-6-25 22:11:7 [log] wx.setStorageSync api invoke</div><div class="line">2019-6-25 22:11:7 [log] wx.setStorageSync return</div><div class="line">2019-6-25 22:11:7 [log] wx.reportMonitor api invoke</div><div class="line">2019-6-25 22:11:7 [log] page packquery/pages/logs/logs onShow have been invoked</div><div class="line">2019-6-25 22:11:7 [log] wx.navigateTo success callback with msg navigateTo:ok</div></pre></td></tr></table></figure><p>LogManager 最多保存 5M 的日志内容，超过5M后，旧的日志内容会被删除。基础库默认会把 App、Page 的生命周期函数和 wx 命名空间下的函数调用写入日志，基础库的日志帮助我们定位具体哪些地方出了问题。</p><h3 id="实时日志"><a href="#实时日志" class="headerlink" title="实时日志"></a>实时日志</h3><p>小程序的 LogManager 有一个很大的痛点，就是必须依赖用户上报，入口又是右上角胶囊-【关于xxxx】-【投诉与反馈】-【功能异常】这么长的路径，甚至用户的反馈过程也会经常丢失日志，导致无法查问题。</p><p>为帮助小程序开发者快捷地排查小程序漏洞、定位问题，微信推出了实时日志功能。从基础库 2.7.1 开始，开发者可通过提供的接口打印日志，日志汇聚并实时上报到小程序后台。</p><p>使用方式如下：  </p><ol><li>使用 wx.getRealtimeLogManager 在代码⾥⾯打⽇志。  </li><li>可从小程序管理后台【开发】-【运维中心】-【实时日志】进入日志查询页面，查看开发者打印的日志信息。  </li></ol><p>开发者可通过设置时间、微信号/OpenID、页面链接、FilterMsg内容（基础库2.7.3及以上支持setFilterMsg）等筛选条件查询指定用户的日志信息:</p><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-logs-1.png" alt=""></p><p>由于后台资源限制，实时日志使用规则如下：  </p><ol><li>为了定位问题方便，日志是按页面划分的，某一个页面，在onShow到onHide（切换到其它页面、右上角圆点退到后台）之间打的日志，会聚合成一条日志上报，并且在小程序管理后台上可以根据页面路径搜索出该条日志。</li><li>每个小程序账号每天限制500万条日志，日志会保留7天，建议遇到问题及时定位。<br>一条日志的上限是5KB，最多包含200次打印日志函数调用（info、warn、error调用都算），所以要谨慎打日志，避免在循环里面调用打日志接口，避免直接重写console.log的方式打日志。</li><li>意见反馈里面的日志，可根据OpenID搜索日志。</li><li>setFilterMsg 可以设置过滤的 Msg。这个接口的目的是提供某个场景的过滤能力，例如<code>setFilterMsg(&#39;scene1&#39;)</code>，则在 MP 上可输入 scene1 查询得到该条日志。比如上线过程中，某个监控有问题，可以根据 FilterMsg 过滤这个场景下的具体的用户日志。FilterMsg 仅支持大小写字母。如果需要添加多个关键字，建议使用 addFilterMsg 替代 setFilterMsg。</li></ol><h2 id="日志开发技巧"><a href="#日志开发技巧" class="headerlink" title="日志开发技巧"></a>日志开发技巧</h2><hr><p>既然官方提供了 LogManager 和实时日志，我们当然是两个都要用啦。</p><h3 id="log-js"><a href="#log-js" class="headerlink" title="log.js"></a>log.js</h3><p>我们将所有日志的能力都封装在一起，暴露一个通用的接口给调用方使用：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// log.js</span></div><div class="line"><span class="keyword">const</span> VERSION = <span class="string">"0.0.1"</span>; <span class="comment">// 业务代码版本号，用户灰度过程中观察问题</span></div><div class="line"></div><div class="line"><span class="keyword">const</span> canIUseLogManage = wx.canIUse(<span class="string">"getLogManager"</span>);</div><div class="line"><span class="keyword">const</span> logger = canIUseLogManage ? wx.getLogManager(&#123;<span class="attr">level</span>: <span class="number">0</span>&#125;) : <span class="literal">null</span>;</div><div class="line"><span class="keyword">var</span> realtimeLogger = wx.getRealtimeLogManager ? wx.getRealtimeLogManager() : <span class="literal">null</span>;</div><div class="line"></div><div class="line"><span class="comment">/**</span></div><div class="line"> * @param &#123;string&#125; file 所在文件名</div><div class="line"> * @param  &#123;...any&#125; arg 参数</div><div class="line"> */</div><div class="line"></div><div class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">DEBUG</span>(<span class="params">file, ...args</span>) </span>&#123;</div><div class="line">  <span class="built_in">console</span>.debug(file, <span class="string">" | "</span>, ...args);</div><div class="line">  <span class="keyword">if</span> (canIUseLogManage) &#123;</div><div class="line">    logger!.debug(<span class="string">`[<span class="subst">$&#123;VERSION&#125;</span>]`</span>, file, <span class="string">" | "</span>, ...args);</div><div class="line">  &#125;</div><div class="line">  realtimeLogger &amp;&amp; realtimeLogger.info(<span class="string">`[<span class="subst">$&#123;VERSION&#125;</span>]`</span>, file, <span class="string">" | "</span>, ...args);</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">/**</span></div><div class="line"> *</div><div class="line"> * @param &#123;string&#125; file 所在文件名</div><div class="line"> * @param  &#123;...any&#125; arg 参数</div><div class="line"> */</div><div class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">RUN</span>(<span class="params">file, ...args</span>) </span>&#123;</div><div class="line">  <span class="built_in">console</span>.log(file, <span class="string">" | "</span>, ...args);</div><div class="line">  <span class="keyword">if</span> (canIUseLogManage) &#123;</div><div class="line">    logger!.log(<span class="string">`[<span class="subst">$&#123;VERSION&#125;</span>]`</span>, file, <span class="string">" | "</span>, ...args);</div><div class="line">  &#125;</div><div class="line">  realtimeLogger &amp;&amp; realtimeLogger.info(<span class="string">`[<span class="subst">$&#123;VERSION&#125;</span>]`</span>, file, <span class="string">" | "</span>, ...args);</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">/**</span></div><div class="line"> *</div><div class="line"> * @param &#123;string&#125; file 所在文件名</div><div class="line"> * @param  &#123;...any&#125; arg 参数</div><div class="line"> */</div><div class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">ERROR</span>(<span class="params">file, ...args</span>) </span>&#123;</div><div class="line">  <span class="built_in">console</span>.error(file, <span class="string">" | "</span>, ...args);</div><div class="line">  <span class="keyword">if</span> (canIUseLogManage) &#123;</div><div class="line">    logger!.warn(<span class="string">`[<span class="subst">$&#123;VERSION&#125;</span>]`</span>, file, <span class="string">" | "</span>, ...args);</div><div class="line">  &#125;</div><div class="line">  <span class="keyword">if</span> (realtimeLogger) &#123;</div><div class="line">    realtimeLogger.error(<span class="string">`[<span class="subst">$&#123;VERSION&#125;</span>]`</span>, file, <span class="string">" | "</span>, ...args);</div><div class="line">    <span class="comment">// 判断是否支持设置模糊搜索</span></div><div class="line">    <span class="comment">// 错误的信息可记录到 FilterMsg，方便搜索定位</span></div><div class="line">    <span class="keyword">if</span> (realtimeLogger.addFilterMsg) &#123;</div><div class="line">      <span class="keyword">try</span> &#123;</div><div class="line">        realtimeLogger.addFilterMsg(</div><div class="line">          <span class="string">`[<span class="subst">$&#123;VERSION&#125;</span>] <span class="subst">$&#123;file&#125;</span> <span class="subst">$&#123;<span class="built_in">JSON</span>.stringify(args)&#125;</span>`</span></div><div class="line">        );</div><div class="line">      &#125; <span class="keyword">catch</span> (e) &#123;</div><div class="line">        realtimeLogger.addFilterMsg(<span class="string">`[<span class="subst">$&#123;VERSION&#125;</span>] <span class="subst">$&#123;file&#125;</span>`</span>);</div><div class="line">      &#125;</div><div class="line">    &#125;</div><div class="line">  &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// 方便将页面名字自动打印</span></div><div class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">getLogger</span>(<span class="params">fileName: string</span>) </span>&#123;</div><div class="line">  <span class="keyword">return</span> &#123;</div><div class="line">    <span class="attr">DEBUG</span>: <span class="function"><span class="keyword">function</span>(<span class="params">...args</span>) </span>&#123;</div><div class="line">      DEBUG(fileName, ...args);</div><div class="line">    &#125;,</div><div class="line">    <span class="attr">RUN</span>: <span class="function"><span class="keyword">function</span>(<span class="params">...args</span>) </span>&#123;</div><div class="line">      RUN(fileName, ...args);</div><div class="line">    &#125;,</div><div class="line">    <span class="attr">ERROR</span>: <span class="function"><span class="keyword">function</span>(<span class="params">...args</span>) </span>&#123;</div><div class="line">      ERROR(fileName, ...args);</div><div class="line">    &#125;</div><div class="line">  &#125;;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>通过这样的方式，我们在一个页面中使用日志的时候，可以这么使用：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> &#123; getLogger &#125; <span class="keyword">from</span> <span class="string">"./log"</span>;</div><div class="line"><span class="keyword">const</span> PAGE_MANE = <span class="string">"page_name"</span>;</div><div class="line"><span class="keyword">const</span> logger = getLogger(PAGE_MANE);</div></pre></td></tr></table></figure><h3 id="autolog-behavior"><a href="#autolog-behavior" class="headerlink" title="autolog-behavior"></a>autolog-behavior</h3><p>现在有了日志组件，我们需要在足够多的地方记录日志，才能在问题出现的时候及时进行定位。一般来说，我们需要在每个方法在被调用的时候都打印一个日志，所以这里封装了一个 autolog-behavior 的方式，每个页面（需要是 Component 方式）中只需要引入这个 behavior，就可以在每个方法调用的时候，打印日志：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// autolog-behavior.js</span></div><div class="line"><span class="keyword">import</span> * <span class="keyword">as</span> Log <span class="keyword">from</span> <span class="string">"../utils/log"</span>;</div><div class="line"><span class="comment">/**</span></div><div class="line"> * 本 Behavior 会在小程序 methods 中每个方法调用前添加一个 Log 说明</div><div class="line"> * 需要在 Component 的 data 属性中添加 PAGE_NAME，用于描述当前页面</div><div class="line"> */</div><div class="line"></div><div class="line"><span class="keyword">export</span> <span class="keyword">default</span> Behavior(&#123;</div><div class="line">  definitionFilter(defFields) &#123;</div><div class="line">    <span class="comment">// 获取定义的方法</span></div><div class="line">    <span class="built_in">Object</span>.keys(defFields.methods || &#123;&#125;).forEach(<span class="function"><span class="params">methodName</span> =&gt;</span> &#123;</div><div class="line">      <span class="keyword">const</span> originMethod = defFields.methods![methodName];</div><div class="line">      <span class="comment">// 遍历更新每个方法</span></div><div class="line">      defFields.methods![methodName] = <span class="function"><span class="keyword">function</span>(<span class="params">ev, ...args</span>) </span>&#123;</div><div class="line">        <span class="keyword">if</span> (ev &amp;&amp; ev.target &amp;&amp; ev.currentTarget &amp;&amp; ev.currentTarget.dataset) &#123;</div><div class="line">          <span class="comment">// 如果是事件类型，则只需要记录 dataset 数据</span></div><div class="line">          Log.RUN(defFields.data.PAGE_NAME, <span class="string">`<span class="subst">$&#123;methodName&#125;</span> invoke, event dataset = `</span>, ev.currentTarget.dataset, <span class="string">"params = "</span>, ...args);</div><div class="line">        &#125; <span class="keyword">else</span> &#123;</div><div class="line">          <span class="comment">// 其他情况下，则都记录日志</span></div><div class="line">          Log.RUN( defFields.data.PAGE_NAME, <span class="string">`<span class="subst">$&#123;methodName&#125;</span> invoke, params = `</span>, ev, ...args);</div><div class="line">        &#125;</div><div class="line">        <span class="comment">// 触发原有的方法</span></div><div class="line">        originMethod.call(<span class="keyword">this</span>, ev, ...args);</div><div class="line">      &#125;;</div><div class="line">    &#125;);</div><div class="line">  &#125;</div><div class="line">&#125;);</div></pre></td></tr></table></figure><p>我们能看到，日志打印依赖了页面中定义了一个<code>PAGE_NAME</code>的 data 数据，所以我们在使用的时候可以这么处理：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> &#123; getLogger &#125; <span class="keyword">from</span> <span class="string">"../../utils/log"</span>;</div><div class="line"><span class="keyword">import</span> autologBehavior <span class="keyword">from</span> <span class="string">"../../behaviors/autolog-behavior"</span>;</div><div class="line"></div><div class="line"><span class="keyword">const</span> PAGE_NAME = <span class="string">"page_name"</span>;</div><div class="line"><span class="keyword">const</span> logger = getLogger(PAGE_NAME);</div><div class="line"></div><div class="line">Component(&#123;</div><div class="line">  <span class="attr">behaviors</span>: [autologBehavior],</div><div class="line">  <span class="attr">data</span>: &#123;</div><div class="line">    PAGE_NAME,</div><div class="line">    <span class="comment">// 其他数据</span></div><div class="line">  &#125;,</div><div class="line">  <span class="attr">methods</span>: &#123;</div><div class="line">    <span class="comment">// 定义的方法会在调用的时候自动打印日志</span></div><div class="line">  &#125;</div><div class="line">&#125;);</div></pre></td></tr></table></figure><blockquote><p>页面如何使用 Behavior<br>看看<a href="https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/component.html" target="_blank" rel="external">官方文档</a>：事实上，小程序的页面也可以视为自定义组件。因而，页面也可以使用<code>Component</code>构造器构造，拥有与普通组件一样的定义段与实例方法。但此时要求对应<code>json</code>文件中包含<code>usingComponents</code>定义段。</p></blockquote><p>完整的项目可以参考<a href="https://github.com/godbasin/wxapp-typescript-demo" target="_blank" rel="external">wxapp-typescript-demo</a>。</p><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><ul><li><a href="https://developers.weixin.qq.com/miniprogram/dev/api/base/debug/LogManager.html" target="_blank" rel="external">LogManager</a></li><li><a href="https://developers.weixin.qq.com/miniprogram/dev/framework/realtimelog/" target="_blank" rel="external">实时日志</a></li><li><a href="https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/component.html" target="_blank" rel="external">Component构造器</a></li><li><a href="https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/behaviors.html" target="_blank" rel="external">behaviors</a></li></ul><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><hr><p>使用自定义组件的方式来写页面，有特别多好用的技巧，behavior 就是其中一个比较重要的能力，大家可以发挥自己的想象力来实现很多奇妙的功能。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;小程序定位问题也是一大难题，而官方提供了 LogManager 和实时日志，这两个能力是如何结合使用、是否还有更便捷的方式来使用呢？&lt;br&gt;
    
    </summary>
    
      <category term="小程序双皮奶" scheme="https://godbasin.github.io/categories/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%8F%8C%E7%9A%AE%E5%A5%B6/"/>
    
    
      <category term="教程" scheme="https://godbasin.github.io/tags/%E6%95%99%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>前端这几年--4.生命与健康</title>
    <link href="https://godbasin.github.io/2019/11/30/about-front-end-4-health/"/>
    <id>https://godbasin.github.io/2019/11/30/about-front-end-4-health/</id>
    <published>2019-11-30T13:04:33.000Z</published>
    <updated>2019-11-30T14:20:18.208Z</updated>
    
    <content type="html"><![CDATA[<p>996、福报等话题也是越来越成为程序员的日常，而除了肚腩、秃头等影响外观的健康因素，关于猝死、患病等话题也渐渐成为互联网世界的话题之一。我们到底是从什么时候开始，才越来越轻视一些身体发出的求救信号呢？</p><a id="more"></a><h1 id="最重要的是那个1"><a href="#最重要的是那个1" class="headerlink" title="最重要的是那个1"></a>最重要的是那个1</h1><p>以前经常听长辈叨叨，我们的身体是 1，所有的财富、荣耀、名声都是后面的 0，只有 1 在的时候，这些 0 才会有意义呀。</p><p>有意思的是，对于程序员来说，1 和 0 可以组成任意的内容、代表着整个世界。但如果说生命是 1 而其他事物是 0 的话，很多时候我们在做选择的时候总会忽视了已有的 1。</p><h2 id="上进心很重要"><a href="#上进心很重要" class="headerlink" title="上进心很重要"></a>上进心很重要</h2><p>想要成功在任何时候都不会有错。的确，我们有特别多想要得到的东西，可能是权力，可能是金钱，可能是职位。越是年轻，我们拥有的越少，想要的越多。所以刚入职场的时候，我们积极、拼搏、热情，因为我们迫切地希望获得更多的成长和其他的收获。</p><p>如今互联网就业压力大，996、修福报等已经是常见的操作了。大多数打工仔没法选择，因为显然“你不做会有无数人愿意做”。于是，大家心怀鬼胎，下班了依然呆在工位不回家，只是因为“大家都还没走，我不能成为第一个”。</p><p>曾经我也有段时间每天下班比较早，那段时间刚好身体不大舒服比较困，早点回家休息。结果没过几天就被组长喊去谈话，“你不能那么早走，领导们都还在，大家都看着呢”。当然，我也早就离开了那样扯淡的小组了。</p><p>是的，我们想要成长就必须要付出。我个人也同样认为上进心很重要，但过于强求反而容易反噬。凡事有个度，有一条线是迈过去再也回不来的。</p><h2 id="但什么都比不上你的健康"><a href="#但什么都比不上你的健康" class="headerlink" title="但什么都比不上你的健康"></a>但什么都比不上你的健康</h2><p>“我们都还年轻，还可以拼一下，老了就拼不动了”。其实老了拼不动，正是因为我们年轻的时候没有节制地消耗自己的身体。</p><p>我们的生活中充斥着老一辈人的叨叨，身体最重要呀，老师和公务员比较舒服呀，这条路很难走呀不要去呀。大多数的我们也依然会选择尝试一下，不试一下怎么知道不行呢？</p><p>刚从华为出来，自学前端转行的时候，每天上班干活、下班继续学习补充基础到夜里。挤在一个三室26个女生、只有一个洗手间的房子里，每天下班后打着台灯窝在上铺里看书学习。</p><p>直到有段时间，开始每天都觉得肚子不大舒服，还带点低烧、发冷。那会刚换的工作，不好请假看医生。差不多熬了快一个月，直到有一天差点晕了过去，然后打了个车去医院，检查之后就收到了入院通知。</p><p>那段时间，由于自己的一些坚持和家人闹翻了，有几乎好几个月没有联系了。医院的病床也已经满了，于是在过道的地方临时加了个床位。打电话跟领导请假，换来了一句“女生就是矫情”，挂电话后十分钟不到所有相关的群都被移除了。</p><p>后面咨询了学医的一个同学，他告诉我情况很严重，让我马上打电话给我家人跟他们说。这位同学平时笑嘻嘻的，此时是无比的认真严厉。哥第二天来接我，回家那边的医院看，走的时候医院还让签了一份免责声明书。</p><p>术前要稳定病情，那段时间我打了这辈子都没见过那么多的针，到后面护士都找不到可以扎针的地方了，留置针也基本上一次就用不了了。手术结束后还进了ICU好几天，我到现在还记得，当时血压一直升不起来医生给打了肾上腺素，高烧不退只能物理降温，身边堆满了冰袋。我跟医生说好冷啊，医生象征性地在我的床架上搭了个被子，说没有办法了你就忍忍吧。</p><p>后来阿姨跟我说，我从手术室推出来的时候，我爸心疼得眼红红又站一边不知所措，自尊心那么强的一个人啊。出院的时候只剩下骨头，晚上睡觉都能磕着自己，如果这一切要让我再经历一次，我觉得自己没有勇气坚持过来。</p><p>从那以后，生命和健康真正成为我最重视的一道线。努力可以，奋斗也可以，但长期的加班熬夜通宵、无节制地消耗身体这样的行为则成了我的底线。血的教训让我狠狠地体会了一把，什么事情对于我来说才是最重要的。</p><p>所以，即便有一万个不愿意把这些写下来，但如果能让哪怕一个人有所启发，不至于重蹈覆辙这样的痛苦，我也觉得是值得的。</p><h1 id="什么都比不上身边有你们"><a href="#什么都比不上身边有你们" class="headerlink" title="什么都比不上身边有你们"></a>什么都比不上身边有你们</h1><p>也是从这事之后，和家人的关系缓和了很多，再也没有说话很冲。因为我们都知道，彼此都能健康快乐地活着就是最大的幸福。</p><p>因为我知道，当我遇到困难的时候，愿意接受我、陪伴我、帮助我的人，永远是最亲近的人，家人和朋友。</p><h2 id="工作不是你的全部"><a href="#工作不是你的全部" class="headerlink" title="工作不是你的全部"></a>工作不是你的全部</h2><p>不知道是因为出了象牙塔的原因，还是因为这些年来社会风气变化的原因，似乎有太多太多的人把工作放在最重要的位置。身体、家人、生活甚至有时候还成为一些人通往权利之路上的阻碍。</p><p>当我做一些自己认为对的事情，坚持一些认为很重要的事情，有人甚至跟我说，“哇，被删你三观好正啊”。所以这个世界是从什么时候开始，三观正常变成了一件很稀有的事情呢？</p><p>有些人为了工作、赚钱，不顾身体健康。有些人为了获得更多，开始接受一些越线的事情。有些人仅仅为了拿到更好的业绩，不择手段、落井下石。而几乎所有人都觉得这是很正常的事情，大家都不相信公正，但却会觉得有些遭遇了不幸的人肯定有原因的。这是为什么呢？</p><p>有时候我是偏激的，有时候我又是矫情的，但不管什么时候，我都能知道什么是最重要的。人生苦短，请及时行乐。工作几乎是占每个人一半人生的一件事，但明明是一半的人生为什么每个人都期盼着五年、十年就能达到顶峰呢，为什么每个人都那么着急呢。我们现在做下的选择，能在十年二十年后骄傲地告诉后辈自己做的这些事情吗？</p><p>人的一生里，除了激情的工作、拼搏的回忆，更还有那些选择陪伴在我们身边的人。即使没有大富大贵，即使没有成名成就，那些在你人生低谷愿意花时间听你倾诉、安慰和帮你分析想办法的人们，才是该去好好珍惜、好好对待的人。为什么我们愿意吹捧同事和领导，却对最亲爱的人出言不逊呢？</p><h2 id="最重要的还有你们的那个1"><a href="#最重要的还有你们的那个1" class="headerlink" title="最重要的还有你们的那个1"></a>最重要的还有你们的那个1</h2><p>我原本以为我能放下和理解当时的一些情况，而当如今写下来的时候，我才知道自己依然是恨着的。</p><p>那天，家人突然遭遇车祸送院抢救。情况很危急，与死神赛跑。我依然记得当天手术后医生给我们讲述情况的时候，胸口闷得发慌，全身发抖站不住。当天晚上，还守在ICU外面的时候，收到了“今年要淘汰末位20%，你还要面临晋级、结婚生娃等，要加油努力了”这样的消息。发消息的人也知道家人还在ICU情况不稳定，未曾有一句问候和关心，却发来这么一大段。在后续情况稳定回到普通病房后，和家人轮流照顾时，还让联调一个不重要也不紧急的接口。心都凉透了。</p><p>那段日子里，我对深圳也是有一丝怨恨的。情况稳定后，回到工作中的第一件事就换组了。有些事情，可以理解是一回事，接受则是另外一回事了。</p><p>有时候我觉得自己就是在演电视剧，遇到很多奇葩的事情，这些都只是其中的一部分。很多人会受到“公正世界”效应的影响，相信我们遭到了什么报应，所以很多人在遇到一些事情的时候，做的第一件事情就是责怪自己。</p><p>但这个世界不是公正的，随机性的事情随时随刻都可能发生在任何人身上。所以相比于相信正义终有一天会到来，不如当下选择勇敢面对。</p><p>看着家人还都在，偶尔聊聊天、撒撒娇，工作中再多的不顺畅也值得。家人的问候从来都两句，记得锻炼身体、要过得开心，曾经觉得没什么用处的“废话”，到如今成了人生的座右铭。</p><p>这个世界很任性，我们规划了很多很多，都赶不上一个意外、一些奇葩的人和事来打乱。工作后，一泼泼凉水浇灭了很多的东西，而庆幸的是，心还是温暖跳动的。重要的不是你成功的时候有多少人追捧你，而是在遭遇什么事情的时候愿意向你伸手的人。</p><p>渴望成功没有错，但失败也不是什么大不了的事情。因为不管怎样，我还有身后一直在支持我的那些朋友家人。最重要的事情，当然也还有你们的那个1。</p><p>只要人还在，一切都还能重来呢。</p><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><p>照顾好自己，不要期待公正。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;996、福报等话题也是越来越成为程序员的日常，而除了肚腩、秃头等影响外观的健康因素，关于猝死、患病等话题也渐渐成为互联网世界的话题之一。我们到底是从什么时候开始，才越来越轻视一些身体发出的求救信号呢？&lt;/p&gt;
    
    </summary>
    
      <category term="工作这杯茶" scheme="https://godbasin.github.io/categories/%E5%B7%A5%E4%BD%9C%E8%BF%99%E6%9D%AF%E8%8C%B6/"/>
    
    
      <category term="心态" scheme="https://godbasin.github.io/tags/%E5%BF%83%E6%80%81/"/>
    
  </entry>
  
  <entry>
    <title>小程序开发月刊第11期（20191115）</title>
    <link href="https://godbasin.github.io/2019/11/15/wxapp-latest-20191115/"/>
    <id>https://godbasin.github.io/2019/11/15/wxapp-latest-20191115/</id>
    <published>2019-11-15T15:00:15.000Z</published>
    <updated>2019-11-16T15:03:08.259Z</updated>
    
    <content type="html"><![CDATA[<p>这个月的新内容还不错噢<del>~你值得拥有</del><br><a id="more"></a></p><h1 id="小程序-latest"><a href="#小程序-latest" class="headerlink" title="小程序 latest"></a>小程序 latest</h1><h2 id="小程序能力"><a href="#小程序能力" class="headerlink" title="小程序能力"></a>小程序能力</h2><h3 id="小程序·服务商订阅消息设置接口上线"><a href="#小程序·服务商订阅消息设置接口上线" class="headerlink" title="小程序·服务商订阅消息设置接口上线"></a>小程序·服务商订阅消息设置接口上线</h3><p>为便于服务商代小程序更快设置订阅消息，提高开发效率，降低沟通成本。 现在向服务商提供订阅消息设置接口，具体详见：<a href="https://developers.weixin.qq.com/community/develop/doc/000c8a142ac080094779976b550801" target="_blank" rel="external">《订阅消息设置》</a></p><h3 id="同层渲染进度同步"><a href="#同层渲染进度同步" class="headerlink" title="同层渲染进度同步"></a>同层渲染进度同步</h3><p>为了解决小程序原生组件存在的一些<a href="https://developers.weixin.qq.com/miniprogram/dev/component/native-component.html" target="_blank" rel="external">使用限制</a>，我们对原生组件引入了同层渲染。支持同层渲染的原生组件层级与非原生组件一致，可直接使用非原生组件（如 view、image）结合 z-index 对原生组件进行覆盖，而无需使用 cover-view 或 cover-image。此外，同层渲染的原生组件也可被放置在 scroll-view、swiper 或 movable-view 容器中。目前，以下组件已支持同层渲染：</p><table><thead><tr><th>支持同层渲染的原生组件</th><th>最低版本</th></tr></thead><tbody><tr><td>video</td><td>v2.4.0</td></tr><tr><td>map</td><td>v2.7.0</td></tr><tr><td>canvas 2d（新接口）</td><td>v2.9.0</td></tr><tr><td>live-player</td><td>v2.9.1</td></tr><tr><td>live-pusher</td><td>v2.9.1</td></tr></tbody></table><p>其他原生组件（textarea、camera、webgl 及 input）也会在近期逐步支持同层渲染。</p><h3 id="小程序实时日志功能更新"><a href="#小程序实时日志功能更新" class="headerlink" title="小程序实时日志功能更新"></a>小程序实时日志功能更新</h3><ol><li>为满足第三方服务商和开发者分析日志的需求，小程序新增实时日志查询接口，开发者可通过实时日志查询接口查询小程序打印的实时日志。详情可查看<a href="https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/operation/operation.realtimelogSearch.html" target="_blank" rel="external">《实时日志查询接口》</a>和<a href="https://developers.weixin.qq.com/miniprogram/dev/extended/log/" target="_blank" rel="external">《实时日志开发文档》</a>。</li><li>每个小程序账号每天可打印的日志条数提升至500万条，日志保存天数提升至7天。</li></ol><h3 id="OCR-能力更新"><a href="#OCR-能力更新" class="headerlink" title="OCR 能力更新"></a>OCR 能力更新</h3><p>OCR 能力方面，有两处更新点。其一，是身份证 OCR 新增性别和民族字段的支持。其二，是身份证和银行卡 OCR去除了 type（拍照、扫描模式）字段，简化统一调用。新调用这两个接口不再需要 type 字段，而之前使用 type 字段的调用仍然继续兼容。详细介绍请参考<a href="https://developers.weixin.qq.com/doc/offiaccount/Intelligent_Interface/OCR.html" target="_blank" rel="external">OCR接口文档</a>。</p><h3 id="更新日志"><a href="#更新日志" class="headerlink" title="更新日志"></a>更新日志</h3><ul><li><a href="https://developers.weixin.qq.com/community/develop/doc/000c8a142ac080094779976b550801" target="_blank" rel="external">社区问题反馈以及功能优化更新（11.04-11.08）</a>  </li><li><a href="https://developers.weixin.qq.com/community/develop/doc/000c4e5f4fc080923569a391356c01" target="_blank" rel="external">社区问题反馈以及功能优化更新（10.21-10.25）</a>  </li><li><a href="https://developers.weixin.qq.com/community/develop/doc/0004c4e9dc408058b659da9bf56801" target="_blank" rel="external">社区问题反馈以及功能优化更新（10.14-10.18）</a>  </li><li><a href="https://developers.weixin.qq.com/community/develop/doc/000608bc8d46d0d7125941cb157801" target="_blank" rel="external">社区问题反馈以及功能优化更新（10.07-10.11）</a>  </li></ul><h2 id="小程序·云开发"><a href="#小程序·云开发" class="headerlink" title="小程序·云开发"></a>小程序·云开发</h2><blockquote><p>云开发峰会: 本次小程序云开发峰会的干货 PPT 已经发布在 <a href="http://km.oa.com/articles/show/429844?ts=1571884911" target="_blank" rel="external">KM</a> 中，大家可以去查看。</p></blockquote><h3 id="小程序·云开发新增高级日志服务"><a href="#小程序·云开发新增高级日志服务" class="headerlink" title="小程序·云开发新增高级日志服务"></a>小程序·云开发新增高级日志服务</h3><p>为方便开发者通过日志快速的发现和定位问题，小程序·云开发提供了高级日志服务。通过高级日志服务，开发者可以更加灵活地采集和检索日志，每条日志可最长存储30天。</p><ul><li>详细介绍请参考: <a href="https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/functions/logservice.html" target="_blank" rel="external">高级日志</a></li></ul><h3 id="云开发代金券"><a href="#云开发代金券" class="headerlink" title="云开发代金券"></a>云开发代金券</h3><p>为了惠及到更多的小程序开发者，云开发增加了代金券申请功能。达到一定 uv 的小程序可以申请专业版（104 元/月）或旗舰版（860 元/月）的代金券，申请成功后每月发放一张，持续12个月。获取的代金券可用于抵扣相应套餐的购买金额。</p><ul><li>功能介绍：<a href="https://developers.weixin.qq.com/miniprogram/dev/wxcloud/billing/voucher.html" target="_blank" rel="external">https://developers.weixin.qq.com/miniprogram/dev/wxcloud/billing/voucher.html</a></li><li>最新开发者工具：<a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/nightly.html" target="_blank" rel="external">https://developers.weixin.qq.com/miniprogram/dev/devtools/nightly.html</a></li></ul><h3 id="云函数优化"><a href="#云函数优化" class="headerlink" title="云函数优化"></a>云函数优化</h3><p>10月11日 - 12日上线了1个优化点：对执行时间小于200ms的云函数请求进行同步化，链路耗时下降了75ms左右</p><h2 id="开发者工具"><a href="#开发者工具" class="headerlink" title="开发者工具"></a>开发者工具</h2><h3 id="微信开发者工具新增文档搜索功能"><a href="#微信开发者工具新增文档搜索功能" class="headerlink" title="微信开发者工具新增文档搜索功能"></a>微信开发者工具新增文档搜索功能</h3><p>微信开发者工具新增文档搜索功能，方便开发者在开发过程中查询相关开发文档。开发者可通过下载最新 Nightly 版本工具体验功能。</p><ul><li>详细介绍请参考: <a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/search.html" target="_blank" rel="external">文档搜索</a></li></ul><h3 id="更多更新说明"><a href="#更多更新说明" class="headerlink" title="更多更新说明"></a>更多更新说明</h3><p>参考<a href="https://developers.weixin.qq.com/community/develop/doc/000aaef24f4ee0b36e59755895b801" target="_blank" rel="external">微信开发者工具稳定版 1.02.1910120 更新说明</a>，这期新增能力包括：</p><ol><li>本地编译时进行合并编译。  </li></ol><ul><li>本地编译时使用合并编译可以加快小程序加载的速度，通过【项目详情 - 本地设置 - 本地编译时进行合并编译】可以打开此功能</li></ul><ol><li>真机调试支持直接触发更新周期性缓存数据。  </li></ol><ul><li>开发者工具【设置 - 通用设置 - 启用 PC 端自动预览】，可以将开发版小程序通过自动预览推送到同登录态的 PC 微信上</li></ul><ol><li>自动真机调试。  </li></ol><ul><li>增加自动真机调试功能，减少真机调试扫码的交互</li></ul><ol><li>使用测试号进行多帐号调试。</li></ol><ul><li>在【菜单 - 工具 - 多帐号调试】，可以打开多帐号调试帐号管理面板</li><li>本次为所有 appid 添加了 4 个测试帐号，可以使用测试号进行多帐号调试</li></ul><ol><li>支持多线程 worker 的单步调试。</li></ol><ul><li>本次更新优化了多线程 worker 的编译和代码加载的方式，从而支持了单步调试</li></ul><ol><li>公众号网页调试增加 url 收藏功能。</li><li>云开发套餐支持代金券支付。<br>等等。</li></ol><h1 id="小程序教程"><a href="#小程序教程" class="headerlink" title="小程序教程"></a>小程序教程</h1><h2 id="社区精选文章"><a href="#社区精选文章" class="headerlink" title="社区精选文章"></a>社区精选文章</h2><ul><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/000200eb844228d72f79291a651c13" target="_blank" rel="external">小程序多平台同构方案分析-kbone 与 remax</a></li><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/000c2279bf8f60f31d79854c85bc13" target="_blank" rel="external">做了一个颜色选择器</a></li><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/0004a20a114a28608669881bc5c013" target="_blank" rel="external">基于Kbone使用React同构小程序开发实践总结</a></li><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/0004686e3c8980b53469f176e51413" target="_blank" rel="external">使用 MobX 来管理小程序的跨页面数据</a></li><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/000e48820100100f2269be0975b813" target="_blank" rel="external">kbone，十分钟让 Vue 项目同时支持小程序</a></li><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/00048e5ed784b037b959757385b413" target="_blank" rel="external">小程序顶部自定义导航组件实现原理及坑分享</a><br>往期内容可以查看<a href="https://developers.weixin.qq.com/community/develop/article" target="_blank" rel="external">文章分享</a>。</li></ul><h2 id="最新踩坑-amp-amp-Tips"><a href="#最新踩坑-amp-amp-Tips" class="headerlink" title="最新踩坑 &amp;&amp; Tips"></a>最新踩坑 &amp;&amp; Tips</h2><h3 id="【同步踩坑信息】-安卓7-0-8内测版本白屏问题"><a href="#【同步踩坑信息】-安卓7-0-8内测版本白屏问题" class="headerlink" title="【同步踩坑信息】- 安卓7.0.8内测版本白屏问题"></a>【同步踩坑信息】- 安卓7.0.8内测版本白屏问题</h3><p>该版本下做了内存优化，在页面不可见时回收渲染资源（也就是会白屏）。如果使用navigateBack了多层页面，这个路径下没有正确恢复第一个页面的渲染，会导致白屏。<br>目前小程序团队已修复该问题，后续遇到相似的问题可以往该方向考虑定位下。</p><h3 id="【同步踩坑信息】-安卓wx-getStorage"><a href="#【同步踩坑信息】-安卓wx-getStorage" class="headerlink" title="【同步踩坑信息】- 安卓wx.getStorage"></a>【同步踩坑信息】- 安卓wx.getStorage</h3><p>7.0.6开始，部分安卓手机，wx.getStorage会有意料外的回调发生。在获取缓存失败的情况下，可能不会回调fail，而是回调了success，且 res.data 是 undefined。</p><h3 id="【同步踩坑信息】-小程序换行会导致多余空格"><a href="#【同步踩坑信息】-小程序换行会导致多余空格" class="headerlink" title="【同步踩坑信息】- 小程序换行会导致多余空格"></a>【同步踩坑信息】- 小程序换行会导致多余空格</h3><p>一般使用编辑器开发，格式化会导致一些换行，在小程序里会导致多余空格，如图：<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-latest-20191015.png" alt=""></p><h3 id="【同步收集信息】-首页逻辑"><a href="#【同步收集信息】-首页逻辑" class="headerlink" title="【同步收集信息】- 首页逻辑"></a>【同步收集信息】- 首页逻辑</h3><p>微信7.0.7版本起，当用户打开的小程序最底层页面是非首页时，默认展示“返回首页”按钮。<br>开发者可在页面 onShow 中调用 hideHomeButton 进行隐藏。</p><ul><li>参考地址: <a href="https://developers.weixin.qq.com/miniprogram/dev/api/ui/navigation-bar/wx.hideHomeButton.html" target="_blank" rel="external">https://developers.weixin.qq.com/miniprogram/dev/api/ui/navigation-bar/wx.hideHomeButton.html</a></li></ul><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><p>最近好像没什么好文好句，推荐几本好书吧~</p><ul><li>《数学之美》</li><li>《算法图解》</li><li>《清醒思考的艺术》</li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;这个月的新内容还不错噢&lt;del&gt;~你值得拥有&lt;/del&gt;&lt;br&gt;
    
    </summary>
    
      <category term="小程序双皮奶" scheme="https://godbasin.github.io/categories/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%8F%8C%E7%9A%AE%E5%A5%B6/"/>
    
    
      <category term="教程" scheme="https://godbasin.github.io/tags/%E6%95%99%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>前端 CHANGELOG 生成指南</title>
    <link href="https://godbasin.github.io/2019/11/10/change-log/"/>
    <id>https://godbasin.github.io/2019/11/10/change-log/</id>
    <published>2019-11-10T02:40:25.000Z</published>
    <updated>2019-11-10T02:41:44.351Z</updated>
    
    <content type="html"><![CDATA[<p>我们在多人协同开发的时候，经常会遇到版本打包发布时，需要手动收集更新了什么内容、修复了什么 BUG，如果日常开发中并没有养成 commit 的好习惯，我们在合入 CHANGELOG 的时候就很容易遗漏特性。本文记录前端常用的自动生成 CHANGELOG 的接入过程。<br><a id="more"></a></p><h1 id="Commit-规范化"><a href="#Commit-规范化" class="headerlink" title="Commit 规范化"></a>Commit 规范化</h1><p>不管是哪种自动生成 CHANGELOG 的工具，基本上都依赖于每次提交 git commit 的信息。从 git commit 信息开始进行规范化，这样就可以通过工具把关键信息找出来，并自动生成到 CHANGELOG 中。</p><h2 id="Angular-规范"><a href="#Angular-规范" class="headerlink" title="Angular 规范"></a>Angular 规范</h2><p>目前，社区有多种 Commit message 的写法规范，这里我们使用的是 <a href="https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#heading=h.greljkmo14y0" target="_blank" rel="external">Angular 规范</a>，这是目前使用最广的写法，比较合理和系统化，并且有配套的工具（参考<a href="https://www.ruanyifeng.com/blog/2016/01/commit_message_change_log.html" target="_blank" rel="external">《Commit message 和 Change log 编写指南》</a>）。</p><p>每次提交，Commit message 都包括三个部分：Header（必须），Body（可省略） 和 Footer（可省略）。</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">&lt;<span class="built_in">type</span>, 必填&gt;(&lt;scope，可省略&gt;): &lt;subject，必填&gt;</div><div class="line">// 空一行</div><div class="line">&lt;body，可省略&gt;</div><div class="line">// 空一行</div><div class="line">&lt;footer，可省略&gt;</div></pre></td></tr></table></figure><ol><li><code>type</code>。<code>type</code>用于说明 commit 的类别，一般来说只允许使用下面7个标识：</li></ol><table><thead><tr><th>标识名</th><th>说明</th><th>是否会出现在 CHANGELOG 中</th></tr></thead><tbody><tr><td>feat</td><td>新功能（feature）</td><td>会</td></tr><tr><td>fix</td><td>修补bug</td><td>会</td></tr><tr><td>docs</td><td>文档（documentation）</td><td>自行决定</td></tr><tr><td>style</td><td>格式（不影响代码运行的变动）</td><td>自行决定</td></tr><tr><td>refactor</td><td>重构（即不是新增功能，也不是修改bug的代码变动）</td><td>自行决定</td></tr><tr><td>test</td><td>增加测试</td><td>自行决定</td></tr><tr><td>chore</td><td>构建过程或辅助工具的变动</td><td>自行决定</td></tr></tbody></table><ol><li><code>scope</code>。<code>scope</code>用于说明 commit 影响的范围，比如某个模块、某个功能。</li><li><code>subject</code>。<code>subject</code>是 commit 目的的简短描述，不超过50个字符。</li><li><code>body</code>。<code>body</code>部分是对本次 commit 的详细描述，可以分成多行。</li><li><code>footer</code>。<code>footer</code>部分只用于两种情况：不兼容变动、关闭 Issue。</li></ol><h2 id="conventional-changelog-方案"><a href="#conventional-changelog-方案" class="headerlink" title="conventional-changelog 方案"></a>conventional-changelog 方案</h2><p>关于自动生成 CHANGELOG，社区中使用较多的则是 <a href="https://github.com/conventional-changelog/conventional-changelog" target="_blank" rel="external">conventional-changelog</a> 方案。</p><h3 id="conventional-changelog-介绍"><a href="#conventional-changelog-介绍" class="headerlink" title="conventional-changelog 介绍"></a>conventional-changelog 介绍</h3><p><a href="https://github.com/conventional-changelog/conventional-changelog" target="_blank" rel="external">conventional-changelog</a> 可以根据项目的 commit 和 metadata 信息自动生成 CHANGELOG 和 release notes的系列工具，并且在辅助 standard-version 工具的情况下，可以自动帮你完成生成version、打tag, 生成CHANGELOG等系列过程。（参考<a href="https://zhuanlan.zhihu.com/p/51894196" target="_blank" rel="external">《git commit 、CHANGELOG 和版本发布的标准自动化》</a>）</p><h4 id="支持-Conventional-Changelog-的插件"><a href="#支持-Conventional-Changelog-的插件" class="headerlink" title="支持 Conventional Changelog 的插件"></a>支持 Conventional Changelog 的插件</h4><ul><li><a href="https://github.com/btford/grunt-conventional-changelog" target="_blank" rel="external">grunt</a></li><li><a href="https://github.com/conventional-changelog/gulp-conventional-changelog" target="_blank" rel="external">gulp</a></li><li><a href="https://github.com/conventional-changelog/atom-conventional-changelog" target="_blank" rel="external">atom</a></li><li><a href="https://github.com/axetroy/vscode-changelog-generator" target="_blank" rel="external">vscode</a></li></ul><h4 id="Conventional-Changelog-生态系统重要的模块"><a href="#Conventional-Changelog-生态系统重要的模块" class="headerlink" title="Conventional Changelog 生态系统重要的模块"></a>Conventional Changelog 生态系统重要的模块</h4><ul><li><a href="https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-cli" target="_blank" rel="external">conventional-changelog-cli</a> - 功能齐全的核心命令行工具</li><li><a href="https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/standard-changelog" target="_blank" rel="external">standard-changelog</a> - 针对 angular commit 格式的命令行工具</li><li><a href="https://github.com/conventional-changelog/conventional-github-releaser" target="_blank" rel="external">conventional-github-releaser</a> - 利用 git metadata 针对 Github 的发布工具</li><li><a href="https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-recommended-bump" target="_blank" rel="external">conventional-recommended-bump</a> - 根据 commit message 判断需要升级哪一位版本号</li><li><a href="https://github.com/conventional-changelog/conventional-commits-detector" target="_blank" rel="external">conventional-commits-detector</a> - commit message 规范引用检测</li><li><a href="https://github.com/commitizen/cz-cli" target="_blank" rel="external">commitizen</a> - 针对开发者简单的 commit 规范</li><li><a href="https://github.com/conventional-changelog/commitlint" target="_blank" rel="external">commitlint</a> - commit Lint 工具</li></ul><p>以上是 conventional-changelog 生态重要的几个主要模块，实际工作中这几个工具常常是配套使用的，我们也是可以根据自己的情况来挑着使用。</p><h3 id="规范-commit-命令行工具–commitizen"><a href="#规范-commit-命令行工具–commitizen" class="headerlink" title="规范 commit 命令行工具–commitizen"></a>规范 commit 命令行工具–commitizen</h3><p>一般来说，我们提供一个脚本工具给到开发者来按照指引生成符合规范的 commit 信息也是够用的，这里我们使用<code>commitizen</code>工具。</p><ol><li>安装<code>commitizen</code>：</li></ol><figure class="highlight cmd"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">npm install -g commitizen</div></pre></td></tr></table></figure><ol><li>通过以下命令来初始化项目以使用<code>cz-conventional-changelog</code>适配器（每个项目和构建过程都有不同的要求，因此commitizen通过适配器的方式，来保持Commitizen的扩展性）：</li></ol><figure class="highlight cmd"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">commitizen init cz-conventional-changelog --save-dev --save-exact</div></pre></td></tr></table></figure><p>该命令做以下三件事情：</p><ul><li>安装<code>cz-conventional-changelog</code>适配器npm模块</li><li>将其保存到<code>package.json</code>的依赖项或<code>devDependencies</code></li><li>将<code>config.commitizen</code>配置添加到<code>package.json</code>的根目录，该配置告诉<code>commitizen</code>，当我们尝试提交此仓库时，我们实际上希望使用哪个适配器</li></ul><ol><li>我们可以通过执行<code>git cz</code>命令，来提交 git commit：</li></ol><figure class="highlight cmd"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">&gt; git cz</div><div class="line"></div><div class="line">cz-cli@<span class="number">4</span>.<span class="number">0</span>.<span class="number">3</span>, cz-conventional-changelog@<span class="number">3</span>.<span class="number">0</span>.<span class="number">2</span></div><div class="line"></div><div class="line">? Select the <span class="built_in">type</span> of change that you're committing: (Use arrow keys)</div><div class="line">&gt; feat:        A new feature</div><div class="line"><span class="function">  fix:         <span class="title">A</span> <span class="title">bug</span> <span class="title">fix</span></span></div><div class="line">  <span class="title">improvement</span>: <span class="title">An</span> <span class="title">improvement</span> <span class="title">to</span> <span class="title">a</span> <span class="title">current</span> <span class="title">feature</span></div><div class="line">  <span class="title">docs</span>:        <span class="title">Documentation</span> <span class="title">only</span> <span class="title">changes</span></div><div class="line">  <span class="title">style</span>:       <span class="title">Changes</span> <span class="title">that</span> <span class="title">do</span> <span class="title">not</span> <span class="title">affect</span> <span class="title">the</span> <span class="title">meaning</span> <span class="title">of</span> <span class="title">the</span> <span class="title">code</span> (<span class="title">white</span>-<span class="title">space</span>, <span class="title">formatting</span>, <span class="title">missing</span> <span class="title">semi</span>-<span class="title">colons</span>, <span class="title">etc</span>)</div><div class="line">  <span class="title">refactor</span>:    <span class="title">A</span> <span class="title">code</span> <span class="title">change</span> <span class="title">that</span> <span class="title">neither</span> <span class="title">fixes</span> <span class="title">a</span> <span class="title">bug</span> <span class="title">nor</span> <span class="title">adds</span> <span class="title">a</span> <span class="title">feature</span></div><div class="line">  <span class="title">perf</span>:        <span class="title">A</span> <span class="title">code</span> <span class="title">change</span> <span class="title">that</span> <span class="title">improves</span> <span class="title">performance</span></div><div class="line">(<span class="title">Move</span> <span class="title">up</span> <span class="title">and</span> <span class="title">down</span> <span class="title">to</span> <span class="title">reveal</span> <span class="title">more</span> <span class="title">choices</span>)</div></pre></td></tr></table></figure><h3 id="强制执行-commit-规范–commitlint-husky"><a href="#强制执行-commit-规范–commitlint-husky" class="headerlink" title="强制执行 commit 规范–commitlint+husky"></a>强制执行 commit 规范–commitlint+husky</h3><p>使用<code>commitizen</code>工具，我们可以通过执行<code>git cz</code>命令来提交符合规范的 commit 信息，但是在日常开发中，很多小伙伴并不是通过命令行的方式来提交 commit 的，如果我们要强制校验其他人通过 vscode/webstorm 等其他工具的方式提交 commit，可以使用<code>commitlint</code>+<code>husky</code>的方式来配合使用。</p><ol><li><code>commitlint</code>检查我们的 commit message 是否符合常规的提交格式，可通过以下方式安装：</li></ol><figure class="highlight cmd"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">npm install --save-dev @commitlint/config-conventional @commitlint/cli</div></pre></td></tr></table></figure><ol><li>在<code>package.json</code>中添加配置（还可以通过<code>commitlint.config.js</code>，<code>.commitlintrc.js</code>，<code>.commitlintrc.json</code>，或<code>.commitlintrc.yml</code>文件等方式配置），此处<code>@commitlint/config-conventional</code>为基于 Angular 格式的配置：</li></ol><figure class="highlight json"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">&#123;  </div><div class="line">  <span class="attr">"commitlint"</span>: &#123;</div><div class="line">    <span class="attr">"extends"</span>: [<span class="string">"@commitlint/config-conventional"</span>]</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><ol><li><code>husky</code>继承了 git 下所有的钩子，在触发钩子的时候，<code>husky</code>可以阻止不合法的 commit、push 等等。安装<code>husky</code>：</li></ol><figure class="highlight cmd"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">npm install husky --save-dev</div></pre></td></tr></table></figure><ol><li>使用<code>husky</code>添加 commit-msg 的钩子，用于检查<code>commitlint</code>规范：</li></ol><figure class="highlight json"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">  <span class="attr">"husky"</span>: &#123;</div><div class="line">    <span class="attr">"hooks"</span>: &#123;</div><div class="line">      <span class="attr">"commit-msg"</span>: <span class="string">"commitlint -E HUSKY_GIT_PARAMS"</span></div><div class="line">    &#125;</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>这样，不管我们通过什么方式来提交 commit，如果 commit 信息不符合我们的规范，都会进行报错。例如我提交内容为<code>test</code>的 commit，会进行以下报错：</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">husky &gt; commit-msg (node v10.<span class="number">16</span>.<span class="number">2</span>)</div><div class="line">⧗   input: test</div><div class="line">✖   subject may <span class="keyword">not</span> be empty [subject-empty]</div><div class="line">✖   <span class="built_in">type</span> may <span class="keyword">not</span> be empty [<span class="built_in">type</span>-empty]</div><div class="line"></div><div class="line">✖   found <span class="number">2</span> problems, <span class="number">0</span> warnings</div><div class="line">ⓘ   Get <span class="built_in">help</span>: https://github.com/conventional-changelog/commitlint/#what-is-commitlint</div></pre></td></tr></table></figure><h3 id="自动生成-CHANGELOG–conventional-changelog-cli"><a href="#自动生成-CHANGELOG–conventional-changelog-cli" class="headerlink" title="自动生成 CHANGELOG–conventional-changelog-cli"></a>自动生成 CHANGELOG–conventional-changelog-cli</h3><p>如果你的所有 Commit 都符合 Angular 格式，那么发布新版本时， CHANGELOG 就可以用脚本自动生成。</p><ol><li>conventional-changelog-cli 就是生成 CHANGELOG 的工具，我们首先来安装一下：</li></ol><figure class="highlight cmd"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">npm install -g conventional-changelog-cli</div></pre></td></tr></table></figure><ol><li>通过执行以下命令，则可以生成 CHANGELOG.md 文件：</li></ol><figure class="highlight cmd"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">conventional-changelog -p angular -i CHANGELOG.<span class="built_in">md</span> -s</div></pre></td></tr></table></figure><p>我们也可以将该命令配置到<code>scripts</code>中，就可以通过执行<code>npm run changelog</code>命令来生成 CHANGELOG 了:</p><figure class="highlight json"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">  <span class="attr">"scripts"</span>: &#123;</div><div class="line">    <span class="attr">"changelog"</span>: <span class="string">"conventional-changelog -p angular -i CHANGELOG.md -s"</span></div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>生成的 CHANGELOG 最终样式如下：</p><figure class="highlight md"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="section"># 1.0.0 (2019-11-06)</span></div><div class="line"></div><div class="line"><span class="section">### Features</span></div><div class="line"></div><div class="line"><span class="bullet">- </span><span class="strong">**自动化:**</span> 新增自动生成 CHANGELOG 相关功能 ([<span class="string">a9ebf7e</span>](<span class="link">https://github.com/godbasin/wxapp-typescript-demo/commit/a9ebf7ee0ca53a4906ed77106b65f6d6bef92f9b</span>))</div></pre></td></tr></table></figure><p>最终的代码可参考<a href="https://github.com/godbasin/wxapp-typescript-demo" target="_blank" rel="external">wxapp-typescript-demo</a>。</p><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><ul><li><a href="https://www.ruanyifeng.com/blog/2016/01/commit_message_change_log.html" target="_blank" rel="external">《Commit message 和 Change log 编写指南》</a></li><li><a href="https://zhuanlan.zhihu.com/p/51894196" target="_blank" rel="external">《git commit 、CHANGELOG 和版本发布的标准自动化》</a></li></ul><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><hr><p>自动生成 CHANGELOG 其实是一个很好用的功能，同时其实前端自动化还会包括 CI/CD、自动化测试等功能。将一些重复性的工作进行脚本化和工具化，不正是我们程序员最擅长做的一些事情吗？</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;我们在多人协同开发的时候，经常会遇到版本打包发布时，需要手动收集更新了什么内容、修复了什么 BUG，如果日常开发中并没有养成 commit 的好习惯，我们在合入 CHANGELOG 的时候就很容易遗漏特性。本文记录前端常用的自动生成 CHANGELOG 的接入过程。&lt;br&gt;
    
    </summary>
    
      <category term="自动化甜筒" scheme="https://godbasin.github.io/categories/%E8%87%AA%E5%8A%A8%E5%8C%96%E7%94%9C%E7%AD%92/"/>
    
    
      <category term="教程" scheme="https://godbasin.github.io/tags/%E6%95%99%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>小程序奇技淫巧之globalDataBehavior管理全局状态</title>
    <link href="https://godbasin.github.io/2019/11/09/wxapp-global-data-behavior/"/>
    <id>https://godbasin.github.io/2019/11/09/wxapp-global-data-behavior/</id>
    <published>2019-11-09T11:53:59.000Z</published>
    <updated>2019-12-14T05:01:32.124Z</updated>
    
    <content type="html"><![CDATA[<p>全局状态管理在小程序里也算是一道难题了，有些小伙伴会选择引入一些类 Store 的库来管理全局状态。今天来给大家分享一个，使用 Behavior 来管理全局状态的小技巧。<br><a id="more"></a></p><h2 id="Behaviors"><a href="#Behaviors" class="headerlink" title="Behaviors"></a>Behaviors</h2><hr><p>自定义组件中，提供了<a href="https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/behaviors.html" target="_blank" rel="external"><code>behaviors</code>的使用和定义</a>。</p><p>从官方文档我们能看到：</p><blockquote><p><code>behaviors</code>是用于组件间代码共享的特性，类似于一些编程语言中的“mixins”或“traits”。<br>每个<code>behavior</code>可以包含一组属性、数据、生命周期函数和方法，组件引用它时，它的属性、数据和方法会被合并到组件中，生命周期函数也会在对应时机被调用。每个组件可以引用多个<code>behavior</code>。</p></blockquote><p>简单来说，我们能通过<code>behaviors</code>来重构<code>Component</code>的能力。Behavior的用处很多，前面也有介绍 <a href="https://godbasin.github.io/2018/12/23/wxapp-computed/">computed 计算属性</a>、<a href="https://godbasin.github.io/2018/12/26/wxapp-watch/">watch 观察属性</a>的实现，都是使用的 Behavior。</p><h3 id="全局状态管理"><a href="#全局状态管理" class="headerlink" title="全局状态管理"></a>全局状态管理</h3><p>我们希望全局共享一些数据状态，如果只是通过一个文件的方式进行维护，那么我们无法在状态更新的时候及时地同步到页面。我们需要额外调用 setData 才能更新页面中的 data 数据，才能告诉渲染层这块的数据渲染需要变更，而很多的 Store 状态管理库也是通过这样的方式实现的（事件通知 + setData + 全局状态）。</p><p>在小程序 Behavior 能力的支持下，我们可以通过一个全局的 globalData Behavior 注入到每个需要用到的 Component 中，这样就可以在需要的页面中直接引入该 Behavior，就能获取到了。不啰嗦，Behavior的实现如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// globalDataStore 用来全局记录 globalData，为了跨页面同步 globalData 用</span></div><div class="line"><span class="keyword">export</span> <span class="keyword">let</span> globalDataStore = &#123;&#125;;</div><div class="line"><span class="comment">// 获取本地的 gloabalData 缓存</span></div><div class="line"><span class="keyword">try</span> &#123;</div><div class="line">  <span class="keyword">const</span> gloabalData = wx.getStorageSync(<span class="string">"gloabalData"</span>);</div><div class="line">  <span class="comment">// 有缓存的时候加上</span></div><div class="line">  <span class="keyword">if</span> (gloabalData) &#123;</div><div class="line">    globalDataStore = &#123; ...gloabalData &#125;;</div><div class="line">  &#125;</div><div class="line">&#125; <span class="keyword">catch</span> (error) &#123;</div><div class="line">  <span class="built_in">console</span>.error(<span class="string">"gloabalData getStorageSync error"</span>, <span class="string">"e ="</span>, error);</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// globalCount 用来全局记录 setGlobalData 的调用次数，为了在 B 页面回到 A 页面的时候，</span></div><div class="line"><span class="comment">// 检查页面 __setGlobalDataCount 和 globalCount 是否一致来判断在 B 页面是否有 setGlobalData,</span></div><div class="line"><span class="comment">// 以此来同步 globalData</span></div><div class="line"><span class="keyword">let</span> globalCount = <span class="number">0</span>;</div><div class="line"></div><div class="line"><span class="keyword">export</span> <span class="keyword">default</span> Behavior(&#123;</div><div class="line">  <span class="attr">data</span>: &#123;</div><div class="line">    <span class="attr">globalData</span>: <span class="built_in">Object</span>.assign(&#123;&#125;, globalDataStore)</div><div class="line">  &#125;,</div><div class="line">  <span class="attr">lifetimes</span>: &#123;</div><div class="line">    attached() &#123;</div><div class="line">      <span class="comment">// 页面 onLoad 的时候同步一下 globalCount</span></div><div class="line">      <span class="keyword">this</span>.__setGlobalDataCount = globalCount;</div><div class="line">      <span class="comment">// 同步 globalDataStore 的内容</span></div><div class="line">      <span class="keyword">this</span>.setData(&#123;</div><div class="line">        <span class="attr">globalData</span>: <span class="built_in">Object</span>.assign(</div><div class="line">          &#123;&#125;,</div><div class="line">          <span class="keyword">this</span>.data.globalData || &#123;&#125;,</div><div class="line">          globalDataStore</div><div class="line">        )</div><div class="line">      &#125;);</div><div class="line">    &#125;</div><div class="line">  &#125;,</div><div class="line">  <span class="attr">pageLifetimes</span>: &#123;</div><div class="line">    show() &#123;</div><div class="line">      <span class="comment">// 为了在 B 页面回到 A 页面的时候，检查页面 __setGlobalDataCount 和 globalCount 是否一致来判断在 B 页面是否有 setGlobalData</span></div><div class="line">      <span class="keyword">if</span> (<span class="keyword">this</span>.__setGlobalDataCount != globalCount) &#123;</div><div class="line">        <span class="comment">// 同步 globalData</span></div><div class="line">        <span class="keyword">this</span>.__setGlobalDataCount = globalCount;</div><div class="line">        <span class="keyword">this</span>.setGlobalData(<span class="built_in">Object</span>.assign(&#123;&#125;, globalDataStore));</div><div class="line">      &#125;</div><div class="line">    &#125;</div><div class="line">  &#125;,</div><div class="line">  <span class="attr">methods</span>: &#123;</div><div class="line">    <span class="comment">// setGlobalData 实现，主要内容为将 globalDataStore 的内容设置进页面的 data 的 globalData 属性中。</span></div><div class="line">    setGlobalData(obj: any) &#123;</div><div class="line">      globalCount = globalCount + <span class="number">1</span>;</div><div class="line">      <span class="keyword">this</span>.__setGlobalDataCount = <span class="keyword">this</span>.__setGlobalDataCount + <span class="number">1</span>;</div><div class="line">      obj = obj || &#123;&#125;;</div><div class="line">      <span class="keyword">let</span> outObj = <span class="built_in">Object</span>.keys(obj).reduce(<span class="function">(<span class="params">sum, key</span>) =&gt;</span> &#123;</div><div class="line">        <span class="keyword">let</span> _key = <span class="string">"globalData."</span> + key;</div><div class="line">        sum[_key] = obj[key];</div><div class="line">        <span class="keyword">return</span> sum;</div><div class="line">      &#125;, &#123;&#125;);</div><div class="line">      <span class="keyword">this</span>.setData(outObj, () =&gt; &#123;</div><div class="line">        globalDataStore = <span class="keyword">this</span>.data.globalData;</div><div class="line">      &#125;);</div><div class="line">    &#125;,</div><div class="line">    <span class="comment">// setGlobalDataAndStorage 实现，先调用 setGlobalData，然后存到 storage 里</span></div><div class="line">    setGlobalDataAndStorage(obj: any) &#123;</div><div class="line">      <span class="keyword">this</span>.setGlobalData(obj);</div><div class="line">      <span class="keyword">try</span> &#123;</div><div class="line">        <span class="keyword">let</span> gloabalData = wx.getStorageSync(<span class="string">"gloabalData"</span>);</div><div class="line">        <span class="comment">// 有缓存的时候加上</span></div><div class="line">        <span class="keyword">if</span> (gloabalData) &#123;</div><div class="line">          gloabalData = &#123; ...gloabalData, ...obj &#125;;</div><div class="line">        &#125; <span class="keyword">else</span> &#123;</div><div class="line">          gloabalData = &#123; ...obj &#125;;</div><div class="line">        &#125;</div><div class="line">        wx.setStorageSync(<span class="string">"gloabalData"</span>, gloabalData);</div><div class="line">      &#125; <span class="keyword">catch</span> (e) &#123;</div><div class="line">        <span class="built_in">console</span>.error(<span class="string">"gloabalData setStorageSync error"</span>, <span class="string">"e ="</span>, e);</div><div class="line">      &#125;</div><div class="line">    &#125;</div><div class="line">  &#125;</div><div class="line">&#125;);</div></pre></td></tr></table></figure><p>显然，该 Behavior 主要提供了几个能力：  </p><ul><li>会在小程序 data 添加 globalData 的属性，在 WXML 文件中可以直接通过<code></code>获取到</li><li>提供<code>setGlobalData()</code>方法，用于更新全局状态</li><li>提供<code>setGlobalDataAndStorage()</code>方法，用于更新全局状态，同时写入缓存（会在下次启动应用的时候自动获取缓存数据）</li></ul><p>这样，我们在初始化 Component 的时候直接引入就可以使用：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">Component(&#123;</div><div class="line">  <span class="comment">// 在behaviors中引入globalDataBehavior</span></div><div class="line">  behaviors: [globalDataBehavior],</div><div class="line">  <span class="comment">// 其他选项</span></div><div class="line">  methods: &#123;</div><div class="line">    test() &#123;</div><div class="line">      <span class="comment">// 使用this.setGlobalData可以更新全局的数据状态</span></div><div class="line">      <span class="keyword">this</span>.setGlobalData(&#123; <span class="attr">test</span>: <span class="string">"hello world"</span> &#125;);</div><div class="line">      <span class="comment">// 使用this.setGlobalDataAndStorage可以更新全局的数据状态，并写入缓存</span></div><div class="line">      <span class="comment">// 下次globalDataBehavior会默认从缓存中获取</span></div><div class="line">      <span class="keyword">this</span>.setGlobalDataAndStorage(&#123; <span class="attr">test</span>: <span class="string">"hello world"</span> &#125;);</div><div class="line">    &#125;</div><div class="line">  &#125;</div><div class="line">&#125;);</div></pre></td></tr></table></figure><p>在引入了 globalDataBehavior 之后，我们的 WXML 就可以直接使用了：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">view</span>&gt;</span>&#123;&#123; globalData.test &#125;&#125;<span class="tag">&lt;/<span class="name">view</span>&gt;</span></div></pre></td></tr></table></figure><h3 id="页面如何使用-Behavior"><a href="#页面如何使用-Behavior" class="headerlink" title="页面如何使用 Behavior"></a>页面如何使用 Behavior</h3><p><code>Component</code>是<code>Page</code>的超集，因此可以使用<code>Component</code>构造器构造页面。</p><p>看看<a href="https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/component.html" target="_blank" rel="external">官方文档</a>：事实上，小程序的页面也可以视为自定义组件。因而，页面也可以使用<code>Component</code>构造器构造，拥有与普通组件一样的定义段与实例方法。但此时要求对应<code>json</code>文件中包含<code>usingComponents</code>定义段。</p><p>更详细的使用方法，在 <a href="https://godbasin.github.io/2018/12/23/wxapp-computed/">computed 计算属性</a>、<a href="https://godbasin.github.io/2018/12/26/wxapp-watch/">watch 观察属性</a>两篇文章中也有描述，大家可以自行参考。</p><p>或者直接查看最终的项目代码：<a href="https://github.com/godbasin/wxapp-typescript-demo" target="_blank" rel="external">wxapp-typescript-demo</a>。</p><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><ul><li><a href="https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/component.html" target="_blank" rel="external">Component构造器</a></li><li><a href="https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/behaviors.html" target="_blank" rel="external">behaviors</a></li></ul><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><hr><p>Behavior 其实是很强大的一个能力，我们能用它来对自己的小程序做很多的能力拓展，缺啥补啥，还可以“混入”给每个 Component 每个方法打入日志，就不用每个组件自己手动打印代码拉。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;全局状态管理在小程序里也算是一道难题了，有些小伙伴会选择引入一些类 Store 的库来管理全局状态。今天来给大家分享一个，使用 Behavior 来管理全局状态的小技巧。&lt;br&gt;
    
    </summary>
    
      <category term="小程序双皮奶" scheme="https://godbasin.github.io/categories/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%8F%8C%E7%9A%AE%E5%A5%B6/"/>
    
    
      <category term="教程" scheme="https://godbasin.github.io/tags/%E6%95%99%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>小程序开发月刊第十期（20191015）</title>
    <link href="https://godbasin.github.io/2019/10/15/wxapp-latest-20191015/"/>
    <id>https://godbasin.github.io/2019/10/15/wxapp-latest-20191015/</id>
    <published>2019-10-15T15:05:07.000Z</published>
    <updated>2019-10-15T15:05:29.979Z</updated>
    
    <content type="html"><![CDATA[<p>国庆期间新功能比较少，但月刊也不能少~~<br><a id="more"></a></p><h1 id="小程序-latest"><a href="#小程序-latest" class="headerlink" title="小程序 latest"></a>小程序 latest</h1><h2 id="小程序能力"><a href="#小程序能力" class="headerlink" title="小程序能力"></a>小程序能力</h2><h3 id="「微信官方文档」支持在线交互式预览"><a href="#「微信官方文档」支持在线交互式预览" class="headerlink" title="「微信官方文档」支持在线交互式预览"></a>「微信官方文档」支持在线交互式预览</h3><p>微信官方文档部分组件“示例代码”已从“静态代码+静态贴图预览”模式升级为“代码编辑器+交互式预览”，实现阅读示例代码时可在浏览器端同步体验示例效果。目前该功能已覆盖大部分小程序<a href="https://developers.weixin.qq.com/miniprogram/dev/component/button.html" target="_blank" rel="external">表单组件</a>。</p><h3 id="小程序模板消息能力调整通知"><a href="#小程序模板消息能力调整通知" class="headerlink" title="小程序模板消息能力调整通知"></a>小程序模板消息能力调整通知</h3><p>为提升小程序模板消息能力的使用体验，平台对模板消息的下发条件进行了调整，由用户自主订阅所需消息，包括：一次性订阅消息、长期性订阅消息。<br>小程序订阅消息接口上线后，原先的模板消息接口将停止使用，详情如下：</p><ol><li>开发者可登录小程序管理后台开启订阅消息功能，接口开发可参考文档：<a href="https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/subscribe-message.html" target="_blank" rel="external">小程序订阅消息</a></li><li>开发者使用订阅消息能力时，需遵循运营规范，不可用奖励或其它形式强制用户订阅，不可下发与用户预期不符或违反国家法律法规的内容。具体可参考文档：<a href="https://developers.weixin.qq.com/miniprogram/product/#_5-21-%E6%BB%A5%E7%94%A8%E8%AE%A2%E9%98%85%E6%B6%88%E6%81%AF" target="_blank" rel="external">小程序订阅消息接口运营规范</a></li><li>原有的小程序模板消息接口将于 2020 年 1 月 10 日下线，届时将无法使用此接口发送模板消息，请各位开发者注意及时调整接口。 </li></ol><ul><li><a href="https://developers.weixin.qq.com/community/develop/doc/00008a8a7d8310b6bf4975b635a401" target="_blank" rel="external">参考链接</a></li><li><a href="https://developers.weixin.qq.com/community/develop/doc/00008a8a7d8310b6bf4975b635a401" target="_blank" rel="external">帖子详情查看</a></li></ul><blockquote><p>微信7.0.7版本起，当用户打开的小程序最底层页面是非首页时，默认展示“返回首页”按钮，开发者可在页面 onShow 中调用 hideHomeButton 进行隐藏。</p></blockquote><h3 id="更新日志"><a href="#更新日志" class="headerlink" title="更新日志"></a>更新日志</h3><ul><li><a href="https://developers.weixin.qq.com/community/develop/doc/00004843288058ed4039d223951401" target="_blank" rel="external">关于微信小程序内容安全要求规范</a>  </li><li><a href="https://developers.weixin.qq.com/community/develop/doc/000a4881f7c5f0ee9049654bf5b001" target="_blank" rel="external">社区问题反馈以及功能优化更新（09.23-09.27）</a>  </li><li><a href="https://developers.weixin.qq.com/community/develop/doc/000a023c8a05888f68395602e51c01" target="_blank" rel="external">社区问题反馈以及功能优化更新（09.16-09.20）</a>  </li><li><a href="https://developers.weixin.qq.com/community/develop/doc/00026861b98900d2ef29d0d2956c01" target="_blank" rel="external">社区问题反馈以及功能优化更新（09.02-09.13）</a>  </li></ul><h2 id="小程序·云开发"><a href="#小程序·云开发" class="headerlink" title="小程序·云开发"></a>小程序·云开发</h2><p>没啥新能力上线。</p><h2 id="开发者工具"><a href="#开发者工具" class="headerlink" title="开发者工具"></a>开发者工具</h2><p>没啥新能力推出。</p><h3 id="更多更新说明"><a href="#更多更新说明" class="headerlink" title="更多更新说明"></a>更多更新说明</h3><p>参考<a href="https://developers.weixin.qq.com/community/develop/doc/000aa65c3d4cb852b349826b751401" target="_blank" rel="external">微信开发者工具 1.02.1910121 RC 更新说明</a>，这期新增能力包括：</p><ol><li>优化再次打开项目时的首次编译速度。  </li><li>真机调试支持直接触发更新周期性缓存数据。  </li><li>默认打开 GPU 加速。  </li><li>修复文件保存后编译不生效的问题。  </li><li>修复全新安装时无法打开的问题。</li><li>修复未使用体验评分导致内存泄漏。</li><li>只有未授权时直接调用 getUserInfo 才会出现升级提示。  </li></ol><h1 id="小程序教程"><a href="#小程序教程" class="headerlink" title="小程序教程"></a>小程序教程</h1><h2 id="社区精选文章"><a href="#社区精选文章" class="headerlink" title="社区精选文章"></a>社区精选文章</h2><p>没有新的好文章，往期内容可以查看<a href="https://developers.weixin.qq.com/community/develop/article" target="_blank" rel="external">文章分享</a>。</p><h2 id="最新踩坑-amp-amp-Tips"><a href="#最新踩坑-amp-amp-Tips" class="headerlink" title="最新踩坑 &amp;&amp; Tips"></a>最新踩坑 &amp;&amp; Tips</h2><h3 id="【开发Tips】-input兼容性"><a href="#【开发Tips】-input兼容性" class="headerlink" title="【开发Tips】-input兼容性"></a>【开发Tips】-input兼容性</h3><p>背景：<code>&lt;input&gt;</code>number类型 需要显示344（如123 4567 8910）这样的手机格式。<br>ios可以通过在js中加入空格来显示，但是在安卓下。空格不显示，被过滤了。</p><h3 id="【踩坑信息】-chooseImage完showLoading失效"><a href="#【踩坑信息】-chooseImage完showLoading失效" class="headerlink" title="【踩坑信息】- chooseImage完showLoading失效"></a>【踩坑信息】- chooseImage完showLoading失效</h3><p>IOS 微信 7.0.7 版本 chooseImage 完 showLoading 失效，客户端下个大版本修复。</p><ul><li><a href="https://developers.weixin.qq.com/community/develop/doc/000a6c36870868acd329d975e51800?highLine=chooseImage%2520showLoading" target="_blank" rel="external">参考地址</a></li></ul><h3 id="【收集信息】-云函数"><a href="#【收集信息】-云函数" class="headerlink" title="【收集信息】- 云函数"></a>【收集信息】- 云函数</h3><p>云函数会以日志形式保存请求，响应内容，其中请求日志是内部日志，响应日志是外部日志，就是在控制台中看到的云函数日志。<br>在数据流请求中，为了有效的帮助客户解答一些问题，所以目前会记记录请求的入参和精简后的出参：</p><ol><li>日志有权限控制，日志没加白名单是不能访问的。</li><li>日志存放时间7天。<br>响应回包在日志中会做个精简的记录, 会记录外层的key和value。value会记录几十个字符。</li></ol><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><p>国庆期间没啥新能力上线，大家来看看我家猫好了。<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/kitty/kitty-1.jpg" alt="image"><br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/kitty/kitty-2.jpg" alt="image"><br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/kitty/kitty-3.jpg" alt="image"><br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/kitty/kitty-4.jpg" alt="image"><br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/kitty/kitty-5.jpg" alt="image"><br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/kitty/kitty-6.jpg" alt="image"><br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/kitty/kitty-7.jpg" alt="image"></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;国庆期间新功能比较少，但月刊也不能少~~&lt;br&gt;
    
    </summary>
    
      <category term="小程序双皮奶" scheme="https://godbasin.github.io/categories/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%8F%8C%E7%9A%AE%E5%A5%B6/"/>
    
    
      <category term="教程" scheme="https://godbasin.github.io/tags/%E6%95%99%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>前端这几年--3.关于成长和焦虑</title>
    <link href="https://godbasin.github.io/2019/10/13/about-front-end-3-growth/"/>
    <id>https://godbasin.github.io/2019/10/13/about-front-end-3-growth/</id>
    <published>2019-10-13T13:19:40.000Z</published>
    <updated>2019-10-13T13:26:07.504Z</updated>
    
    <content type="html"><![CDATA[<p>身为技术人，当然很关注自身的成长，我们都想学到更多的技能。但当环顾周围很多开发，都觉得深陷繁忙状态，但焦虑却只增不减。</p><a id="more"></a><h1 id="什么是成长"><a href="#什么是成长" class="headerlink" title="什么是成长"></a>什么是成长</h1><p>工作几年之后，许多人会陷入一种困境，感觉自己一直在做重复枯燥的工作，而常常这样没有技术含量的工作已经侵蚀到正常生活中。每天都很忙，但不知道在忙什么。偶尔可以早下班，也只是想躺在沙发上看会电视，或者打一会游戏调整状态。</p><p>久而久之，某一天忽然发现自己甚至没有刚毕业的年轻人热情积极，而身体也不经熬了，惊出一身冷汗。</p><p>成长是一件长期坚持的事情，所以当我们懈怠的时候也不会立即感受到缺失了什么，但当我们发现自己已经不进则退的时候，常常也焦虑得无法正常进行后面的规划。</p><h2 id="关于成长的误区"><a href="#关于成长的误区" class="headerlink" title="关于成长的误区"></a>关于成长的误区</h2><p>前段时间关于 996 的讨论很激烈，尤其在程序员这样的行业，我们常常无法避开。</p><p>我们都知道，什么情况下加班是需要且可接受的，什么情况下是不可取的。但是反观我们身边，为什么加班的人那么多，产能缺往往升不上去呢？当然，除去一部分并不真正在工作的“加班”，更多的情况是忙，真的很忙。</p><p>每天都很多事情做，源源不断。乍一看似乎都很充实，但过去一段日子之后，却发现除了功能开发出来以外没有任何的收获。</p><p>因为，我们常常会将一些概念搅浑，例如觉得“忙=付出”，从而认为“付出=收获”。这是很简单的道理，但作为当局者的时候，换成谁都容易陷入误区。</p><p>如果你现在处于“忙不过来”的状态，请先理清楚是否在做有用功，再决定是否要继续或者调整。</p><h2 id="可持续的成长"><a href="#可持续的成长" class="headerlink" title="可持续的成长"></a>可持续的成长</h2><p>业界有个被默认的现象，大家都认为35岁以后的程序员会进入中年危机和职业危机，当然也有不少这样的案例被暴露在大家眼里。</p><p>其实不只是程序员，优胜劣汰的事情发生在生活、工作、社会等各个场景下。要讨论这件事，我们首先要拎清楚一件事：对于你自身来说，优势在哪里，哪些是你的王牌。</p><p>我们能看到，如今很多的程序员都在“拼”。当然很多情况下，付出和收获是在稍微公平的范围。但是这里的收获更多是外在的，包括职级、金钱、个人影响力等等。我们很拼命，用自己的时间和身体健康换取到这些身外物。在这种情况下，我们将自身的竞争力与自身的时间、拼命程度划上了等号。</p><p>但有意思的是，对于一个人来说，时间是越消耗越少，身体也是在过度使用情况下会越虚弱。而我们收获的东西，金钱、权力这些，是无法用来补充我们的竞争力的。简单说，就是钱和权换不回时间和健康。</p><p>那么问题来了，我们的竞争力会越来越弱。结论是，用时间和拼命程度作为竞争力的话，最终几乎都会被淘汰。</p><p>所以，回到前面讲的，在你经营你的职业生涯时，请一定要找到自身的优势和竞争力在哪。如果你擅长沟通、与别人打交道，那你可以往这方向去成长，即使在这种情况下你付出了时间（这里非常不建议大家将身体健康作为筹码），但你的优势在不断扩大，而总有一天会对你自身的劣势（时间、身体状况）进行反哺。</p><p>称之为可持续的成长。</p><h2 id="千万不要急"><a href="#千万不要急" class="headerlink" title="千万不要急"></a>千万不要急</h2><p>现在的社会氛围会有些急躁，外在条件常常更加吸引人，因为美貌、金钱、职级权力这些都是所见即所得的。而内在的精神世界，只有自己能看到。</p><p>一个人的精神力量，是需要大量的思考、反省、自我挑战来支撑的。但如今一看，懂得沉淀不焦虑、有明确的方向并坚持的职业者，比当初想象还要少。</p><h3 id="再懒也不能懒思考"><a href="#再懒也不能懒思考" class="headerlink" title="再懒也不能懒思考"></a>再懒也不能懒思考</h3><p>有人跟我说，说好羡慕我有时间研究技术、写文章。其实我开始写博客的时候，几乎是我最忙的时候，那会几乎每隔几天就一个通宵发版。但越是忙的时候，反而越要去思考怎么改变现状，越要去想想怎么提升自己。</p><p>我换了好多份工作了，感受是工作永远不会给你提供成长的机会的，你要去从工作里挖掘到这样机会。</p><p>很多情况下，我们之所以会陷入“忙得没有喘息的时间”的困境，常常是因为懒得思考。每天被工作淹没，我们很容易就会因为疲惫而给了自己不去思考的借口。正如越是忙的时候越要挤时间去锻炼身体一样，越是缺乏自由的时间，就越是要去思考怎么进行调整。</p><p>你要花时间去看清，看清你当前付出代价所收获的，到底是不是你想要的。你还要花时间去想清楚，想清楚情况可能会持续多久，大概需要花费多少的代价，如果发现不可接受需要及时调整来止损。</p><p>我也看到过有的小伙伴已经“忙碌”到麻木，每天加班到一两点、两三点，但是工作效果却差强人意。他们一直处于焦虑中，总是用压力太大、没有退路、做不好就完了这样的方式来强迫自己继续。我也曾问过他们有些很浅显的道理为什么会看不到（或是不愿意去看），回答是太忙了没时间想。</p><p>如果你在哪个时刻突然发现自己有“太忙了没时间想这个”这样的想法，希望你要警惕起来，看看自己是否陷入了当局者的状况。花点时间梳理下目前的情况，找一下能尽快脱离这种困境的方法，因为持续下去身心疲惫的你，调整的可能性更低、成本也会更大。</p><h3 id="请时刻保持清醒"><a href="#请时刻保持清醒" class="headerlink" title="请时刻保持清醒"></a>请时刻保持清醒</h3><p>在我们一生中，每时每刻都在做取舍，所以状态在线很重要。</p><p>职场中也是，我们会遇到很多的困难和挫折，也可能会获得一时的成功和光环。胜不骄，败不馁，时刻知道自己在做什么，要做什么。</p><p>我是一个很直白的人，遇到不对的事情就会指出来，遇到不公的事情就会坐不住。工作后也遇到了不少让人刷新三观的事情，处理的时候也很直接甚至可能让人产生不愉快。而在职场中，攀比、贪便宜、走捷径等各种事情都会存在，因为有人在的地方就会有社会。内心也常常会纠结，因为身边会有人通过捷径爬到你上方，你是否要放弃自己一直以来坚持的，去获得这样的位置呢？</p><p>其实很庆幸，一直以来都能保持一定的清醒度。即使是渴得不行，即使参加盛宴，不管是火冒三丈或者情绪低落，都没有贪杯。虽然有时候会破罐子破摔，但是事情都一直在往好的方向进行。</p><p>我们会在事情不如期的时候，产生困惑和矛盾。“大家都是这样的”、“这是不是我的问题”、“关键时期，以后不会了”这样的念头会不断地冒出来，能保持清醒，理清楚自己真正所想、然后坚持，才是最难的。</p><p>与别人战斗能满足胜负欲、能收获名利，但与自己的战斗才能让你成长。</p><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><p>我们在遇到问题的时候，“看看别人是怎么做的”是最容易又最简单的解决方法。但自我拷问得出的结论，才是真正值得你去相信和使用的答案。</p><blockquote><p>最近博客更新频率稍微降低，因为在研究一些别的事情，在做一些没做过的尝试，挑战一下自己。</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;身为技术人，当然很关注自身的成长，我们都想学到更多的技能。但当环顾周围很多开发，都觉得深陷繁忙状态，但焦虑却只增不减。&lt;/p&gt;
    
    </summary>
    
      <category term="工作这杯茶" scheme="https://godbasin.github.io/categories/%E5%B7%A5%E4%BD%9C%E8%BF%99%E6%9D%AF%E8%8C%B6/"/>
    
    
      <category term="心态" scheme="https://godbasin.github.io/tags/%E5%BF%83%E6%80%81/"/>
    
  </entry>
  
  <entry>
    <title>小程序开发月刊第九期（20190916）</title>
    <link href="https://godbasin.github.io/2019/09/16/wxapp-latest-20190916/"/>
    <id>https://godbasin.github.io/2019/09/16/wxapp-latest-20190916/</id>
    <published>2019-09-16T15:52:31.000Z</published>
    <updated>2019-09-22T12:24:09.355Z</updated>
    
    <content type="html"><![CDATA[<p>天气转凉好个秋~~<br><a id="more"></a></p><h1 id="小程序-latest"><a href="#小程序-latest" class="headerlink" title="小程序 latest"></a>小程序 latest</h1><h2 id="小程序能力"><a href="#小程序能力" class="headerlink" title="小程序能力"></a>小程序能力</h2><h3 id="「微信官方文档」支持移动端搜索"><a href="#「微信官方文档」支持移动端搜索" class="headerlink" title="「微信官方文档」支持移动端搜索"></a>「微信官方文档」支持移动端搜索</h3><p>「微信官方文档」，包括小程序、小游戏、公众号与开放平台官方文档均已支持移动端搜索功能，方便大家随时随地搜索查阅。</p><ul><li><a href="https://developers.weixin.qq.com/community/develop/doc/000ac42c28c718812f092bf1151801" target="_blank" rel="external">帖子详情查看</a></li></ul><h3 id="微信小程序自动化框架-Python-版-–-Minium-公测"><a href="#微信小程序自动化框架-Python-版-–-Minium-公测" class="headerlink" title="微信小程序自动化框架 Python 版 – Minium 公测"></a>微信小程序自动化框架 Python 版 – Minium 公测</h3><p>Minium 是小程序/小游戏自动化测试框架 MiniTest 的一部分，着重帮助开发/测试人员解决小程序的自动化测试难题。Minium 支持 IDE、iOS、Android 三端运行，详细的使用方法可以移步到 文档 查看，最新的微信开发者工具已经支持自动化，欢迎下载体验。</p><ul><li><a href="https://developers.weixin.qq.com/community/develop/doc/000c046e8382102d950940f0953401" target="_blank" rel="external">帖子详情查看</a></li></ul><h3 id="小程序扩展能力更新"><a href="#小程序扩展能力更新" class="headerlink" title="小程序扩展能力更新"></a>小程序扩展能力更新</h3><ul><li><p><strong>MobX 绑定辅助库</strong><br>小程序团队做了一个 MobX 绑定辅助库，可以用来辅助 MobX 构建数据仓库或者数据关联，有需要的话欢迎体验。</p><ul><li><a href="https://github.com/wechat-miniprogram/mobx-miniprogram-bindings" target="_blank" rel="external">查看详情</a></li></ul></li><li><p><strong>WeUI组件库</strong><br>WeUI 组件库目前已支持按需下载需要的组件。</p><ul><li><a href="https://developers.weixin.qq.com/miniprogram/dev/extended/weui/download.html" target="_blank" rel="external">查看详情</a></li></ul></li></ul><blockquote><p>Tips: 下载后的组件，目前会包含一个比较大的 weui 样式文件，大概38K左右</p></blockquote><h3 id="小程序新-Canvas-接口公测"><a href="#小程序新-Canvas-接口公测" class="headerlink" title="小程序新 Canvas 接口公测"></a>小程序新 Canvas 接口公测</h3><p>为了提高 Canvas 组件的性能，我们计划在小程序基础库 v2.9.0 正式开放一套全新的 Canvas 接口。该接口符合 HTML Canvas 2D 的标准，实现上采用 GPU 硬件加速，渲染性能相比于现有的 Canvas 接口有一倍左右的提升。现邀请广大开发者参与 Canvas 接口的公测。</p><ul><li><a href="https://developers.weixin.qq.com/community/develop/doc/00020a02c2c040114d19a398f5b001" target="_blank" rel="external">帖子详情查看</a></li></ul><blockquote><p>公测需使用 iOS v7.0.5 版本，接口用法可参考<a href="https://developers.weixin.qq.com/s/MdYVhXmB7lak" target="_blank" rel="external">该代码片段</a>。安卓用户也可以到<a href="https://weixin.qq.com/" target="_blank" rel="external">官网</a>安装最新版微信体验新的 canvas 接口。（备注：安装完成登录之后需等待3~5分钟）</p></blockquote><h3 id="小程序新增“实时日志”功能"><a href="#小程序新增“实时日志”功能" class="headerlink" title="小程序新增“实时日志”功能"></a>小程序新增“实时日志”功能</h3><p>“实时日志”功能可以便捷地查询最近三日内的小程序日志，帮助开发者排查漏洞、定位问题。“实时日志”提供了三种日志等级，开发者可根据自己的业务场景打印日志，并可在“小程序管理后台-开发-运维中心-实时日志”根据用户的微信号或 OpenID 查询日志信息。</p><ul><li><a href="https://developers.weixin.qq.com/miniprogram/dev/extended/log/" target="_blank" rel="external">查看详情</a></li></ul><h3 id="数据周期更新功能"><a href="#数据周期更新功能" class="headerlink" title="数据周期更新功能"></a>数据周期更新功能</h3><p>为了提升弱网环境下小程序的使用体验，对于用户七天内使用过的小程序，开发者可每隔12个小时将所需数据预先下载到用户手机本地，以支持下次小程序在弱网或者无网络条件下的正常使用。</p><ul><li><a href="https://developers.weixin.qq.com/miniprogram/dev/framework/ability/background-fetch.html" target="_blank" rel="external">参考文档</a></li></ul><h3 id="更新日志"><a href="#更新日志" class="headerlink" title="更新日志"></a>更新日志</h3><ul><li><a href="https://developers.weixin.qq.com/community/develop/doc/0008280ec20ac879c919d932556001" target="_blank" rel="external">社区问题反馈以及功能优化更新（08.26-08.30）</a>  </li><li><a href="https://developers.weixin.qq.com/community/develop/doc/00066c5757894889271996e015b001" target="_blank" rel="external">社区问题反馈以及功能优化更新（8.19-8.23）</a>  </li><li><a href="https://developers.weixin.qq.com/community/develop/doc/000c046e8382102d950940f0953401" target="_blank" rel="external">社区问题反馈以及功能优化更新（8.12-8.16）</a>  </li><li><a href="https://developers.weixin.qq.com/community/develop/doc/000ac42c28c718812f092bf1151801" target="_blank" rel="external">社区问题反馈以及功能优化更新（8.05-8.09）</a>  </li></ul><h2 id="小程序·云开发"><a href="#小程序·云开发" class="headerlink" title="小程序·云开发"></a>小程序·云开发</h2><h3 id="小程序·云开发新增实时数据推送能力"><a href="#小程序·云开发新增实时数据推送能力" class="headerlink" title="小程序·云开发新增实时数据推送能力"></a>小程序·云开发新增实时数据推送能力</h3><p>小程序·云开发新增实时数据推送能力，可以实时监听数据库中的数据变更。该功能有效地解决了即时通信以及实时更新和推送数据的问题，极大地降低在此类场景下的开发成本。<br>基于是实时数据推送能力，在给定查询条件的情况下，每当数据库更新而导致查询条件对应的查询结果发生变更时，小程序可收到一个更新事件，其中可获取更新内容和更新后的查询结果快照。</p><p>实时数据推送有着广泛的应用场景，包括但不限于：</p><ul><li>聊天/即时通信：小游戏内聊天、大厅广播、区服广播等；企业内部小程序中的即时通信能力等</li><li>多人小游戏：使用状态同步的小游戏，如棋牌类等回合制游戏</li><li>协作工具：如在线协作文档、团队任务管理等</li><li><p>实时应用状态同步：以信息流为例，可以实时获取最新文章、以及最新评论、点赞、通知等内容，让交互更顺畅自然</p></li><li><p><a href="https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/database/realtime.html" target="_blank" rel="external">查看详情</a></p></li></ul><h3 id="「小程序·云开发」资源配额调整"><a href="#「小程序·云开发」资源配额调整" class="headerlink" title="「小程序·云开发」资源配额调整"></a>「小程序·云开发」资源配额调整</h3><p>为了让开发者能够更方便的使用小程序·云开发，我们对云开发的部分资源配额进行了调整，具体调整内容包括：</p><ul><li>去除云函数调用次数限制（之前基础版为20万/月，豪华版为5亿/月）</li><li>云函数并发数统一调整为1000（之前基础版为20，豪华版为1000）<br>同时，我们新增了四个新套餐：专业版 plus、专业版 plus、CDN 版 plus、以及旗舰版 plus。</li></ul><h2 id="开发者工具"><a href="#开发者工具" class="headerlink" title="开发者工具"></a>开发者工具</h2><h3 id="本地编译时进行合并编译"><a href="#本地编译时进行合并编译" class="headerlink" title="本地编译时进行合并编译"></a>本地编译时进行合并编译</h3><p>本地编译时使用合并编译可以加快小程序加载的速度，<br>通过【项目详情 - 本地设置 - 本地编译时进行合并编译】可以打开此功能</p><h3 id="PC-微信开发版小程序自动预览"><a href="#PC-微信开发版小程序自动预览" class="headerlink" title="PC 微信开发版小程序自动预览"></a>PC 微信开发版小程序自动预览</h3><p><a href="https://dldir1.qq.com/weixin/Windows/WeChat2.7.0_beta.exe" target="_blank" rel="external">PC 微信内测版</a>支持小程序【开发者工具 设置 - 通用设置 - 启用 PC 端自动预览】，可以将开发版小程序通过自动预览推送到同登录态的 PC 微信上。</p><h3 id="更多新增能力"><a href="#更多新增能力" class="headerlink" title="更多新增能力"></a>更多新增能力</h3><p>参考<a href="https://developers.weixin.qq.com/community/develop/doc/00066696834858a9d91912c755e401" target="_blank" rel="external">微信开发者工具 1.02.1909051 RC 更新说明</a>，这期新增能力包括：</p><ul><li>自动真机调试</li><li>使用测试号进行多帐号调试</li><li>支持多线程 worker 的单步调试</li><li>公众号网页调试增加 url 收藏功能</li><li>云开发套餐支持代金券支付</li><li>新增周期性更新调试</li><li>支持小游戏关系链互动数据开发</li></ul><p><a href="https://developers.weixin.qq.com/community/develop/doc/00048a4a24c248f64d2974eea5bc01" target="_blank" rel="external">微信开发者工具 1.02.1909111 RC 更新说明</a>新增能力包括：</p><ul><li>调试器最大日志行数设置</li><li>通过导入二维码创建自定义编译条件</li><li>公众号网页调试增加缓存全部清除按钮</li><li>修复意外断电后导致代码乱码的问题</li></ul><h1 id="小程序教程"><a href="#小程序教程" class="headerlink" title="小程序教程"></a>小程序教程</h1><h2 id="社区精选文章"><a href="#社区精选文章" class="headerlink" title="社区精选文章"></a>社区精选文章</h2><ul><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/00068aec7941f8f57509794be54413" target="_blank" rel="external">微信小程序实战：微信小程序自定义导航栏全适配开发 (完美适配所有手机)</a></li></ul><p>更多可以查看<a href="https://developers.weixin.qq.com/community/develop/article" target="_blank" rel="external">文章分享</a>。</p><blockquote><p>最近社区的好文有点不够哇</p></blockquote><h2 id="最新踩坑-amp-amp-Tips"><a href="#最新踩坑-amp-amp-Tips" class="headerlink" title="最新踩坑 &amp;&amp; Tips"></a>最新踩坑 &amp;&amp; Tips</h2><h3 id="【开发Tips】-小程序插件最低可用版本设置"><a href="#【开发Tips】-小程序插件最低可用版本设置" class="headerlink" title="【开发Tips】-小程序插件最低可用版本设置"></a>【开发Tips】-小程序插件最低可用版本设置</h3><p>【小程序后台-功能-小程序插件-基本设置-最低可用版本】：“版本号小于所选版本的插件版本将在30天后停止服务”。<br>一旦设置这个选项，设置的时刻新发版的小程序就不再能使用旧版本的插件，而30天后，线上所有低版本的插件都会报错。<br>设置这个选项后，插件调用方这三十天内会多次收到提醒，微信、开发者工具、站内信都会有。</p><h3 id="【踩坑信息】-小程序公众号关联策略"><a href="#【踩坑信息】-小程序公众号关联策略" class="headerlink" title="【踩坑信息】- 小程序公众号关联策略"></a>【踩坑信息】- 小程序公众号关联策略</h3><p>公众号关联小程序策略于4月4日发布<a href="https://developers.weixin.qq.com/community/develop/doc/00008e0564435833bf588fbbb56001" target="_blank" rel="external">策略调整</a>，里面提到小程序需要与公众号关联，才可被使用在公众号自定义菜单、模板消息、客服消息等场景中。<br>【目前情况是，公众号无需关联，便可以在自定义菜单绑定任何小程序。】<br>经确认，菜单改为任何人都可以添加。目前小程序团队收到部分反馈，现在正在改为关联后才能添加，预计下周改造完成。</p><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><p>谁终将声震人间，必长久深自缄默。<br>谁终将点燃闪电，必长久如云漂泊。<br>我的时代还没有到来，有的人死后方生。<br>–尼采</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;天气转凉好个秋~~&lt;br&gt;
    
    </summary>
    
      <category term="小程序双皮奶" scheme="https://godbasin.github.io/categories/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%8F%8C%E7%9A%AE%E5%A5%B6/"/>
    
    
      <category term="教程" scheme="https://godbasin.github.io/tags/%E6%95%99%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>小程序开发月刊第八期（20190815）</title>
    <link href="https://godbasin.github.io/2019/08/15/wxapp-latest-20190815/"/>
    <id>https://godbasin.github.io/2019/08/15/wxapp-latest-20190815/</id>
    <published>2019-08-15T15:56:33.000Z</published>
    <updated>2019-08-18T06:40:02.862Z</updated>
    
    <content type="html"><![CDATA[<p>热闹的八月份来了~~<br><a id="more"></a></p><h1 id="小程序-latest"><a href="#小程序-latest" class="headerlink" title="小程序 latest"></a>小程序 latest</h1><h2 id="小程序能力"><a href="#小程序能力" class="headerlink" title="小程序能力"></a>小程序能力</h2><h3 id="微信-PC-版小程序开发者公测"><a href="#微信-PC-版小程序开发者公测" class="headerlink" title="微信 PC 版小程序开发者公测"></a>微信 PC 版小程序开发者公测</h3><p>微信 PC 版新版本中，支持打开聊天中分享的小程序，开发者可下载安装微信 PC 版内测版本进行体验和适配。最新版微信开发者工具新增支持在微信 PC 版中预览小程序。</p><ul><li><a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/pc-dev.html" target="_blank" rel="external">查看详情</a></li><li><a href="https://dldir1.qq.com/weixin/Windows/WeChat2.7.0_beta.exe" target="_blank" rel="external">微信 PC 版内测版</a></li><li><a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/nightly.html" target="_blank" rel="external">微信开发者工具</a></li></ul><blockquote><p>噢？公测第一天我就踩出一个坑了，可以看看文章底部的最新踩坑 &amp;&amp; Tips。</p></blockquote><h3 id="小程序-vue-多端支持"><a href="#小程序-vue-多端支持" class="headerlink" title="小程序 vue 多端支持"></a>小程序 vue 多端支持</h3><p>小程序团队推出了小程序 Vue 多端（目前支持微信小程序 + web）方案，已经在我们“微信开放社区”的小程序和 web 落地。</p><ul><li><a href="https://github.com/wechat-miniprogram/kbone" target="_blank" rel="external">kbone 文档</a></li></ul><h3 id="小程序扩展能力提供"><a href="#小程序扩展能力提供" class="headerlink" title="小程序扩展能力提供"></a>小程序扩展能力提供</h3><p>官方提供了一些扩展能力（包括扩展UI组件、多端方案kbone、工具库、官方插件等等），希望可以帮大家一起快速搭建小程序。</p><ul><li><a href="https://developers.weixin.qq.com/miniprogram/dev/extended/" target="_blank" rel="external">查看详情</a></li></ul><h3 id="微信卡券“网页链接跳转”能力即将下线"><a href="#微信卡券“网页链接跳转”能力即将下线" class="headerlink" title="微信卡券“网页链接跳转”能力即将下线"></a>微信卡券“网页链接跳转”能力即将下线</h3><p>为了确保微信生态良性发展，微信卡券将于2019年8月13日下线网页链接跳转能力。下线后，卡券创建流程中，“自定义入口”与“卡券详情&gt;立即使用跳转”不再支持跳转至网页配置，你可尝试使用跳转小程序作为替代。本次调整仅针对券，会员卡原有能力暂时不变。</p><ul><li><a href="https://developers.weixin.qq.com/community/develop/doc/0006cef80b05f8b09fe87e70c5fc01" target="_blank" rel="external">帖子详情查看</a></li></ul><h3 id="「微信开放社区」小程序更新、搜索能力优化、支持扫码分享"><a href="#「微信开放社区」小程序更新、搜索能力优化、支持扫码分享" class="headerlink" title="「微信开放社区」小程序更新、搜索能力优化、支持扫码分享"></a>「微信开放社区」小程序更新、搜索能力优化、支持扫码分享</h3><ul><li>小程序更新：社区小程序部分版块已支持完整的阅读、评论与回复功能，欢迎扫码体验。</li><li>搜索能力优化：大幅度优化搜索结果匹配度，并支持对搜索结果的内容类型、排序方式、时间范围进行二次筛选。</li><li>支持扫码分享：社区公告、文章、问答等内容支持在页面底部“复制链接”或在扫码打开小程序进行分享。</li></ul><blockquote><p>这个微信开放社区小程序，是用 kbone 多端支持做的噢~</p></blockquote><h3 id="更新日志"><a href="#更新日志" class="headerlink" title="更新日志"></a>更新日志</h3><ul><li><a href="https://developers.weixin.qq.com/community/develop/doc/000402b383c3303803f8e16145bc01" target="_blank" rel="external">社区问题反馈以及功能优化更新（07.22-07.26）</a>  </li><li><a href="https://developers.weixin.qq.com/community/develop/doc/0008464a59c0f8b468e834c7451c01" target="_blank" rel="external">社区问题反馈以及功能优化更新（07.08-07.19）</a>  </li></ul><h2 id="小程序·云开发"><a href="#小程序·云开发" class="headerlink" title="小程序·云开发"></a>小程序·云开发</h2><h3 id="小程序·云开发支持第三方平台代开发"><a href="#小程序·云开发支持第三方平台代开发" class="headerlink" title="小程序·云开发支持第三方平台代开发"></a>小程序·云开发支持第三方平台代开发</h3><p>小程序·云开发目前已支持第三方平台代开发。第三方平台可在小程序权限集中勾选云开发管理权限，并通知小程序进行云开发权限集授权。授权完成后即可进行代开发，具体开发过程可参考<a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/ext.html" target="_blank" rel="external">第三方平台代开发</a>。</p><p>同时，为方便第三方平台进行代开发，云开发还提供了多种 API 接口，包括开通云开发、创建云环境以及创建云函数等。详见<a href="https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&amp;t=resource/res_list&amp;verify=1&amp;id=21565259008XrkFz&amp;token=&amp;lang=zh_CN" target="_blank" rel="external">云开发接入指南</a>。</p><h2 id="开发者工具"><a href="#开发者工具" class="headerlink" title="开发者工具"></a>开发者工具</h2><h3 id="小程序支持自动化测试"><a href="#小程序支持自动化测试" class="headerlink" title="小程序支持自动化测试"></a>小程序支持自动化测试</h3><p><a href="http://npmjs.org/package/miniprogram-automator" target="_blank" rel="external">小程序自动化 SDK</a> 为开发者提供了一套通过外部脚本操控小程序的方案，从而实现小程序自动化测试的目的。</p><p>如果你之前使用过 <a href="https://www.seleniumhq.org/projects/webdriver/" target="_blank" rel="external">Selenium WebDriver</a> 或者 <a href="https://pptr.dev/" target="_blank" rel="external">Puppeteer</a>，那你可以很容易快速上手。小程序自动化 SDK 与它们的工作原理是类似的，主要区别在于控制对象由浏览器换成了小程序。</p><ul><li>更多详情，点击查看<a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/auto/quick-start.html" target="_blank" rel="external">小程序自动化快速开始</a></li></ul><p>该能力于<a href="https://developers.weixin.qq.com/community/develop/doc/000c085a7244b092e4e8237b451c01" target="_blank" rel="external">开发者工具预发布版 1.02.1907301</a> 支持。</p><blockquote><p>期待测试用例录制功能，特别想要！</p></blockquote><h1 id="小程序教程"><a href="#小程序教程" class="headerlink" title="小程序教程"></a>小程序教程</h1><h2 id="社区精选文章"><a href="#社区精选文章" class="headerlink" title="社区精选文章"></a>社区精选文章</h2><ul><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/0000a8d54acaf0c962e820a1a5e413" target="_blank" rel="external">让小程序页面和自定义组件支持 computed 和 watch 数据监听器</a></li></ul><p>更多可以查看<a href="https://developers.weixin.qq.com/community/develop/article" target="_blank" rel="external">文章分享</a>。</p><blockquote><p>最近社区的好文有点不够哇</p></blockquote><h2 id="最新踩坑-amp-amp-Tips"><a href="#最新踩坑-amp-amp-Tips" class="headerlink" title="最新踩坑 &amp;&amp; Tips"></a>最新踩坑 &amp;&amp; Tips</h2><h3 id="【踩坑信息】-小程序跳小程序闪退"><a href="#【踩坑信息】-小程序跳小程序闪退" class="headerlink" title="【踩坑信息】- 小程序跳小程序闪退"></a>【踩坑信息】- 小程序跳小程序闪退</h3><p>接口：wx.navigateToMiniProgram<br>问题描述：小程序 A 开发版 -&gt; 小程序 B 正式版 -&gt; 小程序 A 正式版（闪退）<br>小程序跳转间需要注意版本：</p><ol><li>开发 - 跳正式 - 跳正式，是行不通的 </li><li>要么正式 - 跳正式 - 跳正式，要么是开发/体验 - 跳开发/体验 - 跳开发/体验</li></ol><ul><li>文档说明：envVersion 如果当前小程序是正式版，则打开的小程序必定是正式版。</li><li>参考：<a href="https://developers.weixin.qq.com/miniprogram/dev/api/open-api/miniprogram-navigate/wx.navigateToMiniProgram.html" target="_blank" rel="external">https://developers.weixin.qq.com/miniprogram/dev/api/open-api/miniprogram-navigate/wx.navigateToMiniProgram.html</a></li></ul><h3 id="【开发Tips】-开发者工具增强编译"><a href="#【开发Tips】-开发者工具增强编译" class="headerlink" title="【开发Tips】- 开发者工具增强编译"></a>【开发Tips】- 开发者工具增强编译</h3><p>开发者工具ES6转ES5，支持Object.keys，Object.entries和Object.values 是ES2017的内容，需要单独引入polyfill。可以使用开发者工具的增强编译</p><ul><li><a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/codecompile.html" target="_blank" rel="external">参考</a></li></ul><h3 id="【官方建设】-weui-wxss-v2-0-版本"><a href="#【官方建设】-weui-wxss-v2-0-版本" class="headerlink" title="【官方建设】- weui-wxss v2.0 版本"></a>【官方建设】- weui-wxss v2.0 版本</h3><p>微信客户端 7.0 开始，UI 界面进行了大改版。小程序也进行了基础组件的样式升级。app.json 中配置 “style”: “v2”可表明启用新版的组件样式。</p><ul><li><a href="https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#style" target="_blank" rel="external">参考</a></li><li>兼容性：对于2.8.0以下版本基础库，会自动降级为旧版本UI，包括button icon radio checkbox switch slider 等</li></ul><h3 id="【踩坑信息】-小程序下拉的背景颜色"><a href="#【踩坑信息】-小程序下拉的背景颜色" class="headerlink" title="【踩坑信息】- 小程序下拉的背景颜色"></a>【踩坑信息】- 小程序下拉的背景颜色</h3><ul><li>背景：小程序下拉的背景颜色，在iOS下分成backgroundColor、backgroundColorTop、backgroundColorBottom三个部分，而在安卓下需要设置backgroundColor。旧版的小程序开发工具中，为了兼容安卓，会把 backgroundColorTop 的值设到 backgroundColor 的配置上。<br>但这种做法和文档不一致（<a href="https://developers.weixin.qq.com/community/develop/doc/0002847bf0c368d623d8f90ba51c00" target="_blank" rel="external">相关反馈查看</a>），所以新版的工具去掉了这个逻辑，可能导致部分背景样式与之前不一致的问题，开发者若依赖了工具的兼容，后续需要自行进行兼容。</li><li>兼容方法：如果需要设置安卓的背景色只能使用 backgroundColor 来设置整个背景色为某个颜色。</li></ul><h3 id="【踩坑信息】-PC-版小程序-cookie"><a href="#【踩坑信息】-PC-版小程序-cookie" class="headerlink" title="【踩坑信息】 - PC 版小程序 cookie"></a>【踩坑信息】 - PC 版小程序 cookie</h3><p>PC 版小程序中，接口请求多个set-cookie会被合并，只有最后一个会生效。可能会导致登录态丢失的问题，小程序团队已修复。</p><h3 id="【开发Tips】-小程序参数长度"><a href="#【开发Tips】-小程序参数长度" class="headerlink" title="【开发Tips】- 小程序参数长度"></a>【开发Tips】- 小程序参数长度</h3><p>关于小程序参数长度限制的说明如下：</p><ol><li>scene 相关。<ul><li>scene 值作为小程序的场景值参数，在小程序生命周期中属于全局变量，一般为 4 位数整型，如：scene=1001。<a href="https://developers.weixin.qq.com/miniprogram/dev/reference/scene-list.html" target="_blank" rel="external">文档参考</a>。</li><li>scene 值会作为 query 参数传递给小程序/小游戏。用户扫描该码进入小程序/小游戏后，开发者可以获取到二维码中的 scene 值并做处理逻辑，一般为32位长度限制； 如：<code>pages/sign/subject/subject?scene=1665290702232</code>。<a href="https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/qr-code/wxacode.getUnlimited.html" target="_blank" rel="external">文档参考</a>。</li></ul></li><li>url后面的query参数目前没有长度限制.</li></ol><ul><li>如：pages/index/index/index?key=28383989394949494….</li><li><a href="https://developers.weixin.qq.com/miniprogram/dev/api/route/wx.reLaunch.html#%E5%8F%82%E6%95%B0" target="_blank" rel="external">文档参考</a></li></ul><h3 id="【踩坑信息】-input"><a href="#【踩坑信息】-input" class="headerlink" title="【踩坑信息】- input"></a>【踩坑信息】- input</h3><p><a href="https://developers.weixin.qq.com/miniprogram/dev/component/input.html" target="_blank" rel="external">input 组件</a>的 bindkeyboardheightchange 方法，在<code>Android &amp;&amp; type !==text</code>的情况下，返回的 height 是错误的。可以用 bindfocus 方法，bindfocus 在 1.9.90 版本后加上了 height 字段，返回了键盘的高度。</p><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><p>如果你想富有，请把自己变成一个小孩。因为小孩子的框框最少，他们诚实、快乐、乐于学习。 –《小狗钱钱》</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;热闹的八月份来了~~&lt;br&gt;
    
    </summary>
    
      <category term="小程序双皮奶" scheme="https://godbasin.github.io/categories/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%8F%8C%E7%9A%AE%E5%A5%B6/"/>
    
    
      <category term="教程" scheme="https://godbasin.github.io/tags/%E6%95%99%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>被删的前端游乐场建成！</title>
    <link href="https://godbasin.github.io/2019/07/27/front-end-playground/"/>
    <id>https://godbasin.github.io/2019/07/27/front-end-playground/</id>
    <published>2019-07-27T12:20:31.000Z</published>
    <updated>2019-07-27T12:45:45.009Z</updated>
    
    <content type="html"><![CDATA[<p>耗费好多周的精力，把以前写的一些博客整理了一遍，然后加上了喜欢的猫猫，此时此刻终于建成了！快来看看吧~<br>（数一下有多少只猫猫哈哈哈哈）</p><a id="more"></a><p>我和阿猪的<a href="https://godbasin.github.io/front-end-playground">前端游乐场</a>成功建成！欢迎来边学前端边撸猫~~</p><h2 id="游乐场内容"><a href="#游乐场内容" class="headerlink" title="游乐场内容"></a>游乐场内容</h2><p>欢迎来玩，门票只需要一份学习的热情~</p><p>将前端分成了几个系列：</p><ul><li>前端基础入门系列</li><li>前端框架和库使用教程（Vue、小程序、其他工具等）系列</li><li>前端深入理解系列</li><li>前端的进击系列（拓展前端技术栈）</li></ul><p>然后配备了<a href="https://godbasin.github.io/front-end-playground/faq.html">FAQ</a>，梳理了大家容易遇到的一些前端发展的疑问，欢迎提更多的问题噢。</p><h2 id="无处不在的猫猫"><a href="#无处不在的猫猫" class="headerlink" title="无处不在的猫猫"></a>无处不在的猫猫</h2><p>带着猫粮存钱罐的牧羊猪，会陪伴你一块学习噢，你可以拖动他到你喜欢的位置。</p><blockquote><p>刷新页面可以切换其他颜色的猫猫<br>嫌它烦你还可以双击让他回家喵T^T</p></blockquote><p>滚动到页面下面，会发现有只躲在星球后的猫猫，点击可以送你上页面顶部噢。</p><p>还有每篇文章的底部，都有随机加载的猫猫图片，温馨又治愈噢。</p><h2 id="欢迎参与编辑"><a href="#欢迎参与编辑" class="headerlink" title="欢迎参与编辑"></a>欢迎参与编辑</h2><p>虽然每篇文章底下的署名都是我，其实主要是为了放那些无处可去的猫猫们。</p><p>虽然整个网站的名字也叫被删的前端游乐场，但也希望有一天它会变成所有前端的游乐场。后面也会努力支持每篇署名的配置，同时也希望可以支持贡献者噢。</p><p>如果你有好的内容调整或者新增，欢迎给我提 Issues 或 Pull Requests。<br>虽然我要求很高，不过我人很好，所以不要紧张。</p><h2 id="Feature"><a href="#Feature" class="headerlink" title="Feature"></a>Feature</h2><p>既然是游乐场，除了到处溜达的猫猫之外，最想要的当然还有游戏啦！</p><p>还想要搭一些和猫和前端相关的小游戏或是动画交互可以玩，最好当然是可以一边撸猫一边学前端啦。</p><p>另外可能后面如果有空，还会录制一些视频吧~因为文章能描述和表达的东西比较局限，代码调试、项目设计、写代码的思考过程，这些其实是很美妙的事情，希望也能把那份快乐给到你们。</p><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><p>其实最近在思考很多事情，像一些只做前端真的就不行？只有全栈才是出路？或许那天会来临，但我依然想做一些尝试：把我理解的前端的有趣的地方、有挑战性的地方、可以深度思考和理解的地方，都能给到你们，直到我知识枯竭的那天为止。</p><p>也有过想去做一些带收益的课程，但是挣扎了很久，还是希望用自己最想要的开源与分享的模式给到你们。如果哪天买不起猫粮了，再去考虑赚钱的事情叭哈哈哈。</p><p>其实也收到了一些小伙伴的赞赏，虽然不多，但是留言都是满满的感动，偶尔给阿猪加的一些罐头他也吃得很香。</p><p>阿猪表示很开心（看他最近胖的）：<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/zhu123.jpg" style="width: 300px;"></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;耗费好多周的精力，把以前写的一些博客整理了一遍，然后加上了喜欢的猫猫，此时此刻终于建成了！快来看看吧~&lt;br&gt;（数一下有多少只猫猫哈哈哈哈）&lt;/p&gt;
    
    </summary>
    
      <category term="喵" scheme="https://godbasin.github.io/categories/%E5%96%B5/"/>
    
    
      <category term="晒猫" scheme="https://godbasin.github.io/tags/%E6%99%92%E7%8C%AB/"/>
    
  </entry>
  
  <entry>
    <title>9102全员学Vue--3.把页面拼成个Web应用</title>
    <link href="https://godbasin.github.io/2019/07/21/vue-for-everyone-3/"/>
    <id>https://godbasin.github.io/2019/07/21/vue-for-everyone-3/</id>
    <published>2019-07-21T06:17:18.000Z</published>
    <updated>2019-07-21T06:27:56.771Z</updated>
    
    <content type="html"><![CDATA[<p>本节内容主要介绍使用 vue-router 来管理路由，包括应用和路由的设计和配置、页面间跳转、路由鉴权等。然后将上一节拼出来的页面，再拼出来一个完整的 Web 应用叭。<br><a id="more"></a></p><h1 id="Vue-应用概念"><a href="#Vue-应用概念" class="headerlink" title="Vue 应用概念"></a>Vue 应用概念</h1><p>我们<a href="https://godbasin.github.io/2019/06/27/vue-for-everyone-1/">第一节内容</a>里，最后用<a href="https://cli.vuejs.org/zh/" target="_blank" rel="external">Vue CLI</a>生成了一个 demo，我们称之为一个 Web 应用。</p><h2 id="单页应用"><a href="#单页应用" class="headerlink" title="单页应用"></a>单页应用</h2><p>单页应用这个概念，是随着前几年 AngularJS、React、Ember 等这些框架的出现而出现的。<a href="https://godbasin.github.io/2019/06/27/vue-for-everyone-1/">第一节内容</a>里，我们在页面渲染中讲了页面的局部刷新，而单页应用则是使用了页面的局部刷新的能力，在切换页面的时候刷新页面内容，从而获取更好的体验。</p><h3 id="SPA-与-MPA"><a href="#SPA-与-MPA" class="headerlink" title="SPA 与 MPA"></a>SPA 与 MPA</h3><p>单页应用（SinglePage Web Application，SPA）和多页应用（MultiPage Application，MPA）的区别可以参考：</p><table><thead><tr><th>-</th><th>单页面应用</th><th>多页面应用</th></tr></thead><tbody><tr><td>组成</td><td>一个外壳页面和多个页面片段组成</td><td>多个完整页面构成</td></tr><tr><td>资源共用(css,js)</td><td>共用，只需在外壳部分加载</td><td>不共用，每个页面都需要加载</td></tr><tr><td>刷新方式</td><td>页面局部刷新或更改</td><td>整页刷新</td></tr><tr><td>url 模式</td><td>a.com/#/pageone<br>a.com/#/pagetwo</td><td>a.com/pageone.html<br>a.com/pagetwo.html</td></tr><tr><td>用户体验</td><td>页面片段间的切换快，用户体验良好</td><td>页面切换加载缓慢，流畅度不够，用户体验比较差</td></tr><tr><td>页面跳转动画</td><td>容易实现</td><td>无法实现</td></tr><tr><td>数据传递</td><td>容易</td><td>依赖 url 传参、或者 cookie 、localStorage 等</td></tr><tr><td>搜索引擎优化(SEO)</td><td>需要单独方案、实现较为困难、不利于SEO检索 可利用服务器端渲染(SSR)优化</td><td>实现方法简易</td></tr></tbody></table><blockquote><p>以上表格内容来自<a href="https://juejin.im/post/5a0ea4ec6fb9a0450407725c" target="_blank" rel="external">《前端：你要懂的单页面应用和多页面应用》</a>，个人认为整理得挺到位的。</p></blockquote><h3 id="其他应用"><a href="#其他应用" class="headerlink" title="其他应用"></a>其他应用</h3><p>所以其实可以看到，应用和页面的关系，可以简单地理解为多个页面拼成一个应用的关系。应用也分很多种噢，前端里最常见的是浏览器中的 Web 应用，除了这个以外，还有<a href="https://developer.mozilla.org/zh-CN/docs/Web/Progressive_web_apps" target="_blank" rel="external">渐进式 Web 应用（Progressive Web Apps，PWA）</a>，以及原生应用、移动应用。原生应用这些年也有很火的框架出现，像 React Native，Flutter 和 Xamarin 这样的框架允许使用不同于每种标准本地语言的语言来开发所有平台的本地应用程序。</p><p>从产品的角度来说，一个应用可以理解为给用户提供一套完整的定向服务，会包括用户登录、服务分类（Tab）、交互设计和内容展示等等。从工程项目的角度来看，包括用户身份管理、页面管理、路由管理、应用状态管理等。</p><p>这一节内容，我们主要来讲路由管理，也就是会把<a href="https://godbasin.github.io/2019/07/11/vue-for-everyone-2/">上一节内容</a>中拼好的一个个页面，串成一个完整的应用。</p><blockquote><p>原谅我脑袋里忽然想起了小虎队的“把你的心我的心，串一串，串一株幸运草，串一个同心圆”</p></blockquote><h2 id="页面划分"><a href="#页面划分" class="headerlink" title="页面划分"></a>页面划分</h2><p>在直接讲我们的路由怎么配置前，我们需要先知道我们的应用要怎么划分，路由和页面路径是一一对应的，所以我们需要先设计应用的页面逻辑。我们要知道怎么设计一个应用，或者说根据已有的产品、设计交互，怎么规划我们项目的结构。</p><p>我们看看上一节的<a href="http://vue-for-everyone.godbasin.com/2/index.html" target="_blank" rel="external">页面效果</a>：<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-for-everyone-2-1.jpg" alt=""></p><h3 id="页面结构设计"><a href="#页面结构设计" class="headerlink" title="页面结构设计"></a>页面结构设计</h3><p>这是常用的一种管理端页面结构，我们可以基于这样的页面设计几种类型的页面拼装：</p><table><thead><tr><th>序号</th><th>页面形式</th><th>页面能力</th></tr></thead><tbody><tr><td>1</td><td>登录页</td><td>只有用户名和密码的输入</td></tr><tr><td>2</td><td>列表 + 表单</td><td>单页可以完成某类服务的增删查改</td></tr><tr><td>3</td><td>列表页</td><td>只有列表展示，提供查和删服务，需要配合 4 的表单页完成增和改</td></tr><tr><td>4</td><td>表单页</td><td>只有表单编辑内容，可提供新增、修改等能力给 3 使用</td></tr></tbody></table><p>上述 2-4 结构的页面，可以配合路由，整理出这样的菜单信息：<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-for-everyone-3-2.jpg" alt=""></p><h3 id="页面路由设计"><a href="#页面路由设计" class="headerlink" title="页面路由设计"></a>页面路由设计</h3><p>上述情况下，以<code>/</code>作为根路由（对应的组件为 App.vue），我们设计这么几种路由和页面：</p><table><thead><tr><th>路由</th><th>页面内容</th><th>页面对应的 Component</th><th>页面组成</th></tr></thead><tbody><tr><td><code>/login</code></td><td>登录页</td><td>Login</td><td>表单，包括<code>username</code>和<code>password</code></td></tr><tr><td><code>/home</code></td><td>应用首页</td><td>Home</td><td>左侧菜单<code>&lt;Menu&gt;</code>，右侧路由内容<code>&lt;router-view&gt;</code></td></tr><tr><td><code>/home/service</code></td><td>服务信息页</td><td>Service</td><td>为 Home 的子路由组件，包括列表和表单</td></tr><tr><td><code>/home/product</code></td><td>产品容器页</td><td>Product</td><td>为 Home 的子路由组件，包括<code>&lt;router-view&gt;</code></td></tr><tr><td><code>/home/product/list</code></td><td>产品信息页</td><td>ProductList</td><td>为 Product 的子路由组件，包括列表</td></tr><tr><td><code>/home/product/edit</code></td><td>产品编辑页</td><td>ProductEdit</td><td>为 Product 的子路由组件，包括表单</td></tr></tbody></table><p>页面结构和路由嵌套管理，其实是这样的：</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div></pre></td><td class="code"><pre><div class="line">/login                     /home                     /home/service  </div><div class="line">+------------------+       +-----------------+       +-----------------+</div><div class="line">| App              |       | App             |       | App             |</div><div class="line">| +--------------+ |       | +-------------+ |       | +-------------+ |</div><div class="line">| | Login        | |       | | Home        | |       | | Home        | |</div><div class="line">| |              | |       | |             | |       | | +---------+ | |</div><div class="line">| |              | |  +--) | |&lt;router-view&gt;| |  +--) | | | Service | | |</div><div class="line">| |              | |       | |  无对应内容  | |       | | |列表+表单 | | |</div><div class="line">| |              | |       | |             | |       | | +---------+ | |</div><div class="line">| +--------------+ |       | +-------------+ |       | +-------------+ |</div><div class="line">+------------------+       +-----------------+       +-----------------+</div><div class="line"></div><div class="line"></div><div class="line">      /home/product                /home/product/list              /home/product/edit</div><div class="line">      +---------------------+      +------------------------+      +------------------------+</div><div class="line">      | App                 |      | App                    |      | App                    |</div><div class="line">      | +-----------------+ |      | +--------------------+ |      | +--------------------+ |</div><div class="line">      | | Home            | |      | | Home               | |      | | Home               | |</div><div class="line">      | | +-------------+ | |      | | +----------------+ | |      | | +----------------+ | |</div><div class="line"> +--) | | | Product     | | | +--) | | | Product        | | | +--) | | | Product        | | |</div><div class="line">      | | |&lt;router-view&gt;| | |      | | | +------------+ | | |      | | | +------------+ | | |</div><div class="line">      | | |  无对应内容  | | |      | | | | ProductList| | | |      | | | | ProductEdit| | | |</div><div class="line">      | | |             | | |      | | | | 单列表页    | | | |      | | | | 单表单页    | | | |</div><div class="line">      | | |             | | |      | | | +------------+ | | |      | | | +------------+ | | |</div><div class="line">      | | +-------------+ | |      | | +----------------+ | |      | | +----------------+ | |</div><div class="line">      | +-----------------+ |      | +--------------------+ |      | +--------------------+ |</div><div class="line">      +---------------------+      +------------------------+      +------------------------+</div><div class="line"></div><div class="line">      (=′∇`=）画这个图累死俺了</div></pre></td></tr></table></figure><p>我们能看到，这里包括了层层的路由嵌套关系，我们后面在配置路由的时候也能看到这样的结构。</p><h3 id="目录结构划分"><a href="#目录结构划分" class="headerlink" title="目录结构划分"></a>目录结构划分</h3><p>我们看到上面的路由划分示意图，使用框框框起来的代表一个 Vue component，而在 Vue 中，其实一切皆组件（页面是特殊的组件），那我们要怎么区分页面和组件呢，一般可以使用项目目录来简单做一些划分：</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">├─dist                      // 编译之后的项目文件</div><div class="line">├─src                       // 开发目录</div><div class="line">│  ├─assets                 // 静态资源</div><div class="line">│     ├─less                // 公共less</div><div class="line">│     ├─img                 // 图片资源</div><div class="line">│  ├─components             // **放这里是组件**</div><div class="line">│  ├─pages                  // **放这里是页面** 根据路由结构划分</div><div class="line">│  ├─utils                  // 工具库</div><div class="line">│  ├─App.vue                // 启动页面，最外层容器组件</div><div class="line">│  ├─main.js                // 入口脚本</div><div class="line">├─babel.config.js          // babel 配置文件</div><div class="line">├─vue.config.js            // vue 自定义配置，与 webpack 配置相关</div><div class="line">├─package.json             // 项目配置</div><div class="line">├─README.<span class="built_in">md</span>                // 项目说明</div></pre></td></tr></table></figure><p>目录结构清晰，其实对一个项目的可维护性非常重要，一眼看去你就知道这个项目大概包括了哪些内容，分别都放在哪里。好看的目录结构和命名，和好看的代码结构和命名一样，已经是天然的说明了，这是很好的编码习惯呢。</p><p>好了，项目目录和路由结构我们划分好了，我们来看看怎么根据上面的设计来配置路由，以及实现相互跳转吧。</p><h2 id="路由配置"><a href="#路由配置" class="headerlink" title="路由配置"></a>路由配置</h2><p>Vue 框架本身的定位是核心关注视图层，所以路由配置、状态管理、其他千奇百怪的测试、mock功能等都不是自带的，我们需要自己找到对应的开源库配合使用（像 angular 这种大而全的框架，则会提供了开箱即用的完整功能，适合大型项目使用）。例如与 Vue 结合的路由管理，一般选择 <a href="https://router.vuejs.org/zh/" target="_blank" rel="external">vue-router</a>。</p><h3 id="使用开源库和工具"><a href="#使用开源库和工具" class="headerlink" title="使用开源库和工具"></a>使用开源库和工具</h3><p>这里顺便介绍下前端怎么找开源库和工具，包括几种途径：</p><ol><li>google 搜 <code>vue router</code>。（没有爬墙工具的小伙伴，可以使用 <a href="https://cn.bing.com/" target="_blank" rel="external">bing</a> 噢）</li><li>在 <a href="https://github.com/" target="_blank" rel="external">github</a> 上搜 <code>vue router</code>。</li><li>去 <a href="https://www.npmjs.com/" target="_blank" rel="external">npm</a> 上找 <code>vue router</code> 资源包。（因为前端开源都是基于 npm 包管理，所以基本上都能在 npm 里找到）</li></ol><p>如图：<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-for-everyone-3-1.jpg" alt="搜索开源库和工具"></p><h3 id="安装和引入-vue-router"><a href="#安装和引入-vue-router" class="headerlink" title="安装和引入 vue-router"></a>安装和引入 vue-router</h3><p>其实官方文档也有介绍怎么安装和使用，这里再简单介绍一下哈：</p><h4 id="1-安装依赖"><a href="#1-安装依赖" class="headerlink" title="1. 安装依赖"></a>1. 安装依赖</h4><figure class="highlight cmd"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"># 安装依赖</div><div class="line">npm install vue-router</div></pre></td></tr></table></figure><h4 id="2-引入-vue-router"><a href="#2-引入-vue-router" class="headerlink" title="2. 引入 vue-router"></a>2. 引入 vue-router</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// main.js</span></div><div class="line"><span class="keyword">import</span> Vue <span class="keyword">from</span> <span class="string">'vue'</span>;</div><div class="line"><span class="comment">// 引入 vue-router</span></div><div class="line"><span class="keyword">import</span> VueRouter <span class="keyword">from</span> <span class="string">'vue-router'</span>;</div><div class="line">Vue.use(VueRouter); <span class="comment">// 使用 vue-router</span></div></pre></td></tr></table></figure><h3 id="使用-vue-router-添加路由"><a href="#使用-vue-router-添加路由" class="headerlink" title="使用 vue-router 添加路由"></a>使用 vue-router 添加路由</h3><p>这里我们根据前面的应用设计，讲一下要怎么配置，vue-router 怎么使用。</p><h4 id="1-配置路由信息"><a href="#1-配置路由信息" class="headerlink" title="1. 配置路由信息"></a>1. 配置路由信息</h4><p>根据以上的嵌套关系，我们可以设置最外层的根路由为<code>&quot;/&quot;</code>，加上其他嵌套子路由配置为：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 配置路由信息</span></div><div class="line"><span class="comment">// 每个路由应该映射一个组件。 其中"component" 可以是</span></div><div class="line"><span class="comment">// 通过 Vue.extend() 创建的组件构造器，</span></div><div class="line"><span class="comment">// 或者，只是一个组件配置对象。</span></div><div class="line"><span class="keyword">const</span> routes = [</div><div class="line">  &#123;</div><div class="line">    <span class="attr">path</span>: <span class="string">"/"</span>, <span class="comment">// 父路由路径</span></div><div class="line">    component: App, <span class="comment">// 父路由组件，传入 vue component</span></div><div class="line">    name: <span class="string">"App"</span>, <span class="comment">// 路由名称</span></div><div class="line">    <span class="comment">// 设置子路由</span></div><div class="line">    children: [</div><div class="line">      &#123; </div><div class="line">        <span class="attr">path</span>: <span class="string">"login"</span>, <span class="comment">// 子路由路径</span></div><div class="line">        component: Login, <span class="comment">// 子路由组件，会替换父组件中&lt;router-view&gt;中的内容</span></div><div class="line">        name: <span class="string">"Login"</span> <span class="comment">// 路由名称</span></div><div class="line">      &#125;,</div><div class="line">      &#123;</div><div class="line">        <span class="comment">// 应用首页</span></div><div class="line">        path: <span class="string">"home"</span>, <span class="attr">component</span>: Home,  <span class="attr">name</span>: <span class="string">"Home"</span>,</div><div class="line">        <span class="attr">children</span>: [</div><div class="line">          <span class="comment">// 服务列表</span></div><div class="line">          &#123; <span class="attr">path</span>: <span class="string">"service"</span>, <span class="attr">component</span>: Service, <span class="attr">name</span>: <span class="string">"Service"</span> &#125;,</div><div class="line">          <span class="comment">// 产品容器</span></div><div class="line">          &#123; <span class="attr">path</span>: <span class="string">"product"</span>, <span class="attr">component</span>: Product, <span class="attr">name</span>: <span class="string">"Product"</span>,</div><div class="line">            <span class="attr">children</span>: [ <span class="comment">// 子路由内容</span></div><div class="line">              <span class="comment">// 产品列表</span></div><div class="line">              &#123; <span class="attr">path</span>: <span class="string">"list"</span>, <span class="attr">component</span>: ProductList, <span class="attr">name</span>: <span class="string">"ProductList"</span> &#125;,</div><div class="line">              <span class="comment">// 产品新增</span></div><div class="line">              &#123; <span class="attr">path</span>: <span class="string">"add/0"</span>, <span class="attr">component</span>: ProductEdit, <span class="attr">name</span>: <span class="string">"ProductAdd"</span> &#125;,</div><div class="line">              <span class="comment">// 产品编辑</span></div><div class="line">              <span class="comment">// 我们能看到，新增和编辑其实最终使用的是同一个组件，所以后面会有一些需要兼容处理的地方</span></div><div class="line">              <span class="comment">// :id可匹配任意值，且可在组件中通过this.$route.params.id获取该值</span></div><div class="line">              &#123; <span class="attr">path</span>: <span class="string">"edit/:id"</span>, <span class="attr">component</span>: ProductEdit, <span class="attr">name</span>: <span class="string">"ProductEdit"</span> &#125;</div><div class="line">            ]</div><div class="line">          &#125;</div><div class="line">        ]</div><div class="line">      &#125;</div><div class="line">    ]</div><div class="line">  &#125;</div><div class="line">];</div></pre></td></tr></table></figure><h4 id="2-Vue-中加载-vue-router-和路由信息"><a href="#2-Vue-中加载-vue-router-和路由信息" class="headerlink" title="2. Vue 中加载 vue-router 和路由信息"></a>2. Vue 中加载 vue-router 和路由信息</h4><p>路由配置设计好之后，我们可以通过将 router 配置参数注入路由，让整个应用都有路由功能：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 加载路由信息</span></div><div class="line"><span class="keyword">const</span> router = <span class="keyword">new</span> VueRouter(&#123;</div><div class="line">  <span class="comment">// mode: 路由模式，'hash' | 'history'</span></div><div class="line">  <span class="comment">// routes：传入路由配置信息，后面会讲怎么配置</span></div><div class="line">  routes <span class="comment">// （缩写）相当于 routes: routes</span></div><div class="line">&#125;);</div><div class="line"><span class="comment">// 启动一个 Vue 应用</span></div><div class="line"><span class="keyword">new</span> Vue(&#123;</div><div class="line">  <span class="attr">el</span>: <span class="string">"#app"</span>,</div><div class="line">  router, <span class="comment">// 传入路由能力</span></div><div class="line">  render: <span class="function"><span class="params">h</span> =&gt;</span> h(App)</div><div class="line">&#125;);</div></pre></td></tr></table></figure><p>这里的<a href="https://router.vuejs.org/zh/api/#mode" target="_blank" rel="external">路由模式-mode</a>包括两种：</p><ul><li><code>hash</code><ul><li>使用 URL hash 值来作路由。支持所有浏览器，包括不支持 HTML5 History Api 的浏览器</li><li>例如上面说的<code>a.com/#/pageone</code>，便是 hash 模式</li></ul></li><li><code>history</code><ul><li>充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面</li><li>例如<code>a.com/pageone</code>，如果觉得 hash 模式丑可以使用这种</li><li><strong>注意！！</strong>依赖 <a href="https://router.vuejs.org/zh/guide/essentials/history-mode.html#html5-history-%E6%A8%A1%E5%BC%8F" target="_blank" rel="external">HTML5 History API 和服务器配置</a></li></ul></li></ul><p>到这里，我们路由配置和启动的部分已经完成，可以在 <a href="https://github.com/godbasin/vue-element-demo/blob/master/3/src/main.js" target="_blank" rel="external">main.js</a> 文件查看完整代码。</p><h4 id="3-lt-router-view-gt-使用"><a href="#3-lt-router-view-gt-使用" class="headerlink" title="3. &lt;router-view&gt;使用"></a>3. <code>&lt;router-view&gt;</code>使用</h4><p><code>&lt;router-view&gt;</code>组件是一个 functional 组件，渲染路径匹配到的视图组件。它渲染的组件还可以内嵌自己的<code>&lt;router-view&gt;</code>，根据嵌套路径，渲染嵌套组件。</p><p>我们来看看<code>&lt;router-view&gt;</code>的使用，这里以<code>App.vue</code>和<code>Home.vue</code>作为例子：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="comment">&lt;!-- 这里是最外层 /app 路由的组件，App.vue --&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></div><div class="line">  <span class="comment">&lt;!-- 使用 &lt;router-view&gt;&lt;/router-view&gt; 来嵌套路由 --&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">router-view</span>&gt;</span><span class="tag">&lt;/<span class="name">router-view</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></div><div class="line"></div><div class="line"><span class="comment">&lt;!-- 这里是 /app/home 路由的组件，Home.vue --&gt;</span></div><div class="line"><span class="comment">&lt;!-- 这里采用了简写，省略了一些非关键内容，更多内容可以参考上一节 --&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">el-container</span>&gt;</span></div><div class="line">    <span class="comment">&lt;!-- 左侧菜单, Menu.vue --&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">Menu</span>&gt;</span><span class="tag">&lt;/<span class="name">Menu</span>&gt;</span></div><div class="line">    <span class="comment">&lt;!-- 右侧内容 --&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">el-container</span>&gt;</span></div><div class="line">      <span class="comment">&lt;!-- 上边的头部栏 --&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">el-header</span>&gt;</span><span class="tag">&lt;/<span class="name">el-header</span>&gt;</span></div><div class="line">      <span class="comment">&lt;!-- 子路由页面的内容 --&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">router-view</span>&gt;</span><span class="tag">&lt;/<span class="name">router-view</span>&gt;</span></div><div class="line">    <span class="tag">&lt;/<span class="name">el-container</span>&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">el-container</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></div></pre></td></tr></table></figure><h4 id="4-lt-router-link-gt-使用"><a href="#4-lt-router-link-gt-使用" class="headerlink" title="4. &lt;router-link&gt;使用"></a>4. <code>&lt;router-link&gt;</code>使用</h4><p><a href="https://godbasin.github.io/2019/07/11/vue-for-everyone-2/">上一节内容</a>中我们拼了这么一个页面：<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-for-everyone-2-1.jpg" alt=""></p><p>大家<a href="http://vue-for-everyone.godbasin.com/2/index.html" target="_blank" rel="external">查看页面效果</a>的时候会发现，左侧的菜单点击没有什么反应，因为我们还没有加上路由。那么现在就使用这里的菜单，来展示下<code>&lt;router-link&gt;</code>的使用吧。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div></pre></td><td class="code"><pre><div class="line"><span class="comment">&lt;!-- 这里是 Menu.vue，即上一节内容种拼的左侧菜单 --&gt;</span></div><div class="line"><span class="comment">&lt;!-- 这里主要针对路由相关内容，更多的注释省略了，有需要可查看最终代码 --&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></div><div class="line">  <span class="comment">&lt;!-- 此处有个 default-active 属性需要注意，是用来设置菜单的选中样式，我们需要根据当前路由情况来选中 --&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">el-menu</span> <span class="attr">:collapse</span>=<span class="string">"isMenuCollapse"</span> <span class="attr">:default-openeds</span>=<span class="string">"['0', '1']"</span> <span class="attr">:default-active</span>=<span class="string">"activeIndex"</span>&gt;</span></div><div class="line">    <span class="comment">&lt;!-- 遍历生成父菜单选项 --&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">template</span> <span class="attr">v-for</span>=<span class="string">"menu in menus"</span>&gt;</span></div><div class="line">      <span class="comment">&lt;!-- 有子菜单的时候 --&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">el-submenu</span> <span class="attr">v-if</span>=<span class="string">"menu.subMenus &amp;&amp; menu.subMenus.length"</span> <span class="attr">:index</span>=<span class="string">"menu.index"</span> <span class="attr">:key</span>=<span class="string">"menu.index"</span></span></div><div class="line">      &gt;</div><div class="line">        <span class="tag">&lt;<span class="name">template</span> <span class="attr">slot</span>=<span class="string">"title"</span>&gt;</span></div><div class="line">          <span class="tag">&lt;<span class="name">i</span> <span class="attr">:class</span>=<span class="string">"menu.icon"</span>&gt;</span><span class="tag">&lt;/<span class="name">i</span>&gt;</span></div><div class="line">          <span class="tag">&lt;<span class="name">span</span> <span class="attr">slot</span>=<span class="string">"title"</span>&gt;</span>&#123;&#123;menu.text&#125;&#125;<span class="tag">&lt;/<span class="name">span</span>&gt;</span></div><div class="line">        <span class="tag">&lt;/<span class="name">template</span>&gt;</span></div><div class="line">        <span class="tag">&lt;<span class="name">el-menu-item-group</span>&gt;</span></div><div class="line">          <span class="comment">&lt;!-- 使用 router-link 组件来导航. --&gt;</span></div><div class="line">          <span class="comment">&lt;!-- 通过传入 `to` 属性指定链接. --&gt;</span></div><div class="line">          <span class="comment">&lt;!-- &lt;router-link&gt; 默认会被渲染成一个 `&lt;a&gt;` 标签 --&gt;</span></div><div class="line">          <span class="tag">&lt;<span class="name">router-link</span> <span class="attr">tag</span>=<span class="string">"div"</span> <span class="attr">v-for</span>=<span class="string">"subMenu in menu.subMenus"</span></span></div><div class="line">            <span class="attr">:key</span>=<span class="string">"subMenu.index"</span> <span class="attr">:to</span>=<span class="string">"&#123;name: subMenu.routerName&#125;"</span>&gt;</div><div class="line">          <span class="tag">&lt;<span class="name">el-menu-item</span> <span class="attr">:index</span>=<span class="string">"subMenu.index"</span>&gt;</span>&#123;&#123;subMenu.text&#125;&#125;<span class="tag">&lt;/<span class="name">el-menu-item</span>&gt;</span></div><div class="line">          <span class="tag">&lt;/<span class="name">router-link</span>&gt;</span></div><div class="line">        <span class="tag">&lt;/<span class="name">el-menu-item-group</span>&gt;</span></div><div class="line">      <span class="tag">&lt;/<span class="name">el-submenu</span>&gt;</span></div><div class="line">      <span class="comment">&lt;!-- 只有单个父菜单的时候，也要给这个父菜单添加路由，同样的 to 指向要去的地方 --&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">router-link</span> <span class="attr">v-else</span> <span class="attr">:index</span>=<span class="string">"menu.index"</span> <span class="attr">:key</span>=<span class="string">"menu.index"</span> <span class="attr">tag</span>=<span class="string">"div"</span> <span class="attr">:to</span>=<span class="string">"&#123;name: menu.routerName&#125;"</span>&gt;</span></div><div class="line">        <span class="comment">&lt;!-- 没子菜单的时候，就用 el-menu-item，也要绑个序号 index --&gt;</span></div><div class="line">        <span class="tag">&lt;<span class="name">el-menu-item</span>&gt;</span></div><div class="line">          <span class="tag">&lt;<span class="name">i</span> <span class="attr">:class</span>=<span class="string">"menu.icon"</span>&gt;</span><span class="tag">&lt;/<span class="name">i</span>&gt;</span></div><div class="line">          <span class="tag">&lt;<span class="name">span</span> <span class="attr">slot</span>=<span class="string">"title"</span>&gt;</span>&#123;&#123;menu.text&#125;&#125;<span class="tag">&lt;/<span class="name">span</span>&gt;</span></div><div class="line">        <span class="tag">&lt;/<span class="name">el-menu-item</span>&gt;</span></div><div class="line">      <span class="tag">&lt;/<span class="name">router-link</span>&gt;</span></div><div class="line">    <span class="tag">&lt;/<span class="name">template</span>&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">el-menu</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></div></pre></td></tr></table></figure><p><code>&lt;router-link&gt;</code>的使用，除了通过 name 来跳转之外，还可通过 path 跳转、带上参数、激活样式、tag 设置等：</p><ul><li>to: 一个路径字符串, 或者一个对象 location descriptor</li><li>tag: 渲染成的html元素类型，默认是</li><li>exact: 用于控制当前激活项的行为</li><li>append: 控制相对链接路径的追加方式</li><li>replace: 替代而不是作为历史条目压榨</li><li>active-class: 当链接项激活时增加的 CSS 样式</li></ul><p>更多的大家可以参考<a href="https://router.vuejs.org/zh/api/#router-link" target="_blank" rel="external">官网 router-link API</a>。也可以在 <a href="https://github.com/godbasin/vue-element-demo/blob/master/3/src/components/Menu.vue" target="_blank" rel="external">Menu.vue</a> 文件查看 Menu 组件的完整代码。</p><h4 id="5-使用-watch-监控路由变化"><a href="#5-使用-watch-监控路由变化" class="headerlink" title="5. 使用 watch 监控路由变化"></a>5. 使用 watch 监控路由变化</h4><p>对应的，我们需要在 menus 里加上 routerName，用来跳转：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// routerName 为对应的路由的路由名称</span></div><div class="line"><span class="keyword">const</span> menus = [</div><div class="line">  &#123;</div><div class="line">    <span class="attr">text</span>: <span class="string">"服务管理"</span>, <span class="attr">icon</span>: <span class="string">"el-icon-setting"</span>,</div><div class="line">    <span class="attr">subMenus</span>: [&#123; <span class="attr">text</span>: <span class="string">"服务信息"</span>, <span class="attr">routerName</span>: <span class="string">'Service'</span> &#125;]  </div><div class="line">  &#125;,</div><div class="line">  &#123;</div><div class="line">    <span class="attr">text</span>: <span class="string">"产品管理"</span>, <span class="attr">icon</span>: <span class="string">"el-icon-menu"</span>,</div><div class="line">    <span class="attr">subMenus</span>: [</div><div class="line">        &#123; <span class="attr">text</span>: <span class="string">"产品信息"</span>, <span class="attr">routerName</span>: <span class="string">'ProductList'</span> &#125;, </div><div class="line">        &#123; <span class="attr">text</span>: <span class="string">"新增"</span>, <span class="attr">routerName</span>: <span class="string">'ProductAdd'</span> &#125;</div><div class="line">    ]</div><div class="line">  &#125;,</div><div class="line">  <span class="comment">// 日志信息这里为空，则不会进行跳转</span></div><div class="line">  &#123; <span class="attr">text</span>: <span class="string">"日志信息"</span>, <span class="attr">icon</span>: <span class="string">"el-icon-message"</span>, <span class="attr">routerName</span>: <span class="string">''</span> &#125;</div><div class="line">].map(<span class="function">(<span class="params">x, i</span>) =&gt;</span> &#123;</div><div class="line">  <span class="comment">// 添加 index，可用于默认展开 default-openeds 属性，和激活状态 efault-active 属性的设置</span></div><div class="line">  <span class="keyword">return</span> &#123;</div><div class="line">    ...x,</div><div class="line">    <span class="comment">// 子菜单就拼接$&#123;父菜单index&#125;-$&#123;子菜单index&#125;</span></div><div class="line">    subMenus: <span class="function">(<span class="params">x.subMenus || []</span>).<span class="params">map</span>(<span class="params">(y, j</span>) =&gt;</span> &#123;</div><div class="line">      <span class="keyword">return</span> &#123; ...y, <span class="attr">index</span>: <span class="string">`<span class="subst">$&#123;i&#125;</span>-<span class="subst">$&#123;j&#125;</span>`</span> &#125;;</div><div class="line">    &#125;),</div><div class="line">    <span class="comment">// 父菜单就把 index 加上好了</span></div><div class="line">    index: <span class="string">`<span class="subst">$&#123;i&#125;</span>`</span></div><div class="line">  &#125;;</div><div class="line">&#125;);</div></pre></td></tr></table></figure><p>根据<a href="https://element.eleme.io/#/zh-CN/component/menu#menu-attribute" target="_blank" rel="external">el-menu的配置</a>我们知道，<code>&lt;el-menu&gt;</code>的<code>default-active</code>属性需要设置当前激活菜单的 index，因此我们需要监控路由的变化，并根据路由情况调整绑定的激活 index。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 下面是 Vue 组件</span></div><div class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</div><div class="line">  data() &#123;</div><div class="line">    <span class="keyword">return</span> &#123;</div><div class="line">      menus, <span class="comment">// menus: menus 的简写</span></div><div class="line">      activeIndex: <span class="string">''</span></div><div class="line">    &#125;;</div><div class="line">  &#125;,</div><div class="line">  <span class="attr">watch</span>: &#123;</div><div class="line">    <span class="comment">// 为了设置 el-menu 的 default-active 属性，需要获取到路由状态</span></div><div class="line">    <span class="string">'$route'</span> (to) &#123;</div><div class="line">      <span class="comment">// 对路由变化作出响应...</span></div><div class="line">      <span class="keyword">let</span> activeIndex;</div><div class="line">      <span class="comment">// 找到匹配的 routerName</span></div><div class="line">      <span class="keyword">this</span>.menus.forEach(<span class="function"><span class="params">x</span> =&gt;</span> &#123;</div><div class="line">        <span class="keyword">if</span>(x.routerName === to.name)&#123;</div><div class="line">          activeIndex = x.index;</div><div class="line">        &#125;<span class="keyword">else</span>&#123;</div><div class="line">          <span class="keyword">const</span> subMenuItem = x.subMenus.find(<span class="function"><span class="params">y</span> =&gt;</span> y.routerName === to.name);</div><div class="line">          <span class="keyword">if</span>(subMenuItem)&#123; activeIndex = subMenuItem.index; &#125;</div><div class="line">        &#125;</div><div class="line">      &#125;);</div><div class="line">      <span class="comment">// 并将 activeIndex 设置为对应的 菜单 index</span></div><div class="line">      <span class="keyword">if</span>(activeIndex)&#123;</div><div class="line">        <span class="keyword">this</span>.activeIndex = activeIndex;</div><div class="line">      &#125;</div><div class="line">    &#125;</div><div class="line">  &#125;</div><div class="line">&#125;;</div></pre></td></tr></table></figure><p>我们看到，这里使用了一个叫 watch 的属性。Vue 中监听属性 watch 和计算属性 computed 也是很常用的能力（能节省不少的代码），衍生的其他依赖状态就可以使用 computed 来处理，而某种状态的变更可以使用 watch 监听。这里篇幅关系不多说，大家可以去翻阅下<a href="(https://cn.vuejs.org/v2/guide/computed.html">官方文档</a>)。</p><h4 id="6-路由跳转"><a href="#6-路由跳转" class="headerlink" title="6. 路由跳转"></a>6. 路由跳转</h4><p>除了使用<code>&lt;router-link&gt;</code>来定义导航链接，我们还可以借助 router 的实例方法，通过编写代码来实现。在 Vue 实例内部，我们可以通过 $router 访问路由实例，例如我们在 ProductList 页面需要跳转到 ProductEdit 页面来编辑/新增选项内容：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</div><div class="line">  <span class="comment">// ...其他省略</span></div><div class="line">  methods: &#123;</div><div class="line">    <span class="comment">// 新增/修改一个数据</span></div><div class="line">    updateTableItem(id = <span class="number">0</span>)&#123;</div><div class="line">      <span class="comment">// 跳转到编辑页面，新增则传id为0，否则为编辑</span></div><div class="line">      <span class="comment">// 可以通过 this.$router 访问路由实例</span></div><div class="line">      <span class="keyword">if</span>(id !== <span class="number">0</span>)&#123;</div><div class="line">        <span class="comment">// 传参 name 为路由名字，params 为我们定义的路由 path 的参数，变成 /edit/xxx</span></div><div class="line">        <span class="comment">// 还有另外一种传参方式 query，带查询参数，变成 /edit?id=xxx</span></div><div class="line">        <span class="keyword">this</span>.$router.push(&#123;<span class="attr">name</span>: <span class="string">'ProductEdit'</span>, <span class="attr">params</span>: &#123;id&#125;&#125;)</div><div class="line">      &#125;<span class="keyword">else</span>&#123;</div><div class="line">        <span class="keyword">this</span>.$router.push(&#123;<span class="attr">name</span>: <span class="string">'ProductAdd'</span>&#125;)</div><div class="line">      &#125;</div><div class="line">    &#125;,</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>router 实例的使用和<code>&lt;router-link&gt;</code>其实很相像，也挺简单的，可以参考<a href="https://router.vuejs.org/zh/guide/essentials/navigation.html" target="_blank" rel="external">编程式的导航</a>。</p><h3 id="给路由添加鉴权"><a href="#给路由添加鉴权" class="headerlink" title="给路由添加鉴权"></a>给路由添加鉴权</h3><p>既然我们这一次设计了登录页和应用首页（请各位根据<a href="https://godbasin.github.io/2019/07/11/vue-for-everyone-2/">第二节内容</a>分别自行拼出来），一般来说，我们会设计只有当登录完成之后，才可以进入应用里面的其他页面。</p><h4 id="1-设置简单的全局数据"><a href="#1-设置简单的全局数据" class="headerlink" title="1. 设置简单的全局数据"></a>1. 设置简单的全局数据</h4><p>一般来说，在 Vue 中会使用 Vuex 来管理数据状态。基于本节内容主要讲 vue-router，所以我们简单设计一个全局数据的管理库：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// globalData.js</span></div><div class="line"><span class="comment">// globalData 用来存全局数据</span></div><div class="line"><span class="keyword">let</span> globalData = &#123;&#125;;</div><div class="line"></div><div class="line"><span class="comment">// 获取全局数据</span></div><div class="line"><span class="comment">// 传 key 获取对应的值</span></div><div class="line"><span class="comment">// 不传 key 获取全部值</span></div><div class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">getGlobalData</span>(<span class="params">key</span>)</span>&#123;</div><div class="line">    <span class="keyword">return</span> key ? globalData[key] : globalData;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// 设置全局数据</span></div><div class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">setGlobalData</span>(<span class="params">key, value</span>)</span>&#123;</div><div class="line">    <span class="comment">// 需要传键值对</span></div><div class="line">    <span class="keyword">if</span>(key === <span class="literal">undefined</span> || value === <span class="literal">undefined</span>)&#123;</div><div class="line">        <span class="keyword">return</span>;</div><div class="line">    &#125;</div><div class="line">    globalData = &#123;...globalData, [key]: value&#125;</div><div class="line">    <span class="keyword">return</span> globalData;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// 清除全局数据</span></div><div class="line"><span class="comment">// 传 key 清除对应的值</span></div><div class="line"><span class="comment">// 不传 key 清除全部值</span></div><div class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">clearGlobalData</span>(<span class="params">key</span>)</span>&#123;</div><div class="line">    <span class="comment">// 需要传键值对</span></div><div class="line">    <span class="keyword">if</span>(key === <span class="literal">undefined</span>)&#123;</div><div class="line">        globalData = &#123;&#125;;</div><div class="line">    &#125;<span class="keyword">else</span>&#123;</div><div class="line">        <span class="keyword">delete</span> globalData[key];</div><div class="line">    &#125;</div><div class="line">    <span class="keyword">return</span> globalData;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>使用这种方式的全局数据，是会在页面刷新之后丢失的。而如果用来存用户的登录态信息，为了避免频繁登录，更好的方式是存到 cookie 或者缓存里。</p><h4 id="2-登录页面登录"><a href="#2-登录页面登录" class="headerlink" title="2. 登录页面登录"></a>2. 登录页面登录</h4><p>拼好的页面可以查看 <a href="https://github.com/godbasin/vue-element-demo/blob/master/3/src/pages/Login.vue" target="_blank" rel="external">Login.vue</a> 文件，这里由于篇幅关系，我们只看保存数据和跳转的部分：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> &#123;setGlobalData&#125; <span class="keyword">from</span> <span class="string">'utils/globalData'</span>;</div><div class="line"></div><div class="line"><span class="comment">// 下面是 Vue 组件</span></div><div class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</div><div class="line">  <span class="comment">// ...其他省略</span></div><div class="line">  methods: &#123;</div><div class="line">    <span class="comment">// 提交新增/修改表单</span></div><div class="line">    onSubmit()&#123;</div><div class="line">      <span class="comment">// 校验表单，用户名和密码都必须填入</span></div><div class="line">      <span class="comment">// Element 表单校验规则配置，请查看https://element.eleme.cn/#/zh-CN/component/form</span></div><div class="line">      <span class="keyword">this</span>.$refs[<span class="string">'form'</span>].validate(<span class="function">(<span class="params">valid</span>) =&gt;</span> &#123;</div><div class="line">        <span class="keyword">if</span> (valid) &#123;</div><div class="line">          <span class="comment">// 校验通过</span></div><div class="line">          <span class="comment">// 设置用户名</span></div><div class="line">          setGlobalData(<span class="string">'username'</span>, <span class="keyword">this</span>.form.username);</div><div class="line">          <span class="comment">// 跳转到里页</span></div><div class="line">          <span class="keyword">this</span>.$router.push(&#123;<span class="attr">name</span>: <span class="string">"Home"</span>&#125;);</div><div class="line">        &#125; <span class="keyword">else</span> &#123;</div><div class="line">          <span class="comment">// 校验失败</span></div><div class="line">          <span class="keyword">return</span> <span class="literal">false</span>;</div><div class="line">        &#125;</div><div class="line">      &#125;);</div><div class="line">    &#125;</div><div class="line">  &#125;</div><div class="line">&#125;;</div></pre></td></tr></table></figure><h4 id="3-鉴权进入内页"><a href="#3-鉴权进入内页" class="headerlink" title="3. 鉴权进入内页"></a>3. 鉴权进入内页</h4><p>这里，我们需要使用 vue-router 的<a href="https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%85%A8%E5%B1%80%E5%89%8D%E7%BD%AE%E5%AE%88%E5%8D%AB" target="_blank" rel="external">导航守卫</a>能力，当用户未登录时，则拒绝进入其他路由页面里：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// main.js</span></div><div class="line"><span class="keyword">import</span> &#123; getGlobalData &#125; <span class="keyword">from</span> <span class="string">"utils/globalData"</span>;</div><div class="line"></div><div class="line">router.beforeEach(<span class="function">(<span class="params">to, <span class="keyword">from</span>, next</span>) =&gt;</span> &#123;</div><div class="line">  <span class="keyword">if</span> (to.name !== <span class="string">"Login"</span>) &#123;</div><div class="line">    <span class="comment">// 非 login 页面，检查是否登录</span></div><div class="line">    <span class="comment">// 这里简单前端模拟是否填写了用户名，真实环境需要走后台登录，缓存到本地</span></div><div class="line">    <span class="keyword">if</span> (!getGlobalData(<span class="string">"username"</span>)) &#123;</div><div class="line">      next(&#123; <span class="attr">name</span>: <span class="string">"Login"</span> &#125;);</div><div class="line">    &#125;</div><div class="line">  &#125;</div><div class="line">  <span class="comment">// 其他情况正常执行</span></div><div class="line">  next();</div><div class="line">&#125;);</div></pre></td></tr></table></figure><p>到这里，我们整个应用可以顺利地跑起来了：</p><ul><li><a href="http://vue-for-everyone.godbasin.com/3/index.html" target="_blank" rel="external">页面的效果查看</a></li><li><a href="https://github.com/godbasin/vue-element-demo/tree/master/3" target="_blank" rel="external">页面代码查看</a></li></ul><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><hr><p>很多人会谈到程序员的天花板，前端开发也喜欢谈前端开发的天花板。我们总以为自己所经历的是最艰难的，其实每个人都是一样的。工地的建筑工人、快递外卖小哥、各行各业都有各自的难处，人只要想要往上走，总是要爬坡的。<br>自我感动取之便捷，又容易上瘾，我们绝不能走到半山腰上就嚎啕大哭。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本节内容主要介绍使用 vue-router 来管理路由，包括应用和路由的设计和配置、页面间跳转、路由鉴权等。然后将上一节拼出来的页面，再拼出来一个完整的 Web 应用叭。&lt;br&gt;
    
    </summary>
    
      <category term="vue八宝粥" scheme="https://godbasin.github.io/categories/vue%E5%85%AB%E5%AE%9D%E7%B2%A5/"/>
    
    
      <category term="教程" scheme="https://godbasin.github.io/tags/%E6%95%99%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>小程序开发月刊第七期（20190715）</title>
    <link href="https://godbasin.github.io/2019/07/15/wxapp-latest-20190715/"/>
    <id>https://godbasin.github.io/2019/07/15/wxapp-latest-20190715/</id>
    <published>2019-07-15T15:12:34.000Z</published>
    <updated>2019-07-16T14:00:40.385Z</updated>
    
    <content type="html"><![CDATA[<p>太可惜了这个月啥都没有~~<br><a id="more"></a></p><h1 id="小程序-latest"><a href="#小程序-latest" class="headerlink" title="小程序 latest"></a>小程序 latest</h1><h2 id="小程序能力"><a href="#小程序能力" class="headerlink" title="小程序能力"></a>小程序能力</h2><h3 id="「2-7-3版本基础库」新增功能"><a href="#「2-7-3版本基础库」新增功能" class="headerlink" title="「2.7.3版本基础库」新增功能"></a>「2.7.3版本基础库」新增功能</h3><ul><li>新增页面间通信接口，用于当前页面和被打开页面间，<a href="https://developers.weixin.qq.com/miniprogram/dev/api/route/wx.navigateTo.html" target="_blank" rel="external">详情</a></li><li><code>wx.pageScrollTo</code>新增锚点支持，支持选择器和滚动距离两种方式定位，<a href="https://developers.weixin.qq.com/miniprogram/dev/api/ui/scroll/wx.pageScrollTo.html" target="_blank" rel="external">详情</a></li><li>其他社区反馈问题修复</li></ul><p>更多2.7.3版本基础库的新能力及详情，可查看<a href="https://developers.weixin.qq.com/miniprogram/dev/framework/release/" target="_blank" rel="external">《基础库更新日志》</a>。</p><h3 id="微信证件-OCR-识别能力开放"><a href="#微信证件-OCR-识别能力开放" class="headerlink" title="微信证件 OCR 识别能力开放"></a>微信证件 OCR 识别能力开放</h3><p>微信证件OCR识别能力是微信团队推出的一套提升移动端快捷信息录入的工具，目前支持身份证、银行卡、行驶证 OCR 识别。经过半年时间的内测和持续迭代优化，现在正式对外开放接入。<br>目前有两种调用方式：小程序插件方式和云端API的方式。详情查看<a href="https://developers.weixin.qq.com/community/develop/doc/000888093f4650c1c5c8208405bc01" target="_blank" rel="external">微信证件OCR识别能力开放</a>。</p><h3 id="更新日志"><a href="#更新日志" class="headerlink" title="更新日志"></a>更新日志</h3><ul><li><a href="https://developers.weixin.qq.com/community/develop/doc/000e68c2cbcc986368d8d44d85b401" target="_blank" rel="external">社区问题反馈以及功能优化更新（07.01-07.05）</a>  </li><li><a href="https://developers.weixin.qq.com/community/develop/doc/0002a49cd1c830acadc84a01e5b001" target="_blank" rel="external">社区问题反馈以及功能优化更新（06.24-06.28）</a>  </li><li><a href="https://developers.weixin.qq.com/community/develop/doc/00002a24780f98c843c886fc05b001" target="_blank" rel="external">社区问题反馈以及功能优化更新（06.17-06.21）</a>  </li></ul><h2 id="小程序·云开发"><a href="#小程序·云开发" class="headerlink" title="小程序·云开发"></a>小程序·云开发</h2><h3 id="「小程序·云开发」新增付费功能"><a href="#「小程序·云开发」新增付费功能" class="headerlink" title="「小程序·云开发」新增付费功能"></a>「小程序·云开发」新增付费功能</h3><p>近期「小程序·云开发」将上线付费功能（付费功能针对非基础资源配额，基础资源配额仍可免费使用）。详情查看<a href="https://developers.weixin.qq.com/community/develop/doc/000a6068c88d086fceb8d8afd56801" target="_blank" rel="external">「小程序·云开发」功能更新和问题反馈</a>。</p><h3 id="「小程序·云开发」新增数据库聚合功能"><a href="#「小程序·云开发」新增数据库聚合功能" class="headerlink" title="「小程序·云开发」新增数据库聚合功能"></a>「小程序·云开发」新增数据库聚合功能</h3><p>小程序·云开发新增数据库聚合能力。聚合操作可以将数据分组（或者不分组，即只有一组/每个记录都是一组）然后对每组数据执行多种批处理操作，最后返回结果。有了聚合能力，可以方便的解决很多没有聚合能力时无法实现或只能低效实现的场景，如分组查询、流水线式分阶段批处理以及获取唯一值等。具体使用方式见<a href="https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/database/aggregation/aggregation.html" target="_blank" rel="external">小程序·云开发数据库聚合</a>。</p><h3 id="「小程序·云开发」云调用支持消息推送"><a href="#「小程序·云开发」云调用支持消息推送" class="headerlink" title="「小程序·云开发」云调用支持消息推送"></a>「小程序·云开发」云调用支持消息推送</h3><p>「小程序·云开发」支持通过云函数接收小程序消息推送（如接收到客服消息时触发云函数）。开发者可在云控制台设置页面的全局设置中添加相关消息推送的配置，具体接入方式见<a href="https://developers.weixin.qq.com/miniprogram/dev/framework/server-ability/message-push.html" target="_blank" rel="external">消息推送</a>。  </p><h2 id="开发者工具"><a href="#开发者工具" class="headerlink" title="开发者工具"></a>开发者工具</h2><h3 id="云控制台支持消息推送配置"><a href="#云控制台支持消息推送配置" class="headerlink" title="云控制台支持消息推送配置"></a>云控制台支持消息推送配置</h3><p>在云控制台-设置-全局设置，可以设置将消息推送至指定的云函数，而不再推送到微信公众平台-开发设置-消息推送中配置的域名。</p><h3 id="云控制台支持数据库高级查询"><a href="#云控制台支持数据库高级查询" class="headerlink" title="云控制台支持数据库高级查询"></a>云控制台支持数据库高级查询</h3><p>云控制台-数据库-高级操作中可以通过编写指令对数据库进行高级查询。</p><p>更多的内容可以查看：</p><ul><li><a href="https://developers.weixin.qq.com/community/develop/doc/0006e23bb00bd8652cd8324575b001" target="_blank" rel="external">微信开发者工具 1.02.1907081 更新说明</a></li><li><a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html" target="_blank" rel="external">开发者工具更新日志</a></li></ul><h1 id="小程序教程"><a href="#小程序教程" class="headerlink" title="小程序教程"></a>小程序教程</h1><h2 id="社区精选文章"><a href="#社区精选文章" class="headerlink" title="社区精选文章"></a>社区精选文章</h2><ul><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/0000645ae8cf882129c8b471951c13" target="_blank" rel="external">如何用小程序实现类原生APP下一条无限刷体验</a></li></ul><p>更多可以查看<a href="https://developers.weixin.qq.com/community/develop/article" target="_blank" rel="external">文章分享</a>。</p><h2 id="最新踩坑-amp-amp-Tips"><a href="#最新踩坑-amp-amp-Tips" class="headerlink" title="最新踩坑 &amp;&amp; Tips"></a>最新踩坑 &amp;&amp; Tips</h2><p>1.Promise.then 有些情况下不被调用。查了一下基础库的 bug 历史，以前确实有发现过部分 iOS 系统版本上原生 Promise then 不触发的情况，具体触发条件不明。<br>最新进展：是客户端在预加载时的一个 bug 导致 setTimeout 不会回调（Promise 依赖 setTimeout）。这个问题会跟下个客户端版本修复。 </p><p>2.小程序要实现 1rpx 边框的圆形的时候，在 iOS 上计算px时会有bug，边框会出现疑似被剪切/遮挡的渐变状。<br>解决方案：只用1rpx/0.5px是不行的，要先放大，再缩小。据说，4倍可以cover目前绝大部分手机。</p><p>3.小程序遇到黑屏，可以从内存和图片方向进行排查，少用大图、少用高清大图。</p><p>4.Component 是 Page 的超集，因此可以使用 Component 构造器构造页面，拥有与普通组件一样的定义段与实例方法，同时还能使用各种丰富的 behavior 拓展能力，<a href="https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/component.html" target="_blank" rel="external">参考文档</a>。<br>注意事项：</p><ul><li>对应 json 文件中包含 usingComponents 定义段</li><li>组件的属性可以用于接收页面的参数</li><li>页面的生命周期方法（即 on 开头的方法，如 onLoad），应写在 methods 定义段中</li></ul><p>5.小程序切到后台，目前超过5分钟会被销毁，再次打开冷启动的时候，会默认回到首页。</p><ul><li>更多可以参考<a href="https://developers.weixin.qq.com/miniprogram/dev/framework/runtime/operating-mechanism.html" target="_blank" rel="external">小程序的运行机制</a></li></ul><p>6.小程序页面被关闭 unload 之后，如果有原本在执行的逻辑，是会继续执行完毕的（因为各个 Webview 是共用 JS 线程的）。如果有重定向、跳转等逻辑，为了防止用户自行返回等操作，可以添加当前页面的条件判断是否要执行，页面栈可以通过 getCurrentPages 拿到。</p><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><p>人人都是天才。<br>但如果用爬树技巧来<br>评判一条鱼，<br>那么它一辈子<br>都会相信<br>自己是个蠢材。–爱因斯坦</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;太可惜了这个月啥都没有~~&lt;br&gt;
    
    </summary>
    
      <category term="小程序双皮奶" scheme="https://godbasin.github.io/categories/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%8F%8C%E7%9A%AE%E5%A5%B6/"/>
    
    
      <category term="教程" scheme="https://godbasin.github.io/tags/%E6%95%99%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>9102全员学Vue--2.怎么三两下拼出一个页面</title>
    <link href="https://godbasin.github.io/2019/07/11/vue-for-everyone-2/"/>
    <id>https://godbasin.github.io/2019/07/11/vue-for-everyone-2/</id>
    <published>2019-07-11T15:09:51.000Z</published>
    <updated>2019-07-11T15:20:44.836Z</updated>
    
    <content type="html"><![CDATA[<p>本节内容主要包括使用Vue框架过程中需要掌握的一些基本概念，以及怎么使用现有的一些开源库和组件快速创建项目。另外再附赠对状态管理、数据传递的一些方法和理解叭。总而言之，这一节开始会是与Vue紧密相关的内容啦。<br><a id="more"></a></p><h1 id="Vue基本概念"><a href="#Vue基本概念" class="headerlink" title="Vue基本概念"></a>Vue基本概念</h1><p>首先，要快速写出来一个 Vue 项目，要先理解一些基本的概念。概念这样的东西，一个个介绍讲解会很枯燥，那既然这一节内容是快速创建一个 Vue 项目，那我们就一边讲怎么写一边介绍相关概念叭。</p><p>这里会主要以管理端这样的页面为最终效果，毕竟这是最常见也是最容易写的一类型页面。</p><h2 id="Vue组件"><a href="#Vue组件" class="headerlink" title="Vue组件"></a>Vue组件</h2><p>本来想着从指令讲起的，不过既然上一节中介绍了数据驱动的编码思维，那我们就从数据结构设计起，所以直接开始讲 Vue 组件啦。</p><h3 id="生命周期"><a href="#生命周期" class="headerlink" title="生命周期"></a>生命周期</h3><p>既然要讲 Vue 组件，那生命周期得先了解下。经过上一节内容的讲解，我们知道在 Vue 中要渲染一块页面内容的时候，会有这么几个过程：<br>1). 解析语法生成 AST。<br>2). 根据 AST 结果，完成 data 数据初始化。<br>3). 根据 AST 结果和 data 数据绑定情况，生成虚拟 DOM。<br>4). 将虚拟 DOM 生成真正的 DOM 插入到页面中，此时页面会被渲染。</p><p>当我们绑定的数据进行更新的时候，又会产生以下这些过程：<br>5). 框架接收到数据变更的事件，根据数据生成新的虚拟 DOM 树。<br>6). 比较新旧两棵虚拟 DOM 树，得到差异。<br>7). 把差异应用到真正的 DOM 树上，即根据差异来更新页面内容。</p><p>当我们清空页面内容时，还有：<br>8). 注销实例，清空页面内容，移除绑定事件、监听器等。</p><p>所以在整个页面或是组件中，我们会有以下的一些关键的生命周期钩子：</p><table><thead><tr><th>生命周期钩子</th><th>说明</th><th>对应上述步骤</th></tr></thead><tbody><tr><td>beforeCreate</td><td>初始化实例前，data 属性等不可获取</td><td>1 之后，2 之前</td></tr><tr><td>created</td><td>实例初始化完成，此时可获取 data 里数据，无法获取 DOM</td><td>2 之后，3 之前</td></tr><tr><td>beforeMount</td><td>虚拟 DOM 创建完成，此时未挂载到页面中</td><td>3 之后，4 之前</td></tr><tr><td>mounted</td><td>数据绑定完成，真实 DOM 已挂载到页面</td><td>4 之后</td></tr><tr><td>beforeUpdate</td><td>数据更新，DOM Diff 得到差异，未更新到页面</td><td>6 之后，7 之前</td></tr><tr><td>updated</td><td>数据更新，页面也已更新</td><td>7 之后</td></tr><tr><td>beforeDestroy</td><td>实例销毁前</td><td>8 之前</td></tr><tr><td>destroyed</td><td>实例销毁完成</td><td>8 之后</td></tr></tbody></table><p>这些钩子有什么用呢，我们可以在某些生命周期中做一些事情，例如<code>created</code>事件中，可以拿到基础的数据，并根据这些数据可以开始进行后台请求了。</p><h3 id="数据"><a href="#数据" class="headerlink" title="数据"></a>数据</h3><p>假设我们要做一个管理端的页面，包括常见的增删查改，那会包括菜单、列表、表单这几种内容，如图：</p><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-for-everyone-2-1.jpg" alt=""></p><p>既然要使用数据驱动的方式，那么我们先来设计这个页面的数据包括哪些。每一个都可以抽象成一组数据设计，我们一个个详细来看。</p><h4 id="1-菜单"><a href="#1-菜单" class="headerlink" title="1. 菜单"></a>1. 菜单</h4><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-for-everyone-2-2.jpg" alt=""></p><p>如图，我们能看到，菜单列表主要包括父菜单列表，每个父菜单包括：</p><ul><li>图标 icon</li><li>菜单名字 text</li><li>（可选）子菜单列表 subMenus<ul><li>子菜单名字 text</li></ul></li></ul><p>所以，我们可以抽象出这么一个数据结构:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> menus = [</div><div class="line">  &#123;</div><div class="line">    <span class="attr">text</span>: <span class="string">"服务管理"</span>, <span class="comment">// 父菜单名字</span></div><div class="line">    icon: <span class="string">"el-icon-setting"</span>, <span class="comment">// 父菜单图标</span></div><div class="line">    subMenus: [&#123; <span class="attr">text</span>: <span class="string">"服务信息"</span> &#125;, &#123; <span class="attr">text</span>: <span class="string">"新增"</span> &#125;]  <span class="comment">// 子菜单列表</span></div><div class="line">  &#125;,</div><div class="line">  &#123;</div><div class="line">    <span class="attr">text</span>: <span class="string">"产品管理"</span>,</div><div class="line">    <span class="attr">icon</span>: <span class="string">"el-icon-menu"</span>,</div><div class="line">    <span class="attr">subMenus</span>: [&#123; <span class="attr">text</span>: <span class="string">"产品信息"</span> &#125;]</div><div class="line">  &#125;,</div><div class="line">  &#123;</div><div class="line">    <span class="attr">text</span>: <span class="string">"日志信息"</span>,</div><div class="line">    <span class="attr">icon</span>: <span class="string">"el-icon-message"</span></div><div class="line">  &#125;</div><div class="line">];</div></pre></td></tr></table></figure><h4 id="2-列表"><a href="#2-列表" class="headerlink" title="2. 列表"></a>2. 列表</h4><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-for-everyone-2-3.jpg" alt=""></p><p>如图，我们能看到，列表里每行内容包括：</p><ul><li>日期 date</li><li>姓名 name</li><li>电话 phone</li><li>地址 address</li></ul><p>我们可以先整理到这么一个数据：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> tableItem = &#123;</div><div class="line">  <span class="attr">date</span>: <span class="string">"2019-05-20"</span>, <span class="comment">// 日期</span></div><div class="line">  name: <span class="string">"被删"</span>, <span class="comment">// 姓名</span></div><div class="line">  phone: <span class="string">"13888888888"</span>, <span class="comment">// 电话</span></div><div class="line">  address: <span class="string">"深圳市南山区滨海大道 888 号"</span> <span class="comment">// 地址</span></div><div class="line">&#125;;</div></pre></td></tr></table></figure><p>而在列表这样的增删查改的场景下，一般还需要一个唯一标识来作为标记，这里使用 id，用最简单的方式来拷贝出 20 个数据：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 此处先以 tableItem 为数据源，拷贝生成 20 个数据，然后再根据索引 index 添加上 id</span></div><div class="line"><span class="keyword">const</span> tableData = <span class="built_in">Array</span>(<span class="number">20</span>).fill(tableItem).map(<span class="function">(<span class="params">x, i</span>) =&gt;</span> &#123;<span class="keyword">return</span> &#123;<span class="attr">id</span>: i + <span class="number">1</span>, ...x&#125;;&#125;);</div><div class="line"><span class="built_in">console</span>.log(tableData[<span class="number">1</span>]);</div><div class="line"><span class="comment">// 例如第 2 个数据为：</span></div><div class="line"><span class="comment">/* &#123;</span></div><div class="line">    address: "深圳市南山区滨海大道 888 号"</div><div class="line">    date: "2019-05-20"</div><div class="line">    id: 2</div><div class="line">    name: "被删"</div><div class="line">    phone: "13888888888"</div><div class="line">&#125; */</div></pre></td></tr></table></figure><h3 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h3><p>关于 Vue 的 methods 方法，如果说数据是状态机的话，那事件大概可以当成状态机的扭转。这里以列表作为举例吧，例如新增、删除、上移、下移，我们只需要处理数据就好了：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</div><div class="line">  data() &#123;</div><div class="line">    <span class="comment">// 绑定数据</span></div><div class="line">    <span class="keyword">return</span> &#123;</div><div class="line">      <span class="attr">menus</span>: menus, <span class="comment">// 菜单数据</span></div><div class="line">      tableData: tableData <span class="comment">// 列表数据</span></div><div class="line">    &#125;;</div><div class="line">  &#125;,</div><div class="line">  <span class="attr">methods</span>: &#123;</div><div class="line">    <span class="comment">// 新增一个数据</span></div><div class="line">    addTableItem(item = &#123;&#125;)&#123;</div><div class="line">      <span class="comment">// 添加到列表中，同时自增 id </span></div><div class="line">      <span class="keyword">this</span>.tableData.push(&#123;...item, <span class="attr">id</span>: <span class="keyword">this</span>.tableData.length + <span class="number">1</span>&#125;);</div><div class="line">    &#125;,</div><div class="line">    <span class="comment">// 删除一个数据</span></div><div class="line">    deleteTableItem(id)&#123;</div><div class="line">      <span class="comment">// 查找到对应的索引，然后删除</span></div><div class="line">      <span class="keyword">const</span> index = <span class="keyword">this</span>.tableData.findIndex(<span class="function"><span class="params">x</span> =&gt;</span> x.id === id);</div><div class="line">      <span class="keyword">this</span>.tableData.splice(index, <span class="number">1</span>);</div><div class="line">    &#125;,</div><div class="line">    <span class="comment">// 移动一个数据</span></div><div class="line">    moveTableItem(id, direction)&#123;</div><div class="line">      <span class="keyword">const</span> dataLength = <span class="keyword">this</span>.tableData.length;</div><div class="line">      <span class="comment">// 查找到对应的索引，然后取出，再插入</span></div><div class="line">      <span class="keyword">const</span> index = <span class="keyword">this</span>.tableData.findIndex(<span class="function"><span class="params">x</span> =&gt;</span> x.id === id);</div><div class="line">      <span class="keyword">switch</span>(direction)&#123;</div><div class="line">        <span class="comment">// 上移</span></div><div class="line">        <span class="keyword">case</span> <span class="string">'up'</span>:</div><div class="line">          <span class="keyword">if</span>(index &gt; <span class="number">0</span>) &#123;</div><div class="line">            <span class="comment">// 第一个不进行上移</span></div><div class="line">            <span class="keyword">const</span> item = <span class="keyword">this</span>.tableData.splice(index, <span class="number">1</span>)[<span class="number">0</span>];</div><div class="line">            <span class="keyword">this</span>.tableData.splice(index - <span class="number">1</span>, <span class="number">0</span>, item);</div><div class="line">          &#125;</div><div class="line">          <span class="keyword">break</span>;</div><div class="line">        <span class="comment">// 下移</span></div><div class="line">        <span class="keyword">case</span> <span class="string">'down'</span>:</div><div class="line">          <span class="keyword">if</span>(index &lt; dataLength - <span class="number">1</span>) &#123;</div><div class="line">            <span class="comment">// 最后一个不进行下移</span></div><div class="line">            <span class="keyword">const</span> item = <span class="keyword">this</span>.tableData.splice(index, <span class="number">1</span>)[<span class="number">0</span>];</div><div class="line">            <span class="keyword">this</span>.tableData.splice(index + <span class="number">1</span>, <span class="number">0</span>, item);</div><div class="line">          &#125;</div><div class="line">          <span class="keyword">break</span>;</div><div class="line">      &#125;</div><div class="line">    &#125;</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>当我们把数据更新了之后，Vue 会自动帮我们更新到页面里，具体是怎么实现的呢，可以参考上一节的数据绑定的实现、虚拟 DOM 的内容哈。</p><h2 id="组件"><a href="#组件" class="headerlink" title="组件"></a>组件</h2><p>数据和事件都写好了，接下来就轮到拼页面了。其实前端写样式是一件很蛋疼的事情，但写页面又是一件很有成就感的事情，所以为了不打击大家的学习热情，我们直接跳过学习调节样式的环节，来到组装页面的环节吧~~</p><h3 id="组件的自我修养"><a href="#组件的自我修养" class="headerlink" title="组件的自我修养"></a>组件的自我修养</h3><p>首先我们理解一下，组件是什么呢，个人的理解是（右侧是举例 Vue 中类似的属性或者 API）：</p><ul><li>组件内维护自身的数据和状态：<code>data</code></li><li>组件内维护自身的事件：<code>methods</code>、生命周期钩子</li><li>通过提供配置的方式，来控制展示，或者控制执行逻辑：<code>props</code></li><li>通过一定的方式（事件触发/监听、API 提供），提供与外界（如父组件）通信的方式：<code>$emit</code>、<code>$on</code></li></ul><p>如何在一个页面中，抽象出某些组件出来，涉及的篇幅会很长，大家也可以参考前端抽象+配置化系列：<a href="https://godbasin.github.io/2018/05/26/app-component-isolation/">《页面区块化与应用组件化》</a>、<a href="https://godbasin.github.io/2018/06/02/component-with-itself/">《一个组件的自我修养》</a>、<a href="https://godbasin.github.io/2018/06/09/component-with-configuration/">《组件配置化》</a>、<a href="https://godbasin.github.io/2018/06/17/component-communication-with-data-model/">《数据抽离与数据管理》</a>。（真的很多，加油看）</p><p>一般来说，我们可以使用所见即所得的方式，例如上面的，菜单就是个组件，或者表格就是个组件，来划分。</p><h3 id="Vue-组件"><a href="#Vue-组件" class="headerlink" title="Vue 组件"></a>Vue 组件</h3><p>在 Vue 里，页面也好、某块内容也好，都可以定义为一个组件。而关于组件的，前面也说了会包括生命周期、数据状态、事件处理、模板样式等，基本的可以参考一下<a href="https://cn.vuejs.org/v2/guide/components.html" target="_blank" rel="external">Vue-组件基础</a>，了解一下下面的内容，避免后面直接使用组件的时候有些不了解：</p><ul><li><a href="https://cn.vuejs.org/v2/api/" target="_blank" rel="external">Vue 组件的 api 包括哪些</a></li><li><a href="https://cn.vuejs.org/v2/guide/components.html#data-%E5%BF%85%E9%A1%BB%E6%98%AF%E4%B8%80%E4%B8%AA%E5%87%BD%E6%95%B0" target="_blank" rel="external">data 为什么必须是一个函数</a></li><li><a href="https://cn.vuejs.org/v2/guide/components.html#%E7%BB%84%E4%BB%B6%E7%9A%84%E7%BB%84%E7%BB%87" target="_blank" rel="external">组件怎么组织和设计</a></li><li><a href="https://cn.vuejs.org/v2/guide/components-slots.html" target="_blank" rel="external">组件的 slot 是什么</a></li></ul><h3 id="Element"><a href="#Element" class="headerlink" title="Element"></a>Element</h3><p>这套系列的教程，会直接使用 <a href="https://element.eleme.cn/#/zh-CN/component/installation" target="_blank" rel="external">Element</a> 组件。不要误会，没有收取广告费，是因为我们这边大家都要用 Vue + Element 啦，所以教程以自己人为最高优先级。</p><h4 id="1-使用-Element"><a href="#1-使用-Element" class="headerlink" title="1. 使用 Element"></a>1. 使用 Element</h4><p>首先，我们把 Element 装上，很简单：</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">npm i element-ui -S</div></pre></td></tr></table></figure><p>官方教程也有教我们怎么在 Vue 里使用，也很简单，在 main.js 中写入以下内容：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> Vue <span class="keyword">from</span> <span class="string">'vue'</span>;</div><div class="line"><span class="keyword">import</span> ElementUI <span class="keyword">from</span> <span class="string">'element-ui'</span>; <span class="comment">// 引入 element 组件</span></div><div class="line"><span class="keyword">import</span> <span class="string">'element-ui/lib/theme-chalk/index.css'</span>; <span class="comment">// 加上 element 样式</span></div><div class="line"><span class="keyword">import</span> App <span class="keyword">from</span> <span class="string">'./App.vue'</span>;</div><div class="line"></div><div class="line">Vue.use(ElementUI); <span class="comment">// 在 Vue 里使用 Element</span></div><div class="line"></div><div class="line"><span class="comment">// 启动一个 Vue 应用</span></div><div class="line"><span class="keyword">new</span> Vue(&#123;</div><div class="line">  <span class="attr">el</span>: <span class="string">'#app'</span>,</div><div class="line">  <span class="attr">render</span>: <span class="function"><span class="params">h</span> =&gt;</span> h(App)</div><div class="line">&#125;);</div></pre></td></tr></table></figure><h4 id="2-使用-Element-组件"><a href="#2-使用-Element-组件" class="headerlink" title="2. 使用 Element 组件"></a>2. 使用 Element 组件</h4><p>在官网中，我们能找到很多的组件，如图：<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-for-everyone-2-4.jpg" alt=""></p><p>左侧列表里，全是 Element，接下来就是要拼成一个表单+列表的页面了。</p><p>首先我们得去偷个合适的布局，翻到布局容器 Container 这一个组件页面，我们可以看到一个理想的示例：</p><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-for-everyone-2-5.jpg" alt=""></p><p>点开显示代码，然后尽情拷贝吧~~~鉴于上一节我们用 vue-cli 脚手架生成了个 demo，我们就用在这个 demo 里改，由于主页面内容都放在<code>HelloWorld.vue</code>这个文件里，我们就拷进去吧。</p><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-for-everyone-2-6.jpg" alt=""></p><p>粘贴的时候，会发现编辑器有报错？没有比官方代码贴进来直接报错更糟糕的事情了，我们来瞧瞧是因为什么。</p><p>上一节我们讲了，浏览器里面会解析 HTML/CSS/Javascript 这三种文件，那<code>.vue</code>是什么鬼来的？<code>.vue</code>文件其实是单文件组件，就是把 HTML/CSS/Javascript 写在一个文件里，对于简单的组件来说其实是件好事情，一眼就能看完它做了什么（不过个人还是喜欢分开几个文件的方式，看个人喜好啦）。我们来看看一个<code>.vue</code>文件包括啥：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="comment">&lt;!-- my-component.vue --&gt;</span></div><div class="line"><span class="comment">&lt;!-- </span></div><div class="line">  .vue 文件里，</div><div class="line">  使用 &lt;template&gt; 隔离 HTML，</div><div class="line">  使用 &lt;script&gt; 隔离 Javascript，</div><div class="line">  使用 &lt;style&gt; 隔离 CSS</div><div class="line">  --&gt;</div><div class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">div</span>&gt;</span>This will be pre-compiled<span class="tag">&lt;/<span class="name">div</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></div><div class="line"><span class="comment">&lt;!-- 不喜欢写到一起，script 和 styles 也可以用 src 引入文件 --&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">"./my-component.js"</span>&gt;</span><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">style</span> <span class="attr">src</span>=<span class="string">"./my-component.css"</span>&gt;</span><span class="undefined"></span><span class="tag">&lt;/<span class="name">style</span>&gt;</span></div></pre></td></tr></table></figure><p>所以，原来的示例代码里少了<code>&lt;template&gt;&lt;/template&gt;</code>，这里包裹起来就好啦：</p><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-for-everyone-2-7.jpg" alt=""></p><p>然后打开页面，发现跟想象的差不多，除了几处需要调整：</p><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-for-everyone-2-8.jpg" alt=""></p><p>1) Vue logo 要去掉 -&gt; 在<code>App.vue</code>文件里，把<code>&lt;img alt=&quot;Vue logo&quot; src=&quot;./assets/logo.png&quot;&gt;</code>去掉，还有 body 自带的 margin 也去掉。<br>2) 这些滚动条太丑了，干掉！ -&gt; 把<code>&lt;el-container&gt;</code>里的<code>height: 500px;</code>去掉，然后我们调整下</p><p>然后我们得到一个这个页面：<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-for-everyone-2-9.png" alt=""></p><h3 id="页面绑定"><a href="#页面绑定" class="headerlink" title="页面绑定"></a>页面绑定</h3><p>前面我们给页面抽象了数据和事件，现在要做的是把它们绑定到我们的页面里，我们要先来看看 Element 是怎么设置数据和配置的。</p><h4 id="0-Vue-绑定语法"><a href="#0-Vue-绑定语法" class="headerlink" title="0. Vue 绑定语法"></a>0. Vue 绑定语法</h4><p>既然我们要把数据绑定到组件或是元素里，我们先了解下 Vue 中与数据绑定相关的，各位也可以参考<a href="https://cn.vuejs.org/v2/guide/syntax.html" target="_blank" rel="external">Vue-模板语法</a>一节内容。</p><p><strong>数据绑定</strong><br>我们先来看看数据绑定有哪些最基本的方式：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div></pre></td><td class="code"><pre><div class="line"><span class="comment">&lt;!-- 双大括号可绑定普通文本，此处会把 data 中的 msg 的值绑定到对应的模版上 --&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">span</span>&gt;</span>Message: &#123;&#123; msg &#125;&#125;<span class="tag">&lt;/<span class="name">span</span>&gt;</span></div><div class="line"><span class="comment">&lt;!-- 双大括号可使用 JavaScript 表达式 --&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">span</span>&gt;</span>&#123;&#123; msg.split('').reverse().join('') &#125;&#125;/span&gt;</div><div class="line"><span class="comment">&lt;!-- v-html 输出的内容不会被模版引擎过滤（可参考上节内容），故需要小心 XSS 漏洞 --&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">p</span> <span class="attr">v-html</span>=<span class="string">"rawHtml"</span>&gt;</span><span class="tag">&lt;/<span class="name">p</span>&gt;</span></div><div class="line"><span class="comment">&lt;!-- v-bind 可用来绑定属性（对比双大括号&#123;&#123;&#125;&#125;只能插入到节点内容中） --&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">v-bind:id</span>=<span class="string">"dynamicId"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></div><div class="line"><span class="comment">&lt;!-- v-bind:xxx 可缩写为 :xxx --&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">:id</span>=<span class="string">"dynamicId"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></div><div class="line"></div><div class="line">`v-bind`还可用来传参，关于 props 可以参考[Vue-Prop](https://cn.vuejs.org/v2/guide/components-props.html)一节:</div><div class="line"></div><div class="line">``` html</div><div class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></div><div class="line">  <span class="comment">&lt;!-- 这里传入 tableData 给到 my-table 组件的 data 值里 --&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">my-table</span> <span class="attr">:data</span>=<span class="string">"tableData"</span>&gt;</span><span class="tag">&lt;/<span class="name">my-table</span>&gt;</span></div><div class="line">  <span class="comment">&lt;!-- my-table 组件中可以通过 props 中获取到这个值 --&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="javascript"></span></div><div class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</div><div class="line">  <span class="attr">props</span>: &#123;</div><div class="line">    <span class="attr">data</span>: &#123;</div><div class="line">      <span class="attr">type</span>: <span class="built_in">Array</span>, <span class="comment">// 这是个数组</span></div><div class="line">      <span class="keyword">default</span>: <span class="function"><span class="params">()</span> =&gt;</span> &#123;&#125;, <span class="comment">// 默认值为空数组 []</span></div><div class="line">    &#125;</div><div class="line">  &#125;,</div><div class="line">  <span class="attr">methods</span>: &#123;</div><div class="line">    someFunction()&#123;</div><div class="line">      <span class="comment">// 可以获取到传入 props 中的 data 值，为父组件中的 tableData 变量</span></div><div class="line">      <span class="built_in">console</span>.log(<span class="keyword">this</span>.data); </div><div class="line">    &#125;</div><div class="line">  &#125;</div><div class="line">&#125;</div><div class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></div></pre></td></tr></table></figure><p>父子组件间的数据传递，通常通过 props 和事件进行传递（父组件通过 props 绑定数据给到子组件，通过事件监听获取子组件的数据更新），当然也可以自定义一些状态机制来传递，也可以使用<a href="https://vuex.vuejs.org/zh/guide/" target="_blank" rel="external">Vuex</a>、<a href="https://cn.rx.js.org/" target="_blank" rel="external">Rxjs</a>这种状态管理的工具。</p><p><strong>事件绑定</strong><br>我们来看看，在 Vue 里是怎样进行事件绑定的：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="comment">&lt;!-- v-on:事件名 可以绑定事件监听，在事件触发的时候，则会执行绑定的 js 代码 --&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">button</span> <span class="attr">v-on:click</span>=<span class="string">"counter += 1"</span>&gt;</span>Add 1<span class="tag">&lt;/<span class="name">button</span>&gt;</span></div><div class="line"><span class="comment">&lt;!-- v-on:事件名 可以缩写为 @事件名 --&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">button</span> @<span class="attr">click</span>=<span class="string">"counter += 1"</span>&gt;</span>Add 1<span class="tag">&lt;/<span class="name">button</span>&gt;</span></div><div class="line"><span class="comment">&lt;!-- 事件绑定除了简单的 js 代码，还可以绑定 methods 里的函数 --&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">button</span> @<span class="attr">click</span>=<span class="string">"counterAddOne"</span>&gt;</span>Add 1<span class="tag">&lt;/<span class="name">button</span>&gt;</span></div></pre></td></tr></table></figure><p>事件监听还能用于父子组件的事件传递：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div></pre></td><td class="code"><pre><div class="line"><span class="comment">&lt;!-- 我是子组件 --&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></div><div class="line">  <span class="comment">&lt;!-- v-model 请参考后面的说明 --&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">input</span> <span class="attr">v-model</span>=<span class="string">"val"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">button</span> @<span class="attr">click</span>=<span class="string">"clickDone"</span>&gt;</span>done<span class="tag">&lt;/<span class="name">button</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="javascript"></span></div><div class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</div><div class="line">  data()&#123;</div><div class="line">    <span class="keyword">return</span> &#123;</div><div class="line">      <span class="attr">val</span>: <span class="string">''</span> <span class="comment">// 输入框的值</span></div><div class="line">    &#125;</div><div class="line">  &#125;,</div><div class="line">  <span class="attr">methods</span>: &#123;</div><div class="line">    clickDone()&#123;</div><div class="line">      <span class="comment">// 触发一个 done 名字的事件，把输入框的值传递出去</span></div><div class="line">      <span class="keyword">this</span>.$emit(<span class="string">'done'</span>, <span class="keyword">this</span>.val);</div><div class="line">    &#125;</div><div class="line">  &#125;</div><div class="line">&#125;</div><div class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></div><div class="line"></div><div class="line"><span class="comment">&lt;!-- 我是父组件 --&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></div><div class="line">  <span class="comment">&lt;!-- 监听下子组件的 done 事件 --&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">child-component</span> @<span class="attr">done</span>=<span class="string">"getChildData"</span>&gt;</span><span class="tag">&lt;/<span class="name">child-component</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="javascript"></span></div><div class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</div><div class="line">  <span class="attr">methods</span>: &#123;</div><div class="line">    getChildData(value)&#123;</div><div class="line">      <span class="comment">// 参数 value，是子组件触发事件时传的参数，这里是输入框的值</span></div><div class="line">      alert(value);</div><div class="line">    &#125;</div><div class="line">  &#125;</div><div class="line">&#125;</div><div class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></div></pre></td></tr></table></figure><p>关于 Vue 的事件，还有很多方便的用法噢（例如过滤某个按键等），可以参考<a href="https://cn.vuejs.org/v2/guide/events.html" target="_blank" rel="external">Vue-事件处理</a>一节内容，以及<a href="https://cn.vuejs.org/v2/guide/components-custom-events.html" target="_blank" rel="external">Vue-自定义事件</a>一节内容。</p><p><strong>表单绑定</strong><br>Vue 里有个很好用的指令<code>v-model</code>，常常用来绑定表单的值，可以参考<a href="https://cn.vuejs.org/v2/guide/forms.html" target="_blank" rel="external">Vue-表单输入绑定</a>一节内容。但其实<code>v-model</code>也是语法糖，最终是通过前面的数据和事件绑定结合实现的：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></div><div class="line">  <span class="comment">&lt;!-- 事件绑定除了简单的 js 代码，还可以绑定 methods 里的函数 --&gt;</span></div><div class="line">  <span class="comment">&lt;!-- 例如我们最简单的 v-model 指令，其实是下面的语法糖 --&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">input</span> <span class="attr">:value</span>=<span class="string">"val"</span> @<span class="attr">input</span>=<span class="string">"updateValue"</span> /&gt;</span></div><div class="line">  <span class="comment">&lt;!-- 它也可以写成 v-model --&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">input</span> <span class="attr">v-model</span>=<span class="string">"val"</span> /&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="javascript"></span></div><div class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</div><div class="line">  data()&#123;</div><div class="line">    <span class="keyword">return</span> &#123;</div><div class="line">      <span class="attr">val</span>: <span class="string">''</span></div><div class="line">    &#125;</div><div class="line">  &#125;,</div><div class="line">  <span class="attr">methods</span>: &#123;</div><div class="line">    updateValue(event)&#123;</div><div class="line">      <span class="keyword">this</span>.val = event.target.value;</div><div class="line">    &#125;</div><div class="line">  &#125;</div><div class="line">&#125;</div><div class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></div></pre></td></tr></table></figure><p><code>v-model</code>也可以自定义表单绑定，可参考<a href="https://godbasin.github.io/2018/01/21/vue2-notes-15-custom-v-model/">《Vue2使用笔记15–自定义的表单组件》</a>一文。</p><p>其他的，还有挺常用的一些指令（例如<code>v-if</code>条件、<code>v-for</code>遍历），可以参考<a href="https://cn.vuejs.org/v2/guide/conditional.html" target="_blank" rel="external">条件渲染</a>和<a href="https://cn.vuejs.org/v2/guide/list.html" target="_blank" rel="external">列表渲染</a>，当然你还可以自行开发<a href="https://cn.vuejs.org/v2/guide/custom-directive.html" target="_blank" rel="external">自定义指令</a>，可参考<a href="https://godbasin.github.io/2018/01/27/vue2-notes-16-custom-directive/">《Vue2使用笔记16–自定义指令》</a>一文。</p><h4 id="1-菜单绑定"><a href="#1-菜单绑定" class="headerlink" title="1. 菜单绑定"></a>1. 菜单绑定</h4><p>我们先来看看 Elmenet 里的菜单是怎么用的，可以参考<a href="https://element.eleme.cn/#/zh-CN/component/menu" target="_blank" rel="external">Element-NavMenu导航菜单文档</a>：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line"><span class="comment">&lt;!-- default-openeds 为默认展开的菜单项，以 index 序号 --&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">el-menu</span> <span class="attr">:default-openeds</span>=<span class="string">"['1', '3']"</span>&gt;</span></div><div class="line">  <span class="comment">&lt;!-- el-submenu 为带子菜单的父菜单，index 为每组菜单的序号 --&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">el-submenu</span> <span class="attr">index</span>=<span class="string">"1"</span>&gt;</span></div><div class="line">    <span class="comment">&lt;!-- 下面是父菜单内容，包括父菜单 icon 和父菜单名字 --&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">template</span> <span class="attr">slot</span>=<span class="string">"title"</span>&gt;</span><span class="tag">&lt;<span class="name">i</span> <span class="attr">class</span>=<span class="string">"el-icon-message"</span>&gt;</span><span class="tag">&lt;/<span class="name">i</span>&gt;</span>导航一<span class="tag">&lt;/<span class="name">template</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">el-menu-item-group</span>&gt;</span></div><div class="line">      <span class="comment">&lt;!-- 子菜单选项，包括 index 序号和子菜单名字 --&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">el-menu-item</span> <span class="attr">index</span>=<span class="string">"1-1"</span>&gt;</span>选项1<span class="tag">&lt;/<span class="name">el-menu-item</span>&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">el-menu-item</span> <span class="attr">index</span>=<span class="string">"1-2"</span>&gt;</span>选项2<span class="tag">&lt;/<span class="name">el-menu-item</span>&gt;</span></div><div class="line">    <span class="tag">&lt;/<span class="name">el-menu-item-group</span>&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">el-submenu</span>&gt;</span></div><div class="line">  <span class="comment">&lt;!-- el-menu-item 为不带子菜单的父菜单，index 为每组菜单的序号 --&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">el-menu-item</span> <span class="attr">index</span>=<span class="string">"2"</span>&gt;</span></div><div class="line">    <span class="comment">&lt;!-- 父菜单内容，包括父菜单 icon 和父菜单名字 --&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">i</span> <span class="attr">class</span>=<span class="string">"el-icon-menu"</span>&gt;</span><span class="tag">&lt;/<span class="name">i</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">span</span> <span class="attr">slot</span>=<span class="string">"title"</span>&gt;</span>导航二<span class="tag">&lt;/<span class="name">span</span>&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">el-menu-item</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">el-menu</span>&gt;</span></div></pre></td></tr></table></figure><p>绑定数据之后，就会变成这样啦：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div></pre></td><td class="code"><pre><div class="line"><span class="comment">&lt;!-- 顺便调整了下颜色 --&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">el-menu</span> <span class="attr">:default-openeds</span>=<span class="string">"['0', '1']"</span> <span class="attr">class</span>=<span class="string">"el-menu-vertical-demo"</span></span></div><div class="line">   <span class="attr">background-color</span>=<span class="string">"#545c64"</span> <span class="attr">text-color</span>=<span class="string">"#fff"</span> <span class="attr">active-text-color</span>=<span class="string">"#ffd04b"</span></div><div class="line">  &gt;</div><div class="line">  <span class="comment">&lt;!-- 遍历生成父菜单选项 --&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">template</span> <span class="attr">v-for</span>=<span class="string">"menu in menus"</span>&gt;</span></div><div class="line">    <span class="comment">&lt;!-- 有子菜单的时候，就用 el-submenu，再绑个序号 index --&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">el-submenu</span> <span class="attr">v-if</span>=<span class="string">"menu.subMenus &amp;&amp; menu.subMenus.length"</span> <span class="attr">:index</span>=<span class="string">"menu.index"</span> <span class="attr">:key</span>=<span class="string">"menu.index"</span>&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">template</span> <span class="attr">slot</span>=<span class="string">"title"</span>&gt;</span></div><div class="line">        <span class="comment">&lt;!-- 绑个父菜单的 icon --&gt;</span></div><div class="line">        <span class="tag">&lt;<span class="name">i</span> <span class="attr">:class</span>=<span class="string">"menu.icon"</span>&gt;</span><span class="tag">&lt;/<span class="name">i</span>&gt;</span></div><div class="line">        <span class="comment">&lt;!-- 再绑个父菜单的名称 text --&gt;</span></div><div class="line">        <span class="comment">&lt;!-- slot 其实类似于占位符，可以去 Vue 官方文档了解一下插槽 --&gt;</span></div><div class="line">        <span class="tag">&lt;<span class="name">span</span> <span class="attr">slot</span>=<span class="string">"title"</span>&gt;</span>&#123;&#123;menu.text&#125;&#125;<span class="tag">&lt;/<span class="name">span</span>&gt;</span></div><div class="line">      <span class="tag">&lt;/<span class="name">template</span>&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">el-menu-item-group</span>&gt;</span></div><div class="line">        <span class="comment">&lt;!-- 子菜单也要遍历，同时绑上子菜单名称 text，也要绑个序号 index --&gt;</span></div><div class="line">        <span class="tag">&lt;<span class="name">el-menu-item</span> <span class="attr">v-for</span>=<span class="string">"subMenu in menu.subMenus"</span> <span class="attr">:key</span>=<span class="string">"subMenu.index"</span> <span class="attr">:index</span>=<span class="string">"subMenu.index"</span>&gt;</span>&#123;&#123;subMenu.text&#125;&#125;<span class="tag">&lt;/<span class="name">el-menu-item</span>&gt;</span></div><div class="line">      <span class="tag">&lt;/<span class="name">el-menu-item-group</span>&gt;</span></div><div class="line">    <span class="tag">&lt;/<span class="name">el-submenu</span>&gt;</span></div><div class="line">    <span class="comment">&lt;!-- 没子菜单的时候，就用 el-menu-item，也要绑个序号 index --&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">el-menu-item</span> <span class="attr">v-else</span> <span class="attr">:index</span>=<span class="string">"menu.index"</span> <span class="attr">:key</span>=<span class="string">"menu.index"</span>&gt;</span></div><div class="line">      <span class="comment">&lt;!-- 绑个父菜单的 icon --&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">i</span> <span class="attr">:class</span>=<span class="string">"menu.icon"</span>&gt;</span><span class="tag">&lt;/<span class="name">i</span>&gt;</span></div><div class="line">      <span class="comment">&lt;!-- 再绑个父菜单的名称 text --&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">span</span> <span class="attr">slot</span>=<span class="string">"title"</span>&gt;</span>&#123;&#123;menu.text&#125;&#125;<span class="tag">&lt;/<span class="name">span</span>&gt;</span></div><div class="line">    <span class="tag">&lt;/<span class="name">el-menu-item</span>&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">template</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">el-menu</span>&gt;</span></div></pre></td></tr></table></figure><p>我们之前的 menus 并没有<code>index</code>，这里可以顺便遍历生成一下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">menus = menus.map(<span class="function">(<span class="params">x, i</span>) =&gt;</span> &#123;</div><div class="line">  <span class="keyword">return</span> &#123;</div><div class="line">    ...x,</div><div class="line">    <span class="comment">// 子菜单就拼接$&#123;父菜单index&#125;-$&#123;子菜单index&#125;</span></div><div class="line">    subMenus: <span class="function">(<span class="params">x.subMenus || []</span>).<span class="params">map</span>(<span class="params">(y, j</span>) =&gt;</span> &#123;</div><div class="line">      <span class="keyword">return</span> &#123; ...y, <span class="attr">index</span>: <span class="string">`<span class="subst">$&#123;i&#125;</span>-<span class="subst">$&#123;j&#125;</span>`</span> &#125;;</div><div class="line">    &#125;),</div><div class="line">    <span class="comment">// 父菜单就把 index 加上好了</span></div><div class="line">    index: <span class="string">`<span class="subst">$&#123;i&#125;</span>`</span></div><div class="line">  &#125;;</div><div class="line">&#125;);</div></pre></td></tr></table></figure><p>看~菜单成功生成了：<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-for-everyone-2-10.png" alt=""></p><h4 id="2-列表绑定"><a href="#2-列表绑定" class="headerlink" title="2. 列表绑定"></a>2. 列表绑定</h4><p>Demo 里的列表是不带操作按钮的，我们参考<a href="https://element.eleme.cn/#/zh-CN/component/table" target="_blank" rel="external">Element-Table表格文档</a>以及<a href="https://element.eleme.cn/#/zh-CN/component/button" target="_blank" rel="external">Button按钮文档</a>把自定义选项加上：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"><span class="comment">&lt;!-- data 里绑定表格数据，同时这里调整了下样式 --&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">el-table</span> <span class="attr">stripe</span> <span class="attr">:data</span>=<span class="string">"tableData"</span> <span class="attr">style</span>=<span class="string">"border: 1px solid #ebebeb;border-radius: 3px;margin-top: 10px;"</span>&gt;</span></div><div class="line">  <span class="comment">&lt;!-- prop 传绑定 tableData 的数据 id，表头名称 id，同时设了下宽度 --&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">el-table-column</span> <span class="attr">prop</span>=<span class="string">"id"</span> <span class="attr">label</span>=<span class="string">"id"</span> <span class="attr">width</span>=<span class="string">"100"</span>&gt;</span><span class="tag">&lt;/<span class="name">el-table-column</span>&gt;</span></div><div class="line">  <span class="comment">&lt;!-- prop 传绑定 tableData 的数据 date，表头名称日期 --&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">el-table-column</span> <span class="attr">prop</span>=<span class="string">"date"</span> <span class="attr">label</span>=<span class="string">"日期"</span> <span class="attr">width</span>=<span class="string">"200"</span>&gt;</span><span class="tag">&lt;/<span class="name">el-table-column</span>&gt;</span></div><div class="line">  <span class="comment">&lt;!-- prop 传绑定 tableData 的数据 name，表头名称姓名 --&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">el-table-column</span> <span class="attr">prop</span>=<span class="string">"name"</span> <span class="attr">label</span>=<span class="string">"姓名"</span> <span class="attr">width</span>=<span class="string">"200"</span>&gt;</span><span class="tag">&lt;/<span class="name">el-table-column</span>&gt;</span></div><div class="line">  <span class="comment">&lt;!-- prop 传绑定 tableData 的数据 phone，表头名称电话 --&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">el-table-column</span> <span class="attr">prop</span>=<span class="string">"phone"</span> <span class="attr">label</span>=<span class="string">"电话"</span> <span class="attr">width</span>=<span class="string">"200"</span>&gt;</span><span class="tag">&lt;/<span class="name">el-table-column</span>&gt;</span></div><div class="line">  <span class="comment">&lt;!-- prop 传绑定 tableData 的数据 address，表头名称地址 --&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">el-table-column</span> <span class="attr">prop</span>=<span class="string">"address"</span> <span class="attr">label</span>=<span class="string">"地址"</span>&gt;</span><span class="tag">&lt;/<span class="name">el-table-column</span>&gt;</span></div><div class="line">  <span class="comment">&lt;!-- 该列固定在右侧，表头名称操作 --&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">el-table-column</span> <span class="attr">fixed</span>=<span class="string">"right"</span> <span class="attr">label</span>=<span class="string">"操作"</span> <span class="attr">width</span>=<span class="string">"300"</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">template</span> <span class="attr">slot-scope</span>=<span class="string">"scope"</span>&gt;</span></div><div class="line">      <span class="comment">&lt;!-- 添加了个删除按钮，绑定了前面定义的删除事件 deleteTableItem，传入参数 id --&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">el-button</span> @<span class="attr">click</span>=<span class="string">"deleteTableItem(scope.row.id)"</span> <span class="attr">type</span>=<span class="string">"danger"</span> <span class="attr">size</span>=<span class="string">"small"</span>&gt;</span>删除<span class="tag">&lt;/<span class="name">el-button</span>&gt;</span></div><div class="line">      <span class="comment">&lt;!-- 分别添加了上移和下移按钮，绑定了前面定义的移动事件 moveTableItem，传入参数 id 和移动方向 --&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">el-button</span> @<span class="attr">click</span>=<span class="string">"moveTableItem(scope.row.id, 'up')"</span> <span class="attr">size</span>=<span class="string">"small"</span>&gt;</span>上移<span class="tag">&lt;/<span class="name">el-button</span>&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">el-button</span> @<span class="attr">click</span>=<span class="string">"moveTableItem(scope.row.id, 'down')"</span> <span class="attr">size</span>=<span class="string">"small"</span>&gt;</span>下移<span class="tag">&lt;/<span class="name">el-button</span>&gt;</span></div><div class="line">    <span class="tag">&lt;/<span class="name">template</span>&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">el-table-column</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">el-table</span>&gt;</span></div></pre></td></tr></table></figure><p>然后我们就顺利获得了这样一个列表：<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-for-everyone-2-11.jpg" alt=""></p><h4 id="3-表单绑定"><a href="#3-表单绑定" class="headerlink" title="3. 表单绑定"></a>3. 表单绑定</h4><p>有列表的地方，当然也少不了表单啦<del>那么，同样的方法，我们直接去<a href="https://element.eleme.cn/#/zh-CN/component/form" target="_blank" rel="external">Element-Form表单</a>这里偷代码吧</del>因为这里打算用弹窗的方式来装这个表单的内容，所以我们也抠了<a href="https://element.eleme.cn/#/zh-CN/component/dialog" target="_blank" rel="external">Element-Dialog对话框</a>的代码出来~</p><p>有了前面数据设计和绑定的基础，这里可以直接给出我们的代码：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div></pre></td><td class="code"><pre><div class="line"><span class="comment">&lt;!-- 找个地方添加一个新增的按钮，点击的时候出现表单的弹窗，以及把表单内容设置为初始值 --&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">el-button</span> <span class="attr">type</span>=<span class="string">"primary"</span> @<span class="attr">click</span>=<span class="string">"dialogFormVisible = true;form = &#123;&#125;;"</span>&gt;</span>新增<span class="tag">&lt;/<span class="name">el-button</span>&gt;</span></div><div class="line"><span class="comment">&lt;!-- Form --&gt;</span></div><div class="line"><span class="comment">&lt;!-- el-dialog 是弹窗样式，title 绑定弹窗的标题内容，visible 绑定弹窗是否展示 --&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">el-dialog</span> <span class="attr">title</span>=<span class="string">"新增"</span> <span class="attr">:visible.sync</span>=<span class="string">"dialogFormVisible"</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">el-form</span> <span class="attr">:model</span>=<span class="string">"form"</span>&gt;</span></div><div class="line">    <span class="comment">&lt;!-- el-form-item 绑定表单样式，label 表单的名称，formLabelWidth 设置 label 的宽度 --&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">el-form-item</span> <span class="attr">label</span>=<span class="string">"日期"</span> <span class="attr">:label-width</span>=<span class="string">"formLabelWidth"</span>&gt;</span></div><div class="line">      <span class="comment">&lt;!-- 里面装载表单元素，这里装了个选择日期的组件，v-model 绑定选择值，value-format设置绑定值的格式，type 设置选择的范围，这里 date 表示到天 --&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">el-date-picker</span> <span class="attr">v-model</span>=<span class="string">"form.date"</span> <span class="attr">value-format</span>=<span class="string">"yyyy-MM-dd"</span> <span class="attr">type</span>=<span class="string">"date"</span> <span class="attr">placeholder</span>=<span class="string">"选择日期"</span>&gt;</span><span class="tag">&lt;/<span class="name">el-date-picker</span>&gt;</span></div><div class="line">    <span class="tag">&lt;/<span class="name">el-form-item</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">el-form-item</span> <span class="attr">label</span>=<span class="string">"姓名"</span> <span class="attr">:label-width</span>=<span class="string">"formLabelWidth"</span>&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">el-input</span> <span class="attr">v-model</span>=<span class="string">"form.name"</span>&gt;</span><span class="tag">&lt;/<span class="name">el-input</span>&gt;</span></div><div class="line">    <span class="tag">&lt;/<span class="name">el-form-item</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">el-form-item</span> <span class="attr">label</span>=<span class="string">"电话"</span> <span class="attr">:label-width</span>=<span class="string">"formLabelWidth"</span>&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">el-input</span> <span class="attr">v-model</span>=<span class="string">"form.phone"</span> <span class="attr">type</span>=<span class="string">"tel"</span>&gt;</span><span class="tag">&lt;/<span class="name">el-input</span>&gt;</span></div><div class="line">    <span class="tag">&lt;/<span class="name">el-form-item</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">el-form-item</span> <span class="attr">label</span>=<span class="string">"地址"</span> <span class="attr">:label-width</span>=<span class="string">"formLabelWidth"</span>&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">el-input</span> <span class="attr">v-model</span>=<span class="string">"form.address"</span>&gt;</span><span class="tag">&lt;/<span class="name">el-input</span>&gt;</span></div><div class="line">    <span class="tag">&lt;/<span class="name">el-form-item</span>&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">el-form</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">slot</span>=<span class="string">"footer"</span> <span class="attr">class</span>=<span class="string">"dialog-footer"</span>&gt;</span></div><div class="line">    <span class="comment">&lt;!-- 点击取消的时候，设置弹窗不可见 --&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">el-button</span> @<span class="attr">click</span>=<span class="string">"dialogFormVisible = false"</span>&gt;</span>取 消<span class="tag">&lt;/<span class="name">el-button</span>&gt;</span></div><div class="line">    <span class="comment">&lt;!-- 点击确定的时候，设置弹窗不可见，同时添加一项内容 --&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">el-button</span> <span class="attr">type</span>=<span class="string">"primary"</span> @<span class="attr">click</span>=<span class="string">"dialogFormVisible = false; addTableItem(form)"</span>&gt;</span>确 定<span class="tag">&lt;/<span class="name">el-button</span>&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">el-dialog</span>&gt;</span></div></pre></td></tr></table></figure><p>我们需要新增的数据变量包括：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</div><div class="line">  data() &#123;</div><div class="line">    <span class="keyword">return</span> &#123;</div><div class="line">      <span class="attr">dialogFormVisible</span>: <span class="literal">false</span>, <span class="comment">// 弹窗是否出现</span></div><div class="line">      form: &#123;&#125;, <span class="comment">// 用作表单绑定的内容</span></div><div class="line">      formLabelWidth: <span class="string">'120px'</span>, <span class="comment">// 表单 label 的宽度</span></div><div class="line">    &#125;;</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>Okay，我们的表单就写好了：<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-for-everyone-2-12.jpg" alt=""></p><h3 id="课后作业"><a href="#课后作业" class="headerlink" title="课后作业"></a>课后作业</h3><p>其实到这里，我们已经成功地东拼西凑成一个带菜单、列表和表单的页面了，这也是我们在管理端里最常见的一种页面类型。</p><p>这个页面也有挺多可以完善的地方，例如：</p><ol><li>左侧菜单可以支持收起。</li><li>列表支持修改。</li><li>列表支持批量删除。</li><li>表单支持校验手机号和其他选项不为空。</li></ol><p>这些就当作课后作业来完成吧，如果很懒的你，也可以直接看最终结果：</p><ul><li><a href="http://vue-for-everyone.godbasin.com/2/index.html" target="_blank" rel="external">页面的效果查看</a></li><li><a href="https://github.com/godbasin/vue-element-demo/tree/master/2" target="_blank" rel="external">页面代码查看</a></li></ul><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><hr><p>其实前端发展到现在，已经有很多开源轮子了。所以前端开发的效率在不断提升，会让人有种我很厉害的幻觉。而常常在这样的幻觉消失之后，会发现自己除了会用工具以外，什么都没剩下了。为了避免陷入恐慌的这一天到来，我们应该沉静下来，缺啥补啥，相对于囫囵吞枣，更应该多深入理解和研究下。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本节内容主要包括使用Vue框架过程中需要掌握的一些基本概念，以及怎么使用现有的一些开源库和组件快速创建项目。另外再附赠对状态管理、数据传递的一些方法和理解叭。总而言之，这一节开始会是与Vue紧密相关的内容啦。&lt;br&gt;
    
    </summary>
    
      <category term="vue八宝粥" scheme="https://godbasin.github.io/categories/vue%E5%85%AB%E5%AE%9D%E7%B2%A5/"/>
    
    
      <category term="教程" scheme="https://godbasin.github.io/tags/%E6%95%99%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>前端这几年--2.工作原则和选择</title>
    <link href="https://godbasin.github.io/2019/06/30/about-front-end-2-principle/"/>
    <id>https://godbasin.github.io/2019/06/30/about-front-end-2-principle/</id>
    <published>2019-06-30T07:09:58.000Z</published>
    <updated>2019-06-30T08:06:48.512Z</updated>
    
    <content type="html"><![CDATA[<p>写故事是突发奇想，所以要怎么架构整个系列的内容也没有特别好的想法。所以就从我认为比较重要的开始讲起吧，很多有趣的故事，都是和自己原则的制定和死守相关的~</p><a id="more"></a><h1 id="要成为怎样的人"><a href="#要成为怎样的人" class="headerlink" title="要成为怎样的人"></a>要成为怎样的人</h1><p>工作后遇到了很多事情，如果你要追求纯粹的技术和工作环境，其实都肯定遇到过类似的困扰。世界上最简单的事情，大概是别人告诉你要做什么、要怎么做，你只管做就好了。</p><p>而世界上最有意思的，往往是你做什么遇到了什么困难，但你坚持下来了。</p><h2 id="关于金钱的困惑"><a href="#关于金钱的困惑" class="headerlink" title="关于金钱的困惑"></a>关于金钱的困惑</h2><p>其实从小课本就有教，钱乃身外之物，一个人最大的一笔财富在于他本身。我们学到的知识、经验，还有思考方式、精神力量，才是人生中真正的财富。</p><p>但这个社会又是很现实的，成功似乎越来越被和金钱挂上勾。现代世界，又有多少人会把坚韧而善良的内心、人与人间的真诚和信任、自身的成长和才华，当作一笔真正的财富呢？</p><p>我常常说，金钱它不重要。在我的认知当中，一份工作，我应该关注怎么把它做好，关注怎么给社会创造更大的价值。而我付出的该收获多少，是应该由公司和老板负责的。如果我一边干活还得一边担心自己得不到回报，又怎么能做出好的成绩呢？</p><p>当然，事实总不会如你所愿，大概是这个世界它也有叛逆期。</p><h3 id="钱很重要"><a href="#钱很重要" class="headerlink" title="钱很重要"></a>钱很重要</h3><p>我第一份前端工作工资特别低，而转正的时候我跟老板说想要涨薪。老板破口大骂，说我竟然不懂得感恩，当初要不是他招了我，我怎么会有现在。</p><p>后台几位大哥哥跟我说，去看看其他公司的工作吧，不过要跟他们要到两倍工资噢。（毕竟我当时的工资的确是少的可怜，挤在一个26人住的房子，只有一个洗手间 T_T）</p><p>吭哧吭哧地找工作去了。技术面试过了，最终大佬面又到了谈工资阶段。大佬说，我可以给你想要的，但是这意味着我要去跟HR申请特殊流程。我说，如果需要特殊申请，那也希望你能去申请，我答应了几个后台哥们的，达不到我就继续跟他们干。</p><p>最后当然是给了 offer，当然大佬有些不愿意还是给我申请了，因为技术总监比较看好我（那会前端不好找）。</p><p>我还记得我跟老板提离职的时候，他先是说给我涨薪，见我已经下定决心之后，又开始破口大骂，说我不知感恩，还说什么深圳就这么大，你能走多远之类的。（说实话，当时毕业也才一年多，那瞬间真被吓到了）</p><p>这个世界就是这样子，弱肉强食是很正常的事情。就像每次换工作的时候，HR 都会根据你之前的工资来给你发 offer，虽然都未必如愿但你都屁颠屁颠地接受了。虽然经常说钱不重要，但是有些时候它却偏偏占比很重，因为别人会因为你赚钱多少，来评估你的话语权。</p><h3 id="但它不是你要追求的东西"><a href="#但它不是你要追求的东西" class="headerlink" title="但它不是你要追求的东西"></a>但它不是你要追求的东西</h3><p>从前那些最珍贵的东西，爱和信任、美好的品质、真挚的感情、自我追求，它们都是无价的。而如今却渐渐被标上价格，用来换取金钱和利益，这大概是这个社会最遗憾的矛盾吧。</p><p>但正如《原则》一书所说：</p><blockquote><p>要记住金钱的唯一目的是使你得到你想要的东西，所以要想好你所珍视的是什么，把它置于金钱之上。</p></blockquote><p>本末倒置，无数人都会掉进这个陷阱。我们有很多想要的东西，这其中有很多很多都是需要用钱的，然后我们很容易会以为自己要的是钱，而忘记了初衷。</p><p>关于金钱的原则，你要知道它只能是个附属品，在你追梦路上附带的，而不应该成为你在追求的东西，除非你只喜欢钱了哈哈（你真棒！）。</p><p>身边也很多朋友聊到生活的困难，很多人会经常说到同届的其他人待遇多好，赚钱多厉害。当然，我也会经常疑惑，这个社会衡量一个人的方式，应该是以财富的方式，但财富从什么时候，就真的只剩下金钱二字了呢。</p><p>如果说如今有什么能让我自己骄傲的地方，那大概是我一直都很清楚自己想要的是什么。金钱的确它能让我更加舒适地追求一些理想，但好在我没有把它当初一个终点。</p><h2 id="关于真诚的困惑"><a href="#关于真诚的困惑" class="headerlink" title="关于真诚的困惑"></a>关于真诚的困惑</h2><p>我很喜欢真诚的对话，曾经也会为相互之间的争执而感到困惑。</p><p>在商汤的时候，也遇到了相似的情况。但是在大家放下情绪，各自梳理和分享观点之后，才会惊叹原来是这样的，换了角度来看待之后，突然拓展开的思维其实也很美妙。</p><p>从那时开始，对于吵架这件事，也多了不一样的看法。作为一名程序员，我们应该对开源分享有很好的体会。优秀的合集，能产生非常好的作品。思维和灵感也是，而这个过程一定是会产生摩擦的。</p><h3 id="环境很杂"><a href="#环境很杂" class="headerlink" title="环境很杂"></a>环境很杂</h3><p>现在的工作环境里，很多人都喜欢说到“情商”这样的字眼。</p><p>大多数人的工作分为两部分，一是把事情做好，二是让别人看到你把事情做好了。其实这是很低效的方式，但却偏偏是大环境中真实的一面。</p><p>曾经我也会因为一些不同的想法，和导师起过一些争执。这对我来说是很正常的一件事，只要是不同的人就肯定有不一样的看法。而我也会在矛盾分析结束之后，询问导师的意见，问他这种沟通方式是否会让他不舒服，导师的回答是能有更好的观点产出就可以了。</p><p>然而接下来的事情让我有些措不及防。某一天，有个其他组的同事突然跟我说，让我要注意下平时跟导师的态度。我说为什么呢，被告知我组长跟别人说我不尊重导师。</p><p>这类的事情后面也屡次发生，关心我的人说让我适应下，因为每个地方都是这样的。而我最终也主动离开，换了个环境。他们说的也没错，即使换了环境，也会多多少少有这些事情。</p><p>我想了很久为什么呢？</p><p>大概是因为，并不是全部人都很胜任手上的工作。有些人的确是做得不够好，但他们还是要生活、要赚钱养家，只能通过别的方式来获得认同。踮脚效应便出现了，你要是不站起来，就没法看到前方。</p><p>组织要怎么把控这种效应，还是说分不清、认为这样也没关系，这不是我该苦恼的事情。我只要不断寻找，直到能找到理想的团队。志同道合的价值观十分之来之不易，但不能因为遇不到而强行改变自身的价值观。</p><h3 id="但坚持才会遇到你想要的环境"><a href="#但坚持才会遇到你想要的环境" class="headerlink" title="但坚持才会遇到你想要的环境"></a>但坚持才会遇到你想要的环境</h3><p>我毕业才五年，说实话工作环境基本上一年一换（or more）。很多老同学一段时间没见，聊起来的时候说，被删你怎么又换了？？？</p><p>或许在一些人看来，频繁跳槽意味着这个人不够稳定，做事坚持不了很久。</p><p>但正好相反，换这么多的工作，却恰好是因为一直在坚持着一些事情。回顾这些年来，我能看到自己一步步往前走的轨迹，清晰地指向一个方向。</p><p>当然，一个环境不合适，就要马上离开吗？并不全是，当我在一点点成长的时候，总有一天，我可以把这一切扭转过来，变成我想要的一个环境。所以如果要问我有没有成功做到过，当然目前是还没有的，但我在尝试去做这些改变的时候，遇到了很多有趣的人，也沉淀了一些很棒的思考和想法。</p><p>工作是双向选择的过程，我们不用觉得自己是弱势的一方，也不必要觉得委屈求全。我们对人对事，相对于抱怨，应该用更直接和爽快的方式去正面面对。</p><p>如果你要问我，凭什么我说的这些就是对的呢？大概是上天比较照顾我，运气还不错的我，如今也终于遇到了一个很棒的团队。</p><p>如果你要问我，在自己喜欢的环境里工作是什么感受？那大概是每天乐呵呵地来，哼着歌地走，工作做完了在工位呆着想点别的事情也很开心，走路都几乎一蹦一跳的。</p><h3 id="面子是最不重要的东西"><a href="#面子是最不重要的东西" class="headerlink" title="面子是最不重要的东西"></a>面子是最不重要的东西</h3><p>我经常跟别人说，工作学会的最重要的一项技能，就是脸皮要厚。</p><p>刚毕业的时候，我也是个很要面子的人。被指责批评了，会觉得委屈或是难堪，也会介意面子，而很多的话说不出口，烂在肚子里。</p><p>开始工作之后，作为职场的小白菜，经常会遇到打脸的事情。一开始还会脸红，想找个洞钻进去。久而久之便发现，犯错是无法避免的，既然已经错了，就要让这个错误犯得更有价值。而要实现这一点的方法，就是好好反思改进的空间。</p><p>如果说丢一次脸，能收获一次成长，那其实也是某种意义上赢了。</p><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><p>在艺术的历史里，或许你们都发现了，很多的艺术家在世时都不曾被重视。这大概是生活的悖论吧，就像一个人只有成功了，他说的话才有人愿意听。但是仔细想想，历史也大多数是由幸存的人撰写的，所以说不定也是自然选择的一种。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;写故事是突发奇想，所以要怎么架构整个系列的内容也没有特别好的想法。所以就从我认为比较重要的开始讲起吧，很多有趣的故事，都是和自己原则的制定和死守相关的~&lt;/p&gt;
    
    </summary>
    
      <category term="工作这杯茶" scheme="https://godbasin.github.io/categories/%E5%B7%A5%E4%BD%9C%E8%BF%99%E6%9D%AF%E8%8C%B6/"/>
    
    
      <category term="心态" scheme="https://godbasin.github.io/tags/%E5%BF%83%E6%80%81/"/>
    
  </entry>
  
  <entry>
    <title>9102全员学Vue--1.如何理解前端和Vue</title>
    <link href="https://godbasin.github.io/2019/06/27/vue-for-everyone-1/"/>
    <id>https://godbasin.github.io/2019/06/27/vue-for-everyone-1/</id>
    <published>2019-06-27T15:09:59.000Z</published>
    <updated>2019-07-06T07:07:04.680Z</updated>
    
    <content type="html"><![CDATA[<p>最近身边学习 Vue 的人也越来越多了，大家都有很多的疑问，为什么前端要用框架、Vue 做了些什么、要怎么上手，等等。距离上一次写 Vue 也过去三年了，是时候再把这三年自己的理解再整理进去了。由于这个系列主要是希望写给可能前端掌握也不深的人，所以会铺垫一些自己认为需要的内容，本节先来讲讲入门前端，作为铺垫，来理解 Vue 这个框架吧。<br><a id="more"></a></p><h1 id="前端页面解析"><a href="#前端页面解析" class="headerlink" title="前端页面解析"></a>前端页面解析</h1><h2 id="页面组成"><a href="#页面组成" class="headerlink" title="页面组成"></a>页面组成</h2><p>我们打开一个前端项目，经常会看到很多不同后缀的文件，例如一个页面可能包括<code>a.html</code>、<code>a.css</code>、<code>a.js</code>，用了 Vue 还有<code>a.vue</code>，再加上 Typescript 可能还有<code>a.ts</code>。</p><p>相信有些没写过前端的开发们是有点崩溃的，我们先来分别看看一个前端页面都是由什么组成的。其实<strong>最终跑在浏览器中的代码，主要包括三种：HTML、CSS、Javascript。</strong></p><h3 id="HTML"><a href="#HTML" class="headerlink" title="HTML"></a>HTML</h3><p>直接从代码说起，最简单的莫过于：<br><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">head</span>&gt;</span><span class="tag">&lt;/<span class="name">head</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">body</span>&gt;</span></div><div class="line">        <span class="tag">&lt;<span class="name">h1</span>&gt;</span>我的第一个标题<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></div><div class="line">        <span class="tag">&lt;<span class="name">p</span>&gt;</span>我的第一个段落。<span class="tag">&lt;/<span class="name">p</span>&gt;</span></div><div class="line">    <span class="tag">&lt;/<span class="name">body</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></div></pre></td></tr></table></figure></p><p>这里面包括两个子模快：</p><ul><li><code>&lt;head&gt;</code>：常包括控制样式的<code>&lt;link&gt;</code>标签、控制浏览器特殊逻辑的<code>&lt;meta&gt;</code>标签、控制代码执行逻辑的<code>&lt;script&gt;</code>，不展示到页面。</li><li><code>&lt;body&gt;</code>：包括展示在页面的内容。</li></ul><p>通常来说，一段 HTML 代码，最终在浏览器中会生成一堆 DOM 节点树，例如：<br><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">div</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">a</span>&gt;</span>123<span class="tag">&lt;/<span class="name">a</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">p</span>&gt;</span>456<span class="tag">&lt;<span class="name">span</span>&gt;</span>789<span class="tag">&lt;/<span class="name">span</span>&gt;</span><span class="tag">&lt;/<span class="name">p</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></div></pre></td></tr></table></figure></p><p>这段代码在浏览器中渲染时，其实是长这个样子的：<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-3.png" alt=""></p><p>这不一定是最终的结果，因为我们还可以这样操作：  </p><ol><li>通过 CSS 样式，例如<code>display: none</code>来让其中某个节点消失。  </li><li>通过 JS 代码操作 DOM，例如使用<code>document.getElementById</code>获取到某个节点元素，然后再通过设置<code>.innerHTML</code>来改变元素的内容。  </li></ol><h3 id="CSS"><a href="#CSS" class="headerlink" title="CSS"></a>CSS</h3><p>CSS 主要是给我们的 HTML 元素添加样式，可以通过几个方式匹配:  </p><ul><li>DOM 元素匹配：例如<code>p{color: red}</code>会让所有<code>&lt;p&gt;</code>元素的文字都变成红色   </li><li>class 匹配：类的匹配，例如<code>.color-red{color: red}</code>会让所有<code>&lt;xxx class=&quot;color-red&quot;&gt;</code>元素的文字都变成红色   </li><li>id 匹配：id标识符的匹配，例如<code>#color-red{color: red}</code>会让<code>&lt;xxx id=&quot;color-red&quot;&gt;</code>元素的文字都变成红色（若页面内有多个相同的 id，则只有第一个生效）  </li></ul><p>CSS 的调试和编写上手不简单，会需要很多的踩坑和练习，它也不像 JS 那样可以完全根据语法或是逻辑理解，总有兼容性会颠覆你之前的认知。不过 CSS 写好了，对写页面的效率会有很大的提升。</p><h3 id="Javascript"><a href="#Javascript" class="headerlink" title="Javascript"></a>Javascript</h3><p>HTML 是简单的网页静态信息，而 JavaScript 可以在网页上实现复杂的功能。</p><p>我们常常使用 Javascript 来做以下事情：  </p><ul><li>处理事件（点击、输入等）  </li><li>改变 HTML 内容、位置和样式  </li><li>处理 Http 请求、各种业务逻辑的执行  </li><li>很多其他的事情也可以做</li></ul><p>Javascript 是单线程的，更多是因为对页面交互的同步处理。作为浏览器脚本语言，JavaScript的主要用途是与用户互动，以及操作 DOM，若是多线程会导致严重的同步问题。</p><p>关于更多的 Javascript，可以阅读<a href="https://godbasin.github.io/2018/05/05/front-end-5-javascript/">《前端入门5–Javascript》</a>。</p><h2 id="页面渲染"><a href="#页面渲染" class="headerlink" title="页面渲染"></a>页面渲染</h2><h3 id="浏览器的渲染机制"><a href="#浏览器的渲染机制" class="headerlink" title="浏览器的渲染机制"></a>浏览器的渲染机制</h3><p>我们现在知道一个页面的代码里，主要包括了 HTML、CSS、Javascript 三大块内容，那么浏览器是怎么解析和加载这些内容的呢？</p><p>一次浏览器的页面渲染过程中，浏览器会解析三种文件：  </p><ul><li><strong>解析 HTML/SVG/XHTML，会生成一个 DOM 结构树</strong></li><li><strong>解析 CSS，会生成一个 CSS 规则树</strong></li><li><strong>解析 JS，可通过 DOM API 和 CSS API 来操作 DOM 结构树和 CSS 规则树</strong></li></ul><p>CSS 规则树与 DOM 结构树结合，最终生成一个 Render 树（即最终呈现的页面，例如其中会移除 DOM 结构树中匹配到 CSS 里面<code>display:none</code>的 DOM 节点）。一般来说浏览器绘制页面的过程是：  </p><ol><li><strong>计算 CSS 规则树。</strong>  </li><li><strong>生成 Render 树。</strong>  </li><li><strong>计算各个节点的大小/position/z-index。</strong>  </li><li><strong>绘制。</strong>  </li></ol><p>更多的资料，大家可以参考<a href="https://coolshell.cn/articles/9666.html" target="_blank" rel="external">《浏览器的渲染原理简介》</a>，或者英文很棒的你也可以阅读原文<a href="http://taligarsiel.com/Projects/howbrowserswork1.htm" target="_blank" rel="external">《How browsers work》</a></p><h3 id="页面的局部刷新"><a href="#页面的局部刷新" class="headerlink" title="页面的局部刷新"></a>页面的局部刷新</h3><p>一般看来，浏览器生成了最终的 Render 树，页面也已经渲染完毕，似乎浏览器已经完成了它的工作了。但现实中我们的页面更多的不只是静态的页面，还会包括点击、拖拽等事件操作，以及接口请求、数据渲染到页面等动态的交互逻辑，这时候我们会需要更新页面的信息。</p><p>我们的业务代码中情况会复杂得多，除了插入内容，还包括内容更新、删除元素节点等。不管是那种情况，目前来说前端一般分为两种方式：  </p><ol><li>绑定映射表方式。</li><li>直接替换内容方式。</li></ol><h4 id="1-绑定映射表方式"><a href="#1-绑定映射表方式" class="headerlink" title="1. 绑定映射表方式"></a>1. 绑定映射表方式</h4><p>这其实是挺经常使用的一种方式，例如下面这段代码：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> a = <span class="built_in">document</span>.getElementById(<span class="string">'a'</span>);</div></pre></td></tr></table></figure></p><p>这里拿到了<code>&lt;xxx id=&quot;a&quot;&gt;</code>的这样一个元素映射，我们在更新内容、处理节点的时候就可以用这个映射来直接操作，如：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 1. 更改元素里面内容</span></div><div class="line">a.innerHTML = <span class="string">'&lt;p&gt;测试&lt;/p&gt;'</span></div><div class="line"><span class="comment">// 2. 插入一个&lt;a&gt;元素</span></div><div class="line">a.appendChild(<span class="built_in">document</span>.createElement(<span class="string">`a`</span>))</div><div class="line"><span class="comment">// 3. 删除第一个元素，在这里是前面的&lt;p&gt;测试&lt;/p&gt;</span></div><div class="line">a.removeChild(a.firstChild)</div></pre></td></tr></table></figure></p><p>如果我们一个页面里需要绑定变量的元素很多，那每次要更新某块的页面数据，可能会需要保存很多的元素映射，同时需要调用很多很多的<code>createElement()/appendChild()/removeChild()</code>这类方法，也是个不小的开销。这种情况下，我们可以使用直接替换内容的方式。</p><h4 id="2-直接替换内容方式"><a href="#2-直接替换内容方式" class="headerlink" title="2. 直接替换内容方式"></a>2. 直接替换内容方式</h4><p>我们每次更新页面数据和状态，还可以通过<code>innerHTML</code>方法来用新的<code>HTML String</code>替换旧的，这种方法写起来很简单，无非是将各种节点使用字符串的方式拼接起来而已。</p><p>例如，上面的几次更新 a 元素节点，可以调整成这样实现：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 1. 更改元素里面内容</span></div><div class="line">a.innerHTML = <span class="string">'&lt;p&gt;测试&lt;/p&gt;'</span></div><div class="line"><span class="comment">// 2. 插入一个&lt;a&gt;元素</span></div><div class="line">a.innerHTML = <span class="string">'&lt;p&gt;测试&lt;/p&gt;&lt;a&gt;&lt;/a&gt;'</span></div><div class="line"><span class="comment">// 3. 删除第一个元素，在这里是前面的&lt;p&gt;测试&lt;/p&gt;</span></div><div class="line">a.innerHTML = <span class="string">'&lt;a&gt;&lt;/a&gt;'</span></div></pre></td></tr></table></figure></p><p>这种方式来更新页面简单粗暴，但是如果我们更新的节点范围比较大，这时候我们需要替换掉很大一片的<code>HTML String</code>。这种情况下，会面临着可能导致更多的浏览器计算。</p><h3 id="页面回流、重绘"><a href="#页面回流、重绘" class="headerlink" title="页面回流、重绘"></a>页面回流、重绘</h3><p>前面也介绍了，浏览器绘制页面的过程是：1.计算<code>CSS规则树</code> =&gt; 2.生成<code>Render树</code> =&gt; 3.计算各个节点的<code>大小</code>/<code>position</code>/<code>z-index</code> =&gt; 4.绘制。其中计算的环节也是消耗较大的地方。</p><p>我们使用 DOM API 和 CSS API 的时候，通常会触发浏览器的两种操作：Repaint（重绘） 和 Reflow（回流）：  </p><ul><li><strong>Repaint：页面部分重画，通常不涉及尺寸的改变，常见于颜色的变化。</strong>这时候一般只触发绘制过程的第4个步骤。  </li><li><strong>Reflow：意味着节点需要重新计算和绘制，常见于尺寸的改变。</strong>这时候会触发3和4两个步骤。<br>在 Reflow 的时候，浏览器会使渲染树中受到影响的部分失效，并重新构造这部分渲染树，完成 Reflow 后，浏览器会重新绘制受影响的部分到屏幕中，该过程成为 Repaint。</li></ul><p>回流的花销跟render tree有多少节点需要重新构建有关系，这也是为什么前面说使用<code>innerHTML</code>会导致更多的开销。所以到底是使用绑定映射表方式，还是使用直接替换内容方式，都是需要具体问题具体分析的。</p><h2 id="事件驱动"><a href="#事件驱动" class="headerlink" title="事件驱动"></a>事件驱动</h2><p>事件驱动其实是前端开发中最容易理解的编码方式，例如我们写一个提交表单的页面，用事件驱动的方式来写的话，会是这样一个流程：  </p><ol><li>编写静态页面。</li></ol><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">form</span>&gt;</span></div><div class="line">    Name: <span class="tag">&lt;<span class="name">p</span> <span class="attr">id</span>=<span class="string">"name-value"</span>&gt;</span><span class="tag">&lt;/<span class="name">p</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">name</span>=<span class="string">"name"</span> <span class="attr">id</span>=<span class="string">"name-input"</span> /&gt;</span></div><div class="line">    Email: <span class="tag">&lt;<span class="name">p</span> <span class="attr">id</span>=<span class="string">"email-value"</span>&gt;</span><span class="tag">&lt;/<span class="name">p</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">"email"</span> <span class="attr">name</span>=<span class="string">"email"</span> <span class="attr">id</span>=<span class="string">"email-input"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">"submit"</span> /&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></div></pre></td></tr></table></figure><ol><li>给对应的元素绑定对应的事件。例如给 input 输入框绑定输入事件：</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> nameInputEl = <span class="built_in">document</span>.getElementById(<span class="string">'name-input'</span>);</div><div class="line"><span class="keyword">var</span> emailInputEl = <span class="built_in">document</span>.getElementById(<span class="string">'email-input'</span>);</div><div class="line"><span class="comment">// 监听输入事件，此时 updateValue 函数未定义</span></div><div class="line">nameInputEl.addEventListener(<span class="string">'input'</span>, updateNameValue);</div><div class="line">emailInputEl.addEventListener(<span class="string">'input'</span>, updateEmailValue);</div></pre></td></tr></table></figure><ol><li>事件触发时，更新页面内容：</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> nameValueEl = <span class="built_in">document</span>.getElementById(<span class="string">'name-value'</span>);</div><div class="line"><span class="keyword">var</span> emailValueEl = <span class="built_in">document</span>.getElementById(<span class="string">'email-value'</span>);</div><div class="line"><span class="comment">// 定义 updateValue 函数，用来更新页面内容</span></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">updateNameValue</span>(<span class="params">e</span>)</span>&#123;</div><div class="line">    nameValueEl.innerText = e.srcElement.value;</div><div class="line">&#125;</div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">updateEmailValue</span>(<span class="params">e</span>)</span>&#123;</div><div class="line">    emailValueEl.innerText = e.srcElement.value;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>以上这个流程，是很常见的前端编码思维，我们称之为<strong>事件驱动</strong>模式。</p><h1 id="前端思维转变"><a href="#前端思维转变" class="headerlink" title="前端思维转变"></a>前端思维转变</h1><p>很多人不理解这几年来前端的变化，为什么不能再用 jQuery 统一天下呢？为什么要搞那么多的库，还要按照环境呢？不是用个 txt 编辑器就能写完一个页面吗，前端弄那么复杂是为了什么呢？</p><p>既然称之为思维转变，那么可以将事件驱动的思维模式作为过去常见的一种方式，而如今的前端开发过程中，多了很多的新框架、新工具，还有了工程化，这些带来了怎样的思维模式的转变呢？</p><h2 id="前端框架的出现"><a href="#前端框架的出现" class="headerlink" title="前端框架的出现"></a>前端框架的出现</h2><p>其实最初是 AngularJS 开始占领了比较多的地位，后面 React 迎面赶上，在 Angular 断崖升级的帮助下，Vue 结合了各种框架的优势，以及非常容易入门的文档，成功成为了那一匹黑马。既然这一系列写的是 Vue 的入门和使用，那这里当然是基于 Vue 来介绍了。</p><h3 id="数据绑定"><a href="#数据绑定" class="headerlink" title="数据绑定"></a>数据绑定</h3><h4 id="Vue-文本插值"><a href="#Vue-文本插值" class="headerlink" title="Vue 文本插值"></a>Vue 文本插值</h4><p>在 Vue 中，最基础的模板语法是数据绑定，例如：<br><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">div</span>&gt;</span>&#123;&#123; data &#125;&#125;<span class="tag">&lt;/<span class="name">div</span>&gt;</span></div></pre></td></tr></table></figure></p><p>这里绑定了一个 msg 的变量，开发者在 Vue 实例 data 中绑定该变量：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">new</span> Vue(&#123;</div><div class="line">  <span class="attr">data</span>: &#123;</div><div class="line">    <span class="attr">data</span>: <span class="string">'测试文本'</span></div><div class="line">  &#125;</div><div class="line">&#125;)</div></pre></td></tr></table></figure></p><p>最终页面展示内容为<code>&lt;div&gt;测试文本&lt;/div&gt;</code>。</p><h4 id="数据绑定的实现"><a href="#数据绑定的实现" class="headerlink" title="数据绑定的实现"></a>数据绑定的实现</h4><p>这种使用双大括号来绑定变量的方式，我们称之为数据绑定。它是怎么实现的呢，数据绑定的过程其实不复杂：</p><ol><li>解析语法生成 AST。</li><li>根据 AST 结果生成 DOM。</li><li>将数据绑定更新至模板。</li></ol><p>上述这个过程，是模板引擎在做的事情。我们来看看上面在 Vue 里的代码片段<code>&lt;div&gt;&lt;/div&gt;</code>，我们可以解析后获得这样一个 AST 对象：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">thisDiv = &#123;</div><div class="line">    <span class="attr">dom</span>: &#123;</div><div class="line">        <span class="attr">type</span>: <span class="string">'dom'</span>, <span class="attr">ele</span>: <span class="string">'div'</span>, <span class="attr">nodeIndex</span>: <span class="number">0</span>, <span class="attr">children</span>: [</div><div class="line">            &#123;<span class="attr">type</span>: <span class="string">'text'</span>, <span class="attr">value</span>: <span class="string">''</span>&#125;</div><div class="line">        ]</div><div class="line">    &#125;,</div><div class="line">    <span class="attr">binding</span>: [</div><div class="line">        &#123;<span class="attr">type</span>: <span class="string">'dom'</span>, <span class="attr">nodeIndex</span>: <span class="number">0</span>, <span class="attr">valueName</span>: <span class="string">'data'</span>&#125;</div><div class="line">    ]</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>这样，我们在生成一个 DOM 的时候，同时添加对<code>data</code>的监听，数据更新时我们会找到对应的<code>nodeIndex</code>，更新值：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 假设这是一个生成 DOM 的过程，包括 innerHTML 和事件监听</span></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">generateDOM</span>(<span class="params">astObject</span>)</span>&#123;</div><div class="line">    <span class="keyword">const</span> &#123;dom, binding = []&#125; = astObject;</div><div class="line">    <span class="comment">// 生成DOM，这里假装当前节点是baseDom</span></div><div class="line">    baseDom.innerHTML = getDOMString(dom);</div><div class="line">    <span class="comment">// 对于数据绑定的，来进行监听更新吧</span></div><div class="line">    baseDom.addEventListener(<span class="string">'data:change'</span>, (name, value) =&gt; &#123;</div><div class="line">        <span class="comment">// 寻找匹配的数据绑定</span></div><div class="line">        <span class="keyword">const</span> obj = binding.find(<span class="function"><span class="params">x</span> =&gt;</span> x.valueName == name);</div><div class="line">        <span class="comment">// 若找到值绑定的对应节点，则更新其值。</span></div><div class="line">        <span class="keyword">if</span>(obj)&#123;</div><div class="line">            baseDom.find(<span class="string">`[data-node-index="<span class="subst">$&#123;obj.nodeIndex&#125;</span>"]`</span>).innerHTML = value;</div><div class="line">        &#125;</div><div class="line">    &#125;);</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// 获取DOM字符串，这里简单拼成字符串</span></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">getDOMString</span>(<span class="params">domObj</span>)</span>&#123;</div><div class="line">    <span class="comment">// 无效对象返回''</span></div><div class="line">    <span class="keyword">if</span>(!domObj) <span class="keyword">return</span> <span class="string">''</span>;</div><div class="line">    <span class="keyword">const</span> &#123;type, children = [], nodeIndex, ele, value&#125; = domObj;</div><div class="line">    <span class="keyword">if</span>(type == <span class="string">'dom'</span>)&#123;</div><div class="line">        <span class="comment">// 若有子对象，递归返回生成的字符串拼接</span></div><div class="line">        <span class="keyword">const</span> childString = <span class="string">''</span>;</div><div class="line">        children.forEach(<span class="function"><span class="params">x</span> =&gt;</span> &#123;</div><div class="line">            childString += getDOMString(x);</div><div class="line">        &#125;);</div><div class="line">        <span class="comment">// dom对象，拼接生成对象字符串</span></div><div class="line">        <span class="keyword">return</span> <span class="string">`&lt;<span class="subst">$&#123;ele&#125;</span> data-node-index="<span class="subst">$&#123;nodeIndex&#125;</span>"&gt;<span class="subst">$&#123;childString&#125;</span>&lt;/<span class="subst">$&#123;ele&#125;</span>&gt;`</span>;</div><div class="line">    &#125;<span class="keyword">else</span> <span class="keyword">if</span>(type == <span class="string">'text'</span>)&#123;</div><div class="line">        <span class="comment">// 若为textNode，返回text的值</span></div><div class="line">        <span class="keyword">return</span> value;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>关于模板引擎更多的情况，可以查看<a href="https://godbasin.github.io/2017/10/21/template-engine/">《前端模板引擎》</a>。</p><h3 id="虚拟-DOM"><a href="#虚拟-DOM" class="headerlink" title="虚拟 DOM"></a>虚拟 DOM</h3><p>虚拟 DOM 如今已经被作为前端面试基础题了，多多少少面试者都要去了解下，当初 React 就是因为虚拟 DOM 的提出，暂时赢下了浏览器性能之争。当然，这都是几年前的事情了，如今几大框架的性能问题，早也不是什么大问题了。</p><p>虚拟 DOM 大概是这么个过程：<br>(1) 用 JS 对象模拟 DOM 树，得到一棵虚拟 DOM 树。<br>(2) 当页面数据变更时，生成新的虚拟 DOM 树，比较新旧两棵虚拟 DOM 树的差异。<br>(3) 把差异应用到真正的 DOM 树上。  </p><p>为什么要用到虚拟 DOM 呢？这是因为一个 DOM 节点它包括了太多太多的属性、元素和事件对象，感觉有上百个。但是我们并不是全部都会用到，通常包括节点内容、元素位置、样式、节点的添加删除等方法，而我们通过用 JS 对象表示 DOM 元素的方式，大大降低了比较差异的计算量。</p><p>虚拟 DOM 中，差异对比也是很关键的一步，这里简单说一下。当状态变更的时候，重新构造一棵新的对象树。然后用新的树和旧的树进行比较，记录两棵树差异。通常来说这样的差异需要记录：需要替换掉原来的节点、移动、删除、新增子节点、修改了节点的属性、 对于文本节点的文本内容改变。经过差异对比之后，我们能获得一组差异记录，接下里我们需要使用它。</p><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-4.png" alt="DOM 树对比"></p><p>如图，这里我们对比两棵 DOM 树，得到的差异有：p 元素插入了一个 span 元素子节点，然后原先的文本节点挪到了span 元素子节点下面，最后通过 JS 操作就可以实现完。</p><h3 id="XSS-漏洞"><a href="#XSS-漏洞" class="headerlink" title="XSS 漏洞"></a>XSS 漏洞</h3><p>模板引擎还可以协助预防下 XSS 相关漏洞。我们知道，XSS 的整个攻击过程大概为：</p><ol><li>通常页面中包含的用户输入内容都在固定的容器或者属性内，以文本的形式展示。  </li><li>攻击者利用这些页面的用户输入片段，拼接特殊格式的字符串，突破原有位置的限制，形成了代码片段。  </li><li>攻击者通过在目标网站上注入脚本，使之在用户的浏览器上运行，从而引发潜在风险。  </li></ol><p>避免 XSS 的方法之一主要是将用户所提供的内容进行过滤，而大多数模板引擎会自带 HTML 转义功能。在 Vue 中，默认的数据绑定方式（双大括号、<code>v-bind</code>等）会进行 HTML 转义，将数据解释为普通文本，而非 HTML 代码。</p><p>当然，如果你一定要输出 HTML 代码，也可以使用<code>v-html</code>指令输出。<a href="https://cn.vuejs.org/v2/guide/syntax.html#%E5%8E%9F%E5%A7%8B-HTML" target="_blank" rel="external">官方文档</a>也有特殊说明：</p><blockquote><p>你的站点上动态渲染的任意 HTML 可能会非常危险，因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值，绝不要对用户提供的内容使用插值。</p></blockquote><p>关于 XSS 漏洞，更多的可以查看<a href="https://tech.meituan.com/2018/09/27/fe-security.html" target="_blank" rel="external">《前端安全系列（一）：如何防止XSS攻击？》</a>一文。</p><h3 id="Vue简介"><a href="#Vue简介" class="headerlink" title="Vue简介"></a>Vue简介</h3><p>看看这些模板引擎都做了啥事，原本就是一个<code>&lt;div&gt;</code>，经过 AST 生成一个对象，最终还是生成一个<code>&lt;div&gt;</code>，这是多余的步骤吗？不是的，在这个过程中我们可以实现一些功能：</p><ul><li>排除无效 DOM 元素，并在构建过程可进行报错  </li><li>使用自定义组件的时候，可匹配出来  </li><li>可方便地实现数据绑定、事件绑定等，具备自动更新页面的功能  </li><li>为虚拟 DOM Diff 过程打下铺垫  </li><li>HTML 转义（预防 XSS 漏洞）  </li></ul><p>所以 Vue 它只是一个模板引擎吗？怎么说呢，模板引擎可能是我们选择框架的原因里最重要的一个，毕竟如果没有框架的话，所有上述这些很好用的能力都需要自己搭建，不然开发效率会很受影响。</p><p>我们看看 Vue 官方的介绍：</p><blockquote><p>Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是，Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层，不仅易于上手，还便于与第三方库或既有项目整合。</p></blockquote><p>关于 Vue 和其他框架的对比，可以看看官方文档-<a href="https://cn.vuejs.org/v2/guide/comparison.html" target="_blank" rel="external">对比其他框架</a>。易于上手这块，是大多数人都比较认可的，框架的性能也不错，这也是技术选型中比较重要的一些考虑。</p><h2 id="数据驱动"><a href="#数据驱动" class="headerlink" title="数据驱动"></a>数据驱动</h2><p>前面也介绍了，在 jQuery 年代，我们通常是使用事件驱动的模式去进行开发了。那么使用了 Vue 之后，写代码的方式会有哪些不一样吗？</p><p>既然前面介绍了事件模型一般的编码流程，我们再来看看，同样的们写一个提交表单的页面，用数据驱动的方式来写的话，会变成这么一个流程：</p><ol><li>设计数据结构。</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 包括一个 name 和 一个 email 的值</span></div><div class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</div><div class="line">  data() &#123;</div><div class="line">    <span class="keyword">return</span> &#123;</div><div class="line">      <span class="attr">name</span>: <span class="string">''</span>,</div><div class="line">      <span class="attr">email</span>: <span class="string">''</span></div><div class="line">    &#125;</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><ol><li>把数据绑定到页面中需要使用/展示的地方。</li></ol><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">form</span>&gt;</span></div><div class="line">    Name: <span class="tag">&lt;<span class="name">p</span>&gt;</span>&#123;&#123; name &#125;&#125;<span class="tag">&lt;/<span class="name">p</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">name</span>=<span class="string">"name"</span> <span class="attr">v-bind:value</span>=<span class="string">"name"</span> <span class="attr">v-on:input</span>=<span class="string">"updateNameValue"</span> /&gt;</span></div><div class="line">    Email: <span class="tag">&lt;<span class="name">p</span>&gt;</span>&#123;&#123; email &#125;&#125;<span class="tag">&lt;/<span class="name">p</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">"email"</span> <span class="attr">name</span>=<span class="string">"email"</span> <span class="attr">v-bind:value</span>=<span class="string">"email"</span> <span class="attr">v-on:input</span>=<span class="string">"updateEmailValue"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">"submit"</span> /&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></div></pre></td></tr></table></figure><ol><li>事件触发时，更新数据。</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</div><div class="line">  data() &#123;</div><div class="line">    <span class="keyword">return</span> &#123;</div><div class="line">      <span class="attr">name</span>: <span class="string">''</span>,</div><div class="line">      <span class="attr">email</span>: <span class="string">''</span></div><div class="line">    &#125;</div><div class="line">  &#125;,</div><div class="line">  <span class="attr">methods</span>: &#123;</div><div class="line">    <span class="comment">// 绑定 input 事件，获取到输入值，设置到对应的数据中</span></div><div class="line">    updateNameValue(event)&#123;</div><div class="line">      <span class="keyword">this</span>.name = event.target.value;</div><div class="line">    &#125;,</div><div class="line">    updateEmailValue(event)&#123;</div><div class="line">      <span class="keyword">this</span>.email = event.target.value;</div><div class="line">    &#125;</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>我们在设置数据（<code>this.name = event.target.value</code>）的时候，Vue 已经替我们把将数据更新到页面的逻辑处理了。大家也可以去 <a href="https://codepen.io/anon/pen/qzPbWQ" target="_blank" rel="external">codepen</a> 或者 jsfiddle、<a href="https://stackblitz.com/" target="_blank" rel="external">stackblitz</a> 这些在线编码网站上尝试下。</p><p>所以事件驱动和数据驱动一个很重要的区别在于，我们是从每个事件的触发开始设计我们的代码，还是以数据为中心，接收事件触发和更新数据状态的方式来写代码。</p><h3 id="页面编写"><a href="#页面编写" class="headerlink" title="页面编写"></a>页面编写</h3><p>如果要问，vue 和 jQuery 有什么不一样？其实它们从定位到设计到实现上都完全不一样，但是对开发者来说，我们可以做个简单直观的区别：<br><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="comment">&lt;!--1. jQuery + 事件驱动--&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">id</span>=<span class="string">"input"</span> /&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">p</span> <span class="attr">id</span>=<span class="string">"p"</span>&gt;</span><span class="tag">&lt;/<span class="name">p</span>&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="javascript"></span></div><div class="line">$(<span class="string">'#input'</span>).on(<span class="string">'click'</span>, e =&gt; &#123;</div><div class="line">    <span class="keyword">const</span> val = e.target.value;</div><div class="line">    $(<span class="string">'#p'</span>).text(val);</div><div class="line">&#125;)</div><div class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></div><div class="line"></div><div class="line"><span class="comment">&lt;!--2. vue + 数据驱动--&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">v-model</span>=<span class="string">"inputValue"</span> /&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">p</span>&gt;</span>&#123;&#123; inputValue &#125;&#125;<span class="tag">&lt;/<span class="name">p</span>&gt;</span></div></pre></td></tr></table></figure></p><p>当我们在 Vue 中，模板引擎帮我们处理了模板渲染、数据绑定的过程，我们只需要知道这里面只有一个有效数据，即输入框的值。</p><h3 id="页面抽象"><a href="#页面抽象" class="headerlink" title="页面抽象"></a>页面抽象</h3><p>既然使用了数据驱动，那么对页面的一个抽象能力也是很重要的。例如我们现在要写一个列表，数据从后台获取到之后，展示到页面中。</p><ol><li><p>当我们需要渲染成列表时：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="comment">&lt;!--1). jQuery + 事件驱动--&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">ul</span> <span class="attr">id</span>=<span class="string">"ul"</span>&gt;</span><span class="tag">&lt;/<span class="name">ul</span>&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="javascript"></span></div><div class="line"><span class="keyword">const</span> dom = $(<span class="string">'#ul'</span>);</div><div class="line">list.forEach(<span class="function"><span class="params">item</span> =&gt;</span> &#123;</div><div class="line">    dom.append(<span class="string">`&lt;li data-id="<span class="subst">$&#123;item.id&#125;</span>"&gt;&lt;span&gt;<span class="subst">$&#123;item.name&#125;</span>&lt;/span&gt;: &lt;a href="<span class="subst">$&#123;item.href&#125;</span>"&gt;<span class="subst">$&#123;item.href&#125;</span>&lt;/a&gt;&lt;/li&gt;`</span>)</div><div class="line">&#125;);</div><div class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></div><div class="line"></div><div class="line"><span class="comment">&lt;!--2). vue + 数据驱动--&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">ul</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">li</span> <span class="attr">v-for</span>=<span class="string">"item in list"</span> <span class="attr">:key</span>=<span class="string">"item.id"</span>&gt;</span><span class="tag">&lt;<span class="name">span</span>&gt;</span>&#123;&#123;item.name&#125;&#125;<span class="tag">&lt;/<span class="name">span</span>&gt;</span><span class="tag">&lt;<span class="name">a</span> <span class="attr">:href</span>=<span class="string">"item.href"</span>&gt;</span>&#123;&#123;item.href&#125;&#125;<span class="tag">&lt;/<span class="name">a</span>&gt;</span><span class="tag">&lt;/<span class="name">li</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">ul</span>&gt;</span></div></pre></td></tr></table></figure></li><li><p>当我们需要更新一个列表中某个 id 的其中一个数据时（这里需要更改 id 为 3 的 name 值）：</p></li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 1). jQuery + 事件驱动</span></div><div class="line"><span class="keyword">const</span> dom = $(<span class="string">'#ul'</span>);</div><div class="line"><span class="keyword">const</span> id = <span class="number">3</span>;</div><div class="line">dom.find(<span class="string">`li[data-id="<span class="subst">$&#123;id&#125;</span>"] span`</span>).text(<span class="string">'newName3'</span>);</div><div class="line"></div><div class="line"><span class="comment">// 2). vue + 数据驱动</span></div><div class="line"><span class="keyword">const</span> id = <span class="number">3</span>;</div><div class="line">list.find(<span class="function"><span class="params">item</span> =&gt;</span> item.id == <span class="number">3</span>).name == <span class="string">'newName3'</span>;</div></pre></td></tr></table></figure><p>在使用数据驱动的时候，模板渲染的事情会交给框架去完成，我们需要做的就是数据处理而已。那么转换了数据驱动之后，有什么好处呢？</p><p>当我们把一个页面抽象成数据来表示之后，可以做的事情就多了。如果我们把所有的页面、组件、展示内容和接口配置等都变成了配置数据，那么我们就可以把所有的东西都进行配置化。以前也写过一篇<a href="https://godbasin.github.io/2018/06/09/component-with-configuration/">《组件配置化》</a>，另外在玩 Angular 的时候也做过类似的设计，包括<a href="http://angular2-form-schame.godbasin.com/index.html" target="_blank" rel="external">组件的配置–通过配置生成表单</a>和<a href="http://angular-custom-app.godbasin.com/#/home/custom-app" target="_blank" rel="external">页面应用的配置–通过配置生成管理端</a>。当然，Vue 也完全可以做这种事情的，配置化的难度常常不在于技术有多难，而在于怎么把业务进行适当的抽象。</p><h2 id="前端工程化"><a href="#前端工程化" class="headerlink" title="前端工程化"></a>前端工程化</h2><p>其实前端工程化这块，我并不是能理解得很深刻，能讲给大家的，应该只是自己的一种理解了。一般来说，现在的主流构建工具应该是 Webpack，包括我们使用 Vue 官方脚手架生成的代码，构建工具也是 Webpack。</p><p>我们在代码中会使用到很多的资源，图片、样式、代码，还有各式各样的依赖包，而打包的时候怎么实现按需分块、处理依赖关系、不包含多余的文件或内容，同时提供开发和生产环境，包括本地调试自动更新到浏览器这些能力，都是由 Webpack 整合起来的。</p><h3 id="npm-依赖包"><a href="#npm-依赖包" class="headerlink" title="npm 依赖包"></a>npm 依赖包</h3><p>要实现工程化，前端开发几乎都离不开 nodejs 的包管理器 npm，比如前端在搭建本地开发服务以及打包编译前端代码等都会用到。在前端开发过程中，经常用到<code>npm install</code>来安装所需的依赖。</p><p>为什么会有 npm 包的出现呢？因为一个人的力量是有限的，我们在写代码的时候，为了可以将注意力集中到业务开发中，会需要使用别人开源的很多工具、框架和代码包。在很早以前，我们是一个个地下载，或是通过<code>&lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;</code>的方式去引用。</p><p>当一个网站依赖的代码越来越多，其实管理依赖和版本是一件很麻烦的事情。然后 npm 就出现了，作为一个把这些代码集中到一起来管理的工具。</p><p>同时，我们可以结合一些 tree shaking 的能力，在本地构建的时候，把使用的别人的依赖包里没用用到的部分去掉，减小代码包的大小等。</p><p>在安装 Node.js 的时候，npm 的能力也会一块提供到，按照完之后就能直接在机器终端使用<code>npm install xxx</code>这种能力啦，需要安装什么依赖包呢，你可以去<a href="https://www.npmjs.com/" target="_blank" rel="external">npmjs官网</a>搜一下，也可以自己发布一些包供别人使用，例如之前我也写过一个<a href="https://www.npmjs.com/package/v-select2-component" target="_blank" rel="external">v-select2-component</a>组件。</p><h3 id="vue-cli"><a href="#vue-cli" class="headerlink" title="vue cli"></a>vue cli</h3><p>通常来说，脚手架可以让你快速地生成示例代码、搭建本地环境，也可以更新依赖的版本等，避免了每个开发者自行调整开发环境、打包逻辑等配置。</p><p>Vue cli 也提供了这样的能力，更多的可以参考<a href="https://cli.vuejs.org/zh/" target="_blank" rel="external">官方文档</a>。</p><blockquote><p>Vue CLI 致力于将 Vue 生态中的工具基础标准化。它确保了各种构建工具能够基于智能的默认配置即可平稳衔接，这样你可以专注在撰写应用上，而不必花好几天去纠结配置的问题。</p></blockquote><p>使用方式很简单：<br><figure class="highlight cmd"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">// 安装脚手架</div><div class="line">npm install -g @vue/cli</div><div class="line">// 脚手架生成 vue 项目，同时会自动安装依赖</div><div class="line">vue create vue-cli-demo</div></pre></td></tr></table></figure></p><p>生成之后的代码目录是这样的：<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-cli-demo-files.jpg" alt=""></p><h3 id="启动项目"><a href="#启动项目" class="headerlink" title="启动项目"></a>启动项目</h3><p>一般来说，项目会有个 README.md 文件，你能看到一些简单的说明，例如这里生成的：<br><figure class="highlight cmd"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"># Project setup</div><div class="line">yarn install</div><div class="line"></div><div class="line"># Compiles and hot-reloads <span class="keyword">for</span> development</div><div class="line">yarn run serve</div><div class="line"></div><div class="line"># Compiles and minifies <span class="keyword">for</span> production</div><div class="line">yarn run build</div><div class="line"></div><div class="line"># Run your tests</div><div class="line">yarn run test</div><div class="line"></div><div class="line"># Lints and fixes files</div><div class="line">yarn run lint</div></pre></td></tr></table></figure></p><p>yarn 跟 npm 都是差不多的包管理器，区别在于 yarn 在安装时会速度更快（并行、离线等），以及版本统一管理的比较好。但如果你不是有特殊的喜好或者习惯，其实两个都可以用，例如这里的<code>yarn run serve</code>也可以用<code>npm run serve</code>来运行。</p><p>如果有些习惯不好的项目缺了 README，这种时候要怎么去启动一个项目呢？可以查看<code>package.json</code>文件：<br><figure class="highlight json"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">  <span class="attr">"scripts"</span>: &#123;</div><div class="line">    <span class="attr">"serve"</span>: <span class="string">"vue-cli-service serve"</span>,</div><div class="line">    <span class="attr">"build"</span>: <span class="string">"vue-cli-service build"</span>,</div><div class="line">    <span class="attr">"lint"</span>: <span class="string">"vue-cli-service lint"</span></div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p><p>一般来说，开发环境是<code>dev</code>、<code>serve</code>等，生产环境是<code>build</code>，<code>scripts</code>里是一些任务，运行命令<code>npm run taskName</code>就可以启动了。<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-cli-demo-serve.jpg" alt=""></p><p>上图中，我们可以看到任务已经启动了，访问输出的地址（这里是<code>http://localhost:8080/</code>或者<code>http://10.40.120.53:8080/</code>）就能看到我们的项目跑起来了。<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-cli-demo-page.jpg" alt=""></p><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><hr><p>第一 part 结束了，更多的内容还是前端相关。这些主要是这几年对页面渲染的一些新的理解，然后简单用脚手架启动了个 demo 项目。内容是按照自己觉得较清晰的逻辑展开来讲的，如果说你有更好的想法，或是发现我的描述中有哪些不到位的地方，也十分欢迎来交流。<br>工具始终是工具没错，但一个工具为什么受这么多人追捧，它到底解决了什么，你可以从中学习到什么，这些才是个人认为的在使用工具时候收获的很重要的东西。在 Vue 官方文档很完善的情况下，我来给你们补上文档以外的一些技巧和内容吧。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;最近身边学习 Vue 的人也越来越多了，大家都有很多的疑问，为什么前端要用框架、Vue 做了些什么、要怎么上手，等等。距离上一次写 Vue 也过去三年了，是时候再把这三年自己的理解再整理进去了。由于这个系列主要是希望写给可能前端掌握也不深的人，所以会铺垫一些自己认为需要的内容，本节先来讲讲入门前端，作为铺垫，来理解 Vue 这个框架吧。&lt;br&gt;
    
    </summary>
    
      <category term="vue八宝粥" scheme="https://godbasin.github.io/categories/vue%E5%85%AB%E5%AE%9D%E7%B2%A5/"/>
    
    
      <category term="教程" scheme="https://godbasin.github.io/tags/%E6%95%99%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>小程序开发月刊第六期（20190617）</title>
    <link href="https://godbasin.github.io/2019/06/17/wxapp-latest-20190617/"/>
    <id>https://godbasin.github.io/2019/06/17/wxapp-latest-20190617/</id>
    <published>2019-06-17T15:17:13.000Z</published>
    <updated>2019-06-17T15:18:01.387Z</updated>
    
    <content type="html"><![CDATA[<p>太可惜了这个月啥都没有~~<br><a id="more"></a></p><h1 id="小程序-latest"><a href="#小程序-latest" class="headerlink" title="小程序 latest"></a>小程序 latest</h1><h2 id="小程序能力"><a href="#小程序能力" class="headerlink" title="小程序能力"></a>小程序能力</h2><h3 id="「2-7-1版本基础库」新增功能"><a href="#「2-7-1版本基础库」新增功能" class="headerlink" title="「2.7.1版本基础库」新增功能"></a>「2.7.1版本基础库」新增功能</h3><ul><li>新增云开发 Network 面板，<a href="https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/debug.html" target="_blank" rel="external">详情</a>  </li><li>更新组件 cover-view 开发者工具支持  </li><li>其他社区反馈问题修复  </li></ul><p>更多2.7.1版本基础库的新能力及详情，可查看<a href="https://developers.weixin.qq.com/miniprogram/dev/framework/release/" target="_blank" rel="external">《基础库更新日志》</a>。</p><h3 id="「微信开放社区」上线帖子相关问题能力"><a href="#「微信开放社区」上线帖子相关问题能力" class="headerlink" title="「微信开放社区」上线帖子相关问题能力"></a>「微信开放社区」上线帖子相关问题能力</h3><p><img src="http://km.oa.com/files/photos/pictures//20190617//1560737512_91.png" alt="帖子相关"></p><h3 id="更新日志"><a href="#更新日志" class="headerlink" title="更新日志"></a>更新日志</h3><ul><li><a href="https://developers.weixin.qq.com/community/develop/doc/00048cfe698378e701b86d72c56c01" target="_blank" rel="external">社区问题反馈以及功能优化更新（06.03-06.06）</a>  </li><li><a href="https://developers.weixin.qq.com/community/develop/doc/000e00bc1d81e051aca8b759f56401" target="_blank" rel="external">社区问题反馈以及功能优化更新（05.27-05.31）</a>  </li><li><a href="https://developers.weixin.qq.com/community/develop/doc/0002e212b94b58861ba81bbda56801" target="_blank" rel="external">社区问题反馈以及功能优化更新（05.20-05.24）</a>  </li><li><a href="https://developers.weixin.qq.com/community/develop/doc/00000ec37743f0906a98518df51801" target="_blank" rel="external">社区问题反馈以及功能优化更新（05.13-05.17）</a>  </li></ul><h1 id="小程序教程"><a href="#小程序教程" class="headerlink" title="小程序教程"></a>小程序教程</h1><h2 id="社区精选文章"><a href="#社区精选文章" class="headerlink" title="社区精选文章"></a>社区精选文章</h2><ul><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/0000c42fea0668ff36b80d20451813" target="_blank" rel="external">小程序开发另类小技巧 –用户授权篇</a></li><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/0008a4c4f28f30fe3eb863b2750813" target="_blank" rel="external">浅谈小程序运行机制</a></li></ul><p>更多可以查看<a href="https://developers.weixin.qq.com/community/develop/article" target="_blank" rel="external">文章分享</a>。</p><h2 id="最新踩坑-amp-amp-Tips"><a href="#最新踩坑-amp-amp-Tips" class="headerlink" title="最新踩坑 &amp;&amp; Tips"></a>最新踩坑 &amp;&amp; Tips</h2><ol><li>Promise.then 有些情况下不被调用。查了一下基础库的 bug 历史，以前确实有发现过部分 iOS 系统版本上原生 Promise then 不触发的情况，具体触发条件不明。建议考虑自己打个 polyfill 试试。</li></ol><blockquote><p>可以写段检测Promise是否生效的逻辑，然后考虑兜底兼容例如降级到H5之类的…</p></blockquote><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><p>看起来这个月踩坑不多，为啥还是那么忙呢？</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;太可惜了这个月啥都没有~~&lt;br&gt;
    
    </summary>
    
      <category term="小程序双皮奶" scheme="https://godbasin.github.io/categories/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%8F%8C%E7%9A%AE%E5%A5%B6/"/>
    
    
      <category term="教程" scheme="https://godbasin.github.io/tags/%E6%95%99%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>小程序的诞生</title>
    <link href="https://godbasin.github.io/2019/06/17/wxapp-generate/"/>
    <id>https://godbasin.github.io/2019/06/17/wxapp-generate/</id>
    <published>2019-06-17T15:14:52.682Z</published>
    <updated>2018-08-25T06:10:21.026Z</updated>
    
    <content type="html"><![CDATA[<p>大家坐稳，我要开始装逼了。说好要讲小程序系列，替埋头苦干低调做事的开发哥哥们多讲讲小程序的故事吧。<br><a id="more"></a></p><h2 id="小程序与-WebView"><a href="#小程序与-WebView" class="headerlink" title="小程序与 WebView"></a>小程序与 WebView</h2><hr><h3 id="WebView-的飞速发展"><a href="#WebView-的飞速发展" class="headerlink" title="WebView 的飞速发展"></a>WebView 的飞速发展</h3><p>随着公众号的出现和繁荣，WebView 的使用频率也越来越高。不少的企业或是小商家、外包公司开始做 H5 页面，各式各样的 H5 活动页、小商城、小测试、小游戏满天飞。H5 的劣势也很明显，体验太渣，甚至还不能获取很多底层 APP 拥有的功能。</p><p>微信团队也因此提供了一些 JS-SDK 给 Web 开发使用，包括拍摄、录音、语音识别、二维码、地图、支付、分享等能力。从此，Web 开发者可以使用到微信的原生能力，去完成一些之前做不到或者难以做到的事情。</p><h3 id="体验不足的-WebView"><a href="#体验不足的-WebView" class="headerlink" title="体验不足的 WebView"></a>体验不足的 WebView</h3><p>相信大多数人都经历过这样的糟糕体验——白屏。没错，就是这货：</p><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/1535165451%281%29.png" alt="iamge"></p><p>这样的体验直到今天还会出现，有些甚至连加载中的字样都没有。打开一个 WebView 通常会经历以下几个阶段：</p><ul><li>交互无反馈</li><li>到达新的页面，页面白屏</li><li>页面基本框架出现，但是没有数据；页面处于 loading 状态</li><li>出现所需的数据</li></ul><p><strong>除了白屏，影响 Web 体验的问题还有缺少操作的反馈，主要表现在两个方面：页面切换的生硬和点击的迟滞感。</strong></p><p>如果从程序上观察，WebView 启动过程大概分为以下几个阶段：<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/time.png" alt="iamge"></p><p>对于一些有经验的 Web 开发者而言，会使用一些 SPA 的框架，来模拟客户端原生的页面切换过渡，同时使用缓存、CSS 反馈交互、直出页面等技术，来改善体验。</p><p>但并不是所有开发者都有精力和能力去做这么多优化，而作为一个平台，优化用户体验也是平台责任的一部分。</p><h3 id="小程序的出现"><a href="#小程序的出现" class="headerlink" title="小程序的出现"></a>小程序的出现</h3><p>曾经有较长一段时间，我都以为小程序是类似 Weex、React Native 这些框架一样，最终呈现的是原生应用。直到开始写小程序，一些疑惑开始不断冒出来：为什么 CSS 样式的编写跟普通 Web 几乎一摸一样呢？</p><p>才发现，小程序最终的呈现是 WebView，同时夹杂着一些原生组件。所以，小程序到底做了什么，使得体验比正常 WebView 好太多呢？</p><p>我们先来对比下，常见的 <strong>Web 和 Native 的区别</strong>：</p><table><thead><tr><th>-</th><th>Native</th><th>Web</th><th>期望</th></tr></thead><tbody><tr><td>开发门槛</td><td>高</td><td>低</td><td>低</td></tr><tr><td>体验</td><td>好</td><td>白屏、交互反馈差</td><td>接近原生体验</td></tr><tr><td>版本更新</td><td>需审核，迭代慢</td><td>在线更新</td><td>在线更新</td></tr><tr><td>管控性</td><td>平台可管控</td><td>难管控</td><td>可管控</td></tr></tbody></table><p>关于最后一点的管控性，其实作为一个平台这是必须具备的能力，不然平台被滥用，对公众或是平台自身都不是什么好事情。就像现在很多云服务的 COS，也开始管控起来，包括域名绑定、备案等，一是防止有心人士使用平台做些不好的事情，二是对平台自身的保护（防止被封禁等）。</p><p>在这样的需求和期待中，小程序诞生了。</p><h3 id="小程序是期望的产物"><a href="#小程序是期望的产物" class="headerlink" title="小程序是期望的产物"></a>小程序是期望的产物</h3><p><strong>使用 WebView 开发，门槛低，可云端更新。</strong></p><p>不同于 RN、Weex 这些框架，原生组件的开发、样式调整等都和 Web 有太多的不同。对于一个前端开发来说，开发成本较高、调试效率低，若不小心掉到坑里，都不知道该怎么爬出来。</p><p>使用 WebView，可最大化前端开发的优势，同时异步加载的方式，也允许开发者进行在线的版本更新和 BUG 修复。</p><p><strong>通过提供基础能力、原生组件结合等方式，提升用户体验。</strong></p><p>小程序框架提供了完整的基础库，通过微信内置基础库、双线程渲染等方式，提升了小程序启动的体验。同时，开发者可以借用原生组件、API 等能力，做很多普通页面开发做不到的事情，用户也能以此获得原生应用般的体验。</p><p><strong>通过平台发布、审核、下架、封禁等能力，具备对小程序的管控能力。</strong></p><p>小程序框架提供了云端更新的能力，通过代码上传、审核等方式，增强了对开发者的管控能力。保护用户的同时，也保护了平台，以及平台中的其他开发者。</p><p><strong>双线程（逻辑层和渲染层分开），隔离 DOM、BOM 能力，提升体验的同时，可保证 WebView 安全性。</strong></p><p>双线程的模式，使得页面渲染和逻辑代码的加载分开，降低了页面卡壳的可能性。</p><p>同时，由于逻辑层被隔离 DOM 和 BOM 对象，无法获取渲染层的内容，也在一定程度上保护了用户的数据安全。</p><h3 id="打开小程序的新世界"><a href="#打开小程序的新世界" class="headerlink" title="打开小程序的新世界"></a>打开小程序的新世界</h3><p>二维码扫描、搜索、分享、推送等各种方式，都可以打开一个小程序。依靠轻量无需安装、体验优秀、管控严格、方便快捷等各种优势，小程序给用户打开了一个通向新世界的大门。</p><p>而对于开发者，开发成本低、能力齐全、可快速迭代这样的开发模式，又基于大体量的用户数，几乎是无法拒绝的。齐备的底层基础库、自带优化后的体验性能，使得开发者可以专注于业务逻辑的开发，各司其职，一起把产品做好。</p><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><ul><li><a href="https://tech.meituan.com/WebViewPerf.html" target="_blank" rel="external">《WebView性能、体验分析与优化》</a></li><li><a href="https://developers.weixin.qq.com/ebook?action=get_post_info&amp;token=935589521&amp;volumn=1&amp;lang=zh_CN&amp;book=miniprogram&amp;docid=000668c6910b784b00860870a5ac0a" target="_blank" rel="external">《小程序开发指南》</a></li></ul><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><hr><p>用心做好一个产品，在如今乱糟糟的社会里其实也不容易呢。而要做一个优秀的作品，需要有很清晰的方向，即使需要不断探索，也不会迷失方向吧。<br>技术什么的，只要方向正确，总是有办法解决的。后面章节我们也来讲讲小程序的底层框架和设计吧。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;大家坐稳，我要开始装逼了。说好要讲小程序系列，替埋头苦干低调做事的开发哥哥们多讲讲小程序的故事吧。&lt;br&gt;
    
    </summary>
    
      <category term="小程序双皮奶" scheme="https://godbasin.github.io/categories/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%8F%8C%E7%9A%AE%E5%A5%B6/"/>
    
    
      <category term="分享" scheme="https://godbasin.github.io/tags/%E5%88%86%E4%BA%AB/"/>
    
  </entry>
  
  <entry>
    <title>小程序开发月刊第五期（20190515）</title>
    <link href="https://godbasin.github.io/2019/05/15/wxapp-latest-20190515/"/>
    <id>https://godbasin.github.io/2019/05/15/wxapp-latest-20190515/</id>
    <published>2019-05-15T15:52:31.000Z</published>
    <updated>2019-05-19T04:15:07.150Z</updated>
    
    <content type="html"><![CDATA[<p>听说小程序开发月刊还是挺实用的~~<br><a id="more"></a></p><h1 id="小程序-latest"><a href="#小程序-latest" class="headerlink" title="小程序 latest"></a>小程序 latest</h1><h2 id="小程序能力"><a href="#小程序能力" class="headerlink" title="小程序能力"></a>小程序能力</h2><h3 id="「2-7-0版本基础库」新增功能"><a href="#「2-7-0版本基础库」新增功能" class="headerlink" title="「2.7.0版本基础库」新增功能"></a>「2.7.0版本基础库」新增功能</h3><ol><li>相机组件新增逐帧数据接口，该接口可让开发者对相机摄像头捕捉到的图像进行实时地识别和处理，实现如实时美妆、文字识别等功能，详见<a href="https://developers.weixin.qq.com/miniprogram/dev/component/camera.html" target="_blank" rel="external">《camera组件》</a>。  </li><li>新增富文本编辑器组件，用户可以方便地对图文进行编辑，实现更丰富的图文展示效果，可扫描以下小程序码体验，接入指引详见<a href="https://developers.weixin.qq.com/miniprogram/dev/component/editor.html" target="_blank" rel="external">《富文本编辑器组件》</a>。  </li><li>TabBar新增支持配置网络图片，方便开发者在不同节日、活动等场景下，更灵活地配置底部导航栏内的相应图标，详见<a href="https://developers.weixin.qq.com/miniprogram/dev/api/wx.setTabBarItem.html" target="_blank" rel="external">《TabBar》</a>。</li><li>为了帮助开发者解决“刘海屏”的适配问题，新增查询手机安全区域功能，在安全区域中的内容可保证不被遮挡，详见<a href="https://developers.weixin.qq.com/miniprogram/dev/api/wx.getSystemInfo.html" target="_blank" rel="external">《getSystemInfo》</a>。  </li><li>为了提升用户对图片的操作效率以及快速“识别图中的小程序码”，在用户未进入图片预览模式时，也可以通过长按图片唤起图片的操作菜单，详见<a href="https://developers.weixin.qq.com/miniprogram/dev/component/image.html" target="_blank" rel="external">《image组件》</a>。</li><li>canvas画布组件支持WebGL，提升了图形渲染效率，可更流畅地展示3D场景和模型。</li></ol><p>更多2.7.0版本基础库的新能力及详情，可查看<a href="https://developers.weixin.qq.com/miniprogram/dev/framework/release.html" target="_blank" rel="external">《更新日志》</a>。</p><h3 id="「微信开放社区」新增关注功能"><a href="#「微信开放社区」新增关注功能" class="headerlink" title="「微信开放社区」新增关注功能"></a>「微信开放社区」新增关注功能</h3><p>微信开放社区新增「关注用户」功能，社区用户可以对自己关心的用户进行关注，成为这个用户的关注者，更方便看到该用户的动态。</p><blockquote><p>例如关注下被删什么的</p></blockquote><h3 id="wx-getWifiList接口需获取用户位置信息授权"><a href="#wx-getWifiList接口需获取用户位置信息授权" class="headerlink" title="wx.getWifiList接口需获取用户位置信息授权"></a>wx.getWifiList接口需获取用户位置信息授权</h3><p>2019年5月17日起新提交发布的版本若未获取用户位置权限，则在android平台上将无法正常调用wx.getWifiList接口。该调整策略在微信android客户端 7.0.4 版本生效。<br>更多详情请查看<a href="https://developers.weixin.qq.com/community/develop/doc/0002ec7dc6cd5894957808bd854c01" target="_blank" rel="external">wx.getWifiList接口需获取用户位置信息授权后使用</a>。</p><h3 id="「小程序管理后台」新增-Source-Map-文件下载功能"><a href="#「小程序管理后台」新增-Source-Map-文件下载功能" class="headerlink" title="「小程序管理后台」新增 Source Map 文件下载功能"></a>「小程序管理后台」新增 Source Map 文件下载功能</h3><p>小程序管理后台-【运维中心】-【错误查询】新增线上版本 Source Map 文件下载功能，开发者可通过 Source Map 文件定位错误信息对应的源代码位置。</p><h3 id="更新日志"><a href="#更新日志" class="headerlink" title="更新日志"></a>更新日志</h3><ul><li><a href="https://developers.weixin.qq.com/community/develop/doc/0002c407d0c0d023d8889391651001" target="_blank" rel="external">社区问题反馈以及功能优化更新（05.06-05.10）</a></li><li><a href="https://developers.weixin.qq.com/community/develop/doc/00000a9a2040505a5b887ef7156c01" target="_blank" rel="external">社区问题反馈以及功能优化更新（04.22-04.26）</a></li><li><a href="https://developers.weixin.qq.com/community/develop/doc/00008813390720866b784e0fd5ac01" target="_blank" rel="external">社区问题反馈以及功能优化更新（04.15-04.19）</a></li></ul><h2 id="开发者工具"><a href="#开发者工具" class="headerlink" title="开发者工具"></a>开发者工具</h2><h3 id="微信开发者工具支持ES6-转ES5"><a href="#微信开发者工具支持ES6-转ES5" class="headerlink" title="微信开发者工具支持ES6+转ES5"></a>微信开发者工具支持ES6+转ES5</h3><p>新版工具增加了增强编译的选项来增强ES6转ES5的能力，启用后会使用新的编译逻辑以及提供额外的选项供开发者使用。</p><ul><li>使用preset-env,支持最新的ECMAScript语法</li><li>共享helpers函数，默认放在项目@babel/runtime目录，可通过项目配置文件配置</li><li>支持<code>async/await</code>语法，按需注入regeneratorRuntime，目录位置与helpers函数一致</li><li>关于polyfill，基础库中已经引入了大量的es6相关的polyfill可参考文档，增强编译下，新增：<code>Array.prototype.includes</code>(es7)、<code>Object.entries</code>(es8)、<code>Object.values</code>(es8)</li></ul><p>更多详细能力请参考<a href="https://developers.weixin.qq.com/community/develop/doc/00066877c54eb0ff5488b54885b801" target="_blank" rel="external">微信开发者工具 1.02.1905081 更新说明</a>。</p><p>这里补充两个容易踩的坑：</p><ol><li>旧版开发者工具只支持ES6转ES5，这里的ES6并不包括ES7/ES8，所以<code>Object.entries</code>(es8)、<code>Object.values</code>(es8)、<code>async/await</code>这些不支持，需要自己编译。</li><li>微信开发者工具 1.02.1905081 已知bug:<ul><li>体验评分会卡死</li><li>带分包功能的小程序，预览后分包页面出现白屏</li></ul></li><li>项目使用babel编译，在编译async/await时会生成Promise，同时Promise里捕捉到的异常都没有处理。会导致js语法错误不能被MP平台监控，从而没法正常告警（也无法在App.onError里捕捉到）。使用开发者工具编译可能也会存在同样的问题，建议自行hack Promise，加上catch把异常抛出或添加到App.onError里。</li></ol><h3 id="「小程序·云开发」新增了HTTP-API支持"><a href="#「小程序·云开发」新增了HTTP-API支持" class="headerlink" title="「小程序·云开发」新增了HTTP API支持"></a>「小程序·云开发」新增了HTTP API支持</h3><p>云调用新增了HTTP API支持，目前提供了云函数触发、数据库导入导出、文件下载上传删除、获取腾讯云API调用凭证等能力。欢迎大家体验及反馈。</p><ul><li><a href="https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/index.html" target="_blank" rel="external">文档</a></li><li><a href="https://cloud.tencent.com/document/api/876/34809" target="_blank" rel="external">腾讯云可用API</a></li></ul><h3 id="「小程序·云开发」新增-消息推送-支持"><a href="#「小程序·云开发」新增-消息推送-支持" class="headerlink" title="「小程序·云开发」新增 消息推送 支持"></a>「小程序·云开发」新增 消息推送 支持</h3><p>云调用近期将新增 消息推送 支持，现已支持客服消息推送，物流等功能的消息推送后续将陆续推出，现在已经可以在内测版开发者工具中体验客服消息推送了（在云函数中接收客服消息和回复），可新建云开发快速启动模板然后查看里面的云调用客服消息示例，欢迎大家来体验~<br>文档：</p><ul><li><a href="https://developers.weixin.qq.com/miniprogram/dev/framework/server-ability/message-push.html" target="_blank" rel="external">消息推送文档</a></li><li><a href="https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/customer-message/receive.html" target="_blank" rel="external">客服消息事件</a></li><li><a href="https://developers.weixin.qq.com/miniprogram/dev/api-backend/customerServiceMessage.send.html?t=19051721#method-cloud" target="_blank" rel="external">云调用</a></li></ul><h3 id="「小程序·云开发」新增免-SessionKey-获取开放数据"><a href="#「小程序·云开发」新增免-SessionKey-获取开放数据" class="headerlink" title="「小程序·云开发」新增免 SessionKey 获取开放数据"></a>「小程序·云开发」新增免 SessionKey 获取开放数据</h3><p>云调用近期将新增支持免 SessionKey 获取开放数据，现已经可以在内测版开发者工具上体验了（开放数据包括用户信息，分享信息，微信运动等），待基础库 2.7.0 发布完成即可在手机上使用，在内测版工具上可以选择 9.9.9 版本基础库使用，欢迎大家来体验和吐槽~</p><ul><li><a href="https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html" target="_blank" rel="external">开放数据获取方式文档</a></li><li><a href="https://developers.weixin.qq.com/miniprogram/dev/api/wx.getUserInfo.html" target="_blank" rel="external">示例 getUserInfo 文档</a></li><li>支持的 API 和组件有：wx.getUserInfo，wx.getShareInfo, wx.getWeRunData 以及 button 组件的 getUserInfo</li></ul><h3 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h3><p><strong>Typescript能力：</strong><br>最新的小程序官方Typing库更新了！新增Component/云函数等支持！</p><ul><li>Git地址: <a href="https://github.com/wechat-miniprogram/api-typings" target="_blank" rel="external">https://github.com/wechat-miniprogram/api-typings</a></li></ul><h1 id="小程序教程"><a href="#小程序教程" class="headerlink" title="小程序教程"></a>小程序教程</h1><h2 id="社区精选文章"><a href="#社区精选文章" class="headerlink" title="社区精选文章"></a>社区精选文章</h2><ul><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/00022e1ce2cd38f14e88cdee15bc13" target="_blank" rel="external">永远对微信小程序保持尊重——小程序心得体会和开发经验</a></li><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/00080a33d6c4284bb57886c8e56c13" target="_blank" rel="external">腾讯课堂小程序详情页开发总结</a></li><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/000e462d9f0348423778380095b413" target="_blank" rel="external">有赞前端质量保障体系</a></li><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/000aa441bdc990492478911e85c013" target="_blank" rel="external">小打卡|如何组件化拆分一个200+页面的小程序</a></li></ul><p>更多可以查看<a href="https://developers.weixin.qq.com/community/develop/article" target="_blank" rel="external">文章分享</a>。</p><h2 id="最新踩坑-amp-amp-Tips"><a href="#最新踩坑-amp-amp-Tips" class="headerlink" title="最新踩坑 &amp;&amp; Tips"></a>最新踩坑 &amp;&amp; Tips</h2><ol><li><p>wx.getLocation无响应（既不会success，也不会fail，可能导致小程序不可用），在安卓上会偶现，和机型无关。<br>目前建议的解决方法是手动加个超时。</p></li><li><p>小程序授权获取实名信息文档:<a href="https://developers.weixin.qq.com/community/business/doc/000804439ac77080c8672c77451c0d。" target="_blank" rel="external">https://developers.weixin.qq.com/community/business/doc/000804439ac77080c8672c77451c0d。</a><br>同时文中 设置-接口能力 路径改为 开发-接口能力。</p></li><li><p><code>&lt;picker&gt;</code>组件，IOS不支持YYYY-MM-DD的日期格式，而picker默认返回的格式是YYYY-MM-DD，有日期转时间戳的场景的话，需要正则替换一下。<br>PS: H5里也存在同样的问题。</p></li><li><p>小程序做自适应客户端字体大小，目前需要通过<a href="https://developers.weixin.qq.com/miniprogram/dev/api/wx.getSystemInfo.html" target="_blank" rel="external">wx.getSystemInfo</a>接口获取fontSizeSetting字段，来调整样式。</p><ul><li>fontSizeSetting: 用户字体大小（单位px）。以微信客户端「我-设置-通用-字体大小」中的设置为准 </li></ul></li><li><p>发布新的小程序后，终端用户手机里面还有老的小程序的缓存，可使用<a href="https://developers.weixin.qq.com/miniprogram/dev/api/UpdateManager.html" target="_blank" rel="external">UpdateManager</a>强制用户升级到最新版本。</p></li><li><p>小程序基础库版本兼容问题，可设置最低基础库版本：登录小程序管理后台，进入“设置-&gt;基本设置-&gt;基础库最低版本设置”进行配置。</p><ul><li>在配置前，开发者可查看近30天内访问小程序的用户的基础库版本占比，以帮助开发者了解当前用户使用的情况。</li><li>上述设置需要在iOS 6.5.8或安卓 6.5.7及以上微信客户端版本生效。太低版本不支持最低基础库版本设置。</li></ul></li></ol><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><p>在这个世界上，没有人能在所有事情上都与你看法一致，所以既然有人在与你在最重要的方面价值观相同，也与你有实践价值观的相同做法，那就要确保与这些人为伍。 –《原则》</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;听说小程序开发月刊还是挺实用的~~&lt;br&gt;
    
    </summary>
    
      <category term="小程序双皮奶" scheme="https://godbasin.github.io/categories/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%8F%8C%E7%9A%AE%E5%A5%B6/"/>
    
    
      <category term="教程" scheme="https://godbasin.github.io/tags/%E6%95%99%E7%A8%8B/"/>
    
  </entry>
  
</feed>
