Repository navigation
Expand file tree
/
Copy pathatom.xml
More file actions
459 lines (273 loc) · 317 KB
/
Copy pathatom.xml
File metadata and controls
459 lines (273 loc) · 317 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
<?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-07-11T15:20:44.836Z</updated>
<id>https://godbasin.github.io/</id>
<author>
<name>被删</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<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"> {</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: [{ <span class="attr">text</span>: <span class="string">"服务信息"</span> }, { <span class="attr">text</span>: <span class="string">"新增"</span> }] <span class="comment">// 子菜单列表</span></div><div class="line"> },</div><div class="line"> {</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>: [{ <span class="attr">text</span>: <span class="string">"产品信息"</span> }]</div><div class="line"> },</div><div class="line"> {</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"> }</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 = {</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">};</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>) =></span> {<span class="keyword">return</span> {<span class="attr">id</span>: i + <span class="number">1</span>, ...x};});</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">/* {</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">} */</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> {</div><div class="line"> data() {</div><div class="line"> <span class="comment">// 绑定数据</span></div><div class="line"> <span class="keyword">return</span> {</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"> };</div><div class="line"> },</div><div class="line"> <span class="attr">methods</span>: {</div><div class="line"> <span class="comment">// 新增一个数据</span></div><div class="line"> addTableItem(item = {}){</div><div class="line"> <span class="comment">// 添加到列表中,同时自增 id </span></div><div class="line"> <span class="keyword">this</span>.tableData.push({...item, <span class="attr">id</span>: <span class="keyword">this</span>.tableData.length + <span class="number">1</span>});</div><div class="line"> },</div><div class="line"> <span class="comment">// 删除一个数据</span></div><div class="line"> deleteTableItem(id){</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> =></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"> },</div><div class="line"> <span class="comment">// 移动一个数据</span></div><div class="line"> moveTableItem(id, direction){</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> =></span> x.id === id);</div><div class="line"> <span class="keyword">switch</span>(direction){</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 > <span class="number">0</span>) {</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"> }</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 < dataLength - <span class="number">1</span>) {</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"> }</div><div class="line"> <span class="keyword">break</span>;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</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({</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> =></span> h(App)</div><div class="line">});</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"><!-- my-component.vue --></span></div><div class="line"><span class="comment"><!-- </span></div><div class="line"> .vue 文件里,</div><div class="line"> 使用 <template> 隔离 HTML,</div><div class="line"> 使用 <script> 隔离 Javascript,</div><div class="line"> 使用 <style> 隔离 CSS</div><div class="line"> --></div><div class="line"><span class="tag"><<span class="name">template</span>></span></div><div class="line"> <span class="tag"><<span class="name">div</span>></span>This will be pre-compiled<span class="tag"></<span class="name">div</span>></span></div><div class="line"><span class="tag"></<span class="name">template</span>></span></div><div class="line"><span class="comment"><!-- 不喜欢写到一起,script 和 styles 也可以用 src 引入文件 --></span></div><div class="line"><span class="tag"><<span class="name">script</span> <span class="attr">src</span>=<span class="string">"./my-component.js"</span>></span><span class="undefined"></span><span class="tag"></<span class="name">script</span>></span></div><div class="line"><span class="tag"><<span class="name">style</span> <span class="attr">src</span>=<span class="string">"./my-component.css"</span>></span><span class="undefined"></span><span class="tag"></<span class="name">style</span>></span></div></pre></td></tr></table></figure><p>所以,原来的示例代码里少了<code><template></template></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 要去掉 -> 在<code>App.vue</code>文件里,把<code><img alt="Vue logo" src="./assets/logo.png"></code>去掉,还有 body 自带的 margin 也去掉。<br>2) 这些滚动条太丑了,干掉! -> 把<code><el-container></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"><!-- 双大括号可绑定普通文本,此处会把 data 中的 msg 的值绑定到对应的模版上 --></span></div><div class="line"><span class="tag"><<span class="name">span</span>></span>Message: {{ msg }}<span class="tag"></<span class="name">span</span>></span></div><div class="line"><span class="comment"><!-- 双大括号可使用 JavaScript 表达式 --></span></div><div class="line"><span class="tag"><<span class="name">span</span>></span>{{ msg.split('').reverse().join('') }}/span></div><div class="line"><span class="comment"><!-- v-html 输出的内容不会被模版引擎过滤(可参考上节内容),故需要小心 XSS 漏洞 --></span></div><div class="line"><span class="tag"><<span class="name">p</span> <span class="attr">v-html</span>=<span class="string">"rawHtml"</span>></span><span class="tag"></<span class="name">p</span>></span></div><div class="line"><span class="comment"><!-- v-bind 可用来绑定属性(对比双大括号{{}}只能插入到节点内容中) --></span></div><div class="line"><span class="tag"><<span class="name">div</span> <span class="attr">v-bind:id</span>=<span class="string">"dynamicId"</span>></span><span class="tag"></<span class="name">div</span>></span></div><div class="line"><span class="comment"><!-- v-bind:xxx 可缩写为 :xxx --></span></div><div class="line"><span class="tag"><<span class="name">div</span> <span class="attr">:id</span>=<span class="string">"dynamicId"</span>></span><span class="tag"></<span class="name">div</span>></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"><<span class="name">template</span>></span></div><div class="line"> <span class="comment"><!-- 这里传入 tableData 给到 my-table 组件的 data 值里 --></span></div><div class="line"> <span class="tag"><<span class="name">my-table</span> <span class="attr">:data</span>=<span class="string">"tableData"</span>></span><span class="tag"></<span class="name">my-table</span>></span></div><div class="line"> <span class="comment"><!-- my-table 组件中可以通过 props 中获取到这个值 --></span></div><div class="line"><span class="tag"></<span class="name">template</span>></span></div><div class="line"><span class="tag"><<span class="name">script</span>></span><span class="javascript"></span></div><div class="line"><span class="keyword">export</span> <span class="keyword">default</span> {</div><div class="line"> <span class="attr">props</span>: {</div><div class="line"> <span class="attr">data</span>: {</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> =></span> {}, <span class="comment">// 默认值为空数组 []</span></div><div class="line"> }</div><div class="line"> },</div><div class="line"> <span class="attr">methods</span>: {</div><div class="line"> someFunction(){</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"> }</div><div class="line"> }</div><div class="line">}</div><div class="line"><span class="tag"></<span class="name">script</span>></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"><!-- v-on:事件名 可以绑定事件监听,在事件触发的时候,则会执行绑定的 js 代码 --></span></div><div class="line"><span class="tag"><<span class="name">button</span> <span class="attr">v-on:click</span>=<span class="string">"counter += 1"</span>></span>Add 1<span class="tag"></<span class="name">button</span>></span></div><div class="line"><span class="comment"><!-- v-on:事件名 可以缩写为 @事件名 --></span></div><div class="line"><span class="tag"><<span class="name">button</span> @<span class="attr">click</span>=<span class="string">"counter += 1"</span>></span>Add 1<span class="tag"></<span class="name">button</span>></span></div><div class="line"><span class="comment"><!-- 事件绑定除了简单的 js 代码,还可以绑定 methods 里的函数 --></span></div><div class="line"><span class="tag"><<span class="name">button</span> @<span class="attr">click</span>=<span class="string">"counterAddOne"</span>></span>Add 1<span class="tag"></<span class="name">button</span>></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"><!-- 我是子组件 --></span></div><div class="line"><span class="tag"><<span class="name">template</span>></span></div><div class="line"> <span class="comment"><!-- v-model 请参考后面的说明 --></span></div><div class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">v-model</span>=<span class="string">"val"</span> /></span></div><div class="line"> <span class="tag"><<span class="name">button</span> @<span class="attr">click</span>=<span class="string">"clickDone"</span>></span>done<span class="tag"></<span class="name">button</span>></span></div><div class="line"><span class="tag"></<span class="name">template</span>></span></div><div class="line"><span class="tag"><<span class="name">script</span>></span><span class="javascript"></span></div><div class="line"><span class="keyword">export</span> <span class="keyword">default</span> {</div><div class="line"> data(){</div><div class="line"> <span class="keyword">return</span> {</div><div class="line"> <span class="attr">val</span>: <span class="string">''</span> <span class="comment">// 输入框的值</span></div><div class="line"> }</div><div class="line"> },</div><div class="line"> <span class="attr">methods</span>: {</div><div class="line"> clickDone(){</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"> }</div><div class="line"> }</div><div class="line">}</div><div class="line"><span class="tag"></<span class="name">script</span>></span></div><div class="line"></div><div class="line"><span class="comment"><!-- 我是父组件 --></span></div><div class="line"><span class="tag"><<span class="name">template</span>></span></div><div class="line"> <span class="comment"><!-- 监听下子组件的 done 事件 --></span></div><div class="line"> <span class="tag"><<span class="name">child-component</span> @<span class="attr">done</span>=<span class="string">"getChildData"</span>></span><span class="tag"></<span class="name">child-component</span>></span></div><div class="line"><span class="tag"></<span class="name">template</span>></span></div><div class="line"><span class="tag"><<span class="name">script</span>></span><span class="javascript"></span></div><div class="line"><span class="keyword">export</span> <span class="keyword">default</span> {</div><div class="line"> <span class="attr">methods</span>: {</div><div class="line"> getChildData(value){</div><div class="line"> <span class="comment">// 参数 value,是子组件触发事件时传的参数,这里是输入框的值</span></div><div class="line"> alert(value);</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div><div class="line"><span class="tag"></<span class="name">script</span>></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"><<span class="name">template</span>></span></div><div class="line"> <span class="comment"><!-- 事件绑定除了简单的 js 代码,还可以绑定 methods 里的函数 --></span></div><div class="line"> <span class="comment"><!-- 例如我们最简单的 v-model 指令,其实是下面的语法糖 --></span></div><div class="line"> <span class="tag"><<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> /></span></div><div class="line"> <span class="comment"><!-- 它也可以写成 v-model --></span></div><div class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">v-model</span>=<span class="string">"val"</span> /></span></div><div class="line"><span class="tag"></<span class="name">template</span>></span></div><div class="line"><span class="tag"><<span class="name">script</span>></span><span class="javascript"></span></div><div class="line"><span class="keyword">export</span> <span class="keyword">default</span> {</div><div class="line"> data(){</div><div class="line"> <span class="keyword">return</span> {</div><div class="line"> <span class="attr">val</span>: <span class="string">''</span></div><div class="line"> }</div><div class="line"> },</div><div class="line"> <span class="attr">methods</span>: {</div><div class="line"> updateValue(event){</div><div class="line"> <span class="keyword">this</span>.val = event.target.value;</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div><div class="line"><span class="tag"></<span class="name">script</span>></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"><!-- default-openeds 为默认展开的菜单项,以 index 序号 --></span></div><div class="line"><span class="tag"><<span class="name">el-menu</span> <span class="attr">:default-openeds</span>=<span class="string">"['1', '3']"</span>></span></div><div class="line"> <span class="comment"><!-- el-submenu 为带子菜单的父菜单,index 为每组菜单的序号 --></span></div><div class="line"> <span class="tag"><<span class="name">el-submenu</span> <span class="attr">index</span>=<span class="string">"1"</span>></span></div><div class="line"> <span class="comment"><!-- 下面是父菜单内容,包括父菜单 icon 和父菜单名字 --></span></div><div class="line"> <span class="tag"><<span class="name">template</span> <span class="attr">slot</span>=<span class="string">"title"</span>></span><span class="tag"><<span class="name">i</span> <span class="attr">class</span>=<span class="string">"el-icon-message"</span>></span><span class="tag"></<span class="name">i</span>></span>导航一<span class="tag"></<span class="name">template</span>></span></div><div class="line"> <span class="tag"><<span class="name">el-menu-item-group</span>></span></div><div class="line"> <span class="comment"><!-- 子菜单选项,包括 index 序号和子菜单名字 --></span></div><div class="line"> <span class="tag"><<span class="name">el-menu-item</span> <span class="attr">index</span>=<span class="string">"1-1"</span>></span>选项1<span class="tag"></<span class="name">el-menu-item</span>></span></div><div class="line"> <span class="tag"><<span class="name">el-menu-item</span> <span class="attr">index</span>=<span class="string">"1-2"</span>></span>选项2<span class="tag"></<span class="name">el-menu-item</span>></span></div><div class="line"> <span class="tag"></<span class="name">el-menu-item-group</span>></span></div><div class="line"> <span class="tag"></<span class="name">el-submenu</span>></span></div><div class="line"> <span class="comment"><!-- el-menu-item 为不带子菜单的父菜单,index 为每组菜单的序号 --></span></div><div class="line"> <span class="tag"><<span class="name">el-menu-item</span> <span class="attr">index</span>=<span class="string">"2"</span>></span></div><div class="line"> <span class="comment"><!-- 父菜单内容,包括父菜单 icon 和父菜单名字 --></span></div><div class="line"> <span class="tag"><<span class="name">i</span> <span class="attr">class</span>=<span class="string">"el-icon-menu"</span>></span><span class="tag"></<span class="name">i</span>></span></div><div class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">slot</span>=<span class="string">"title"</span>></span>导航二<span class="tag"></<span class="name">span</span>></span></div><div class="line"> <span class="tag"></<span class="name">el-menu-item</span>></span></div><div class="line"><span class="tag"></<span class="name">el-menu</span>></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"><!-- 顺便调整了下颜色 --></span></div><div class="line"><span class="tag"><<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"> ></div><div class="line"> <span class="comment"><!-- 遍历生成父菜单选项 --></span></div><div class="line"> <span class="tag"><<span class="name">template</span> <span class="attr">v-for</span>=<span class="string">"menu in menus"</span>></span></div><div class="line"> <span class="comment"><!-- 有子菜单的时候,就用 el-submenu,再绑个序号 index --></span></div><div class="line"> <span class="tag"><<span class="name">el-submenu</span> <span class="attr">v-if</span>=<span class="string">"menu.subMenus && 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"> <span class="tag"><<span class="name">template</span> <span class="attr">slot</span>=<span class="string">"title"</span>></span></div><div class="line"> <span class="comment"><!-- 绑个父菜单的 icon --></span></div><div class="line"> <span class="tag"><<span class="name">i</span> <span class="attr">:class</span>=<span class="string">"menu.icon"</span>></span><span class="tag"></<span class="name">i</span>></span></div><div class="line"> <span class="comment"><!-- 再绑个父菜单的名称 text --></span></div><div class="line"> <span class="comment"><!-- slot 其实类似于占位符,可以去 Vue 官方文档了解一下插槽 --></span></div><div class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">slot</span>=<span class="string">"title"</span>></span>{{menu.text}}<span class="tag"></<span class="name">span</span>></span></div><div class="line"> <span class="tag"></<span class="name">template</span>></span></div><div class="line"> <span class="tag"><<span class="name">el-menu-item-group</span>></span></div><div class="line"> <span class="comment"><!-- 子菜单也要遍历,同时绑上子菜单名称 text,也要绑个序号 index --></span></div><div class="line"> <span class="tag"><<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>></span>{{subMenu.text}}<span class="tag"></<span class="name">el-menu-item</span>></span></div><div class="line"> <span class="tag"></<span class="name">el-menu-item-group</span>></span></div><div class="line"> <span class="tag"></<span class="name">el-submenu</span>></span></div><div class="line"> <span class="comment"><!-- 没子菜单的时候,就用 el-menu-item,也要绑个序号 index --></span></div><div class="line"> <span class="tag"><<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>></span></div><div class="line"> <span class="comment"><!-- 绑个父菜单的 icon --></span></div><div class="line"> <span class="tag"><<span class="name">i</span> <span class="attr">:class</span>=<span class="string">"menu.icon"</span>></span><span class="tag"></<span class="name">i</span>></span></div><div class="line"> <span class="comment"><!-- 再绑个父菜单的名称 text --></span></div><div class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">slot</span>=<span class="string">"title"</span>></span>{{menu.text}}<span class="tag"></<span class="name">span</span>></span></div><div class="line"> <span class="tag"></<span class="name">el-menu-item</span>></span></div><div class="line"> <span class="tag"></<span class="name">template</span>></span></div><div class="line"><span class="tag"></<span class="name">el-menu</span>></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>) =></span> {</div><div class="line"> <span class="keyword">return</span> {</div><div class="line"> ...x,</div><div class="line"> <span class="comment">// 子菜单就拼接${父菜单index}-${子菜单index}</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>) =></span> {</div><div class="line"> <span class="keyword">return</span> { ...y, <span class="attr">index</span>: <span class="string">`<span class="subst">${i}</span>-<span class="subst">${j}</span>`</span> };</div><div class="line"> }),</div><div class="line"> <span class="comment">// 父菜单就把 index 加上好了</span></div><div class="line"> index: <span class="string">`<span class="subst">${i}</span>`</span></div><div class="line"> };</div><div class="line">});</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"><!-- data 里绑定表格数据,同时这里调整了下样式 --></span></div><div class="line"><span class="tag"><<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>></span></div><div class="line"> <span class="comment"><!-- prop 传绑定 tableData 的数据 id,表头名称 id,同时设了下宽度 --></span></div><div class="line"> <span class="tag"><<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>></span><span class="tag"></<span class="name">el-table-column</span>></span></div><div class="line"> <span class="comment"><!-- prop 传绑定 tableData 的数据 date,表头名称日期 --></span></div><div class="line"> <span class="tag"><<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>></span><span class="tag"></<span class="name">el-table-column</span>></span></div><div class="line"> <span class="comment"><!-- prop 传绑定 tableData 的数据 name,表头名称姓名 --></span></div><div class="line"> <span class="tag"><<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>></span><span class="tag"></<span class="name">el-table-column</span>></span></div><div class="line"> <span class="comment"><!-- prop 传绑定 tableData 的数据 phone,表头名称电话 --></span></div><div class="line"> <span class="tag"><<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>></span><span class="tag"></<span class="name">el-table-column</span>></span></div><div class="line"> <span class="comment"><!-- prop 传绑定 tableData 的数据 address,表头名称地址 --></span></div><div class="line"> <span class="tag"><<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>></span><span class="tag"></<span class="name">el-table-column</span>></span></div><div class="line"> <span class="comment"><!-- 该列固定在右侧,表头名称操作 --></span></div><div class="line"> <span class="tag"><<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>></span></div><div class="line"> <span class="tag"><<span class="name">template</span> <span class="attr">slot-scope</span>=<span class="string">"scope"</span>></span></div><div class="line"> <span class="comment"><!-- 添加了个删除按钮,绑定了前面定义的删除事件 deleteTableItem,传入参数 id --></span></div><div class="line"> <span class="tag"><<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>></span>删除<span class="tag"></<span class="name">el-button</span>></span></div><div class="line"> <span class="comment"><!-- 分别添加了上移和下移按钮,绑定了前面定义的移动事件 moveTableItem,传入参数 id 和移动方向 --></span></div><div class="line"> <span class="tag"><<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>></span>上移<span class="tag"></<span class="name">el-button</span>></span></div><div class="line"> <span class="tag"><<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>></span>下移<span class="tag"></<span class="name">el-button</span>></span></div><div class="line"> <span class="tag"></<span class="name">template</span>></span></div><div class="line"> <span class="tag"></<span class="name">el-table-column</span>></span></div><div class="line"><span class="tag"></<span class="name">el-table</span>></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"><!-- 找个地方添加一个新增的按钮,点击的时候出现表单的弹窗,以及把表单内容设置为初始值 --></span></div><div class="line"><span class="tag"><<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 = {};"</span>></span>新增<span class="tag"></<span class="name">el-button</span>></span></div><div class="line"><span class="comment"><!-- Form --></span></div><div class="line"><span class="comment"><!-- el-dialog 是弹窗样式,title 绑定弹窗的标题内容,visible 绑定弹窗是否展示 --></span></div><div class="line"><span class="tag"><<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>></span></div><div class="line"> <span class="tag"><<span class="name">el-form</span> <span class="attr">:model</span>=<span class="string">"form"</span>></span></div><div class="line"> <span class="comment"><!-- el-form-item 绑定表单样式,label 表单的名称,formLabelWidth 设置 label 的宽度 --></span></div><div class="line"> <span class="tag"><<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>></span></div><div class="line"> <span class="comment"><!-- 里面装载表单元素,这里装了个选择日期的组件,v-model 绑定选择值,value-format设置绑定值的格式,type 设置选择的范围,这里 date 表示到天 --></span></div><div class="line"> <span class="tag"><<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>></span><span class="tag"></<span class="name">el-date-picker</span>></span></div><div class="line"> <span class="tag"></<span class="name">el-form-item</span>></span></div><div class="line"> <span class="tag"><<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>></span></div><div class="line"> <span class="tag"><<span class="name">el-input</span> <span class="attr">v-model</span>=<span class="string">"form.name"</span>></span><span class="tag"></<span class="name">el-input</span>></span></div><div class="line"> <span class="tag"></<span class="name">el-form-item</span>></span></div><div class="line"> <span class="tag"><<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>></span></div><div class="line"> <span class="tag"><<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>></span><span class="tag"></<span class="name">el-input</span>></span></div><div class="line"> <span class="tag"></<span class="name">el-form-item</span>></span></div><div class="line"> <span class="tag"><<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>></span></div><div class="line"> <span class="tag"><<span class="name">el-input</span> <span class="attr">v-model</span>=<span class="string">"form.address"</span>></span><span class="tag"></<span class="name">el-input</span>></span></div><div class="line"> <span class="tag"></<span class="name">el-form-item</span>></span></div><div class="line"> <span class="tag"></<span class="name">el-form</span>></span></div><div class="line"> <span class="tag"><<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>></span></div><div class="line"> <span class="comment"><!-- 点击取消的时候,设置弹窗不可见 --></span></div><div class="line"> <span class="tag"><<span class="name">el-button</span> @<span class="attr">click</span>=<span class="string">"dialogFormVisible = false"</span>></span>取 消<span class="tag"></<span class="name">el-button</span>></span></div><div class="line"> <span class="comment"><!-- 点击确定的时候,设置弹窗不可见,同时添加一项内容 --></span></div><div class="line"> <span class="tag"><<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>></span>确 定<span class="tag"></<span class="name">el-button</span>></span></div><div class="line"> <span class="tag"></<span class="name">div</span>></span></div><div class="line"><span class="tag"></<span class="name">el-dialog</span>></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> {</div><div class="line"> data() {</div><div class="line"> <span class="keyword">return</span> {</div><div class="line"> <span class="attr">dialogFormVisible</span>: <span class="literal">false</span>, <span class="comment">// 弹窗是否出现</span></div><div class="line"> form: {}, <span class="comment">// 用作表单绑定的内容</span></div><div class="line"> formLabelWidth: <span class="string">'120px'</span>, <span class="comment">// 表单 label 的宽度</span></div><div class="line"> };</div><div class="line"> }</div><div class="line">}</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">
<p>本节内容主要包括使用Vue框架过程中需要掌握的一些基本概念,以及怎么使用现有的一些开源库和组件快速创建项目。另外再附赠对状态管理、数据传递的一些方法和理解叭。总而言之,这一节开始会是与Vue紧密相关的内容啦。<br>
</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">
<p>写故事是突发奇想,所以要怎么架构整个系列的内容也没有特别好的想法。所以就从我认为比较重要的开始讲起吧,很多有趣的故事,都是和自己原则的制定和死守相关的~</p>
</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"><<span class="name">html</span>></span></div><div class="line"> <span class="tag"><<span class="name">head</span>></span><span class="tag"></<span class="name">head</span>></span></div><div class="line"> <span class="tag"><<span class="name">body</span>></span></div><div class="line"> <span class="tag"><<span class="name">h1</span>></span>我的第一个标题<span class="tag"></<span class="name">h1</span>></span></div><div class="line"> <span class="tag"><<span class="name">p</span>></span>我的第一个段落。<span class="tag"></<span class="name">p</span>></span></div><div class="line"> <span class="tag"></<span class="name">body</span>></span></div><div class="line"><span class="tag"></<span class="name">html</span>></span></div></pre></td></tr></table></figure></p><p>这里面包括两个子模快:</p><ul><li><code><head></code>:常包括控制样式的<code><link></code>标签、控制浏览器特殊逻辑的<code><meta></code>标签、控制代码执行逻辑的<code><script></code>,不展示到页面。</li><li><code><body></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"><<span class="name">div</span>></span></div><div class="line"> <span class="tag"><<span class="name">a</span>></span>123<span class="tag"></<span class="name">a</span>></span></div><div class="line"> <span class="tag"><<span class="name">p</span>></span>456<span class="tag"><<span class="name">span</span>></span>789<span class="tag"></<span class="name">span</span>></span><span class="tag"></<span class="name">p</span>></span></div><div class="line"><span class="tag"></<span class="name">div</span>></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><p></code>元素的文字都变成红色 </li><li>class 匹配:类的匹配,例如<code>.color-red{color: red}</code>会让所有<code><xxx class="color-red"></code>元素的文字都变成红色 </li><li>id 匹配:id标识符的匹配,例如<code>#color-red{color: red}</code>会让<code><xxx id="color-red"></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><xxx id="a"></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">'<p>测试</p>'</span></div><div class="line"><span class="comment">// 2. 插入一个<a>元素</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. 删除第一个元素,在这里是前面的<p>测试</p></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">'<p>测试</p>'</span></div><div class="line"><span class="comment">// 2. 插入一个<a>元素</span></div><div class="line">a.innerHTML = <span class="string">'<p>测试</p><a></a>'</span></div><div class="line"><span class="comment">// 3. 删除第一个元素,在这里是前面的<p>测试</p></span></div><div class="line">a.innerHTML = <span class="string">'<a></a>'</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> => 2.生成<code>Render树</code> => 3.计算各个节点的<code>大小</code>/<code>position</code>/<code>z-index</code> => 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"><<span class="name">form</span>></span></div><div class="line"> Name: <span class="tag"><<span class="name">p</span> <span class="attr">id</span>=<span class="string">"name-value"</span>></span><span class="tag"></<span class="name">p</span>></span></div><div class="line"> <span class="tag"><<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> /></span></div><div class="line"> Email: <span class="tag"><<span class="name">p</span> <span class="attr">id</span>=<span class="string">"email-value"</span>></span><span class="tag"></<span class="name">p</span>></span></div><div class="line"> <span class="tag"><<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> /></span></div><div class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"submit"</span> /></span></div><div class="line"><span class="tag"></<span class="name">form</span>></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>{</div><div class="line"> nameValueEl.innerText = e.srcElement.value;</div><div class="line">}</div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">updateEmailValue</span>(<span class="params">e</span>)</span>{</div><div class="line"> emailValueEl.innerText = e.srcElement.value;</div><div class="line">}</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"><<span class="name">div</span>></span>{{ data }}<span class="tag"></<span class="name">div</span>></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({</div><div class="line"> <span class="attr">data</span>: {</div><div class="line"> <span class="attr">data</span>: <span class="string">'测试文本'</span></div><div class="line"> }</div><div class="line">})</div></pre></td></tr></table></figure></p><p>最终页面展示内容为<code><div>测试文本</div></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><div></div></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 = {</div><div class="line"> <span class="attr">dom</span>: {</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"> {<span class="attr">type</span>: <span class="string">'text'</span>, <span class="attr">value</span>: <span class="string">''</span>}</div><div class="line"> ]</div><div class="line"> },</div><div class="line"> <span class="attr">binding</span>: [</div><div class="line"> {<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>}</div><div class="line"> ]</div><div class="line">}</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>{</div><div class="line"> <span class="keyword">const</span> {dom, binding = []} = 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) => {</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> =></span> x.valueName == name);</div><div class="line"> <span class="comment">// 若找到值绑定的对应节点,则更新其值。</span></div><div class="line"> <span class="keyword">if</span>(obj){</div><div class="line"> baseDom.find(<span class="string">`[data-node-index="<span class="subst">${obj.nodeIndex}</span>"]`</span>).innerHTML = value;</div><div class="line"> }</div><div class="line"> });</div><div class="line">}</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>{</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> {type, children = [], nodeIndex, ele, value} = domObj;</div><div class="line"> <span class="keyword">if</span>(type == <span class="string">'dom'</span>){</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> =></span> {</div><div class="line"> childString += getDOMString(x);</div><div class="line"> });</div><div class="line"> <span class="comment">// dom对象,拼接生成对象字符串</span></div><div class="line"> <span class="keyword">return</span> <span class="string">`<<span class="subst">${ele}</span> data-node-index="<span class="subst">${nodeIndex}</span>"><span class="subst">${childString}</span></<span class="subst">${ele}</span>>`</span>;</div><div class="line"> }<span class="keyword">else</span> <span class="keyword">if</span>(type == <span class="string">'text'</span>){</div><div class="line"> <span class="comment">// 若为textNode,返回text的值</span></div><div class="line"> <span class="keyword">return</span> value;</div><div class="line"> }</div><div class="line">}</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><div></code>,经过 AST 生成一个对象,最终还是生成一个<code><div></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> {</div><div class="line"> data() {</div><div class="line"> <span class="keyword">return</span> {</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"> }</div><div class="line"> }</div><div class="line">}</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"><<span class="name">form</span>></span></div><div class="line"> Name: <span class="tag"><<span class="name">p</span>></span>{{ name }}<span class="tag"></<span class="name">p</span>></span></div><div class="line"> <span class="tag"><<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> /></span></div><div class="line"> Email: <span class="tag"><<span class="name">p</span>></span>{{ email }}<span class="tag"></<span class="name">p</span>></span></div><div class="line"> <span class="tag"><<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> /></span></div><div class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"submit"</span> /></span></div><div class="line"><span class="tag"></<span class="name">form</span>></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> {</div><div class="line"> data() {</div><div class="line"> <span class="keyword">return</span> {</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"> }</div><div class="line"> },</div><div class="line"> <span class="attr">methods</span>: {</div><div class="line"> <span class="comment">// 绑定 input 事件,获取到输入值,设置到对应的数据中</span></div><div class="line"> updateNameValue(event){</div><div class="line"> <span class="keyword">this</span>.name = event.target.value;</div><div class="line"> },</div><div class="line"> updateEmailValue(event){</div><div class="line"> <span class="keyword">this</span>.email = event.target.value;</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</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"><!--1. jQuery + 事件驱动--></span></div><div class="line"><span class="tag"><<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> /></span></div><div class="line"><span class="tag"><<span class="name">p</span> <span class="attr">id</span>=<span class="string">"p"</span>></span><span class="tag"></<span class="name">p</span>></span></div><div class="line"><span class="tag"><<span class="name">script</span>></span><span class="javascript"></span></div><div class="line">$(<span class="string">'#input'</span>).on(<span class="string">'click'</span>, e => {</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">})</div><div class="line"><span class="tag"></<span class="name">script</span>></span></div><div class="line"></div><div class="line"><span class="comment"><!--2. vue + 数据驱动--></span></div><div class="line"><span class="tag"><<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> /></span></div><div class="line"><span class="tag"><<span class="name">p</span>></span>{{ inputValue }}<span class="tag"></<span class="name">p</span>></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"><!--1). jQuery + 事件驱动--></span></div><div class="line"><span class="tag"><<span class="name">ul</span> <span class="attr">id</span>=<span class="string">"ul"</span>></span><span class="tag"></<span class="name">ul</span>></span></div><div class="line"><span class="tag"><<span class="name">script</span>></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> =></span> {</div><div class="line"> dom.append(<span class="string">`<li data-id="<span class="subst">${item.id}</span>"><span><span class="subst">${item.name}</span></span>: <a href="<span class="subst">${item.href}</span>"><span class="subst">${item.href}</span></a></li>`</span>)</div><div class="line">});</div><div class="line"><span class="tag"></<span class="name">script</span>></span></div><div class="line"></div><div class="line"><span class="comment"><!--2). vue + 数据驱动--></span></div><div class="line"><span class="tag"><<span class="name">ul</span>></span></div><div class="line"> <span class="tag"><<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>></span><span class="tag"><<span class="name">span</span>></span>{{item.name}}<span class="tag"></<span class="name">span</span>></span><span class="tag"><<span class="name">a</span> <span class="attr">:href</span>=<span class="string">"item.href"</span>></span>{{item.href}}<span class="tag"></<span class="name">a</span>></span><span class="tag"></<span class="name">li</span>></span></div><div class="line"><span class="tag"></<span class="name">ul</span>></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">${id}</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> =></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><script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script></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">{</div><div class="line"> <span class="attr">"scripts"</span>: {</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"> }</div><div class="line">}</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">
<p>最近身边学习 Vue 的人也越来越多了,大家都有很多的疑问,为什么前端要用框架、Vue 做了些什么、要怎么上手,等等。距离上一次写 Vue 也过去三年了,是时候再把这三年自己的理解再整理进去了。由于这个系列主要是希望写给可能前端掌握也不深的人,所以会铺垫一些自己认为需要的内容,本节先来讲讲入门前端,作为铺垫,来理解 Vue 这个框架吧。<br>
</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="最新踩坑 && Tips"></a>最新踩坑 && 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">
<p>太可惜了这个月啥都没有~~<br>
</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&token=935589521&volumn=1&lang=zh_CN&book=miniprogram&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">
<p>大家坐稳,我要开始装逼了。说好要讲小程序系列,替埋头苦干低调做事的开发哥哥们多讲讲小程序的故事吧。<br>
</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="最新踩坑 && Tips"></a>最新踩坑 && 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><picker></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>小程序基础库版本兼容问题,可设置最低基础库版本:登录小程序管理后台,进入“设置->基本设置->基础库最低版本设置”进行配置。</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">
<p>听说小程序开发月刊还是挺实用的~~<br>
</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>小程序开发月刊第四期(20190415)</title>
<link href="https://godbasin.github.io/2019/04/15/wxapp-latest-20190415/"/>
<id>https://godbasin.github.io/2019/04/15/wxapp-latest-20190415/</id>
<published>2019-04-15T15:54:30.000Z</published>
<updated>2019-04-21T06:15:14.652Z</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>小程序评测能力已上线beta版本,登录管理后台-【功能】-【小程序评测】可以查看。<br>评测达标的小程序,可获得「2小时极速审核」和「内测功能体验」奖励。</p><ul><li>常见问题查看:<a href="http://kf.qq.com/faq/190108BJnmUN190108RrEnqE.html" target="_blank" rel="external">http://kf.qq.com/faq/190108BJnmUN190108RrEnqE.html</a></li></ul><h3 id="小程序管理后台新增页面收录设置的开关"><a href="#小程序管理后台新增页面收录设置的开关" class="headerlink" title="小程序管理后台新增页面收录设置的开关"></a>小程序管理后台新增页面收录设置的开关</h3><p>小程序管理后台新增页面收录设置的开关,开发者可根据业务需要进行设置:</p><ol><li>小程序管理后台-【设置】-【基本设置】-【页面收录设置】,可对你的小程序帐号进行收录的开启和关闭的设置。</li><li>更新 微信开发者工具 ,可对 sitemap 进行特定页面的配置,可参考<a href="https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html" target="_blank" rel="external">小程序开发文档</a>。</li><li>此设置默认开启。若小程序中存在不适合展示的内容,或开发者不希望使用微信展示其小程序,建议开发者自行关闭该设置,<a href="https://mp.weixin.qq.com/wxopen/readtemplate?t=config/collection_agreement_tmpl" target="_blank" rel="external">详情查看</a>。</li></ol><p>PS: sitemap 功能仿原生目前是不会被收录的。开关的逻辑他们会特殊处理下,默认关闭(目前是默认开启)。同时:</p><ol><li>如果设置了不允许被搜索,但开启了允许被收录:不会进入搜索。</li><li>如果关闭了允许被收录,sitemap 设置会无效。</li></ol><h3 id="小程序关联公众号策略调整"><a href="#小程序关联公众号策略调整" class="headerlink" title="小程序关联公众号策略调整"></a>小程序关联公众号策略调整</h3><p>为了降低公众号与小程序间的合作门槛,我们将调整小程序关联公众号策略如下:</p><ol><li>公众号关联小程序将无需小程序管理员确认。</li><li>取消“小程序最多关联500个公众号”的限制。</li><li>若希望小程序在被关联时保留管理员确认环节,可前往“小程序管理后台-设置-基本设置-关联公众号设置”修改设置项。</li><li>公众号文章中可直接使用小程序素材,无需关联小程序。<br>开发者可在小程序管理后台-【设置】-【关联设置】中管理已关联的公众号。</li></ol><h3 id="小程序用户访问数据上报优化"><a href="#小程序用户访问数据上报优化" class="headerlink" title="小程序用户访问数据上报优化"></a>小程序用户访问数据上报优化</h3><p>为了提供更准确的用户访问数据,小程序数据上报做了系统优化,由微信客户端上报切换为基础库上报。当用户离开小程序页面,触发<code>onHide</code>或<code>onUnload</code>函数时,公共库会上报此次用户访问行为。<br>优化详情如下:</p><ul><li>优化了部分小程序存在页面脏数据的问题</li><li>优化了部分小程序存在错误小程序跳转数据的问题</li><li>当用户点击进入小程序,但小程序框架未加载完成,用户退出小程序,则不做上报,确保每次上报数据均为有效访问行为</li></ul><h3 id="更新日志"><a href="#更新日志" class="headerlink" title="更新日志"></a>更新日志</h3><ul><li><a href="https://developers.weixin.qq.com/community/develop/doc/000c8033998118cb3168228965b401" target="_blank" rel="external">周社区问题反馈以及功能优化更新(04.01-04.05)</a></li><li><a href="https://developers.weixin.qq.com/community/develop/doc/0006882b218580bcaf58036f556c01" target="_blank" rel="external">周社区问题反馈以及功能优化更新(03.25-03.29)</a></li><li><a href="https://developers.weixin.qq.com/community/develop/doc/000c249a62c968e59648fdcd051001" target="_blank" rel="external">周社区问题反馈以及功能优化更新(03.11-03.16)</a></li><li><a href="https://developers.weixin.qq.com/community/develop/doc/000e8a372e0608040c481445956001" target="_blank" rel="external">周社区问题反馈以及功能优化更新(03.04-03.08)</a></li></ul><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/miniprogram/dev/devtools/qywx-dev.html" target="_blank" rel="external">https://developers.weixin.qq.com/miniprogram/dev/devtools/qywx-dev.html</a></li></ul><h3 id="「小程序·云开发」云函数本地调试功能上线"><a href="#「小程序·云开发」云函数本地调试功能上线" class="headerlink" title="「小程序·云开发」云函数本地调试功能上线"></a>「小程序·云开发」云函数本地调试功能上线</h3><p>小程序·云开发提供了云函数本地调试功能,方便开发者在本地进行云函数调试,提高开发效率。开发者可通过右键点击云函数名唤起本地调试界面。目前云函数本地调试的支持手动触发和模拟器触发两种请求方式。</p><ul><li>参考文档:<a href="https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/functions/local-debug.html" target="_blank" rel="external">https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/functions/local-debug.html</a></li></ul><h3 id="「小程序·云开发」新增云调用"><a href="#「小程序·云开发」新增云调用" class="headerlink" title="「小程序·云开发」新增云调用"></a>「小程序·云开发」新增云调用</h3><p>云调用是云开发提供的基于云函数使用小程序开放接口的能力。目前覆盖服务端调用的场景,后续将会陆续开放开放数据调用、消息推送、支付等其他多种使用场景。<br>云调用需要在云函数中通过<code>wx-server-sdk</code>使用。在云函数中使用云调用调用服务端接口无需换取<code>access_token</code>,只要是在从小程序端触发的云函数中发起的云调用都经过微信自动鉴权,可以在登记权限后直接调用如发送模板消息等开放接口。</p><ul><li>云调用文档:<a href="https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/functions/openapi.html" target="_blank" rel="external">https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/functions/openapi.html</a></li></ul><h3 id="「小程序·云开发」全新云控制台上线"><a href="#「小程序·云开发」全新云控制台上线" class="headerlink" title="「小程序·云开发」全新云控制台上线"></a>「小程序·云开发」全新云控制台上线</h3><p>云开发控制台经过全新设计和改版,优化交互和视觉体验,功能分类更加清晰、各项功能更加易用</p><h3 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h3><p><strong>开发工具新增版本区分:</strong></p><ul><li><a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/nightly.html" target="_blank" rel="external">开发版 Nightly Build</a>: 日常构建版本,用于尽快修复缺陷和敏捷上线小的特性;开发自测验证,稳定性欠佳</li><li><a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/rc.html" target="_blank" rel="external">预发布版 RC Build</a>: 预发布版本,包含大的特性;通过内部测试,稳定性尚可</li><li><a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/stable.html" target="_blank" rel="external">稳定版 Stable Build</a></li></ul><p>大家可以根据需要,下载对应的版本开发~</p><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/0004e0543b8878a53b589986451413" target="_blank" rel="external">小程序自定义组件知多少</a></li><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/000eaadb944de06374485c3ed51813" target="_blank" rel="external">小程序多端框架全面测评</a></li><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/000caad3c4cbc03a5648e01e951013" target="_blank" rel="external">一种小成本的线下定位方案 —2019腾讯数字文创节小程序开发有感</a></li><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/000aa4e19a0d50bf0f6893b9f56c13" target="_blank" rel="external">Comi - 小程序 markdown 渲染和代码高亮解决方案</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="最新踩坑 && Tips"></a>最新踩坑 && Tips</h2><ol><li><p>如果小程序里已经授权过,例如地理位置信息,取消授权的方法:右上角… -> 关于xxx -> 设置。</p></li><li><p>【小程序体验评分】遇到性能体验的问题,可以在小程序开发工具里找到协助定位性能的能力。<br>体验评分是一项给小程序的体验好坏打分的功能,它会在小程序运行过程中实时检查,分析出一些可能导致体验不好的地方,并且定位出哪里有问题,以及给出一些优化建议。</p></li><li><p>小程序里嵌套 web-view,小程序往 web-view 里传数据方法:</p><ol><li>把参数拼装在 url 中传进去,可通过 hash。</li><li>通过 postMessage 传递,只会在特定时机(小程序后退、组件销毁、分享)触发并收到消息, 参考:<a href="https://developers.weixin.qq.com/miniprogram/dev/component/web-view.html。" target="_blank" rel="external">https://developers.weixin.qq.com/miniprogram/dev/component/web-view.html。</a></li><li>通过后台拉取。</li></ol></li><li><p>setData单次设置的数据超过1024kB,工具上测试正常,手机上会报错。Taro 在 setData 的时候会带上一些不需要的数据。</p></li><li><p>小程序的 setStorage 缓存,会在客户端保存尽量久的时间,以下两种情况(会从最不常用的小程序删起):</p><ol><li>客户端空间不够。</li><li>小程序总体容量超过客户端容量5%。</li></ol></li></ol><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><p>这种没人关注依然狗狗祟祟坚持做的事情,大概是我最喜欢和最擅长的了。^_^</p>]]></content>
<summary type="html">
<p>每月都有小程序期刊,这个月新功能特别多~~<br>
</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>前端这几年--1.转岗之路</title>
<link href="https://godbasin.github.io/2019/04/13/about-front-end-1-begin-in/"/>
<id>https://godbasin.github.io/2019/04/13/about-front-end-1-begin-in/</id>
<published>2019-04-13T12:32:34.000Z</published>
<updated>2019-04-13T12:48:41.926Z</updated>
<content type="html"><![CDATA[<p>常常在想,成为前端也有好几年了,新知识新技术层出不穷,那这些年来到底积累了什么呢。如果说有什么可以给到你们,除去前端技术相关,大概也只剩下这一路上的历程和思考,以及一些方式和方法了。<br><a id="more"></a></p><h1 id="从哪里来"><a href="#从哪里来" class="headerlink" title="从哪里来"></a>从哪里来</h1><p>无名小辈的自传,总是不值得关注的。不过我自己的这一生,如果写成一本书或是拍成电视剧的话,毫无疑问我也能成为个主角。</p><h2 id="我的名字被删"><a href="#我的名字被删" class="headerlink" title="我的名字被删"></a>我的名字被删</h2><p>所以首先要自我介绍一下,我叫被删。为什么要叫被删,因为这是我名字的谐音。我爱用谐音,这不为什么我起的英文名叫 basin,出国旅游的时候才知道自己有多蠢,现在倒是挺喜欢用 deleted,不过都没啥影响,你们只需要记得被删就好了,很好记。</p><h2 id="非科班出身"><a href="#非科班出身" class="headerlink" title="非科班出身"></a>非科班出身</h2><p>如果真的写自传,可能我得从幼儿园记事开始写起了。虽然我的成长过程,回想起来也色彩缤纷,也很丰富精彩,不过那些都和如今要跟你们讲的没有多大关系。所以,我们从大学毕业开始吧。</p><p>很遗憾,我大学学的光信息,每天都在各种计算推导,稍微好玩些的也就是全息相关的实验。说来怎么会和写代码扯上关系的呢?大学里我闲得无聊,刚好有个朋友在跟着教授写项目,刚好去参观了下。教授搞的 web 物理引擎,以及游戏设计,他给我演示了在做的一个汽车透视结构图,好像好厉害的样子。</p><p>“好像好厉害”这几个字大概是人生中各种入坑的原因。就跟当初报这个光信息的专业,也是觉得好像很高大上。</p><p>毕业的时候,其实三方签的去华为,做的数通,简单来说包括路由器交换机防火墙这块。培训两个月,然后大家都发配到世界各地去支持了。每天住在酒店里,基本上除了机房、酒店,没有其他地方去,也没有什么人可以交流。做了半年,实在耐不住寂寞,辞职了。离职前刚评了绩效,拿了A跑了,被主管批了一顿。</p><h2 id="外包又怎样"><a href="#外包又怎样" class="headerlink" title="外包又怎样"></a>外包又怎样</h2><p>很显然,非科班出身的我,又没有类似的开发经验,自学了一周多就疯狂投简历。找工作很头疼,裸辞的一番热血,再热烈也很容易被浇灭。</p><p>我还记得找的第一份工作,老板看我学历还行、长得也还可以,让我当秘书。他开了两份offer,秘书的待遇比前端的好一倍不止。那一天真的很苦恼,最后是几位写后台的大哥鼓励下,才下定决心开始学代码。</p><p>那会从华为出来,待遇的落差总会不断地提醒我,到底是为了什么呢?但满怀的热情使得我每天上班充满干劲,下班后也继续在床上打着灯看书和写demo学习。那是jQuery横行的年代,似乎只需要掌握了它,你就能所向披靡。噢当然还有CSS,CSS的调试也是10%的理解+90%的日积月累不断沉淀的。</p><p>那段时间能感觉到,成长很快,几位后台开发小哥哥带着我入门,然后就停不下来了。我曾经在面试的时候说过我学习能力很强,但是通常别人会问,你怎么证明呢?(语塞)</p><p>不知道为什么,现在似乎大家多多少少都会不正视外包,“要不是能力不够也不会当外包”、“不能指望他们能学会什么”这样的话也经常会听到。可能是因为很多人的经历和体验里,都是比较顺畅阻力较少。而我也很荣幸曾经置于职业的低谷,使得很多时候能看到更多的可能性。</p><h2 id="小公司也可以很棒"><a href="#小公司也可以很棒" class="headerlink" title="小公司也可以很棒"></a>小公司也可以很棒</h2><p>成为前端以来,成长最快、回忆最满的一段工作经验,是在商汤度过的。当然,那时候深圳分部还只有几十个人,还只是个小公司。</p><p>接触 angularJS、react.js、vue.js 这些框架,都是在这里完成的。当然最初选型的时候,那会大热的是 angularJS,react 刚刚起步。在其中一个小伙伴还在犹豫用哪个的时候,我就吭哧吭哧地用 angularJS 来重构了。</p><p>后面项目越来越大,来了两个实习生跟着一块干。两个小伙都很棒,当然人多之后,项目管理就会出现问题了。代码规范、接口规范这些都慢慢地一边磨合一边调整,其中一个小弟带着我们一块用上了 Typescript,另外一个则发起组件封装、抽象方式的探讨。</p><p>在这里,深刻领悟到的是,争吵和摩擦其实可以带来很好的正反馈。因为每个人都拥有不一样的想法和角度,在相互碰撞和磨合之后,所谓的集众人之智慧,时可以做出非常棒的事情来。技术博客的念头,也是几位实习生的起哄下开始持续写的。</p><p>新人的优势在于敢想敢做,而老人家的长处在于帮着收拾残局。好几次要发布了,合版本出现很多问题,熬不住的便让他们先走,于是除了在华为之外,熬夜通宵发版的经历,也都是在这里度过的。即使这样,很多新的尝试也让我们快速地成长,收获更多。</p><p>那时候的小团队,大概是目前为止遇到过最优秀的团队了。从前端、后台、平台层、算法层,那段通宵赶版本、去现场跟上线的日子,也打破了对加班的一些偏见。和优秀的人一起,有相同的目标、冲着一样的目的地,努力和坚持便成了回忆里锦上添花的一部分。</p><h2 id="大公司也是人的合集"><a href="#大公司也是人的合集" class="headerlink" title="大公司也是人的合集"></a>大公司也是人的合集</h2><p>曾经,BAT 是所有像我这种从底层爬起的开发者的梦想。当时离开,总监也问我,为什么想走,他从那里面来,也不过如此,这里有更多的机会。我看着他说,体验过的人才有资格说不,我没去过,所以我要去。</p><p>然后我来腾讯了。</p><p>和每次换工作的过程都一样,干脆利落,还没开始准备就开始扔简历了。所以前面几次电面都没过,直到有一个面试官问我,“我看你前面都面了一两次了,你不知道来腾讯这些是必备知识吗?”。我跟他讲,“我知道,我只是还没准备,要是准备了肯定能过。”他给了我一周的时间。</p><p>一周后,电话如约打来,当然我也对答如流,直接约了第二天现场面试,后续当然就是入职啦。在腾讯差不多两年了,也换了一次部门,整体来说,这边的技术能力和氛围,会有些不如预期吧。</p><p>其实早该想明白的,大公司制度再完善,规范再严格。组成公司的,其实也都是一个个有血有肉的人,有人在的地方,都会有躲不掉的一些事情。不过越是困难,原则和坚持才会愈加显得有意义。</p><p>总而言之,时间流逝严重,一眨眼毕业快五年了,而我如今也走到了这里。这里面我遇到了很多的人,也有很多的故事。好的、坏的,一言难尽,等哪天我有心情写故事的时候,再看看要不要写吧~</p><h1 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h1><p>为什么要在博客写这种东西呢?对这个世界来说,成功的人的经历才值得借鉴,他们说的话才有参考价值。</p><p>我还未成功,但是成功的定义在每个人眼里都不一样吧。对我来说,有些事情坚持下来了,几年,十几年,几十年,坚持到一辈子,大概就是属于我的成功了。目前来说,几年下来,也算是达成阶段性的成就了。</p><p>嗯,说不定什么时候,我也只剩下讲故事这项技能了。趁着还爱着敲键盘的时候,代码也好,文字也好,此时此刻,我在做着喜欢的事情,也够了。</p><p>活了这么多年,我也终于发现了自己最擅长的事情,大概是自己搭建舞台,自己表演,然后自己给自己鼓掌。</p><p>“啪,啪,啪”。</p>]]></content>
<summary type="html">
<p>常常在想,成为前端也有好几年了,新知识新技术层出不穷,那这些年来到底积累了什么呢。如果说有什么可以给到你们,除去前端技术相关,大概也只剩下这一路上的历程和思考,以及一些方式和方法了。<br>
</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>小程序开发月刊第三期(20190315)</title>
<link href="https://godbasin.github.io/2019/03/15/wxapp-latest-20190315/"/>
<id>https://godbasin.github.io/2019/03/15/wxapp-latest-20190315/</id>
<published>2019-03-15T15:33:32.000Z</published>
<updated>2019-03-21T15:34:48.507Z</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>数据监听器可以用于监听和响应任何属性和数据字段的变化。从小程序基础库版本 2.6.1 开始支持。<a href="https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/observer.html" target="_blank" rel="external">详情</a></p><h3 id="微信开放社区新增“文章分享”模块"><a href="#微信开放社区新增“文章分享”模块" class="headerlink" title="微信开放社区新增“文章分享”模块"></a>微信开放社区新增“文章分享”模块</h3><p>微信开放社区新增了“文章分享”的模块,希望大家可以将设计、开发和运营的小程序经验分享给更多的用户,将大家平时积累的经验分享出来,也沉淀下来。<br>对于优质的文章,会被选为精选,精选文章会逐步在社区首页展示,并且每周依次在开发者的公众号被推送。</p><h3 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h3><ul><li>新增小程序音频中断开始和结束事件<code>wx.onAudioInterruptionBegin</code>、<code>wx.onAudioInterruptionEnd</code>。<a href="https://developers.weixin.qq.com/miniprogram/dev/api/wx.onAudioInterruptionBegin.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/000cca3e4142280b76388814356c01" target="_blank" rel="external">周社区问题反馈以及功能优化更新(02.26-03.02)</a></li><li><a href="https://developers.weixin.qq.com/community/develop/doc/000ac69de541e8b0fb28c9fab5b001" target="_blank" rel="external">周社区问题反馈以及功能优化更新(02.11-02.22)</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/000e64f299cdd8c55a3848f7451013" target="_blank" rel="external">【优化】小程序优化-代码篇</a></li><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/00026a9b984b604ad9287077a51413" target="_blank" rel="external">从源码看微信小程序启动过程</a></li><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/000c8eba1ec3b8c7ce287954c53c13" target="_blank" rel="external">小程序架构设计(二)</a></li><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/0000cc199900b8f66628f610b56413" target="_blank" rel="external">小程序性能和体验优化方法</a></li><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/000ccc7eec00e0e93c280153251c13" target="_blank" rel="external">一次在微信小程序里跑 h5 页面的尝试</a></li><li><a href="https://developers.weixin.qq.com/community/develop/article/doc/000a4c1620c188f3adf7db9ab5b413" target="_blank" rel="external">小程序架构设计(一)</a></li></ul><p>更多可以查看<a href="https://developers.weixin.qq.com/community/develop/article?tag=%E7%B2%BE%E9%80%89" target="_blank" rel="external">文章分享</a>。</p><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><p>微信开发者社区新增了文章分享后,大家可以多多去逛一下,集合大家的智慧和力量,越走越远。</p>]]></content>
<summary type="html">
<p>这个月很多能力在开发中,不过社区推出文章分享沉淀,赶紧去看看吧~<br>
</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/03/10/work-2-article/"/>
<id>https://godbasin.github.io/2019/03/10/work-2-article/</id>
<published>2019-03-10T14:12:34.000Z</published>
<updated>2019-03-13T13:53:37.165Z</updated>
<content type="html"><![CDATA[<p>经验沉淀、定期思考,并以文章的形式进行记录和分享。工作中也一样,把许多经验整理成文档,当然也不乏“你工作就是写文章吧”这样的声音。至于为什么要这么做呢,我想谈谈我的一些想法。<br><a id="more"></a></p><h2 id="程序员与文档的前世今生"><a href="#程序员与文档的前世今生" class="headerlink" title="程序员与文档的前世今生"></a>程序员与文档的前世今生</h2><p>我们程序员,最讨厌的就是看别人的代码,和没有文档。文档,其实是很小的一件事。项目也好,框架或是工具库都好,作为原作者或者踩过坑的,写文档其实并不会花很多的时间。</p><p>也遇到过,有人纯手动撸了一整个前端框架,好几个项目都在用,但是一句话文档都没有,甚至<code>README</code>都没有。怎么启动项目?有什么注意事项?新人来了都重新踩一遍坑,和口口相传。</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><p>分享,交流,共同成长和提升,在这样一个过程里,很多人会有所收获。</p><p>很多人怕被超越,便悄咪咪地把自己所学所有藏了起来。但真正的核心竞争力,即使你共享出来,也没有人能复制。因为那是你独特的人生经历、性格脾气形成的,没有两个人是完全一致的。</p><p>如果,每个人都把自己踩过的坑填了,把积累的经验都沉淀下来,我们在奔向美好未来的路上是可以越跑越快的。每个人都要学习的课本、所有出版的读物、科研界的成果,不都是这样的存在吗?</p><p>我们每个程序员都会去的 Github,不也正是这样的存在吗?</p><p>狭隘的视角,局限的只是自己。外面世界的步伐,并不会因为某个井口太窄而受到阻碍。</p><h2 id="很多事物需要记录"><a href="#很多事物需要记录" class="headerlink" title="很多事物需要记录"></a>很多事物需要记录</h2><p>很久以前就有开始,每隔一段时间就做一段生活记录,反思、想象、或是单纯的记录。</p><p>接触代码之后,发现要学的东西太多了,也开始慢慢记录踩过的坑、自身的理解、经验的沉淀。接手一个项目,也会习惯性地补齐 README 和一些相关的说明文档。试用框架、工具的新能力,也会顺便记下一些总结和心得。</p><p>这些内容,最终都会落到文章的形式进行沉淀。同时,也顺手扔到博客里,或者其他平台上,若能恰好帮上一些人的忙,便再好不过。</p><p>如果遇到的一些问题,都能在网上找到解决方案。如果找不到答案的一些问题,能在解决之后顺手写下来。那后面来的人,甚至自己又遇到一样的问题,不就可以很快地修复吗?</p><p>工作中,也会遇到很多的牛人。而并不是每一位大牛都喜欢做分享和写文章的,好些厉害的人都是自己默默地玩耍代码,很多的思考和想法都藏在心里,只有偶尔的交流和讨论才能接触到。“哇,原来还能这么做”,“哇,这想法好棒”,真正接触到的时候,禁不住会有这样的想法。</p><p>这些精彩的思考方式,优秀的设计模式,也都想要记下来。很多改变世界的念头,都是在思想的碰撞中产生。所有的这些,属于我的,不属于我的,也都想要写下来。</p><p>这样,即使没有站在巨人的肩膀上,也能依靠每个人上山的时候铺下的路,齐心协力地登上峰顶,看到最美的风景了。</p><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><p>到如今,博客也坚持写了3年多了,而一些生活相关的文章,也断断续续地记录了快 5 年了。</p><p>如果说最大的收获,大概是在这个过程中一边敲下键盘,一边进行思考而沉淀下来的反省。而一路上的历程,也能以某种方式能看的到自身的成长,慢慢调整方向,越走越坚定。</p><p>我觉得这是个很好的习惯,同样地也分享给你们。生活中,职场中,会遇到很多的低谷,依然希望你们能攀上顶峰。祝好。</p><p>——————————————————————————<br>我的工作和生活,界限分明。如果你想要关注非工作非技术状态的我,欢迎关注“牧羊的猪”公众号。</p><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/qrcode_for_gh_0404f6ab1848_258.jpg" alt=""></p>]]></content>
<summary type="html">
<p>经验沉淀、定期思考,并以文章的形式进行记录和分享。工作中也一样,把许多经验整理成文档,当然也不乏“你工作就是写文章吧”这样的声音。至于为什么要这么做呢,我想谈谈我的一些想法。<br>
</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>小程序自定义组件知多少</title>
<link href="https://godbasin.github.io/2019/02/23/wxapp-component/"/>
<id>https://godbasin.github.io/2019/02/23/wxapp-component/</id>
<published>2019-02-23T14:38:58.000Z</published>
<updated>2019-02-23T14:39:23.332Z</updated>
<content type="html"><![CDATA[<p>小程序里,自定义组件作为一个贯穿小程序架构核心的组成,你对它又掌握了多少呢?<br><a id="more"></a></p><h1 id="自定义组件"><a href="#自定义组件" class="headerlink" title="自定义组件"></a>自定义组件</h1><h2 id="why"><a href="#why" class="headerlink" title="why"></a>why</h2><h3 id="代码的复用"><a href="#代码的复用" class="headerlink" title="代码的复用"></a>代码的复用</h3><p>在起初小程序只支持 Page 的时候,就会有这样蛋疼的问题:多个页面有相同的组件,每个页面都要复制粘贴一遍,每次改动都要全局搜索一遍,还说不准哪里改漏了就出翔了。</p><h3 id="组件化设计"><a href="#组件化设计" class="headerlink" title="组件化设计"></a>组件化设计</h3><p>在前端项目中,组件化是很常见的方式,某块通用能力的抽象和设计,是一个必备的技能。组件的管理、数据的管理、应用状态的管理,这些在我们设计的过程中都是需要去思考的。当然你也可以说我就堆代码就好了,不过一个真正的码农是不允许自己这么随便的!</p><p>所以,组件化是现代前端必须掌握的生存技能!</p><h2 id="自定义组件的实现"><a href="#自定义组件的实现" class="headerlink" title="自定义组件的实现"></a>自定义组件的实现</h2><h3 id="一切都从-Virtual-DOM-说起"><a href="#一切都从-Virtual-DOM-说起" class="headerlink" title="一切都从 Virtual DOM 说起"></a>一切都从 Virtual DOM 说起</h3><p>前面<a href="https://godbasin.github.io/2018/10/05/wxapp-set-data/">《解剖小程序的 setData》</a>有讲过,基于小程序的双线程设计,视图层(Webview 线程)和逻辑层(JS 线程)之间通信(表现为 setData),是基于虚拟 DOM 来实现数据通信和模版更新的。</p><p>自定义组件一样的双线程,所以一样滴基于 Virtual DOM 来实现通信。那在这里,Virtual DOM 的一些基本知识(包括生成 VD 对象、Diff 更新等),就不过多介绍啦~</p><h3 id="Shadow-DOM-模型"><a href="#Shadow-DOM-模型" class="headerlink" title="Shadow DOM 模型"></a>Shadow DOM 模型</h3><p>基于 Virtual DOM,我们知道在这样的设计里,需要一个框架来支撑维护整个页面的节点树相关信息,包括节点的属性、事件绑定等。在小程序里,Exparser 承担了这个角色。</p><p>前面<a href="https://godbasin.github.io/2018/09/23/wxapp-basic-lib/">《关于小程序的基础库》</a>也讲过,Exparser 的主要特点包括:</p><ul><li>基于 Shadow DOM 模型</li><li>可在纯 JS 环境中运行</li></ul><p>Shadow DOM 是什么呢,它就是我们在写代码时候写的自定义组件、内置组件、原生组件等。Shadow DOM 为 Web 组件中的 DOM 和 CSS 提供了封装。Shadow DOM 使得这些东西与主文档的 DOM 保持分离。</p><p>简而言之,Shadow DOM 是一个 HTML 的新规范,其允许开发者封装 HTML 组件(类似 vue 组件,将 html,css,js 独立部分提取)。</p><p>例如我们定义了一个自定义组件叫<code><my-component></code>,你在开发者工具可以见到:<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-component-1.jpg" alt="Shadow DOM"></p><p><code>#shadow-root</code>称为影子根,DOM 子树的根节点,和文档的主要 DOM 树分开渲染。可以看到它在<code><my-component></code>里面,换句话说,<code>#shadow-root</code>寄生在<code><my-component></code>上。<code>#shadow-root</code>可以嵌套,形成节点树,即称为影子树(Shadow Tree)。</p><p>像这样:<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-component-2.jpg" alt="Shadow Tree"></p><h3 id="Shadow-Tree-拼接"><a href="#Shadow-Tree-拼接" class="headerlink" title="Shadow Tree 拼接"></a>Shadow Tree 拼接</h3><p>既然组件是基于 Shadow DOM,那组件的嵌套关系,其实也就是 Shadow DOM 的嵌套,也可称为 Shadow Tree 的拼接。</p><p>Shadow Tree 拼接是怎么做的呢?一切又得从模版引擎讲起。</p><p>我们知道,Virtual DOM 机制会将节点解析成一个对象,那这个对象要怎么生成真正的 DOM 节点呢?数据变更又是怎么更新到界面的呢?这大概就是模版引擎做的事情了。</p><p><a href="https://godbasin.github.io/2017/10/21/template-engine/">《前端模板引擎》</a>里有详细描述模版引擎的机制,通常来说主要有这些:</p><ul><li>DOM 节点的创建和管理:<code>appendChild</code>/<code>insertBefore</code>/<code>removeChild</code>/<code>replaceChild</code>等</li><li>DOM 节点的关系(嵌套的处理):<code>parentNode</code>/<code>childNodes</code></li><li>通常创建后的 DOM 节点会保存一个映射,在更新的时候取到映射,然后进行处理(通常包括替换节点、改变内容<code>innerHTML</code>、移动删除新增节点、修改节点属性<code>setAttribute</code>)</li></ul><p>在上面的图我们也可以看到,在 Shadow Tree 拼接的过程中,有些节点并不会最终生成 DOM 节点,例如<code><slot></code>这种。</p><p>但是,常用的前端模版引擎,能直接用在小程序里吗?</p><h2 id="双线程的难题"><a href="#双线程的难题" class="headerlink" title="双线程的难题"></a>双线程的难题</h2><h3 id="自定义组件渲染流程"><a href="#自定义组件渲染流程" class="headerlink" title="自定义组件渲染流程"></a>自定义组件渲染流程</h3><p>双线程的设计,给小程序带来了很多便利,安全性管控力都拥有了,当然什么鬼东西都可以比作一把双刃剑,双线程也不例外。</p><p>我们知道,小程序分为 Webview 和 JS 双线程,逻辑层里是没法拿到真正的 DOM 节点,也没法随便动态变更页面的。那在这种情况下,我们要怎么去使用映射来更新模版呢(因为我们压根拿不到 Webview 节点的映射)?</p><p>所以在双线程下,其实两个线程都需要保存一份节点信息。这份节点信息怎么来的呢?其实就是我们需要在创建组件的时候,通过事件通知的方式,分别在逻辑层和视图层创建一份节点信息。</p><p>同时,视图层里的组件是有层级关系的,但是 JS 里没有怎么办?为了维护好父子嵌套等节点关系,所以我们在 逻辑层也需要维护一棵 Shadow Tree。</p><p>那么我们自定义组件的渲染流程大概是:</p><ol><li>组件创建。<ul><li>逻辑层:先是 wxml + js 生成一个 JS 对象(因为需要访问组件实例 this 呀),然后是 JS 其中节点部分生成 Virtual DOM,拼接 Shadow Tree 什么的,最后通过底层通信通知到 视图层</li><li>视图层:拿到节点信息,然后吭哧吭哧开始创建 Shadow DOM,拼接 Shadow Tree 什么的,最后生成真实 DOM,并保留下映射关系</li></ul></li><li>组件更新。</li></ol><p>这时候我们知道,不管是逻辑层,还是视图层,都维护了一份 Shadow Tree,要怎么保证他们之间保持一致呢?</p><h3 id="让-JS-和-Webview-的组件保持一致"><a href="#让-JS-和-Webview-的组件保持一致" class="headerlink" title="让 JS 和 Webview 的组件保持一致"></a>让 JS 和 Webview 的组件保持一致</h3><p>为了让两边的 Shadow Tree 保持一致,可以使用同步队列来传递信息。(这样就不会漏掉啦)</p><p>同步队列可以,每次变动我们就往队列里塞东西就好了。不过这样还会有个问题,我们也知道 setData 其实在实际项目里是使用比较频繁的,要是像 Component 的 observer 里做了 setData 这类型的操作,那不是每次变动会导致一大堆的 setDate?这样通信效率会很低吧?</p><p>所以,其实可以把一次操作里的所有 setData 都整到一次通信里,通过排序保证好顺序就好啦。</p><h2 id="Page-和-Component"><a href="#Page-和-Component" class="headerlink" title="Page 和 Component"></a>Page 和 Component</h2><h3 id="Component-是-Page-的超集"><a href="#Component-是-Page-的超集" class="headerlink" title="Component 是 Page 的超集"></a>Component 是 Page 的超集</h3><p>事实上,小程序的页面也可以视为自定义组件。因而,页面也可以使用<code>Component</code>构造器构造,拥有与普通组件一样的定义段与实例方法。但此时要求对应 json 文件中包含<code>usingComponents</code>定义段。</p><blockquote><p>来自<a href="https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/component.html" target="_blank" rel="external">官方文档-Component</a></p></blockquote><p>所以,基于 Component 是 Page 的超集,那么其实组件的渲染流程、方式,其实跟页面没多大区别,应该可以一个方式去理解就差不多啦。</p><h3 id="页面渲染"><a href="#页面渲染" class="headerlink" title="页面渲染"></a>页面渲染</h3><p>既然页面就是组件,那其实页面的渲染流程跟组件的渲染流程基本保持一致。</p><p>视图层渲染,可以参考<a href="https://developers.weixin.qq.com/ebook?action=get_post_info&token=935589521&volumn=1&lang=zh_CN&book=miniprogram&docid=00024a319d00b87b008612f5f5640a" target="_blank" rel="external">7.4 视图层渲染</a>说明。</p><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><hr><p>其实很多新框架新工具出来的时候,经常会让人眼前一亮,觉得哇好厉害,哇好高大上。<br>但其实更多时候,我们需要挖掘新事物的核心,其实大多数都是在原有的事物上增加了个新视角,从不一样的视角看,看到的就不一样了呢。作为一名码农,我们要看到不变的共性,变化的趋势。</p>]]></content>
<summary type="html">
<p>小程序里,自定义组件作为一个贯穿小程序架构核心的组成,你对它又掌握了多少呢?<br>
</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>小程序开发月刊第二期(20190215)</title>
<link href="https://godbasin.github.io/2019/02/15/wxapp-latest-20190215/"/>
<id>https://godbasin.github.io/2019/02/15/wxapp-latest-20190215/</id>
<published>2019-02-15T15:21:30.000Z</published>
<updated>2019-02-16T06:24:01.960Z</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://godbasin.github.io/2019/01/10/wxapp-official-functions/">超实用小程序官方能力</a>中提到过 TGit 代码托管,现在 TGit 能力已升级为<a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/wechatvcs.html" target="_blank" rel="external">微信开发者·代码管理</a>。</p><h3 id="video-组件同层渲染"><a href="#video-组件同层渲染" class="headerlink" title="video 组件同层渲染"></a>video 组件同层渲染</h3><p>小程序 video 组件的同层渲染目前已经全量了,基础库 2.4.0 以上都支持。</p><p><strong>啥是同层渲染?下面是辅助理解:</strong></p><ul><li><a href="https://godbasin.github.io/2018/10/05/wxapp-set-data/">小程序系列4–解剖小程序的 setData</a></li><li><a href="https://developers.weixin.qq.com/miniprogram/dev/component/native-component.html" target="_blank" rel="external">原生组件相关说明</a></li><li><a href="https://developers.weixin.qq.com/community/develop/doc/000aa28d030f60a3c4183eecb5d801" target="_blank" rel="external">小程序 video 组件同层渲染公测</a></li></ul><p>总之,同层渲染是利用黑科技让原生组件的层级和非原生组件一样可控,这样我们开发中最蛋疼的原生组件的样式调试就舒服很多了,拜拜<code><cover-view></code>,拜拜<code><cover-image></code></p><blockquote><p>其他的原生组件后续都计划支持同层渲染的,敬请期待。</p></blockquote><h3 id="小程序数据需求征集"><a href="#小程序数据需求征集" class="headerlink" title="小程序数据需求征集"></a>小程序数据需求征集</h3><p>目前小程序已经开放了小程序访问、分享、添加等基础数据,以及用户画像和交易分析等数据。<br>如果大家在使用小程序后台统计模块和小程序数据助手时,有一些不满足的数据需求,或者不好的体验?如果大家对数据工具有更好的想法和建议,欢迎大家留言给小程序团队。</p><blockquote><p>例如,你可以说我想要更详细的加载性能的数据,想要用户行为上报的能力,等等。</p></blockquote><h3 id="更新日志"><a href="#更新日志" class="headerlink" title="更新日志"></a>更新日志</h3><ul><li><a href="https://developers.weixin.qq.com/community/develop/doc/000ae8f2264c6855e718440a95b801" target="_blank" rel="external">周社区问题反馈以及功能优化更新(02.04-02.08)</a></li><li><a href="https://developers.weixin.qq.com/community/develop/doc/000c8a1f334310a3ab08a7ab950801" target="_blank" rel="external">周社区问题反馈以及功能优化更新(01.21-01.25)</a></li><li><a href="https://developers.weixin.qq.com/community/develop/doc/0006a6e1b50c20cb18081c56d5bc01" target="_blank" rel="external">周社区问题反馈以及功能优化更新(01.14-01.18)</a></li></ul><h2 id="开发者工具"><a href="#开发者工具" class="headerlink" title="开发者工具"></a>开发者工具</h2><h3 id="更新日志-1"><a href="#更新日志-1" class="headerlink" title="更新日志"></a>更新日志</h3><ul><li>修复界面调试样式信息显示不全的问题</li><li>修复 app.json usingComponent 没有扩散的问题</li><li>修复 长路径的项目无法正常打开的问题</li><li>更多请查看<a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/uplog.html" target="_blank" rel="external">工具日志</a></li></ul><blockquote><p>听说云函数的本地调试能力在开发ing了,还有很多能力都会在后面支持到,期待~</p></blockquote><h1 id="小程序教程"><a href="#小程序教程" class="headerlink" title="小程序教程"></a>小程序教程</h1><h2 id="最新踩坑-Tips"><a href="#最新踩坑-Tips" class="headerlink" title="最新踩坑 Tips"></a>最新踩坑 Tips</h2><ol><li>小程序有办法类似于通过条件判断然后调用 e.preventDefault 和 stopPropagation 的方法吗?<br>暂时不支持,只能通过 wxml 里面用 catch-tap 阻断,但是这个是一定阻断的,不能写条件。</li></ol><blockquote><p>暂时没有更多了,之前用来记录的临时文件忘记保存,重启了电脑丢掉了233333</p></blockquote><h1 id="tools"><a href="#tools" class="headerlink" title="tools"></a>tools</h1><h2 id="框架"><a href="#框架" class="headerlink" title="框架"></a>框架</h2><blockquote><p>Tips 仅供参考: 小程序开发本身就比较方便,个人感觉最好的方式建议直接使用原生开发噢</p></blockquote><h3 id="taro"><a href="#taro" class="headerlink" title="taro"></a>taro</h3><ul><li>Github: <a href="https://github.com/NervJS/taro" target="_blank" rel="external">https://github.com/NervJS/taro</a><blockquote><p>多端统一开发框架,支持用 React 的开发方式编写一次代码,生成能运行在微信/百度/支付宝/字节跳动小程序、H5、React Native 等的应用。</p></blockquote></li></ul><h2 id="工具"><a href="#工具" class="headerlink" title="工具"></a>工具</h2><h3 id="crypto-js"><a href="#crypto-js" class="headerlink" title="crypto-js"></a>crypto-js</h3><p>JS 加解码库,小程序适用。</p><ul><li>Github: <a href="https://github.com/brix/crypto-js" target="_blank" rel="external">https://github.com/brix/crypto-js</a></li></ul><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><p>本来计划写双周刊的,最后改成了月刊,因为小程序和开发者工具的更新相对而言会慢一些,不像前端每天都有新框架和新工具库。当然,你也可以手动关注小程序社区来获取最新的bug和修复信息~欢迎对月刊内容进行<a href="https://github.com/godbasin/godbasin.github.io/issues/15" target="_blank" rel="external">留言讨论和推荐</a>~</p>]]></content>
<summary type="html">
<p>听说年后会有更多的能力进入开发状态噢,尤其是云开发的能力,大家期待不~<br>
</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>小程序开发月刊第一期(20190114)</title>
<link href="https://godbasin.github.io/2019/01/14/wxapp-latest-20190114/"/>
<id>https://godbasin.github.io/2019/01/14/wxapp-latest-20190114/</id>
<published>2019-01-14T14:55:52.000Z</published>
<updated>2019-02-16T06:23:59.037Z</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><ul><li><a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/settings.html" target="_blank" rel="external">新增黑色主题</a></li><li><a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/edit.html#typescript-%E6%94%AF%E6%8C%81" target="_blank" rel="external">支持 Typescript</a></li></ul><h1 id="小程序教程"><a href="#小程序教程" class="headerlink" title="小程序教程"></a>小程序教程</h1><h2 id="使用-Tips"><a href="#使用-Tips" class="headerlink" title="使用 Tips"></a>使用 Tips</h2><ul><li><a href="https://godbasin.github.io/2019/01/10/wxapp-official-functions/">超实用小程序官方能力</a></li></ul><blockquote><p>目前来说,大多数是我自己的文章,也非常欢迎大家推荐文章来~</p></blockquote><h2 id="最新踩坑-Tips"><a href="#最新踩坑-Tips" class="headerlink" title="最新踩坑 Tips"></a>最新踩坑 Tips</h2><ol><li><p>调试小程序时,有些请求例如上传文件请求,无法再控制台里查看到完整的请求信息。<br>解决办法:小程序开发工具可以设置网络代理,转发到抓包工具例如Charles中即可。</p></li><li><p>在原生页跳转进入小程序场景下(仿原生,没有关闭和后退按钮),有没有内部能力从小程序返回到原生页?<br>可使用<a href="https://developers.weixin.qq.com/miniprogram/dev/api/wx.navigateBackMiniProgram.html?search-key=navigateBackMiniProgram" target="_blank" rel="external">navigateBackMiniProgram</a>返回到上一个小程序。只有在当前小程序是被其他小程序打开时可以调用成功</p><blockquote><p>虽然写的是返回小程序,但是他就是可以用。算是实现的有bug,目前测试没遇到过不行的情况。<br>iOS仿原生点了是切后台,安卓仿原生是关闭,这个无解。<br>想安心一点就用<code><navigator target="miniProgram" open-type="exit"></code>,版本比较高</p></blockquote></li><li><p>小程序右上角的关闭按钮,只是将小程序切换到后台并不会关闭小程序。若需要重新加载,需要在微信首页下拉删掉使用过的小程序;另从后台唤醒时,会触发<code>onShow</code>而不是<code>onLoad</code>。</p></li><li><p>如果用户通过微信首页下拉删掉使用过的小程序,那么小程序代码里面通过 localStorage 保存的缓存信息以及通过文件管理器保存的文件都会被清掉。</p></li><li><p>小程序工具上传代码,勾选 ES6 转 ES5,只会针对 ES6 进行编译,对 ES7/ES8 代码并不会编译,可能导致兼容性问题(如<code>Object.values</code>)。<br>节后工具会上线一个 ES6+ 转 ES5 的能力,一站式全部处理掉。</p></li></ol><blockquote><p>后续踩坑相关的,可能会整理到一个地方一起沉淀吧~~~</p></blockquote><h1 id="tools"><a href="#tools" class="headerlink" title="tools"></a>tools</h1><h2 id="框架"><a href="#框架" class="headerlink" title="框架"></a>框架</h2><blockquote><p>Tips 仅供参考: 小程序开发本身就比较方便,个人感觉最好的方式建议直接使用原生开发噢</p></blockquote><h3 id="wepy"><a href="#wepy" class="headerlink" title="wepy"></a>wepy</h3><ul><li>Github: <a href="https://github.com/Tencent/wepy" target="_blank" rel="external">https://github.com/Tencent/wepy</a></li></ul><h3 id="mpvue"><a href="#mpvue" class="headerlink" title="mpvue"></a>mpvue</h3><ul><li>Github: <a href="https://github.com/mpvue" target="_blank" rel="external">https://github.com/mpvue</a></li></ul><h2 id="工具"><a href="#工具" class="headerlink" title="工具"></a>工具</h2><h3 id="weRequest"><a href="#weRequest" class="headerlink" title="weRequest"></a>weRequest</h3><p>解决繁琐的小程序会话管理,一款自带登录态管理的网络请求组件:</p><ul><li>session 登录态管理(静默续期)</li><li>cache 请求缓存能力</li><li><p>封装请求测速能力</p></li><li><p>Github: <a href="https://github.com/IvinWu/weRequest" target="_blank" rel="external">https://github.com/IvinWu/weRequest</a></p></li></ul><h3 id="we-cropper"><a href="#we-cropper" class="headerlink" title="we-cropper"></a>we-cropper</h3><p>微信小程序图片裁剪工具。</p><ul><li>Github: <a href="https://github.com/we-plugin/we-cropper" target="_blank" rel="external">https://github.com/we-plugin/we-cropper</a></li></ul><h3 id="westore"><a href="#westore" class="headerlink" title="westore"></a>westore</h3><p>微信小程序状态管理解决方案</p><ul><li>Github: <a href="https://github.com/Tencent/westore" target="_blank" rel="external">https://github.com/Tencent/westore</a></li></ul><blockquote><p>Tips: store 为全局状态,不同组件或页面需要注意数据隔离</p></blockquote><h2 id="图表"><a href="#图表" class="headerlink" title="图表"></a>图表</h2><h3 id="echarts-for-weixin"><a href="#echarts-for-weixin" class="headerlink" title="echarts-for-weixin"></a>echarts-for-weixin</h3><p>ECharts 的微信小程序版本</p><ul><li>Github: <a href="https://github.com/ecomfe/echarts-for-weixin" target="_blank" rel="external">https://github.com/ecomfe/echarts-for-weixin</a></li></ul><blockquote><p>注意:移动端中使用 ECharts,折线图的点击事件体验会很差</p></blockquote><h3 id="wx-f2"><a href="#wx-f2" class="headerlink" title="wx-f2"></a>wx-f2</h3><p>F2 的微信小程序图表示例</p><ul><li>Github: <a href="https://github.com/antvis/wx-f2" target="_blank" rel="external">https://github.com/antvis/wx-f2</a></li></ul><h3 id="wx-charts"><a href="#wx-charts" class="headerlink" title="wx-charts"></a>wx-charts</h3><p>微信小程序图表charts组件</p><ul><li>Github: <a href="https://github.com/xiaolin3303/wx-charts" target="_blank" rel="external">https://github.com/xiaolin3303/wx-charts</a></li></ul><blockquote><p>该图表组件已没有维护,但是源码比较简单清晰,可自定义修改</p></blockquote><h2 id="UI-库"><a href="#UI-库" class="headerlink" title="UI 库"></a>UI 库</h2><h3 id="weui-小程序"><a href="#weui-小程序" class="headerlink" title="weui-小程序"></a>weui-小程序</h3><ul><li>Github: <a href="https://github.com/Tencent/weui-wxss" target="_blank" rel="external">https://github.com/Tencent/weui-wxss</a></li></ul><h3 id="wux-weapp"><a href="#wux-weapp" class="headerlink" title="wux-weapp"></a>wux-weapp</h3><ul><li>Github: <a href="https://github.com/wux-weapp/wux-weapp" target="_blank" rel="external">https://github.com/wux-weapp/wux-weapp</a></li></ul><blockquote><p>Tips: 表单相关的 input/textarea 稍微有毒,其他暂可正常使用</p></blockquote><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><p>本期作为第一期,所以把目前的一些工具库和文章沉淀一并发出,大家如果有很好的文章、工具推荐,可以一起<a href="https://github.com/godbasin/godbasin.github.io/issues/15" target="_blank" rel="external">留言讨论和推荐</a>~~</p>]]></content>
<summary type="html">
<p>小程序的一些能力更新、踩坑历史、以及一些开源工具库和框架的推荐记录第一弹。<br>
</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/01/10/wxapp-official-functions/"/>
<id>https://godbasin.github.io/2019/01/10/wxapp-official-functions/</id>
<published>2019-01-10T14:53:58.000Z</published>
<updated>2019-01-10T14:54:09.869Z</updated>
<content type="html"><![CDATA[<p>小程序官方平台和工具里,其实有很多很好用的能力,你都了解吗?<br><a id="more"></a></p><h1 id="小程序管理后台"><a href="#小程序管理后台" class="headerlink" title="小程序管理后台"></a>小程序管理后台</h1><p>微信公众平台里,其实藏着一些好用的能力,一起来看看把。</p><h2 id="问题定位辅助"><a href="#问题定位辅助" class="headerlink" title="问题定位辅助"></a>问题定位辅助</h2><hr><h3 id="运维中心"><a href="#运维中心" class="headerlink" title="运维中心"></a>运维中心</h3><p>在小程序管理后台,【开发】-【运维中心】里,可以有以下能力:</p><ul><li><strong>错误查询</strong>: 可以查到所有小程序运行错误的记录。</li><li><strong>性能监控</strong>: 可以监控小程序运行的性能,包括不同时间段的<code>启动耗时</code>、<code>下载耗时</code>、<code>初次渲染耗时</code>等。</li><li><strong>告警设置</strong>: 错误告警通过微信群来通知,每个小程序对应唯一的告警群,扫码加入后即可接收告警通知。</li></ul><blockquote><p>微信7.0以后:</p><ol><li>如果使用工具压缩和编译代码,会自动带上 sourcemap ,运维中心的错误会显示原来文件名字和行号</li><li>如果使用第三方框架,在代码中内敛 sourcemap 或者有同名 sourcemap 文件存在,工具会自动合并和解析,从而做到错误会显示原来文件名字和行号。</li></ol></blockquote><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/1547096780.jpg" alt="运维中心界面"></p><hr><h3 id="日志管理"><a href="#日志管理" class="headerlink" title="日志管理"></a>日志管理</h3><ol><li>开发中日志打印,使用日志管理器实例<code>LogManager</code>。使用方式查看<a href="https://developers.weixin.qq.com/miniprogram/dev/api/LogManager.html" target="_blank" rel="external">API - LogManager</a>。</li><li>用户在使用过程中,可以在小程序的 profile 页面,点击【投诉与反馈】-【功能异常】-【勾选上传日志】,则可以上传日志。</li></ol><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/1547097426.png" alt="这里需要勾上才会上传"></p><ol><li>在小程序管理后台,【管理】-【反馈管理】,就可以查看上传的日志。</li></ol><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/1547097350.jpg" alt="这里可以下载日志"></p><hr><h3 id="运营数据"><a href="#运营数据" class="headerlink" title="运营数据"></a>运营数据</h3><p>有两种方式可以方便的看到小程序的<a href="https://developers.weixin.qq.com/miniprogram/dev/quickstart/basic/release.html#%E8%BF%90%E8%90%A5%E6%95%B0%E6%8D%AE" target="_blank" rel="external">运营数据</a>:</p><ol><li>在小程序管理后台,【统计】,可以看到常规分析(概况、访问分析、来源分析、用户画像)与自定义分析,点击相应的 tab 可以看到相关的数据。</li><li>使用小程序数据助手,在微信中方便的查看运营数据。</li></ol><p><strong>常规分析(不需要配置或开发)</strong></p><blockquote><p>参考文档: <a href="https://developers.weixin.qq.com/miniprogram/analysis/regular/" target="_blank" rel="external">常规分析</a></p></blockquote><ul><li>概况:提供小程序关键指标趋势以及 top 页面访问数据,快速了解小程序发展概况;</li><li>访问分析:提供小程序用户访问规模、来源、频次、时长、深度、留存以及页面详情等数据,具体分析用户新增、活跃和留存情况;</li><li>实时统计:提供小程序实时访问数据,满足实时监控需求;</li><li>用户画像:提供小程序的用户画像数据,包括用户年龄、性别、地区、终端及机型分布。</li></ul><p>似乎晒点图会更加直观:<br><img src="https://developers.weixin.qq.com/miniprogram/analysis/image/weanalytics/2_1.png?t=19010912" alt="昨日概况"><br><img src="https://developers.weixin.qq.com/miniprogram/analysis/image/weanalytics/4_2.png?t=19010912" alt="访问分布"><br><img src="https://developers.weixin.qq.com/miniprogram/analysis/image/weanalytics/6_4.png?t=19010912" alt="终端及机型分布"></p><p><strong>自定义分析(需自行配置和开发)</strong></p><blockquote><p>参考文档: <a href="https://developers.weixin.qq.com/miniprogram/analysis/custom/" target="_blank" rel="external">自定义分析</a></p></blockquote><p>配置自定义上报,精细跟踪用户在小程序内的行为,结合用户属性、系统属性、事件属性进行灵活多维的事件分析和漏斗分析,满足小程序的个性化分析需求。</p><p>同样,晒点官方图:<br><img src="https://developers.weixin.qq.com/miniprogram/analysis/image/weanalytics/5_14.png?t=19010912" alt=""><br><img src="https://developers.weixin.qq.com/miniprogram/analysis/image/weanalytics/5_21.png?t=19010912" alt=""></p><h2 id="第三方能力"><a href="#第三方能力" class="headerlink" title="第三方能力"></a>第三方能力</h2><hr><h3 id="TGit-代码托管"><a href="#TGit-代码托管" class="headerlink" title="TGit 代码托管"></a>TGit 代码托管</h3><p><strong>重要Tips: TGit 能力即将会升级为 git.weixin.qq.com,将全量免费提供给微信的开发者,期待最新消息!~</strong></p><p>更多详情,可以参考<a href="https://developers.weixin.qq.com/miniprogram/dev/qcloud/tgit.html" target="_blank" rel="external">TGit开通及配置流程</a>。</p><h1 id="小程序开发工具"><a href="#小程序开发工具" class="headerlink" title="小程序开发工具"></a>小程序开发工具</h1><h2 id="调试"><a href="#调试" class="headerlink" title="调试"></a>调试</h2><hr><h3 id="真机调试"><a href="#真机调试" class="headerlink" title="真机调试"></a>真机调试</h3><blockquote><p>参考文档:<a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/remote-debug.html" target="_blank" rel="external">真机调试</a></p></blockquote><p>我们经常会遇到小程序开发工具没有问题,但是真机上跑的时候就出翔了,或者某些UI歪掉了,这时候真机调试就显得特别方便啦~<br>真机远程调试功能可以实现直接利用开发者工具,通过网络连接,对手机上运行的小程序进行调试,帮助开发者更好的定位和查找在手机上出现的问题。</p><p><strong>使用方式</strong>:<br>点击开发者工具的工具栏上 “远程调试” 按钮。</p><p><strong>实用能力</strong>:</p><ul><li>调试:断点、单步调试,可在 console 里调试<code>wx.***</code>能力</li><li>查看和 debug 请求</li><li>查看 UI 布局和样式(定位奇葩的手机兼容的时候,特别好用)</li></ul><p><img src="https://developers.weixin.qq.com/miniprogram/dev/devtools/image/devtools2/remote-debug/iphone.jpg?t=19011013" alt="使用效果"></p><h2 id="构建"><a href="#构建" class="headerlink" title="构建"></a>构建</h2><hr><h3 id="npm-支持"><a href="#npm-支持" class="headerlink" title="npm 支持"></a>npm 支持</h3><blockquote><p>参考文档:<a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/npm.html" target="_blank" rel="external">npm 支持</a></p></blockquote><p>小程序基础库版本<code>2.2.1</code>开始,就支持 npm 构建啦。</p><ol><li>安装 npm 依赖。</li><li>点击开发者工具中的菜单栏:【工具】-【构建 npm】,就可以啦。</li></ol><hr><h3 id="自定义预处理"><a href="#自定义预处理" class="headerlink" title="自定义预处理"></a>自定义预处理</h3><p>通过自定义预处理,我们可以设置在上传代码之前,做一些什么操作,例如跑测试、编译构建等。可以通过<code>project.config.json</code>中的<code>scripts</code>来配置:</p><ul><li><code>beforeCompile</code>: 编译前预处理命令</li><li><code>beforePreview</code>: 预览前预处理命令</li><li><code>beforeUpload</code>: 上传前预处理命令</li></ul><h2 id="测试体验"><a href="#测试体验" class="headerlink" title="测试体验"></a>测试体验</h2><hr><h3 id="小程序开发助手"><a href="#小程序开发助手" class="headerlink" title="小程序开发助手"></a>小程序开发助手</h3><blockquote><p>参考文档:<a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/mydev.html" target="_blank" rel="external">小程序开发助手</a></p></blockquote><p>微信公众平台发布的官方小程序,帮助开发和运营人员在手机端更方便快捷地查看和预览小程序。<br>有权限的项目成员,可以直接点击体验任何一个需要测试或体验的版本,而不需要二维码的口口相传。</p><p><strong>最新消息:小程序开发助手已经升级为小程序助手了,在移动端可以管理版本了~</strong></p><hr><h3 id="体验评分"><a href="#体验评分" class="headerlink" title="体验评分"></a>体验评分</h3><blockquote><p>参考文档:<a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/audits.html" target="_blank" rel="external">体验评分</a></p></blockquote><p>体验评分是一项给小程序的体验好坏打分的功能,它会在小程序运行过程中实时检查,分析出一些可能导致体验不好的地方,并且定位出哪里有问题,以及给出一些优化建议。<br><img src="https://developers.weixin.qq.com/miniprogram/dev/devtools/image/devtools/audits.png?t=19010919" alt="体验评分效果"><br><strong>手动启动:</strong></p><ol><li>在调试器区域切换到 Audits 面板。</li><li>点击左上角”开始“按钮,然后自行操作小程序界面,运行过的页面就会被“体验评分”检测到。</li><li>点击 “Stop” 停止分析,就会看到一份分析报告,之后便可根据分析报告进行相关优化。</li></ol><p><strong>自动运行(实时检查):</strong><br>开发者在工具的右上角 “详情” 面板里勾选 “自动运行体验评分” 选项即可开启。</p><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><ul><li><a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/debug.html" target="_blank" rel="external">小程序调试</a></li></ul><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><hr><p>很多时候,我们觉得小程序的开发和调试总是有哪里不满意,但其实官方在很认真地完善各个环节,不过他们比较低调,很多能力我们都没有用上。<br>很多 web 开发写小程序,都喜欢用像 mpvue、wepy 这些框架。不用去了解小程序的运行机制、底层原理,其实也很方便。不过作为一个细腻的开发,其实了解一下也能发现小程序它棒在哪里了。</p>]]></content>
<summary type="html">
<p>小程序官方平台和工具里,其实有很多很好用的能力,你都了解吗?<br>
</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>如何发布 typescript npm 包</title>
<link href="https://godbasin.github.io/2019/01/05/ts-npm-package/"/>
<id>https://godbasin.github.io/2019/01/05/ts-npm-package/</id>
<published>2019-01-05T01:09:58.000Z</published>
<updated>2019-01-05T01:10:07.235Z</updated>
<content type="html"><![CDATA[<p>有些时候,我们需要搞个 npm 包,typescript 那么棒,我们要怎么发布个带 typing 的 npm 依赖包呢。<br><a id="more"></a></p><h2 id="为啥要用-npm-包捏"><a href="#为啥要用-npm-包捏" class="headerlink" title="为啥要用 npm 包捏"></a>为啥要用 npm 包捏</h2><hr><p>傻孩子,因为你不想在每个项目里都复制一遍你的代码,然后每次改动和修 bug 的时候,在每个使用到的项目里都改一遍吧?</p><p>这个时候,你就可以用 npm 包来管理啦,自带版本功能,你可以用简单的一句<code>npm install my-package@n.m.l</code>就可以更新代码啦。另外,我们还可以不占用自己的任何空间,npm 就会帮你保管各个版本的依赖包,你还可以把每个版本的代码都翻出来看。</p><h2 id="npm-发布"><a href="#npm-发布" class="headerlink" title="npm 发布"></a>npm 发布</h2><hr><p>别急,首先我们来讲下常规的 npm 包发布的流程。</p><h3 id="1-申请-npm-账号"><a href="#1-申请-npm-账号" class="headerlink" title="1. 申请 npm 账号"></a>1. 申请 npm 账号</h3><p><a href="https://www.npmjs.com/signup" target="_blank" rel="external">点这里</a></p><h3 id="2-登录-npm-账号"><a href="#2-登录-npm-账号" class="headerlink" title="2. 登录 npm 账号"></a>2. 登录 npm 账号</h3><p>很简单,在终端命令行直接敲下:<br><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 login</div></pre></td></tr></table></figure></p><p>对的,就是这么直接。然后根据提示输入账号和密码就好啦,成功之后会看到这样的提示:</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">Logged <span class="keyword">in</span> as bamblehorse to scope @username on https://registry.npmjs.org/.</div></pre></td></tr></table></figure><p>棒!</p><h3 id="3-初始化-npm-设置"><a href="#3-初始化-npm-设置" class="headerlink" title="3. 初始化 npm 设置"></a>3. 初始化 npm 设置</h3><p>同样的,你只需要输入以下命令行:<br><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 init</div></pre></td></tr></table></figure></p><p>就可以根据提示,把你的依赖包名字、介绍、email、github 啥的都一块生成,这样:<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/1546593090.jpg" alt="你问的有点多耶"></p><p><strong>配置说明</strong><br>更多常用的配置可以看看<a href="https://docs.npmjs.com/files/package.json" target="_blank" rel="external">官网</a>,这里我们简单讲几个跟代码相关的:</p><ul><li><code>main</code>: 入口文件。别人安装了你的依赖包之后,会根据这里的路径来寻找入口,通常你要<code>module.exports = xxx</code>来输出</li><li><code>scripts</code>: 构建命令,例如你可能会需要<code>dev</code>、<code>test</code>、<code>build</code>等一些构建命令</li><li><code>bin</code>: 脚手架命令。通常在做脚手架的时候,你会通过<code>bin</code>来提供一些脚手架的命令来给开发者使用,像<code>vue init</code>,这里的<code>init</code>便指向一个可执行的脚本文件</li><li><code>dependencies</code>: 这里管理你的依赖包里使用到的一些依赖包,用户在安装你的包的时候,也会将对应的依赖包安装上,这也是为啥通常我们<code>npm install</code>一个包的时候,会看到好多的依赖安装</li></ul><h3 id="4-发布代码"><a href="#4-发布代码" class="headerlink" title="4. 发布代码"></a>4. 发布代码</h3><p>嗯,发布也就一句命令:<br><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 publish</div></pre></td></tr></table></figure></p><p>这里需要注意的是,你如果想发布一个 npm 包,先去<a href="https://www.npmjs.com" target="_blank" rel="external">官网</a>查一下有没有人已经用了这个名字哇,不然发布的时候会提示你咩有权限:<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/1546594083.jpg" alt="像酱紫"><br>一看,发现别人已经占用了:<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/1546596854.jpg" alt="被占用啦"></p><p>正常发布的话,应该就是这样提示成功的:<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/1546595444.jpg" alt="成功!"></p><p>你会发现,你只有一个<code>package.json</code>文件,也能依然成功发布。</p><p><strong>取消发布</strong><br>注意,npm 包的最新版本,跟你发布的<code>version</code>并没有多大关系,即使你上一次发布的是<code>1.1.0</code>版本,你发布个<code>0.0.1</code>版本,也依然会盖掉。</p><p>像上面这里,我就很悲剧地把原有代码给盖掉了。这个时候你可以赶紧取消发布(24小时内有效,更久的得自己申述了):<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/1546595796.jpg" alt="还好还好"></p><h3 id="5-提交到-github"><a href="#5-提交到-github" class="headerlink" title="5. 提交到 github"></a>5. 提交到 github</h3><p>通常,我们发布个 npm 依赖包,也多半是想要开源的。</p><p>这个时候,我们就能在自己的 github 账号下开一个 repo,然后直接把源码提交上去啦。酱紫,别人也可以给你提交 pr 啦。</p><h2 id="typescript-npm-包发布"><a href="#typescript-npm-包发布" class="headerlink" title="typescript npm 包发布"></a>typescript npm 包发布</h2><hr><p>我们接着讲讲怎么发布 typescript 的依赖包叭~</p><h3 id="1-webpack-构建?"><a href="#1-webpack-构建?" class="headerlink" title="1. webpack 构建?"></a>1. webpack 构建?</h3><p>很多时候,我们的依赖包也想用高大上的 ES6/ES7/Typescript 来写,但是这样的代码并不能直接输出到用户,因为他们如果没有构建的话,使用起来兼容性可能就会出翔。</p><p>通常的做法,我们也跟平时一样去搞个 webpack 环境就好啦。然后我们生成两份代码,一份压缩一份不压缩:<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><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></pre></td><td class="code"><pre><div class="line"><span class="comment">// webpack.config.js</span></div><div class="line"><span class="keyword">let</span> path = <span class="built_in">require</span>(<span class="string">"path"</span>);</div><div class="line"><span class="keyword">let</span> webpack = <span class="built_in">require</span>(<span class="string">"webpack"</span>);</div><div class="line"><span class="keyword">let</span> pk = <span class="built_in">require</span>(<span class="string">"./package.json"</span>);</div><div class="line"></div><div class="line"><span class="built_in">module</span>.exports = [</div><div class="line"> { <span class="comment">// 第一份生产环境的压缩代码</span></div><div class="line"> mode: <span class="string">"production"</span>,</div><div class="line"> <span class="attr">entry</span>: <span class="string">"./src/index.ts"</span>, <span class="comment">// 入口文件呀</span></div><div class="line"> <span class="built_in">module</span>: {</div><div class="line"> <span class="attr">rules</span>: [ <span class="comment">// ts loader</span></div><div class="line"> {</div><div class="line"> <span class="attr">test</span>: <span class="regexp">/\.tsx?$/</span>,</div><div class="line"> <span class="attr">use</span>: <span class="string">"ts-loader"</span>,</div><div class="line"> <span class="attr">exclude</span>: <span class="regexp">/node_modules/</span></div><div class="line"> }</div><div class="line"> ]</div><div class="line"> },</div><div class="line"> <span class="attr">resolve</span>: { <span class="comment">// 解析文件呀</span></div><div class="line"> extensions: [<span class="string">".tsx"</span>, <span class="string">".ts"</span>, <span class="string">".js"</span>]</div><div class="line"> },</div><div class="line"> <span class="attr">output</span>: {</div><div class="line"> <span class="attr">path</span>: path.join(__dirname, <span class="string">"build"</span>),</div><div class="line"> <span class="attr">filename</span>: <span class="string">"yourLibName.min.js"</span>,</div><div class="line"> <span class="attr">library</span>: <span class="string">"yourLibName"</span>,</div><div class="line"> <span class="attr">libraryTarget</span>: <span class="string">"commonjs-module"</span></div><div class="line"> }</div><div class="line"> },</div><div class="line"> { <span class="comment">// 第二份开发环境的不压缩代码</span></div><div class="line"> mode: <span class="string">"development"</span>,</div><div class="line"> <span class="attr">entry</span>: <span class="string">"./src/index.ts"</span>,</div><div class="line"> <span class="attr">module</span>: {</div><div class="line"> <span class="attr">rules</span>: [ <span class="comment">// 如果只是正常的 js 构建,请改成 babel-loader 啦</span></div><div class="line"> {</div><div class="line"> <span class="attr">test</span>: <span class="regexp">/\.tsx?$/</span>,</div><div class="line"> <span class="attr">use</span>: <span class="string">"ts-loader"</span>,</div><div class="line"> <span class="attr">exclude</span>: <span class="regexp">/node_modules/</span></div><div class="line"> }</div><div class="line"> ]</div><div class="line"> },</div><div class="line"> <span class="attr">resolve</span>: {</div><div class="line"> <span class="attr">extensions</span>: [<span class="string">".tsx"</span>, <span class="string">".ts"</span>, <span class="string">".js"</span>]</div><div class="line"> },</div><div class="line"> <span class="attr">output</span>: {</div><div class="line"> <span class="attr">path</span>: path.join(__dirname, <span class="string">"build"</span>),</div><div class="line"> <span class="attr">filename</span>: <span class="string">"yourLibName.js"</span>,</div><div class="line"> <span class="attr">library</span>: <span class="string">"yourLibName"</span>,</div><div class="line"> <span class="attr">libraryTarget</span>: <span class="string">"commonjs-module"</span></div><div class="line"> },</div><div class="line"> <span class="attr">devtool</span>: <span class="string">"inline-source-map"</span></div><div class="line"> }</div><div class="line">];</div></pre></td></tr></table></figure></p><p><strong>tsc 构建</strong><br>在一些时候,我们其实并不需要搞很复杂,我们只需要简单的弄一下<code>tsc</code>原地产生js文件输出就好啦。</p><h3 id="2-生成-typing?"><a href="#2-生成-typing?" class="headerlink" title="2. 生成 typing?"></a>2. 生成 typing?</h3><p>我们既然自己用了 typescript 写代码爽歪歪,怎么可以不让其他开发者一起用呢?</p><p>我们不用再重新弄一份整齐的<code>xxx.d.ts</code>文件,只需要在<code>tsconfig.json</code>里加一个命令就好啦:<br><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></pre></td><td class="code"><pre><div class="line">{</div><div class="line"> "compilerOptions": {</div><div class="line"> // ...其他配置</div><div class="line"> "declaration": true // 对,就是它!</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p><p>加上这个,不管是 webpack 还是 tsc,每个 ts 文件都会生成一个<code>xxx.d.ts</code>文件,里面包括你的所有方法的 typing 定义啦~</p><p>效果如图:<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/1546598800.jpg" alt="用得开心用得放心"></p><p>如果你想要了解更多,可以直接拉<a href="https://github.com/godbasin/weRequest/tree/1.1.0_ts" target="_blank" rel="external">这里的代码看看</a>,记得是ts分支噢~</p><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><hr><p>开源的意义,在于和更多的人去合作和分享,结合大家的智慧,来打造很棒的东西!</p>]]></content>
<summary type="html">
<p>有些时候,我们需要搞个 npm 包,typescript 那么棒,我们要怎么发布个带 typing 的 npm 依赖包呢。<br>
</summary>
<category term="柴米油盐工具集" scheme="https://godbasin.github.io/categories/%E6%9F%B4%E7%B1%B3%E6%B2%B9%E7%9B%90%E5%B7%A5%E5%85%B7%E9%9B%86/"/>
<category term="教程" scheme="https://godbasin.github.io/tags/%E6%95%99%E7%A8%8B/"/>
</entry>
<entry>
<title>小程序 gulp 简单构建</title>
<link href="https://godbasin.github.io/2018/12/30/wxapp-gulp/"/>
<id>https://godbasin.github.io/2018/12/30/wxapp-gulp/</id>
<published>2018-12-30T04:00:30.000Z</published>
<updated>2018-12-30T04:00:35.339Z</updated>
<content type="html"><![CDATA[<p>虽然 webpack 用的比较多,不过在小程序这种场景下,简单的 gulp 也是个不错的选择吧~<br><a id="more"></a></p><h2 id="gulp-构建小程序"><a href="#gulp-构建小程序" class="headerlink" title="gulp 构建小程序"></a>gulp 构建小程序</h2><hr><h3 id="简单的-copy"><a href="#简单的-copy" class="headerlink" title="简单的 copy"></a>简单的 copy</h3><p>对小程序来说,除了<code>app.js</code>作为程序入口之外,每个<code>page</code>页面都可以作为一个页面入口,更倾向是固定路径模式的多页应用。</p><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></pre></td><td class="code"><pre><div class="line">├── app.js</div><div class="line">├── app.json</div><div class="line">├── app.wxss</div><div class="line">├── pages</div><div class="line">│ │── index</div><div class="line">│ │ ├── index.wxml</div><div class="line">│ │ ├── index.js</div><div class="line">│ │ ├── index.json</div><div class="line">│ │ └── index.wxss</div><div class="line">│ └── logs</div><div class="line">│ ├── logs.wxml</div><div class="line">│ └── logs.js</div></pre></td></tr></table></figure><p>所以,在编译的过程,很多文件都是需要简单地 copy 到目标目录的。我们定义复制和变动复制的任务:</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></pre></td><td class="code"><pre><div class="line"><span class="comment">// 待复制的文件,不包含需要编译的文件</span></div><div class="line"><span class="keyword">var</span> copyPath = [</div><div class="line"> <span class="string">"src/**/!(_)*.*"</span>,</div><div class="line"> <span class="string">"!src/**/*.less"</span>,</div><div class="line"> <span class="string">"!src/**/*.ts"</span>,</div><div class="line"> <span class="string">"!src/img/**"</span></div><div class="line">];</div><div class="line"><span class="comment">// 复制不包含需要编译的文件,和图片的文件</span></div><div class="line">gulp.task(<span class="string">"copy"</span>, () => {</div><div class="line"> <span class="keyword">return</span> gulp.src(copyPath, option).pipe(gulp.dest(dist));</div><div class="line">});</div><div class="line"><span class="comment">// 复制不包含需要编译的文件,和图片的文件(只改动有变动的文件)</span></div><div class="line">gulp.task(<span class="string">"copyChange"</span>, () => {</div><div class="line"> <span class="keyword">return</span> gulp</div><div class="line"> .src(copyPath, option)</div><div class="line"> .pipe(changed(dist))</div><div class="line"> .pipe(gulp.dest(dist));</div><div class="line">});</div></pre></td></tr></table></figure><h3 id="文件编译"><a href="#文件编译" class="headerlink" title="文件编译"></a>文件编译</h3><p>我们想要用高级语法,想要写<code>async/await</code>,想要用<code>less</code>来写样式,想要用<code>typescript</code>来写代码,则需要针对每种文件做编译。</p><p>这里用<code>ts</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></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> ts = <span class="built_in">require</span>(<span class="string">"gulp-typescript"</span>);</div><div class="line"><span class="keyword">var</span> tsProject = ts.createProject(<span class="string">"tsconfig.json"</span>);</div><div class="line"><span class="keyword">var</span> sourcemaps = <span class="built_in">require</span>(<span class="string">"gulp-sourcemaps"</span>);</div><div class="line"><span class="keyword">var</span> tsPath = [<span class="string">"src/**/*.ts"</span>, <span class="string">"src/app.ts"</span>]; <span class="comment">// 定义ts文件</span></div><div class="line"><span class="comment">// 编译</span></div><div class="line">gulp.task(<span class="string">"tsCompile"</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">return</span> tsProject</div><div class="line"> .src(tsPath)</div><div class="line"> .pipe(sourcemaps.init())</div><div class="line"> .pipe(tsProject())</div><div class="line"> .js.pipe(sourcemaps.write()) <span class="comment">// 添加sourcemap</span></div><div class="line"> .pipe(gulp.dest(<span class="string">"dist"</span>)); <span class="comment">// 最终输出到dist目录对应的位置</span></div><div class="line">});</div></pre></td></tr></table></figure><p>当然,用到 typescript 的话,也记得把<code>tsconfig.json</code>和<code>tslint.json</code>加上哇。</p><h3 id="watch-任务"><a href="#watch-任务" class="headerlink" title="watch 任务"></a>watch 任务</h3><p>在我们写代码的时候,就需要监听文件变动并自动复制、编译和更新,这时候我们就需要 watch 任务:</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">gulp.task(<span class="string">"watch"</span>, () => {</div><div class="line"> gulp.watch(tsPath, gulp.series(<span class="string">"tsCompile"</span>)); <span class="comment">// ts编译</span></div><div class="line"> <span class="keyword">var</span> watcher = gulp.watch(copyPath, gulp.series(<span class="string">"copyChange"</span>)); <span class="comment">// 复制任务</span></div><div class="line"> gulp.watch(watchLessPath, gulp.series(<span class="string">"less"</span>)); <span class="comment">// less处理</span></div><div class="line"> gulp.watch(imgPath, gulp.series(<span class="string">"imgChange"</span>)); <span class="comment">// 图片处理</span></div><div class="line"> watcher.on(<span class="string">"change"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>{</div><div class="line"> <span class="comment">// 删除的时候,也更新删除任务到目标文件夹</span></div><div class="line"> <span class="keyword">if</span> (event.type === <span class="string">"deleted"</span>) {</div><div class="line"> <span class="keyword">var</span> filepath = event.path;</div><div class="line"> <span class="keyword">var</span> filePathFromSrc = path.relative(path.resolve(<span class="string">"src"</span>), filepath);</div><div class="line"> <span class="comment">// Concatenating the 'build' absolute path used by gulp.dest in the scripts task</span></div><div class="line"> <span class="keyword">var</span> destFilePath = path.resolve(<span class="string">"dist"</span>, filePathFromSrc);</div><div class="line"> del.sync(destFilePath);</div><div class="line"> }</div><div class="line"> });</div><div class="line">});</div></pre></td></tr></table></figure><h3 id="最终任务"><a href="#最终任务" class="headerlink" title="最终任务"></a>最终任务</h3><p>最后,我们需要把这些任务一个个拼起来,最终对外输出两种:<code>dev</code>和<code>build</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></pre></td><td class="code"><pre><div class="line"><span class="comment">// dev && watch</span></div><div class="line">gulp.task(</div><div class="line"> <span class="string">"default"</span>,</div><div class="line"> gulp.series(</div><div class="line"> <span class="comment">// sync</span></div><div class="line"> gulp.parallel(<span class="string">"copy"</span>, <span class="string">"img"</span>, <span class="string">"less"</span>, <span class="string">"tsCompile"</span>),</div><div class="line"> <span class="string">"watch"</span></div><div class="line"> )</div><div class="line">);</div><div class="line"></div><div class="line"><span class="comment">// build</span></div><div class="line">gulp.task(</div><div class="line"> <span class="string">"build"</span>,</div><div class="line"> gulp.series( <span class="comment">// 串行任务</span></div><div class="line"> <span class="comment">// sync</span></div><div class="line"> <span class="string">"clear"</span>,</div><div class="line"> gulp.parallel( <span class="comment">// 并行任务</span></div><div class="line"> <span class="comment">// async</span></div><div class="line"> <span class="string">"copy"</span>,</div><div class="line"> <span class="string">"img"</span>,</div><div class="line"> <span class="string">"less"</span>,</div><div class="line"> <span class="string">"tsCompile"</span></div><div class="line"> )</div><div class="line"> )</div><div class="line">);</div></pre></td></tr></table></figure><h3 id="项目目录结构"><a href="#项目目录结构" class="headerlink" title="项目目录结构"></a>项目目录结构</h3><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></pre></td><td class="code"><pre><div class="line">├─dist //编译之后的项目文件(带 sorcemap,支持生产环境告警定位)</div><div class="line">├─src //开发目录</div><div class="line">│ │ app.ts //小程序起始文件</div><div class="line">│ │ app.json</div><div class="line">│ │ app.less</div><div class="line">│ │</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">│ ├─utils //工具库</div><div class="line">│ ├─config //配置文档</div><div class="line">│ ├─pages //小程序相关页面</div><div class="line">│</div><div class="line">│ project.config.json //小程序配置文件</div><div class="line">│ gulpfile.js //工具配置</div><div class="line">│ package.json //项目配置</div><div class="line">│ README.<span class="built_in">md</span> //项目说明</div><div class="line">│ tsconfig.json //typescript配置</div><div class="line">│ tslint.json //代码风格配置</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><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><hr><p>其实小程序也有人出了框架,像 mpvue 和 wepy,开发风格类似 Vue。<br>不过个人的想法不一样,小程序开发和浏览器开发不一样,小程序官方的 API 会一直不停地进化和完善。如果再使用二次封装的框架,框架是否能跟上小程序 API 的更新节奏,二次封装带来更多的学习成本,这些都需要考虑的。或许有一天,框架的能力优势,最终会被小程序自身取代呢。<br>而简单的构建任务,却可以很棒地使用到 ES6/ES7、Less、Typescript 这些好用的语法和工具呢。</p>]]></content>
<summary type="html">
<p>虽然 webpack 用的比较多,不过在小程序这种场景下,简单的 gulp 也是个不错的选择吧~<br>
</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>小程序的奇技淫巧之 watch 观察属性</title>
<link href="https://godbasin.github.io/2018/12/26/wxapp-watch/"/>
<id>https://godbasin.github.io/2018/12/26/wxapp-watch/</id>
<published>2018-12-26T15:45:10.000Z</published>
<updated>2018-12-26T15:46:14.638Z</updated>
<content type="html"><![CDATA[<p>上一节我们介绍了小程序的 computed 计算属性,这次我们来讲讲 watch 观察属性叭~<br><a id="more"></a></p><h2 id="watch-观察属性使用"><a href="#watch-观察属性使用" class="headerlink" title="watch 观察属性使用"></a>watch 观察属性使用</h2><hr><p>这里我们直接先讲解下怎么用哇。</p><h3 id="安装依赖"><a href="#安装依赖" class="headerlink" title="安装依赖"></a>安装依赖</h3><p>整个实现的依赖包,放在 Github 上,大家可以去翻看和点星星:<a href="https://github.com/godbasin/watch-behavior" target="_blank" rel="external">watch-behavior</a>。</p><ul><li>安装<code>watch</code>:</li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">npm <span class="keyword">install</span> <span class="comment">--save miniprogram-watch</span></div></pre></td></tr></table></figure><blockquote><p>使用 behavior 需要依赖小程序基础库 2.2.3 以上版本,同时依赖开发者工具的 npm 构建。具体详情可查阅<a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/npm.html" target="_blank" rel="external">官方 npm 文档</a>。</p></blockquote><h3 id="在-Component-中使用"><a href="#在-Component-中使用" class="headerlink" title="在 Component 中使用"></a>在 Component 中使用</h3><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></pre></td><td class="code"><pre><div class="line"><span class="comment">// 需要开发者工具 npm 依赖</span></div><div class="line"><span class="keyword">const</span> watchBehavior = <span class="built_in">require</span>(<span class="string">"miniprogram-watch"</span>);</div><div class="line"></div><div class="line">Component({</div><div class="line"> <span class="attr">behaviors</span>: [watchBehavior],</div><div class="line"> <span class="attr">properties</span>: {</div><div class="line"> <span class="attr">propA</span>: {</div><div class="line"> <span class="attr">type</span>: <span class="built_in">Number</span>,</div><div class="line"> <span class="attr">value</span>: <span class="number">0</span></div><div class="line"> }</div><div class="line"> },</div><div class="line"> <span class="attr">data</span>: {</div><div class="line"> <span class="attr">a</span>: <span class="number">0</span>,</div><div class="line"> <span class="attr">b</span>: {</div><div class="line"> <span class="attr">c</span>: {</div><div class="line"> <span class="attr">d</span>: <span class="number">33</span></div><div class="line"> },</div><div class="line"> <span class="attr">e</span>: [<span class="number">1</span>, <span class="number">2</span>, [<span class="number">3</span>, <span class="number">4</span>]]</div><div class="line"> }</div><div class="line"> },</div><div class="line"> <span class="comment">// 可以将需要监听的数据放入 watch 里面,当数据改变时推送相应的订阅事件</span></div><div class="line"> <span class="comment">// 支持 data 以及 properties 的监听</span></div><div class="line"> watch: {</div><div class="line"> propA(val, oldVal) {</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"propA new: %s, old: %s"</span>, val, oldVal);</div><div class="line"> },</div><div class="line"> a(val, oldVal) {</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"a new: %s, old: %s"</span>, val, oldVal);</div><div class="line"> },</div><div class="line"> <span class="string">"b.c.d"</span>: <span class="function"><span class="keyword">function</span>(<span class="params">val, oldVal</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"b.c.d new: %s, old: %s"</span>, val, oldVal);</div><div class="line"> },</div><div class="line"> <span class="string">"b.e[2][0]"</span>: <span class="function"><span class="keyword">function</span>(<span class="params">val, oldVal</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"b.e[2][0] new: %s, old: %s"</span>, val, oldVal);</div><div class="line"> },</div><div class="line"> <span class="string">"b.e[3][4]"</span>: <span class="function"><span class="keyword">function</span>(<span class="params">val, oldVal</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"b.e[3][4] new: %s, old: %s"</span>, val, oldVal);</div><div class="line"> }</div><div class="line"> },</div><div class="line"> <span class="attr">methods</span>: {</div><div class="line"> onTap() {</div><div class="line"> <span class="keyword">this</span>.setData({</div><div class="line"> <span class="attr">a</span>: <span class="number">2</span>,</div><div class="line"> <span class="string">"b.c.d"</span>: <span class="number">3</span>,</div><div class="line"> <span class="string">"b.e[2][0]"</span>: <span class="number">444</span>,</div><div class="line"> <span class="attr">c</span>: <span class="number">123</span></div><div class="line"> });</div><div class="line"> <span class="comment">// 不在 data 里面的数据项不会放入观察者列表,比如这里的'b.e[3][4]'</span></div><div class="line"> }</div><div class="line"> }</div><div class="line">});</div></pre></td></tr></table></figure><h3 id="在-Page-中使用"><a href="#在-Page-中使用" class="headerlink" title="在 Page 中使用"></a>在 Page 中使用</h3><p><strong><code>Component</code>是<code>Page</code>的超集,因此可以使用<code>Component</code>构造器构造页面。</strong></p><p><a href="https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/component.html" target="_blank" rel="external">官方文档</a>:</p><blockquote><p>事实上,小程序的页面也可以视为自定义组件。因而,页面也可以使用<code>Component</code>构造器构造,拥有与普通组件一样的定义段与实例方法。但此时要求对应<code>json</code>文件中包含<code>usingComponents</code>定义段。</p></blockquote><ul><li><code>page.json</code></li></ul><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></pre></td><td class="code"><pre><div class="line">{</div><div class="line"> <span class="attr">"usingComponents"</span>: {}</div><div class="line">}</div></pre></td></tr></table></figure><ul><li><code>page.js</code></li></ul><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></pre></td><td class="code"><pre><div class="line"><span class="comment">// 这里我们就可以使用 Component 代替 Page</span></div><div class="line">Component({</div><div class="line"> <span class="attr">data</span>: {</div><div class="line"> <span class="attr">a</span>: <span class="number">0</span>,</div><div class="line"> <span class="attr">b</span>: {</div><div class="line"> <span class="attr">c</span>: {</div><div class="line"> <span class="attr">d</span>: <span class="number">33</span></div><div class="line"> },</div><div class="line"> <span class="attr">e</span>: [<span class="number">1</span>, <span class="number">2</span>, [<span class="number">3</span>, <span class="number">4</span>]]</div><div class="line"> }</div><div class="line"> },</div><div class="line"> <span class="comment">// 可以将需要监听的数据放入 watch 里面,当数据改变时推送相应的订阅事件</span></div><div class="line"> watch: {</div><div class="line"> a(val, oldVal) {</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"a new: %s, old: %s"</span>, val, oldVal);</div><div class="line"> },</div><div class="line"> <span class="string">"b.c.d"</span>: <span class="function"><span class="keyword">function</span>(<span class="params">val, oldVal</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"b.c.d new: %s, old: %s"</span>, val, oldVal);</div><div class="line"> },</div><div class="line"> <span class="string">"b.e[2][0]"</span>: <span class="function"><span class="keyword">function</span>(<span class="params">val, oldVal</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"b.e[2][0] new: %s, old: %s"</span>, val, oldVal);</div><div class="line"> },</div><div class="line"> <span class="string">"b.e[3][4]"</span>: <span class="function"><span class="keyword">function</span>(<span class="params">val, oldVal</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"b.e[3][4] new: %s, old: %s"</span>, val, oldVal);</div><div class="line"> }</div><div class="line"> },</div><div class="line"> <span class="attr">methods</span>: {</div><div class="line"> <span class="comment">// 页面的生命周期方法(即`on`开头的方法,如上面的`onLoad`),应写在`methods`定义段中。</span></div><div class="line"> onLoad() {</div><div class="line"> <span class="comment">// 如访问页面`/pages/index/index?paramA=123&paramB=xyz`,如果声明有属性(`properties`)`paramA`或`paramB`,则它们会被赋值为`123`或`xyz`</span></div><div class="line"> <span class="keyword">this</span>.data.paramA <span class="comment">// 页面参数 paramA 的值</span></div><div class="line"> <span class="keyword">this</span>.data.paramB <span class="comment">// 页面参数 paramB 的值</span></div><div class="line"> }</div><div class="line"> onTap() {</div><div class="line"> <span class="keyword">this</span>.setData({</div><div class="line"> <span class="attr">a</span>: <span class="number">2</span>,</div><div class="line"> <span class="string">"b.c.d"</span>: <span class="number">3</span>,</div><div class="line"> <span class="string">"b.e[2][0]"</span>: <span class="number">444</span>,</div><div class="line"> <span class="attr">c</span>: <span class="number">123</span></div><div class="line"> });</div><div class="line"> <span class="comment">// 不在 data 里面的数据项不会放入观察者列表,比如这里的'b.e[3][4]'</span></div><div class="line"> }</div><div class="line"> }</div><div class="line">});</div></pre></td></tr></table></figure><p>更多的,也可以参考本人的<a href="https://github.com/godbasin/wxapp-typescript-demo" target="_blank" rel="external">wxapp-typescript-demo</a>中的<a href="https://github.com/godbasin/wxapp-typescript-demo/tree/master/src/pages/watch" target="_blank" rel="external">watch page</a>,后续也会持续更新方便好用的能力 demo。</p><h2 id="watch-观察属性实现"><a href="#watch-观察属性实现" class="headerlink" title="watch 观察属性实现"></a>watch 观察属性实现</h2><hr><p>自定义组件中<code>computed</code>计算属性的实现,由<a href="https://github.com/wechat-miniprogram/computed" target="_blank" rel="external">官方</a>提供的。上一篇<a href="https://godbasin.github.io/2018/12/23/wxapp-computed/">《小程序的奇技淫巧之 computed 计算属性》</a>中,也有讲解大致思路和使用方法。</p><p>现在,轮到我们自己来实现一个<code>watch</code>观察属性了。</p><p>这里的实现主要也是针对自定义组件中的<a href="https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/behaviors.html" target="_blank" rel="external"><code>behaviors</code></a>。上一篇已经讲过,这里就不再复述啦。</p><h3 id="watch-触发机制"><a href="#watch-触发机制" class="headerlink" title="watch 触发机制"></a>watch 触发机制</h3><p>其实<code>watch</code>的触发机制,基本都在<code>setData</code>的时候触发。而在自定义组件里,会有两种情况需要需要触发对应的<code>watch</code>监听:</p><ul><li><code>properties</code>属性变化时</li><li><code>data</code>属性变化时(调用<code>setData</code>)</li></ul><h3 id="watch-监听更新机制"><a href="#watch-监听更新机制" class="headerlink" title="watch 监听更新机制"></a>watch 监听更新机制</h3><p>既然<code>properties</code>和<code>data</code>都需要监听,我们来整理下逻辑。大致流程如下:</p><ol><li>在组件初始化的时候,将对应的<code>watch</code>路径加进观察队列<code>observers</code>。</li><li>在<code>properties</code>和<code>data</code>属性变更时,触发更新。<ul><li><code>properties</code>可根据<code>observer</code>触发更新</li><li><code>data</code>可根据<code>setData</code>触发更新</li></ul></li><li>更新时,先对比变更路径,然后根据路径是否匹配(即<code>observers</code>是否存在对应观察者),来确定是否需要通知相应的观察者。</li><li>确定存在变更路径,则对比新数据与旧数据是否一致,一致则拦截不做通知。</li><li>因为<code>watch</code>可能存在循环触发更新,对一次更新的最大通知次数做限制(这里限制5次)。</li></ol><p>具体的实现可以在<a href="https://github.com/godbasin/watch-behavior" target="_blank" rel="external">watch</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>Npm 包的发布,是一件简单也挺复杂的事情。简单的话,你可以发布一个单文件,不带任何构建等。复杂的时候,你需要写好demo、test、构建环境等等。<br>有些时候,自己亲自参与做一下,会让你更容易理解,这也是为什么我们偶尔需要造点轮子的原因。</p>]]></content>
<summary type="html">
<p>上一节我们介绍了小程序的 computed 计算属性,这次我们来讲讲 watch 观察属性叭~<br>
</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>小程序的奇技淫巧之 computed 计算属性</title>
<link href="https://godbasin.github.io/2018/12/23/wxapp-computed/"/>
<id>https://godbasin.github.io/2018/12/23/wxapp-computed/</id>
<published>2018-12-23T09:11:39.000Z</published>
<updated>2018-12-23T09:11:54.451Z</updated>
<content type="html"><![CDATA[<p>小程序的出身,基于安全和管控的考虑,使用了双线程的设计,同时对于 DOM 操作、动态创建 DOM 这些都隔离了。在写代码的时候,模版语法不支持函数计算等,computed 的方法就显得十分重要了。<br><a id="more"></a></p><h2 id="自定义组件"><a href="#自定义组件" class="headerlink" title="自定义组件"></a>自定义组件</h2><hr><p>小程序的自定义组件涉及功能很多,这篇只针对<code>computed</code>展开来讲。</p><p><code>computed</code>比较适合较复杂逻辑的计算,同时在小程序无法在模板里使用<code>methods</code>这样的场景下,计算属性的需求就更强烈了。</p><h3 id="behaviors"><a href="#behaviors" class="headerlink" title="behaviors"></a>behaviors</h3><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>的能力。</p><p>如果说,我们能“混入”<code>Component</code>,其实基本很多能力都能实现啦。其实我们自己封装一层的<code>MyComponent</code>也能达到一定的效果,但是这样的拓展性会变得很糟。</p><p>通过<code>behaviors</code>的方式,每个组件可以按需引入自己需要的<code>behavior</code>啦。</p><h3 id="computed-实现"><a href="#computed-实现" class="headerlink" title="computed 实现"></a>computed 实现</h3><p>我们来梳理下这里的逻辑,我们需要一个<code>computed</code>能力,需要处理的主要是:<code>setData</code>的时候,根据<code>computed</code>来计算哪些数据需要处理。</p><p>所以我们要做的是:</p><ol><li>记下来需要<code>computed</code>的变量。</li><li>在每次<code>setData</code>之前,看看是否包含到需要<code>computed</code>的变量,匹配到了就进行<code>computed</code>处理。</li><li>使用处理后的数据,进行<code>setData</code>。</li></ol><p>官方已经提供了<a href="https://github.com/wechat-miniprogram/computed" target="_blank" rel="external">计算属性实现的behavior</a>,大家也可以尽情翻看实现的<a href="https://github.com/wechat-miniprogram/computed/blob/master/src/index.js" target="_blank" rel="external">源码</a>,和使用这种拓展能力。</p><h2 id="Page-的超集"><a href="#Page-的超集" class="headerlink" title="Page 的超集"></a>Page 的超集</h2><hr><h3 id="hack-实现-Page-computed-能力"><a href="#hack-实现-Page-computed-能力" class="headerlink" title="hack 实现 Page computed 能力"></a>hack 实现 Page computed 能力</h3><p>想必大家都会有疑惑,<code>Component</code>里支持<code>behaviors</code>,但是<code>Page</code>依然写起来很不方便呀。虽然所有的<code>Page</code>最终也能通过<code>Component</code>来实现,但是这样是否需要多包装一层呢?</p><p>答案是不用。</p><h3 id="使用-Component-构造器构造页面"><a href="#使用-Component-构造器构造页面" class="headerlink" title="使用 Component 构造器构造页面"></a>使用 Component 构造器构造页面</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>:</p><blockquote><p>事实上,小程序的页面也可以视为自定义组件。因而,页面也可以使用<code>Component</code>构造器构造,拥有与普通组件一样的定义段与实例方法。但此时要求对应<code>json</code>文件中包含<code>usingComponents</code>定义段。</p></blockquote><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></pre></td><td class="code"><pre><div class="line">Page({</div><div class="line"> <span class="attr">data</span>: {</div><div class="line"> <span class="attr">logs</span>: []</div><div class="line"> },</div><div class="line"> onLoad(query) {</div><div class="line"> <span class="comment">// 如访问页面`/pages/index/index?paramA=123&paramB=xyz`,如果声明有属性(`properties`)`paramA`或`paramB`,则它们会被赋值为`123`或`xyz`</span></div><div class="line"> query.paramA <span class="comment">// 页面参数 paramA 的值</span></div><div class="line"> query.paramA <span class="comment">// 页面参数 paramB 的值</span></div><div class="line"> <span class="keyword">this</span>.setData({</div><div class="line"> <span class="attr">logs</span>: <span class="function">(<span class="params">wx.getStorageSync(<span class="string">"logs"</span></span>) || []).<span class="params">map</span>(<span class="params">(log: number</span>) =></span> {</div><div class="line"> <span class="keyword">return</span> formatTime(<span class="keyword">new</span> <span class="built_in">Date</span>(log));</div><div class="line"> })</div><div class="line"> });</div><div class="line"> }</div><div class="line">});</div></pre></td></tr></table></figure><p>可以这么写:</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></pre></td><td class="code"><pre><div class="line">{</div><div class="line"> <span class="attr">"usingComponents"</span>: {}</div><div class="line">}</div></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><div class="line">Component({</div><div class="line"> <span class="comment">// 组件的属性可以用于接收页面的参数</span></div><div class="line"> properties: {</div><div class="line"> <span class="attr">paramA</span>: <span class="built_in">Number</span>,</div><div class="line"> <span class="attr">paramB</span>: <span class="built_in">String</span>,</div><div class="line"> },</div><div class="line"> <span class="attr">data</span>: {</div><div class="line"> <span class="attr">logs</span>: []</div><div class="line"> },</div><div class="line"> <span class="attr">methods</span>: {</div><div class="line"> onLoad() {</div><div class="line"> <span class="comment">// 如访问页面`/pages/index/index?paramA=123&paramB=xyz`,如果声明有属性(`properties`)`paramA`或`paramB`,则它们会被赋值为`123`或`xyz`</span></div><div class="line"> <span class="keyword">this</span>.data.paramA <span class="comment">// 页面参数 paramA 的值</span></div><div class="line"> <span class="keyword">this</span>.data.paramB <span class="comment">// 页面参数 paramB 的值</span></div><div class="line"> <span class="keyword">this</span>.setData({</div><div class="line"> <span class="attr">logs</span>: <span class="function">(<span class="params">wx.getStorageSync(<span class="string">"logs"</span></span>) || []).<span class="params">map</span>(<span class="params">(log: number</span>) =></span> {</div><div class="line"> <span class="keyword">return</span> formatTime(<span class="keyword">new</span> <span class="built_in">Date</span>(log));</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>这样,我们就能愉快地使用<code>behaviors</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></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> computedBehavior = <span class="built_in">require</span>(<span class="string">"miniprogram-computed"</span>);</div><div class="line">Component({</div><div class="line"> <span class="attr">behaviors</span>: [computedBehavior],</div><div class="line"> <span class="attr">data</span>: {</div><div class="line"> <span class="attr">logs</span>: []</div><div class="line"> },</div><div class="line"> <span class="attr">computed</span>: {</div><div class="line"> logsAfterComputed() {</div><div class="line"> <span class="comment">// 计算属性同样挂在 data 上,每当进行 setData 的时候会重新计算</span></div><div class="line"> <span class="comment">// 比如此字段可以通过 this.data.b 获取到</span></div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.data.logs.map(<span class="function"><span class="params">x</span> =></span> {</div><div class="line"> <span class="keyword">return</span> {</div><div class="line"> <span class="attr">log</span>: x,</div><div class="line"> <span class="attr">logAfterCompute</span>: x + <span class="string">"logAfterCompute"</span></div><div class="line"> };</div><div class="line"> });</div><div class="line"> }</div><div class="line"> },</div><div class="line"> <span class="attr">methods</span>: {</div><div class="line"> onLoad() {</div><div class="line"> <span class="keyword">this</span>.setData({</div><div class="line"> <span class="attr">logs</span>: <span class="function">(<span class="params">wx.getStorageSync(<span class="string">"logs"</span></span>) || []).<span class="params">map</span>(<span class="params">(log: number</span>) =></span> {</div><div class="line"> <span class="keyword">return</span> formatTime(<span class="keyword">new</span> <span class="built_in">Date</span>(log));</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>使用<code>Component</code>构造器构造页面,需要注意:</p><ol><li>组件的属性可以用于接收页面的参数,如访问页面<code>/pages/index/index?paramA=123&paramB=xyz</code>,如果声明有属性(<code>properties</code>)<code>paramA</code>或<code>paramB</code>,则它们会被赋值为<code>123</code>或<code>xyz</code>。(可参考<a href="https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/component.html" target="_blank" rel="external">官方代码示例</a>)</li><li>页面的生命周期方法(即<code>on</code>开头的方法,如上面的<code>onLoad</code>),应写在<code>methods</code>定义段中。</li></ol><p>这样,你就能愉快地在代码里面使用<code>computed</code>计算属性啦~</p><p>更多的,也可以参考本人的<a href="https://github.com/godbasin/wxapp-typescript-demo" target="_blank" rel="external">wxapp-typescript-demo</a>中的<a href="https://github.com/godbasin/wxapp-typescript-demo/tree/master/src/pages/logs" target="_blank" rel="external">log page</a>,后续也会持续更新方便好用的能力 demo。</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>小程序提供的能力其实挺多的,但是很多时候由于文档很多、查找不方便,会导致我们有些很好用的功能没有发现,然后苦逼地一边吐槽一边悲壮地撸代码。<br>官方提供的目前只有<code>computed</code>,大家可以看看,是不是还可以做<code>watch</code>之类的能力呢?</p>]]></content>
<summary type="html">
<p>小程序的出身,基于安全和管控的考虑,使用了双线程的设计,同时对于 DOM 操作、动态创建 DOM 这些都隔离了。在写代码的时候,模版语法不支持函数计算等,computed 的方法就显得十分重要了。<br>
</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/2018/12/16/work-1-choice/"/>
<id>https://godbasin.github.io/2018/12/16/work-1-choice/</id>
<published>2018-12-16T06:10:54.000Z</published>
<updated>2018-12-16T06:11:00.006Z</updated>
<content type="html"><![CDATA[<p>工作中,并不只有技术,还有各式各样的“其他挑战”。关于这些事情,简单地谈谈吧。<br><a id="more"></a></p><h2 id="有关加班"><a href="#有关加班" class="headerlink" title="有关加班"></a>有关加班</h2><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><p>刚开始,我以为程序员这个职业,作为理工科的一份子,多是耿直真诚的直男。但有些时候并不是这样的,技术上拼不过的,演技可以补齐。</p><p>智商情商几乎都被说烂了,但工作里基本不会遇到拼智商的情况,而所谓的情商,大多数只剩下了你情我愿。</p><p>有人爱做试探性的行为,去试探身边人的底线。点到为止,不可能的。本该纯粹的技术工作,有人的地方,就有了利益冲突,便有了斗争。人越是多的地方,就越容易出现争执,也越来越多人跟随着利益既得者的步伐走。</p><p>好些人都跟我说,职场就是这样的,你也稍稍改变下,适应了就好了。</p><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20180109225558.jpg" alt="这个世界,就是这样子的啦"></p><p>我以为是说话方式的问题,也曾有一段时间,各种找人请教是否可以用更好的方式去表达。但有时候并不是这样的,和说话方式没有关系,你有再多的想法,也只能咽肚子里,把别人的想法做出来才是政治正确。</p><p>战争已经开始了,我说我只想粗茶淡饭认真搬砖,却被告之不允许,你必须带上你的面具参战。</p><h2 id="有关选择"><a href="#有关选择" class="headerlink" title="有关选择"></a>有关选择</h2><p>选择,这是我认为最重要的一点。工作和我的关系,是双向选择的关系,直到现在我还是这么坚持的。</p><p>找一份工作就像谈一场恋爱,没有谁对谁错,只是我们并不适合而已。</p><p>但很多人并不这么想的。拿人钱财替人消灾,很神奇的是,本该是你情我愿的事情,总有人爱戴上帽子,“你过来就是替我干活的,我说啥你做啥就好”、“我给了你一份工作,你应该懂得感恩”,这样奇奇怪怪的想法也都遇到过。</p><p>或许有些时候,我并不知道自己真正想要的是什么,但是我很清楚,我不想要什么。颠簸流离的日子不是没有经历过,而我唯一不想要的,就是活丢了自己。</p><p>很多人都爱说,“没办法,我也不想这样”,“老板要求这样,我没得选”。</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><p>我的工作和生活,界限分明。如果你想要关注非工作非技术状态的我,欢迎关注“爱我爱家爱天下”公众号(改名中,新名字“牧羊的猪”)。</p><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/qrcode_for_gh_0404f6ab1848_258.jpg" alt=""></p>]]></content>
<summary type="html">
<p>工作中,并不只有技术,还有各式各样的“其他挑战”。关于这些事情,简单地谈谈吧。<br>
</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>小程序上Typescript啦</title>
<link href="https://godbasin.github.io/2018/11/30/wxapp-typescript/"/>
<id>https://godbasin.github.io/2018/11/30/wxapp-typescript/</id>
<published>2018-11-30T14:36:46.000Z</published>
<updated>2018-11-30T14:41:45.901Z</updated>
<content type="html"><![CDATA[<p>[2018.11.14 A 新增 typescript 支持] 小程序开发工具静悄悄地更新了版本,添加上了对 Typescript 的支持。Typescript ??? Typescript !!!<br><a id="more"></a></p><h1 id="期待已久的-Typescript"><a href="#期待已久的-Typescript" class="headerlink" title="期待已久的 Typescript"></a>期待已久的 Typescript</h1><hr><h2 id="为什么要用-Typescript"><a href="#为什么要用-Typescript" class="headerlink" title="为什么要用 Typescript"></a>为什么要用 Typescript</h2><p>关于 Typescript,可以看看以前写过的这篇<a href="https://godbasin.github.io/2017/09/01/about-typescript/">《关于Typescript》</a>。文末的故事,便是大多数情况下 Typescript 能帮我们解决的痛点。</p><p>过了很久之后,想法还是一样:<strong>Typescript 这事情,当你管理大点的应用的时候,就会感受到它的好处了</strong>。尤其涉及团队配合的时候!</p><p>当然,如果你的项目比较小,或是写个小公(工)举(具)、小 demo 的时候, store 状态管理、typescript 编译这些,除非已经很熟悉、没有额外成本的时候,才勉强适合接入。离了具体场景谈架构,都是耍(xia)流(che)氓(dan)。</p><p>为什么要用 Typescript?</p><h3 id="变量类型不明确"><a href="#变量类型不明确" class="headerlink" title="变量类型不明确"></a>变量类型不明确</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></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> formGroups = <span class="keyword">this</span>.currentStep.formGroups; <span class="comment">// 猜猜我的 formGroups 是数组数组 [[], [], []],还是对象数组 [{}, {}, {}]?</span></div><div class="line"><span class="keyword">let</span> flattenFields = _.flatten(formGroups); <span class="comment">// 不用猜了,我用个 flatten 抹平,它就一定是对象 [{}, {}, {}] 了!</span></div><div class="line"></div><div class="line">flattenFields.forEach(<span class="function"><span class="params">item</span> =></span> {</div><div class="line"> <span class="keyword">if</span> (item.fields) { <span class="comment">// 猜猜我的 item.fields 是数组还是对象?</span></div><div class="line"> flattenFields.push(..._.values(item.fields)); <span class="comment">// 不用猜了,我用个 values 抹平,它就一定是对象了!</span></div><div class="line"> }</div><div class="line">});</div></pre></td></tr></table></figure><p>当我帮忙 debug 个问题的时候,打断点看到:<br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-typescript-1.png" alt="这好像是个数组?"><br><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-typescript-2.png" alt="这怎么变成个对象???"></p><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></pre></td><td class="code"><pre><div class="line"># 我和外包童鞋的对话:</div><div class="line">我:话说你这些到底是什么类型,从命名和上下文都看不出来。。</div><div class="line">我:得去翻更细的代码。。</div><div class="line"></div><div class="line">外包童鞋:values好像可以改一下试试</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">我:(刀.jpg)</div><div class="line">我:卧槽</div><div class="line">我:你这item.fields,有时候是数组,有时候是对象,这样真的好吗</div><div class="line">我:大哥</div><div class="line">我:(刀.jpg)* <span class="number">2</span></div></pre></td></tr></table></figure><p>或许有人想,即使上了 Typescript 也可能会被 any 打败啊。</p><p>什么?在我眼皮底下用 any?!!</p><h3 id="接口协议不符合"><a href="#接口协议不符合" class="headerlink" title="接口协议不符合"></a>接口协议不符合</h3><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></pre></td><td class="code"><pre><div class="line">前端:帮忙看看这个接口为什么返回失败了?</div><div class="line">后台:你这个接口字段少了啊,这个xxx</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>当然,这个案例里稍微夸张了一点,一般我们都会自己一个个对着协议检查哪里不对,但是很多时候被 bug 光环环绕的时候,你就是发现不了问题。</p><p>这个时候,我们就可以用 Typescript 来管理接口啦。</p><figure class="highlight ts"><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">interface</span> IDemoResponse {</div><div class="line"> date: <span class="built_in">string</span>;</div><div class="line"> someNumber: <span class="built_in">number</span>;</div><div class="line"> otherThing: <span class="built_in">any</span>;</div><div class="line">}</div></pre></td></tr></table></figure><p><strong>1. 使用约定的变量的时候,会有相关提示</strong>(请忽略我的强行any)。</p><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-typescript-10.jpg" alt="输入提示"></p><p><strong>2. 使用约定以外的属性时候,会报错提示。</strong></p><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-typescript-11.jpg" alt="错误提示"></p><p>除此以外,还有很多很棒的用法呢~</p><h3 id="一键调整协议"><a href="#一键调整协议" class="headerlink" title="一键调整协议"></a>一键调整协议</h3><p>前端和后台协议约定后,就开始各自开发了。但是,我们总会遇到各种各样的问题,可能导致我们的协议变更。</p><p>字段的变更什么的最讨厌了,例如后台要把某个接口下<code>date</code>改成<code>day</code>。一般来说前端是拒绝的,你不能说让我改我就得改,我得看看我写了多少代码,评估下工作量。</p><p>什么,全局替换?你知道使用<code>date</code>多普遍吗?万一我替换错了咋办??</p><p>这时候,如果你使用了 Typescript 并定义了协议接口的话,就很好办了~</p><p>依然是这段代码:</p><figure class="highlight ts"><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="keyword">interface</span> IDemoResponse {</div><div class="line"> date: <span class="built_in">string</span>;</div><div class="line"> someNumber: <span class="built_in">number</span>;</div><div class="line"> otherThing: <span class="built_in">any</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">const</span> demoResponse: IDemoResponse = {} as <span class="built_in">any</span>;</div><div class="line"><span class="keyword">const</span> date = demoResponse.date;</div></pre></td></tr></table></figure><p><strong>1. 选中需要重命名的属性。</strong></p><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-typescript-12.jpg" alt="选中需要重命名的属性"></p><p><strong>2. 按下F2,重新输入属性名。</strong></p><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-typescript-13.jpg" alt="选中需要重命名的属性"></p><p><strong>3. 按下回车,使用到的地方都会更新。</strong></p><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-typescript-14.jpg" alt="属性更新"></p><p>是不是很酷~~~</p><h3 id="跨过-Babel-直接使用-ES6-ES7,跨过-eslint-直接使用-prettier"><a href="#跨过-Babel-直接使用-ES6-ES7,跨过-eslint-直接使用-prettier" class="headerlink" title="跨过 Babel 直接使用 ES6/ES7,跨过 eslint 直接使用 prettier"></a>跨过 Babel 直接使用 ES6/ES7,跨过 eslint 直接使用 prettier</h3><p>其实小程序工具本身也支持了不少的 ES6 新语法,不过像<code>async/await</code>这种,则还是需要自己搞个 Babel 来编译。</p><p>现在直接上 Typescript,连 Babel 都可以直接跳过啦。</p><p><strong>Prettier</strong></p><p>这里重点推荐 <a href="https://prettier.io/" target="_blank" rel="external">prettier</a> 神器,也是团队配合的好工具啊:</p><ul><li>项目代码没有配 eslint?导致每次拉下来的代码一大堆冲突?</li><li>团队成员使用不同的编辑器?有的没有自动格式化?导致拉下来代码还是一堆冲突?</li><li>用 standard?有些规范和实际项目不符合,但是偏偏没得改??</li></ul><p>偷偷地往项目里装个 Prettier,然后所有的矛盾都不见啦。不管你的代码格式多独特,最终在 Git commit 的时候,就被同化啦,而且 Prettier 的格式化也不会影响到 Git 记录。</p><h2 id="小程序与-Typescript"><a href="#小程序与-Typescript" class="headerlink" title="小程序与 Typescript"></a>小程序与 Typescript</h2><h3 id="Typescript-编译下就可以用?"><a href="#Typescript-编译下就可以用?" class="headerlink" title="Typescript 编译下就可以用?"></a>Typescript 编译下就可以用?</h3><p>其实小程序它最终运行的还是 Javascript,那不是我们直接自己编译下就好了吗?</p><p>少年你太天真了。咱们写 Typescript 最重要的是什么呀?是 Typing 库呀!</p><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-typescript-3.jpg" alt="没错,就是这货"></p><p>网上开源的关于小程序和 Typescript 的工具或者脚手架也一大堆,为啥不用呢?因为小程序的 API 在不断地变化呀~</p><p>有了官方的支持,即使小程序的 API 变了,我们也可以及时地更新呀(奸笑)~</p><h3 id="开箱即用的尝鲜"><a href="#开箱即用的尝鲜" class="headerlink" title="开箱即用的尝鲜"></a>开箱即用的尝鲜</h3><p>既然官方提供支持了,义不容辞地使用呀!</p><ol><li>首先,我们更新到最近的工具版本,然后创建项目就能看到了:</li></ol><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-typescript-4.jpg" alt="看到了没"></p><ol><li>创建模版,我们来看看代码长什么样子。</li></ol><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-typescript-5.jpg" alt="嗯,typing 在就足够了"></p><p>我们可以看到,在 package.json 里面多了俩脚本,其实也就是将 ts 文件原地编译,然后上传代码的时候忽略掉了。</p><ol><li>仔细瞧瞧代码。</li></ol><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-typescript-6.jpg" alt="emmmmm"></p><p>额,好像混入了一些奇怪的东西进去,感叹号是什么鬼???</p><p>后面问了下开发GG,是因为这里比较特殊,目前定义的文件暂时没法兼顾,等后面的版本会兼容。</p><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-typescript-7.jpg" alt="但是还是很棒"></p><p>终于用上 Typescript 啦,爽歪歪~</p><h3 id="调整下代码结构"><a href="#调整下代码结构" class="headerlink" title="调整下代码结构"></a>调整下代码结构</h3><p>小项目的话,其实也不用带什么编译啦。不过如果你还想用 less,也想用 typescript,还不想看到项目下面乱糟糟的文件:<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></pre></td><td class="code"><pre><div class="line">index.js</div><div class="line">index.ts</div><div class="line">index.json</div><div class="line">index.less</div><div class="line">index.wxss</div><div class="line">index.wxml</div></pre></td></tr></table></figure></p><p>我们就简单弄个 gulp,把编译加上吧~</p><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-typescript-8.jpg" alt="会长这样"></p><p>然后我们再把 prettier 愉快地加上。这里就不多讲解啦,大家也可以参考我的 demo 项目:<br><a href="https://github.com/godbasin/wxapp-typescript-demo" target="_blank" rel="external">wxapp-typescript-demo</a></p><p>对了,目前官方的 typing 库也不是非常完善,如果需要写组件、插件、小游戏的你,可能会面临一大堆的 any 冲击波噢~</p><p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-typescript-9.jpg" alt="emmmm"></p><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><ul><li><a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html" target="_blank" rel="external">小程序工具更新</a></li></ul><h2 id="结束语"><a href="#结束语" class="headerlink" title="结束语"></a>结束语</h2><hr><p>Typescript 的普及度其实不算高,小程序的确是又一次给到惊喜。反观下我们自己呢?有没有被业务代码冲得找不到方向呢?<br>很多时候,我们总爱说写业务没啥技术提升,但真的是这样吗?我看过很棒的业务代码,从框架设计到具体的实现,开发者都对自己做了很高的要求。而写技术需求代码的,就一定会写得很好吗?<br>“我们是业务部门,技术肯定比不上”<br>“项目很紧急,怎么快怎么来”<br>“随便找一些能用的就好了,不要浪费时间在这些上面”<br>…<br>以上这些话,我是不认同的。当然项目急的时候可以理解,事后一定要把欠下的债务给还了。(较真脸)</p>]]></content>
<summary type="html">
<p>[2018.11.14 A 新增 typescript 支持] 小程序开发工具静悄悄地更新了版本,添加上了对 Typescript 的支持。Typescript ??? Typescript !!!<br>
</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>